ESP32 Native version of Blinky, featureful controller code for WS2811/WS2812/NeoPixels
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.7KB

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