ESP32 Native version of Blinky, featureful controller code for WS2811/WS2812/NeoPixels
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

132 行
4.4KB

  1. static const char *TAG = "device";
  2. #include <esp_log.h>
  3. #include "device.hpp"
  4. #include "presets.hpp"
  5. #include "utils.hpp"
  6. Device::Device(std::string _id) :
  7. id(_id),
  8. state_topic("light/" + id + "/state"),
  9. cmd_topic("light/" + id + "/cmd"),
  10. data_topic("light/" + id + "/data/+"),
  11. ready(false), should_publish(false),
  12. mutex(xSemaphoreCreateMutex()),
  13. sem(xSemaphoreCreateBinary()),
  14. power_on(false), strip_length(0),
  15. client(start_mqtt_client(on_mqtt_connect, on_mqtt_message, this))
  16. {}
  17. void Device::set_json_config(const cJSON *json) {
  18. xSemaphoreTake(mutex, portMAX_DELAY);
  19. const cJSON *state = cJSON_GetObjectItem(json, "state");
  20. if (state && cJSON_IsString(state)) {
  21. power_on = strcmp(state->valuestring, "ON") == 0;
  22. }
  23. const cJSON *fx = cJSON_GetObjectItem(json, "effect");
  24. if (fx && cJSON_IsString(fx)) {
  25. effect = fx->valuestring;
  26. }
  27. const cJSON *length = cJSON_GetObjectItem(json, "length");
  28. if (length && cJSON_IsNumber(length)) {
  29. strip_length = length->valuedouble;
  30. }
  31. publish_state_locked();
  32. xSemaphoreGive(mutex);
  33. }
  34. void Device::on_mqtt_connect(esp_mqtt_client_handle_t client) {
  35. xSemaphoreTake(mutex, portMAX_DELAY);
  36. ESP_LOGI(TAG, "Connected to MQTT");
  37. if (esp_mqtt_client_subscribe(client, cmd_topic.c_str(), 0) < 0) {
  38. ESP_LOGE(TAG, "Failed to subscribe to %s", cmd_topic.c_str());
  39. }
  40. if (esp_mqtt_client_subscribe(client, data_topic.c_str(), 0) < 0) {
  41. ESP_LOGE(TAG, "Failed to subscribe to %s", data_topic.c_str());
  42. }
  43. // TODO: Re-announce when presets change
  44. cJSON *json = cJSON_CreateObject();
  45. cJSON_AddStringToObject(json, "unique_id", id.c_str());
  46. cJSON *device = cJSON_AddObjectToObject(json, "device");
  47. cJSON_AddStringToObject(device, "name", id.c_str());
  48. cJSON *identifiers = cJSON_AddArrayToObject(device, "identifiers");
  49. cJSON_AddItemToArray(identifiers, cJSON_CreateString(id.c_str()));
  50. cJSON_AddStringToObject(json, "state_topic", state_topic.c_str());
  51. cJSON_AddStringToObject(json, "command_topic", cmd_topic.c_str());
  52. cJSON_AddStringToObject(json, "schema", "json");
  53. cJSON_AddBoolToObject(json, "effect", true);
  54. cJSON *effects = cJSON_AddArrayToObject(json, "effect_list");
  55. for (auto iter = Presets::map_begin(); iter != Presets::map_end(); ++iter)
  56. cJSON_AddItemToArray(effects, cJSON_CreateString(iter->first.c_str()));
  57. char *config = cJSON_PrintUnformatted(json);
  58. if (0 > esp_mqtt_client_publish(
  59. client,
  60. ("homeassistant/light/" + id + "/config").c_str(),
  61. config, 0,
  62. 0, 0
  63. )) {
  64. ESP_LOGE(TAG, "Failed to publish discovery message");
  65. }
  66. cJSON_free(config);
  67. cJSON_Delete(json);
  68. ready = true;
  69. if (should_publish) publish_state_locked();
  70. xSemaphoreGive(mutex);
  71. }
  72. void Device::on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t event) {
  73. std::string topic(event->topic, event->topic_len);
  74. ESP_LOGI(TAG, "Received command via MQTT");
  75. if (topic == cmd_topic) {
  76. cJSON *json = cJSON_ParseWithLength(event->data, event->data_len);
  77. if (json) {
  78. set_json_config(json);
  79. cJSON_Delete(json);
  80. } else {
  81. ESP_LOGE(TAG, "Invalid JSON data");
  82. }
  83. } else /* data topic */ {
  84. size_t slash = topic.rfind('/');
  85. if (slash != std::string::npos) {
  86. write_file(
  87. ("/spiffs/" + topic.substr(slash + 1) + ".json").c_str(),
  88. event->data, event->data_len
  89. );
  90. }
  91. }
  92. xSemaphoreGive(sem);
  93. }
  94. cJSON* Device::make_json_config_locked() const {
  95. cJSON *json = cJSON_CreateObject();
  96. cJSON_AddStringToObject(json, "state", power_on ? "ON" : "OFF");
  97. cJSON_AddStringToObject(json, "effect", effect.c_str());
  98. cJSON_AddNumberToObject(json, "length", strip_length);
  99. return json;
  100. }
  101. void Device::publish_state_locked() {
  102. if (!(should_publish = !ready)) {
  103. cJSON *json = make_json_config_locked();
  104. char *config = cJSON_PrintUnformatted(json);
  105. ESP_ERROR_CHECK(esp_mqtt_client_publish(
  106. client, state_topic.c_str(), config, 0, 0, 0
  107. ));
  108. cJSON_free(config);
  109. cJSON_Delete(json);
  110. }
  111. }