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.

231 lines
6.4 KiB

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