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.

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