From a772aba0127f65ac9ab5685a3a9ffcadc2247fbb Mon Sep 17 00:00:00 2001 From: jrhoffa Date: Thu, 3 Nov 2022 22:27:36 -0700 Subject: [PATCH] Add reboot command via MQTT --- main/device.cpp | 55 ++++++++++++++++++++++++++++++++++++------------- main/device.hpp | 6 +++--- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/main/device.cpp b/main/device.cpp index 6aafeef..a0ae804 100644 --- a/main/device.cpp +++ b/main/device.cpp @@ -8,9 +8,7 @@ static const char *TAG = "device"; Device::Device(std::string _id) : id(_id), - state_topic("light/" + id + "/state"), - cmd_topic("light/" + id + "/cmd"), - data_topic("light/" + id + "/data/+"), + topic_prefix("light/" + id), ready(false), should_publish(false), mutex(xSemaphoreCreateMutex()), sem(xSemaphoreCreateBinary()), @@ -44,11 +42,9 @@ void Device::on_mqtt_connect(esp_mqtt_client_handle_t client) { ESP_LOGI(TAG, "Connected to MQTT"); - if (esp_mqtt_client_subscribe(client, cmd_topic.c_str(), 0) < 0) { - ESP_LOGE(TAG, "Failed to subscribe to %s", cmd_topic.c_str()); - } - if (esp_mqtt_client_subscribe(client, data_topic.c_str(), 0) < 0) { - ESP_LOGE(TAG, "Failed to subscribe to %s", data_topic.c_str()); + std::string topic = topic_prefix + "/#"; + if (esp_mqtt_client_subscribe(client, topic.c_str(), 0) < 0) { + ESP_LOGE(TAG, "Failed to subscribe to %s", topic.c_str()); } // TODO: Re-announce when presets change @@ -58,8 +54,8 @@ void Device::on_mqtt_connect(esp_mqtt_client_handle_t client) { cJSON_AddStringToObject(device, "name", id.c_str()); cJSON *identifiers = cJSON_AddArrayToObject(device, "identifiers"); cJSON_AddItemToArray(identifiers, cJSON_CreateString(id.c_str())); - cJSON_AddStringToObject(json, "state_topic", state_topic.c_str()); - cJSON_AddStringToObject(json, "command_topic", cmd_topic.c_str()); + cJSON_AddStringToObject(json, "state_topic", (topic_prefix + "/state").c_str()); + cJSON_AddStringToObject(json, "command_topic", (topic_prefix + "/cmd").c_str()); cJSON_AddStringToObject(json, "schema", "json"); cJSON_AddBoolToObject(json, "effect", true); cJSON *effects = cJSON_AddArrayToObject(json, "effect_list"); @@ -86,10 +82,29 @@ void Device::on_mqtt_connect(esp_mqtt_client_handle_t client) { xSemaphoreGive(mutex); } +std::string Device::subtopic(const std::string &topic) { + auto slash_pos = topic.find('/', topic_prefix.length()); + if (slash_pos == std::string::npos) return ""; + auto another_slash_pos = topic.find('/', slash_pos + 1); + if (slash_pos == std::string::npos) { + // No further levels + return topic.substr(slash_pos + 1); + } + // First subtopic + return topic.substr(slash_pos + 1, another_slash_pos - (slash_pos + 1)); +} + void Device::on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t event) { std::string topic(event->topic, event->topic_len); - ESP_LOGI(TAG, "Received command via MQTT"); - if (topic == cmd_topic) { + std::string command = subtopic(topic); + + ESP_LOGI(TAG, "Received command via MQTT: %s", command.c_str()); + + if (command == "state") { + // This is from us, just ignore it + return; + + } else if (command == "cmd") { cJSON *json = cJSON_ParseWithLength(event->data, event->data_len); if (json) { set_json_config(json); @@ -97,8 +112,14 @@ void Device::on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t e } else { ESP_LOGE(TAG, "Invalid JSON data"); } + // Fall through & kick semaphore + + } else if (command == "reboot") { + ESP_LOGI(TAG, "Rebooting immediately"); + esp_restart(); + return; - } else /* data topic */ { + } else if (command == "data") { size_t slash = topic.rfind('/'); if (slash != std::string::npos) { write_file( @@ -106,7 +127,13 @@ void Device::on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t e event->data, event->data_len ); } + // Fall through & kick semaphore + + } else { + ESP_LOGE(TAG, "Unhandled command: %s (%d bytes)", command.c_str(), event->data_len); + return; } + xSemaphoreGive(sem); } @@ -123,7 +150,7 @@ void Device::publish_state_locked() { cJSON *json = make_json_config_locked(); char *config = cJSON_PrintUnformatted(json); ESP_ERROR_CHECK(esp_mqtt_client_publish( - client, state_topic.c_str(), config, 0, 0, 0 + client, (topic_prefix + "/state").c_str(), config, 0, 0, 0 )); cJSON_free(config); cJSON_Delete(json); diff --git a/main/device.hpp b/main/device.hpp index a40ba95..ef3404c 100644 --- a/main/device.hpp +++ b/main/device.hpp @@ -33,9 +33,7 @@ public: private: std::string id; - std::string state_topic; - std::string cmd_topic; - std::string data_topic; + std::string topic_prefix; bool ready, should_publish; @@ -59,4 +57,6 @@ private: void on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t); void publish_state_locked(); + + std::string subtopic(const std::string &topic); };