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.

280 lines
8.9 KiB

6 years ago
6 years ago
  1. /*
  2. DOMOTICZ MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if DOMOTICZ_SUPPORT
  6. #include <ArduinoJson.h>
  7. bool _dcz_enabled = false;
  8. std::vector<bool> _dcz_relay_state;
  9. //------------------------------------------------------------------------------
  10. // Private methods
  11. //------------------------------------------------------------------------------
  12. unsigned char _domoticzRelay(unsigned int idx) {
  13. for (unsigned char relayID=0; relayID<relayCount(); relayID++) {
  14. if (domoticzIdx(relayID) == idx) {
  15. return relayID;
  16. }
  17. }
  18. return -1;
  19. }
  20. void _domoticzMqttSubscribe(bool value) {
  21. String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
  22. if (value) {
  23. mqttSubscribeRaw(dczTopicOut.c_str());
  24. } else {
  25. mqttUnsubscribeRaw(dczTopicOut.c_str());
  26. }
  27. }
  28. bool _domoticzStatus(unsigned char id) {
  29. return _dcz_relay_state[id];
  30. }
  31. void _domoticzStatus(unsigned char id, bool status) {
  32. _dcz_relay_state[id] = status;
  33. relayStatus(id, status);
  34. }
  35. void _domoticzMqtt(unsigned int type, const char * topic, const char * payload) {
  36. if (!_dcz_enabled) return;
  37. String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
  38. if (type == MQTT_CONNECT_EVENT) {
  39. // Subscribe to domoticz action topics
  40. mqttSubscribeRaw(dczTopicOut.c_str());
  41. // Send relays state on connection
  42. domoticzSendRelays();
  43. }
  44. if (type == MQTT_MESSAGE_EVENT) {
  45. // Check topic
  46. if (dczTopicOut.equals(topic)) {
  47. // Parse response
  48. DynamicJsonBuffer jsonBuffer;
  49. JsonObject& root = jsonBuffer.parseObject((char *) payload);
  50. if (!root.success()) {
  51. DEBUG_MSG_P(PSTR("[DOMOTICZ] Error parsing data\n"));
  52. return;
  53. }
  54. // IDX
  55. unsigned int idx = root["idx"];
  56. String stype = root["stype"];
  57. if (
  58. (stype.equals("RGB") || stype.equals("RGBW") || stype.equals("RGBWW"))
  59. && domoticzIdx(0) == idx
  60. ) {
  61. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  62. if (lightHasColor()) {
  63. // m field contains information about color mode (enum ColorMode from domoticz ColorSwitch.h):
  64. unsigned int cmode = root["Color"]["m"];
  65. unsigned int cval;
  66. if (cmode == 3 || cmode == 4) { // ColorModeRGB or ColorModeCustom - see domoticz ColorSwitch.h
  67. // RED
  68. cval = root["Color"]["r"];
  69. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received RED value %u for IDX %u\n"), cval, idx);
  70. lightChannel(0, cval);
  71. // GREEN
  72. cval = root["Color"]["g"];
  73. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received GREEN value %u for IDX %u\n"), cval, idx);
  74. lightChannel(1, cval);
  75. // BLUE
  76. cval = root["Color"]["b"];
  77. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received BLUE value %u for IDX %u\n"), cval, idx);
  78. lightChannel(2, cval);
  79. // WARM WHITE or MONOCHROME WHITE if supported
  80. if (lightChannels() > 3) {
  81. cval = root["Color"]["ww"];
  82. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received WARM WHITE value %u for IDX %u\n"), cval, idx);
  83. lightChannel(3, cval);
  84. }
  85. // COLD WHITE if supported
  86. if (lightChannels() > 4) {
  87. cval = root["Color"]["cw"];
  88. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received COLD WHITE value %u for IDX %u\n"), cval, idx);
  89. lightChannel(4, cval);
  90. }
  91. unsigned int brightness = root["Level"];
  92. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received BRIGHTNESS value %u for IDX %u\n"), brightness, idx);
  93. unsigned int br = (brightness / 100.0) * LIGHT_MAX_BRIGHTNESS;
  94. DEBUG_MSG_P(PSTR("[DOMOTICZ] Calculated BRIGHTNESS value %u for IDX %u\n"), br, idx);
  95. lightBrightness(br); // domoticz uses 100 as maximum value while we're using LIGHT_MAX_BRIGHTNESS
  96. // update lights
  97. lightUpdate(true, mqttForward());
  98. }
  99. unsigned char relayID = _domoticzRelay(idx);
  100. if (relayID >= 0) {
  101. unsigned char value = root["nvalue"];
  102. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received value %u for IDX %u\n"), value, idx);
  103. _domoticzStatus(relayID, value > 0);
  104. }
  105. }
  106. #else
  107. DEBUG_MSG_P(PSTR("[DOMOTICZ] ESPurna compiled without LIGHT_PROVIDER"));
  108. #endif
  109. } else {
  110. unsigned char relayID = _domoticzRelay(idx);
  111. if (relayID >= 0) {
  112. unsigned char value = root["nvalue"];
  113. DEBUG_MSG_P(PSTR("[DOMOTICZ] Received value %u for IDX %u\n"), value, idx);
  114. _domoticzStatus(relayID, value == 1);
  115. }
  116. }
  117. }
  118. }
  119. };
  120. #if BROKER_SUPPORT
  121. void _domoticzBrokerCallback(const unsigned char type, const char * topic, unsigned char id, const char * payload) {
  122. // Only process status messages
  123. if (BROKER_MSG_TYPE_STATUS != type) return;
  124. if (strcmp(MQTT_TOPIC_RELAY, topic) == 0) {
  125. unsigned char value = atoi(payload);
  126. domoticzSendRelay(id, value == 1);
  127. }
  128. }
  129. #endif // BROKER_SUPPORT
  130. #if WEB_SUPPORT
  131. bool _domoticzWebSocketOnReceive(const char * key, JsonVariant& value) {
  132. return (strncmp(key, "dcz", 3) == 0);
  133. }
  134. void _domoticzWebSocketOnSend(JsonObject& root) {
  135. unsigned char visible = 0;
  136. root["dczEnabled"] = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
  137. root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC);
  138. root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
  139. JsonArray& relays = root.createNestedArray("dczRelays");
  140. for (unsigned char i=0; i<relayCount(); i++) {
  141. relays.add(domoticzIdx(i));
  142. }
  143. visible = (relayCount() > 0);
  144. #if SENSOR_SUPPORT
  145. _sensorWebSocketMagnitudes(root, "dcz");
  146. visible = visible || (magnitudeCount() > 0);
  147. #endif
  148. root["dczVisible"] = visible;
  149. }
  150. #endif // WEB_SUPPORT
  151. void _domoticzConfigure() {
  152. bool enabled = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
  153. if (enabled != _dcz_enabled) _domoticzMqttSubscribe(enabled);
  154. _dcz_relay_state.reserve(relayCount());
  155. for (size_t n = 0; n < relayCount(); ++n) {
  156. _dcz_relay_state[n] = relayStatus(n);
  157. }
  158. _dcz_enabled = enabled;
  159. }
  160. //------------------------------------------------------------------------------
  161. // Public API
  162. //------------------------------------------------------------------------------
  163. template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue) {
  164. if (!_dcz_enabled) return;
  165. unsigned int idx = getSetting(key).toInt();
  166. if (idx > 0) {
  167. char payload[128];
  168. snprintf(payload, sizeof(payload), "{\"idx\": %u, \"nvalue\": %s, \"svalue\": \"%s\"}", idx, String(nvalue).c_str(), svalue);
  169. mqttSendRaw(getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC).c_str(), payload);
  170. }
  171. }
  172. template<typename T> void domoticzSend(const char * key, T nvalue) {
  173. domoticzSend(key, nvalue, "");
  174. }
  175. void domoticzSendRelay(unsigned char relayID, bool status) {
  176. if (!_dcz_enabled) return;
  177. if (_domoticzStatus(relayID) == status) return;
  178. _dcz_relay_state[relayID] = status;
  179. char buffer[15];
  180. snprintf_P(buffer, sizeof(buffer), PSTR("dczRelayIdx%u"), relayID);
  181. domoticzSend(buffer, status ? "1" : "0");
  182. }
  183. void domoticzSendRelays() {
  184. for (uint8_t relayID=0; relayID < relayCount(); relayID++) {
  185. domoticzSendRelay(relayID, relayStatus(relayID));
  186. }
  187. }
  188. unsigned int domoticzIdx(unsigned char relayID) {
  189. char buffer[15];
  190. snprintf_P(buffer, sizeof(buffer), PSTR("dczRelayIdx%u"), relayID);
  191. return getSetting(buffer).toInt();
  192. }
  193. void domoticzSetup() {
  194. _domoticzConfigure();
  195. #if WEB_SUPPORT
  196. wsOnSendRegister(_domoticzWebSocketOnSend);
  197. wsOnReceiveRegister(_domoticzWebSocketOnReceive);
  198. #endif
  199. #if BROKER_SUPPORT
  200. brokerRegister(_domoticzBrokerCallback);
  201. #endif
  202. // Callbacks
  203. mqttRegister(_domoticzMqtt);
  204. espurnaRegisterReload(_domoticzConfigure);
  205. }
  206. bool domoticzEnabled() {
  207. return _dcz_enabled;
  208. }
  209. #endif