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.

307 lines
8.2 KiB

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