Fork of the espurna firmware for `mhsw` switches
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.

294 lines
7.8 KiB

  1. /*
  2. HOME ASSISTANT MODULE
  3. Copyright (C) 2017-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if HOMEASSISTANT_SUPPORT
  6. #include <ArduinoJson.h>
  7. bool _haEnabled = false;
  8. bool _haSendFlag = false;
  9. // -----------------------------------------------------------------------------
  10. // SENSORS
  11. // -----------------------------------------------------------------------------
  12. #if SENSOR_SUPPORT
  13. void _haSendMagnitude(unsigned char i, JsonObject& config) {
  14. if (_haEnabled) {
  15. unsigned char type = magnitudeType(i);
  16. config["name"] = getSetting("hostname") + String(" ") + magnitudeTopic(type);
  17. config["platform"] = "mqtt";
  18. config["device_class"] = "sensor";
  19. config["state_topic"] = mqttTopic(magnitudeTopicIndex(i).c_str(), false);
  20. config["unit_of_measurement"] = magnitudeUnits(type);
  21. }
  22. }
  23. void _haSendMagnitudes() {
  24. for (unsigned char i=0; i<magnitudeCount(); i++) {
  25. DynamicJsonBuffer jsonBuffer;
  26. JsonObject& config = jsonBuffer.createObject();
  27. _haSendMagnitude(i, config);
  28. String topic = getSetting("haPrefix", HOMEASSISTANT_PREFIX) +
  29. "/sensor/" +
  30. getSetting("hostname") + "_" + String(i) +
  31. "/config";
  32. String output;
  33. if (config.size() > 0) config.printTo(output);
  34. mqttSendRaw(topic.c_str(), output.c_str());
  35. mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
  36. }
  37. }
  38. #endif // SENSOR_SUPPORT
  39. // -----------------------------------------------------------------------------
  40. // SWITCHES & LIGHTS
  41. // -----------------------------------------------------------------------------
  42. void _haSendSwitch(unsigned char i, JsonObject& config, String &type) {
  43. if (_haEnabled) {
  44. String name = getSetting("hostname");
  45. if (relayCount() > 1) {
  46. name += String(" #") + String(i);
  47. }
  48. config["name"] = name;
  49. config["platform"] = "mqtt";
  50. if (relayCount()) {
  51. config["state_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, false);
  52. config["command_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, true);
  53. config["payload_on"] = String("1");
  54. config["payload_off"] = String("0");
  55. config["availability_topic"] = mqttTopic(MQTT_TOPIC_STATUS, false);
  56. config["payload_available"] = String("1");
  57. config["payload_not_available"] = String("0");
  58. }
  59. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  60. if (i == 0) {
  61. if (lightHasColor()) {
  62. config["brightness_state_topic"] = mqttTopic(MQTT_TOPIC_BRIGHTNESS, false);
  63. config["brightness_command_topic"] = mqttTopic(MQTT_TOPIC_BRIGHTNESS, true);
  64. config["rgb_state_topic"] = mqttTopic(MQTT_TOPIC_COLOR_RGB, false);
  65. config["rgb_command_topic"] = mqttTopic(MQTT_TOPIC_COLOR_RGB, true);
  66. config["color_temp_command_topic"] = mqttTopic(MQTT_TOPIC_MIRED, true);
  67. }
  68. if (lightChannels() > 3) {
  69. config["white_value_state_topic"] = mqttTopic(MQTT_TOPIC_CHANNEL, 3, false);
  70. config["white_value_command_topic"] = mqttTopic(MQTT_TOPIC_CHANNEL, 3, true);
  71. }
  72. }
  73. #endif // LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  74. }
  75. #if (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE) || (defined(ITEAD_SLAMPHER))
  76. type = String("light");
  77. #else
  78. type = String("switch");
  79. #endif
  80. }
  81. void _haSendSwitches() {
  82. for (unsigned char i=0; i<relayCount(); i++) {
  83. DynamicJsonBuffer jsonBuffer;
  84. JsonObject& config = jsonBuffer.createObject();
  85. String type;
  86. _haSendSwitch(i, config, type);
  87. String topic = getSetting("haPrefix", HOMEASSISTANT_PREFIX) +
  88. "/" + type +
  89. "/" + getSetting("hostname") + "_" + String(i) +
  90. "/config";
  91. String output;
  92. if (config.size() > 0) config.printTo(output);
  93. mqttSendRaw(topic.c_str(), output.c_str());
  94. mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
  95. }
  96. }
  97. // -----------------------------------------------------------------------------
  98. String _haGetConfig() {
  99. String output;
  100. for (unsigned char i=0; i<relayCount(); i++) {
  101. DynamicJsonBuffer jsonBuffer;
  102. JsonObject& config = jsonBuffer.createObject();
  103. String type;
  104. _haSendSwitch(i, config, type);
  105. output += "\n" + type + ":\n";
  106. bool first = true;
  107. for (auto kv : config) {
  108. if (first) {
  109. output += " - ";
  110. first = false;
  111. } else {
  112. output += " ";
  113. }
  114. output += kv.key + String(": ") + kv.value.as<String>() + String("\n");
  115. }
  116. output += "\n";
  117. }
  118. #if SENSOR_SUPPORT
  119. for (unsigned char i=0; i<magnitudeCount(); i++) {
  120. DynamicJsonBuffer jsonBuffer;
  121. JsonObject& config = jsonBuffer.createObject();
  122. _haSendMagnitude(i, config);
  123. output += "\nsensor:\n";
  124. bool first = true;
  125. for (auto kv : config) {
  126. if (first) {
  127. output += " - ";
  128. first = false;
  129. } else {
  130. output += " ";
  131. }
  132. output += kv.key + String(": ") + kv.value.as<String>() + String("\n");
  133. }
  134. output += "\n";
  135. }
  136. #endif
  137. return output;
  138. }
  139. void _haSend() {
  140. // Pending message to send?
  141. if (!_haSendFlag) return;
  142. // Are we connected?
  143. if (!mqttConnected()) return;
  144. DEBUG_MSG_P(PSTR("[HA] Sending autodiscovery MQTT message\n"));
  145. // Send messages
  146. _haSendSwitches();
  147. #if SENSOR_SUPPORT
  148. _haSendMagnitudes();
  149. #endif
  150. _haSendFlag = false;
  151. }
  152. void _haConfigure() {
  153. bool enabled = getSetting("haEnabled", HOMEASSISTANT_ENABLED).toInt() == 1;
  154. _haSendFlag = (enabled != _haEnabled);
  155. _haEnabled = enabled;
  156. _haSend();
  157. }
  158. #if WEB_SUPPORT
  159. void _haWebSocketOnSend(JsonObject& root) {
  160. root["haVisible"] = 1;
  161. root["haPrefix"] = getSetting("haPrefix", HOMEASSISTANT_PREFIX);
  162. root["haEnabled"] = getSetting("haEnabled", HOMEASSISTANT_ENABLED).toInt() == 1;
  163. }
  164. void _haWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  165. if (strcmp(action, "haconfig") == 0) {
  166. String output = _haGetConfig();
  167. output.replace(" ", "&nbsp;");
  168. output.replace("\n", "<br />");
  169. output = String("{\"haConfig\": \"") + output + String("\"}");
  170. wsSend(client_id, output.c_str());
  171. }
  172. }
  173. #endif
  174. #if TERMINAL_SUPPORT
  175. void _haInitCommands() {
  176. settingsRegisterCommand(F("HA.CONFIG"), [](Embedis* e) {
  177. DEBUG_MSG(_haGetConfig().c_str());
  178. DEBUG_MSG_P(PSTR("+OK\n"));
  179. });
  180. settingsRegisterCommand(F("HA.SEND"), [](Embedis* e) {
  181. setSetting("haEnabled", "1");
  182. _haConfigure();
  183. wsSend(_haWebSocketOnSend);
  184. DEBUG_MSG_P(PSTR("+OK\n"));
  185. });
  186. settingsRegisterCommand(F("HA.CLEAR"), [](Embedis* e) {
  187. setSetting("haEnabled", "0");
  188. _haConfigure();
  189. wsSend(_haWebSocketOnSend);
  190. DEBUG_MSG_P(PSTR("+OK\n"));
  191. });
  192. }
  193. #endif
  194. // -----------------------------------------------------------------------------
  195. void haSetup() {
  196. _haConfigure();
  197. #if WEB_SUPPORT
  198. wsOnSendRegister(_haWebSocketOnSend);
  199. wsOnAfterParseRegister(_haConfigure);
  200. wsOnActionRegister(_haWebSocketOnAction);
  201. #endif
  202. // On MQTT connect check if we have something to send
  203. mqttRegister([](unsigned int type, const char * topic, const char * payload) {
  204. if (type == MQTT_CONNECT_EVENT) _haSend();
  205. });
  206. #if TERMINAL_SUPPORT
  207. _haInitCommands();
  208. #endif
  209. }
  210. #endif // HOMEASSISTANT_SUPPORT