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.

287 lines
7.8 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. /*
  2. THINGSPEAK MODULE
  3. Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. Module key prefix: tspk
  5. */
  6. #if THINGSPEAK_SUPPORT
  7. #if THINGSPEAK_USE_ASYNC
  8. #include <ESPAsyncTCP.h>
  9. AsyncClient * _tspk_client;
  10. #else
  11. #include <ESP8266WiFi.h>
  12. #endif
  13. const char THINGSPEAK_REQUEST_TEMPLATE[] PROGMEM =
  14. "POST %s HTTP/1.1\r\n"
  15. "Host: %s\r\n"
  16. "User-Agent: ESPurna\r\n"
  17. "Connection: close\r\n"
  18. "Content-Type: application/x-www-form-urlencoded\r\n"
  19. "Content-Length: %d\r\n\r\n"
  20. "%s\r\n";
  21. bool _tspk_enabled = false;
  22. char * _tspk_queue[8] = {NULL};
  23. bool _tspk_flush = false;
  24. unsigned long _tspk_last_flush = 0;
  25. // -----------------------------------------------------------------------------
  26. #if WEB_SUPPORT
  27. bool _tspkWebSocketOnReceive(const char * key, JsonVariant& value) {
  28. return (strncmp(key, "tspk", 4) == 0);
  29. }
  30. void _tspkWebSocketOnSend(JsonObject& root) {
  31. unsigned char visible = 0;
  32. root["tspkEnabled"] = getSetting("tspkEnabled", THINGSPEAK_ENABLED).toInt() == 1;
  33. root["tspkKey"] = getSetting("tspkKey");
  34. JsonArray& relays = root.createNestedArray("tspkRelays");
  35. for (byte i=0; i<relayCount(); i++) {
  36. relays.add(getSetting("tspkRelay", i, 0).toInt());
  37. }
  38. if (relayCount() > 0) visible = 1;
  39. #if SENSOR_SUPPORT
  40. JsonArray& list = root.createNestedArray("tspkMagnitudes");
  41. for (byte i=0; i<magnitudeCount(); i++) {
  42. JsonObject& element = list.createNestedObject();
  43. element["name"] = magnitudeName(i);
  44. element["type"] = magnitudeType(i);
  45. element["index"] = magnitudeIndex(i);
  46. element["idx"] = getSetting("tspkMagnitude", i, 0).toInt();
  47. }
  48. if (magnitudeCount() > 0) visible = 1;
  49. #endif
  50. root["tspkVisible"] = visible;
  51. }
  52. #endif
  53. void _tspkConfigure() {
  54. _tspk_enabled = getSetting("tspkEnabled", THINGSPEAK_ENABLED).toInt() == 1;
  55. if (_tspk_enabled && (getSetting("tspkKey").length() == 0)) {
  56. _tspk_enabled = false;
  57. setSetting("tspkEnabled", 0);
  58. }
  59. }
  60. #if THINGSPEAK_USE_ASYNC
  61. void _tspkPost(String data) {
  62. if (_tspk_client == NULL) {
  63. _tspk_client = new AsyncClient();
  64. }
  65. _tspk_client->onDisconnect([](void *s, AsyncClient *c) {
  66. DEBUG_MSG_P(PSTR("[THINGSPEAK] Disconnected\n"));
  67. _tspk_client->free();
  68. delete _tspk_client;
  69. _tspk_client = NULL;
  70. }, 0);
  71. _tspk_client->onTimeout([](void *s, AsyncClient *c, uint32_t time) {
  72. _tspk_client->close(true);
  73. }, 0);
  74. _tspk_client->onData([](void * arg, AsyncClient * c, void * response, size_t len) {
  75. char * b = (char *) response;
  76. b[len] = 0;
  77. char * p = strstr((char *)response, "\r\n\r\n");
  78. unsigned int code = (p != NULL) ? atoi(&p[4]) : 0;
  79. DEBUG_MSG_P(PSTR("[THINGSPEAK] Response value: %d\n"), code);
  80. _tspk_client->close(true);
  81. }, NULL);
  82. _tspk_client->onConnect([data](void * arg, AsyncClient * client) {
  83. DEBUG_MSG_P(PSTR("[THINGSPEAK] Connected to %s:%d\n"), THINGSPEAK_HOST, THINGSPEAK_PORT);
  84. #if THINGSPEAK_USE_SSL
  85. uint8_t fp[20] = {0};
  86. sslFingerPrintArray(THINGSPEAK_FINGERPRINT, fp);
  87. SSL * ssl = _tspk_client->getSSL();
  88. if (ssl_match_fingerprint(ssl, fp) != SSL_OK) {
  89. DEBUG_MSG_P(PSTR("[THINGSPEAK] Warning: certificate doesn't match\n"));
  90. }
  91. #endif
  92. DEBUG_MSG_P(PSTR("[THINGSPEAK] POST %s\n"), data.c_str());
  93. char buffer[strlen_P(THINGSPEAK_REQUEST_TEMPLATE) + strlen(THINGSPEAK_URL) + strlen(THINGSPEAK_HOST) + data.length()];
  94. snprintf_P(buffer, sizeof(buffer),
  95. THINGSPEAK_REQUEST_TEMPLATE,
  96. THINGSPEAK_URL,
  97. THINGSPEAK_HOST,
  98. data.length(),
  99. data.c_str()
  100. );
  101. client->write(buffer);
  102. }, NULL);
  103. #if ASYNC_TCP_SSL_ENABLED
  104. bool connected = _tspk_client->connect(THINGSPEAK_HOST, THINGSPEAK_PORT, THINGSPEAK_USE_SSL);
  105. #else
  106. bool connected = _tspk_client->connect(THINGSPEAK_HOST, THINGSPEAK_PORT);
  107. #endif
  108. if (!connected) {
  109. DEBUG_MSG_P(PSTR("[THINGSPEAK] Connection failed\n"));
  110. _tspk_client->close(true);
  111. }
  112. }
  113. #else // THINGSPEAK_USE_ASYNC
  114. void _tspkPost(String data) {
  115. #if THINGSPEAK_USE_SSL
  116. WiFiClientSecure _tspk_client;
  117. #else
  118. WiFiClient _tspk_client;
  119. #endif
  120. if (_tspk_client.connect(THINGSPEAK_HOST, THINGSPEAK_PORT)) {
  121. DEBUG_MSG_P(PSTR("[THINGSPEAK] Connected to %s:%d\n"), THINGSPEAK_HOST, THINGSPEAK_PORT);
  122. if (!_tspk_client.verify(THINGSPEAK_FINGERPRINT, THINGSPEAK_HOST)) {
  123. DEBUG_MSG_P(PSTR("[THINGSPEAK] Warning: certificate doesn't match\n"));
  124. }
  125. DEBUG_MSG_P(PSTR("[THINGSPEAK] POST %s\n"), data.c_str());
  126. char buffer[strlen_P(THINGSPEAK_REQUEST_TEMPLATE) + strlen(THINGSPEAK_URL) + strlen(THINGSPEAK_HOST) + data.length()];
  127. snprintf_P(buffer, sizeof(buffer),
  128. THINGSPEAK_REQUEST_TEMPLATE,
  129. THINGSPEAK_URL,
  130. THINGSPEAK_HOST,
  131. data.length(),
  132. data.c_str()
  133. );
  134. _tspk_client.print(buffer);
  135. nice_delay(100);
  136. String response = _tspk_client.readString();
  137. int pos = response.indexOf("\r\n\r\n");
  138. unsigned int code = (pos > 0) ? response.substring(pos + 4).toInt() : 0;
  139. DEBUG_MSG_P(PSTR("[THINGSPEAK] Response value: %d\n"), code);
  140. _tspk_client.stop();
  141. return;
  142. }
  143. DEBUG_MSG_P(PSTR("[THINGSPEAK] Connection failed\n"));
  144. }
  145. #endif // THINGSPEAK_USE_ASYNC
  146. void _tspkEnqueue(unsigned char index, char * payload) {
  147. DEBUG_MSG_P(PSTR("[THINGSPEAK] Enqueuing field #%d with value %s\n"), index, payload);
  148. --index;
  149. if (_tspk_queue[index] != NULL) free(_tspk_queue[index]);
  150. _tspk_queue[index] = strdup(payload);
  151. }
  152. void _tspkFlush() {
  153. String data;
  154. // Walk the fields
  155. for (unsigned char id=0; id<8; id++) {
  156. if (_tspk_queue[id] != NULL) {
  157. if (data.length() > 0) data = data + String("&");
  158. data = data + String("field") + String(id+1) + String("=") + String(_tspk_queue[id]);
  159. free(_tspk_queue[id]);
  160. _tspk_queue[id] = NULL;
  161. }
  162. }
  163. // POST data if any
  164. if (data.length() > 0) {
  165. data = data + String("&api_key=") + getSetting("tspkKey");
  166. _tspkPost(data);
  167. _tspk_last_flush = millis();
  168. }
  169. }
  170. // -----------------------------------------------------------------------------
  171. bool tspkEnqueueRelay(unsigned char index, unsigned char status) {
  172. if (!_tspk_enabled) return true;
  173. unsigned char id = getSetting("tspkRelay", index, 0).toInt();
  174. if (id > 0) {
  175. char payload[3] = {0};
  176. itoa(status ? 1 : 0, payload, 10);
  177. _tspkEnqueue(id, payload);
  178. return true;
  179. }
  180. return false;
  181. }
  182. bool tspkEnqueueMeasurement(unsigned char index, char * payload) {
  183. if (!_tspk_enabled) return true;
  184. unsigned char id = getSetting("tspkMagnitude", index, 0).toInt();
  185. if (id > 0) {
  186. _tspkEnqueue(id, payload);
  187. return true;
  188. }
  189. return false;
  190. }
  191. void tspkFlush() {
  192. _tspk_flush = true;
  193. }
  194. bool tspkEnabled() {
  195. return _tspk_enabled;
  196. }
  197. void tspkSetup() {
  198. _tspkConfigure();
  199. #if WEB_SUPPORT
  200. wsOnSendRegister(_tspkWebSocketOnSend);
  201. wsOnAfterParseRegister(_tspkConfigure);
  202. wsOnReceiveRegister(_tspkWebSocketOnReceive);
  203. #endif
  204. DEBUG_MSG_P(PSTR("[THINGSPEAK] Async %s, SSL %s\n"),
  205. THINGSPEAK_USE_ASYNC ? "ENABLED" : "DISABLED",
  206. THINGSPEAK_USE_SSL ? "ENABLED" : "DISABLED"
  207. );
  208. // Register loop
  209. espurnaRegisterLoop(tspkLoop);
  210. }
  211. void tspkLoop() {
  212. if (!_tspk_enabled) return;
  213. if (!wifiConnected() || (WiFi.getMode() != WIFI_STA)) return;
  214. if (_tspk_flush && (millis() - _tspk_last_flush > THINGSPEAK_MIN_INTERVAL)) {
  215. _tspkFlush();
  216. _tspk_flush = false;
  217. }
  218. }
  219. #endif