ESP32 Native version of Blinky, featureful controller code for WS2811/WS2812/NeoPixels
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 line
3.2KB

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