From 91fad8abc657918f4d533f9618cac01565c19636 Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Tue, 2 Jun 2020 08:51:45 -0400 Subject: [PATCH 1/6] added ability to push to brewstatus --- data/settings.htm | 38 +++++++++++++++++++++++- src/SecureWithRedirects.cpp | 7 +++-- src/SecureWithRedirects.h | 3 +- src/http_server.cpp | 33 +++++++++++++++++++++ src/jsonConfigHandler.cpp | 4 +++ src/sendData.cpp | 59 +++++++++++++++++++++++++++++++------ src/sendData.h | 7 +++-- 7 files changed, 135 insertions(+), 16 deletions(-) diff --git a/data/settings.htm b/data/settings.htm index 5f47a3f..d252d13 100644 --- a/data/settings.htm +++ b/data/settings.htm @@ -287,6 +287,42 @@
+
+
+
+
+ BrewStatus Settings +
+
+

These settings control how TiltBridge talks to + BrewStatus. To disable pushing to BrewStatus, delete + the BrewStatus URL. +

+
+
+ +
+ +
+
+
+
+ +
+
+ +
+ +
+
+
+
+ +
+
+
+
+ @@ -327,4 +363,4 @@
- \ No newline at end of file + diff --git a/src/SecureWithRedirects.cpp b/src/SecureWithRedirects.cpp index 61ddfb3..d52e80f 100644 --- a/src/SecureWithRedirects.cpp +++ b/src/SecureWithRedirects.cpp @@ -14,7 +14,7 @@ #include -SecureWithRedirects::SecureWithRedirects(const char * original_url, const char *api_key, const char *data_to_send) { +SecureWithRedirects::SecureWithRedirects(const char * original_url, const char *api_key, const char *data_to_send, const char *content_type) { // Initialize secure_client & HTTPClient secure_client = new WiFiClientSecure; https = new HTTPClient; @@ -31,6 +31,7 @@ SecureWithRedirects::SecureWithRedirects(const char * original_url, const char * url = original_url; apiKey=api_key; dataToSend=data_to_send; + contentType=content_type; } @@ -64,7 +65,7 @@ bool SecureWithRedirects::send_with_redirects() { Serial.println("`"); https->begin(*secure_client, url); - https->addHeader("Content-Type", "application/json"); //Specify content-type header + https->addHeader("Content-Type", contentType); //Specify content-type header if (apiKey) { https->addHeader("X-API-KEY", apiKey); //Specify API key header } @@ -169,4 +170,4 @@ bool SecureWithRedirects::send_with_redirects() { return false; } -} \ No newline at end of file +} diff --git a/src/SecureWithRedirects.h b/src/SecureWithRedirects.h index dfceae4..744d177 100644 --- a/src/SecureWithRedirects.h +++ b/src/SecureWithRedirects.h @@ -13,7 +13,7 @@ class SecureWithRedirects { public: - SecureWithRedirects(const char * original_url, const char *api_key, const char *data_to_send); + SecureWithRedirects(const char * original_url, const char *api_key, const char *data_to_send, const char *content_type); void end(); bool send_with_redirects(); @@ -26,6 +26,7 @@ class SecureWithRedirects { String url; const char *apiKey; const char *dataToSend; + const char *contentType; }; diff --git a/src/http_server.cpp b/src/http_server.cpp index 3e16670..c17350c 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -130,6 +130,39 @@ void processConfig() { Serial.println("Updated fermentrackPushEvery"); } + // Brewstatus Settings + if (server.hasArg("brewstatusURL")) { + // TODO - Add a check here to make sure that brewstatusURL actually changed, and return if it didn't + if (server.arg("brewstatusURL").length() > 255) + return processConfigError(); + else if (server.arg("brewstatusURL").length() < 12) + app_config.config["brewstatusURL"] = ""; + else + app_config.config["brewstatusURL"] = server.arg("brewstatusURL").c_str(); + } + + if (server.hasArg("brewstatusPushEvery")) { + Serial.println("Has brewstatusPushEvery"); + if (server.arg("brewstatusPushEvery").length() > 5) + return processConfigError(); + else if (server.arg("brewstatusPushEvery").length() <= 0) + return processConfigError(); + else if (!isInteger(server.arg("brewstatusPushEvery").c_str())) { + Serial.println("brewstatusPushEvery is not an integer!"); + return processConfigError(); + } + + // At this point, we know that it's an integer. Let's convert to a long so we can test the value + // TODO - Figure out if we want to print error messages for these + long push_every = strtol(server.arg("brewstatusPushEvery").c_str(), nullptr, 10); + if(push_every < 30) + app_config.config["brewstatusPushEvery"] = 30; + else if(push_every > 60*60) + app_config.config["brewstatusPushEvery"] = 60*60; + else + app_config.config["brewstatusPushEvery"] = push_every; + Serial.println("Updated brewstatusPushEvery"); + } // Google Sheets Settings if (server.hasArg("scriptsURL")) { diff --git a/src/jsonConfigHandler.cpp b/src/jsonConfigHandler.cpp index d946510..d118075 100644 --- a/src/jsonConfigHandler.cpp +++ b/src/jsonConfigHandler.cpp @@ -28,6 +28,10 @@ void jsonConfigHandler::initialize() { {"fermentrackURL", ""}, {"fermentrackPushEvery", 30}, + // Brewstatus Settings + {"brewstatusURL", ""}, + {"brewstatusPushEvery", 60}, + // Google Scripts Settings {"scriptsURL", ""}, {"scriptsEmail", ""}, diff --git a/src/sendData.cpp b/src/sendData.cpp index e9c11b7..661b66b 100644 --- a/src/sendData.cpp +++ b/src/sendData.cpp @@ -7,7 +7,6 @@ // for convenience using json = nlohmann::json; - #include "tiltBridge.h" #include "wifi_setup.h" #include "sendData.h" @@ -26,6 +25,7 @@ using json = nlohmann::json; dataSendHandler data_sender; // Global data sender dataSendHandler::dataSendHandler() { + send_to_brewstatus_at = 40 * 1000; // Trigger the first send to BrewStatus 40 seconds out send_to_fermentrack_at = 45 * 1000; // Trigger the first send to Fermentrack 45 seconds out send_to_brewfather_at = 50 * 1000; // Trigger the first send to Fermentrack 50 seconds out send_to_brewers_friend_at = 55 * 1000; // Trigger the first send to Brewer's Friend 55 seconds out @@ -77,7 +77,7 @@ bool dataSendHandler::send_to_fermentrack() { j["tilts"] = tilt_scanner.tilt_to_json(); - if(!send_to_url(app_config.config["fermentrackURL"].get().c_str(), "", j.dump().c_str())) + if(!send_to_url(app_config.config["fermentrackURL"].get().c_str(), "", j.dump().c_str(), "application/json")) result = false; // There was an error with the previous send j.clear(); @@ -85,9 +85,34 @@ bool dataSendHandler::send_to_fermentrack() { } +bool dataSendHandler::send_to_brewstatus() { + bool result = true; + const int payload_size = 512; + char payload[payload_size]; + + // This should look like this when sent to Brewstatus: + // ('Request payload:', 'SG=1.019&Temp=71.0&Color=ORANGE&Timepoint=43984.33630927084&Beer=Beer&Comment=Test') + + // Loop through each of the tilt colors cached by tilt_scanner, sending data for each of the active tilts + for(uint8_t i = 0;iis_loaded()) { + snprintf(payload, payload_size, "SG=%f&Temp=%f&Color=%s&Comment=''", + (float) tilt_scanner.tilt(i)->gravity / 1000, + (float) tilt_scanner.tilt(i)->temp, + tilt_scanner.tilt(i)->color_name().c_str() + ); + if(!send_to_url(app_config.config["brewstatusURL"].get().c_str(), "", payload, "application/x-www-form-urlencoded")) + result = false; // There was an error with the previous send + } + } + + return result; +} + + #ifdef USE_SECURE_GSCRIPTS // For sending data to Google Scripts, we have to use secure_client but otherwise we're doing the same thing as before. -bool dataSendHandler::send_to_url_https(const char *url, const char *apiKey, const char *dataToSend) { +bool dataSendHandler::send_to_url_https(const char *url, const char *apiKey, const char *dataToSend, const char *contentType) { // This handles the generic act of sending data to an endpoint bool result = false; @@ -109,7 +134,7 @@ bool dataSendHandler::send_to_url_https(const char *url, const char *apiKey, con Serial.println("[HTTPS] Calling SWR::send_with_redirects"); #endif - SecureWithRedirects SWR(url, apiKey, dataToSend); + SecureWithRedirects SWR(url, apiKey, dataToSend, contentType); result = SWR.send_with_redirects(); SWR.end(); @@ -174,7 +199,7 @@ bool dataSendHandler::send_to_google() { #ifdef USE_SECURE_GSCRIPTS // When sending the data to GScripts directly, we're sending the payload - not the wrapped payload - if(!send_to_url_https(app_config.config["scriptsURL"].get().c_str(), "", payload.dump().c_str())) + if(!send_to_url_https(app_config.config["scriptsURL"].get().c_str(), "", payload.dump().c_str(), "application/json")) result = false; // There was an error with the previous send payload.clear(); #else @@ -183,7 +208,7 @@ bool dataSendHandler::send_to_google() { // All data for non-secure gscripts goes through the TiltBridge google proxy script. I'm not happy with this // but it's the best I've got until HTTPS can be readded - if(!send_to_url("http://www.tiltbridge.com/tiltbridge_google_proxy/", "", j.dump().c_str())) + if(!send_to_url("http://www.tiltbridge.com/tiltbridge_google_proxy/", "", j.dump().c_str(), "application/json")) result = false; // There was an error with the previous send payload.clear(); j.clear(); @@ -257,7 +282,7 @@ bool dataSendHandler::send_to_bf_and_bf(const uint8_t which_bf) { j["gravity_unit"] = "G"; j["device_source"] = "TiltBridge"; - if(!send_to_url(url.c_str(), apiKeyStr.c_str(), j.dump().c_str())) + if(!send_to_url(url.c_str(), apiKeyStr.c_str(), j.dump().c_str(), "application/json")) result = false; // There was an error with the previous send j.clear(); @@ -268,7 +293,7 @@ bool dataSendHandler::send_to_bf_and_bf(const uint8_t which_bf) { -bool dataSendHandler::send_to_url(const char *url, const char *apiKey, const char *dataToSend) { +bool dataSendHandler::send_to_url(const char *url, const char *apiKey, const char *dataToSend, const char *contentType) { // This handles the generic act of sending data to an endpoint HTTPClient http; bool result = false; @@ -285,7 +310,7 @@ bool dataSendHandler::send_to_url(const char *url, const char *apiKey, const cha // tilt_scanner.wait_until_scan_complete(); http.begin(url); - http.addHeader("Content-Type", "application/json"); //Specify content-type header + http.addHeader("Content-Type", contentType); //Specify content-type header if (apiKey) { http.addHeader("X-API-KEY", apiKey); //Specify API key header } @@ -360,6 +385,22 @@ void dataSendHandler::process() { yield(); } + // Check & send to Brewstatus if necessary + if(send_to_brewstatus_at <= xTaskGetTickCount()) { + if(WiFiClass::status()== WL_CONNECTED && app_config.config["brewstatusURL"].get().length() > BREWSTATUS_MIN_URL_LENGTH) { //Check WiFi connection status + #ifdef DEBUG_PRINTS + Serial.printf("Calling send to Brewstatus\r\n"); + #endif + + send_to_brewstatus(); + send_to_brewstatus_at = xTaskGetTickCount() + (app_config.config["brewstatusPushEvery"].get() * 1000); + } else { + // If the user adds the setting, we want this to kick in within 10 seconds + send_to_brewstatus_at = xTaskGetTickCount() + 10000; + } + yield(); + } + // Check & send to Google Scripts if necessary if(send_to_google_at <= xTaskGetTickCount()) { if(WiFiClass::status()== WL_CONNECTED && app_config.config["scriptsURL"].get().length() > GSCRIPTS_MIN_URL_LENGTH) { diff --git a/src/sendData.h b/src/sendData.h index 1cfb1db..985bc05 100644 --- a/src/sendData.h +++ b/src/sendData.h @@ -24,6 +24,7 @@ #define BREWFATHER_MIN_KEY_LENGTH 5 #define BREWERS_FRIEND_MIN_KEY_LENGTH 12 #define FERMENTRACK_MIN_URL_LENGTH 12 +#define BREWSTATUS_MIN_URL_LENGTH 12 #define GSCRIPTS_MIN_URL_LENGTH 24 #define GSCRIPTS_MIN_EMAIL_LENGTH 7 @@ -45,6 +46,7 @@ class dataSendHandler { private: uint64_t send_to_fermentrack_at; + uint64_t send_to_brewstatus_at; uint64_t send_to_brewers_friend_at; uint64_t send_to_google_at; uint64_t send_to_brewfather_at; @@ -57,13 +59,14 @@ class dataSendHandler { #ifdef USE_SECURE_GSCRIPTS // This is necessary for HTTPS support (which is useless until ESP32 bluetooth support is improved) void setClock(); - static bool send_to_url_https(const char *url, const char *apiKey, const char *dataToSend); + static bool send_to_url_https(const char *url, const char *apiKey, const char *dataToSend, const char *contentType); #endif bool send_to_fermentrack(); + bool send_to_brewstatus(); bool send_to_google(); - static bool send_to_url(const char *url, const char *apiKey, const char *dataToSend); + static bool send_to_url(const char *url, const char *apiKey, const char *dataToSend, const char* contentType); bool send_to_bf_and_bf(uint8_t which_bf); // Handler for both Brewer's Friend and Brewfather }; From 134e923763c08d250a8a64d6328dab2b772d1bbb Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Tue, 2 Jun 2020 11:58:25 -0400 Subject: [PATCH 2/6] include the timepoint --- src/sendData.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sendData.cpp b/src/sendData.cpp index 661b66b..680b850 100644 --- a/src/sendData.cpp +++ b/src/sendData.cpp @@ -2,6 +2,8 @@ // Created by John Beeler on 2/18/19. // +#include + #include // for convenience @@ -90,16 +92,22 @@ bool dataSendHandler::send_to_brewstatus() { const int payload_size = 512; char payload[payload_size]; - // This should look like this when sent to Brewstatus: - // ('Request payload:', 'SG=1.019&Temp=71.0&Color=ORANGE&Timepoint=43984.33630927084&Beer=Beer&Comment=Test') + // The payload should look like this when sent to Brewstatus: + // ('Request payload:', 'SG=1.019&Temp=71.0&Color=ORANGE&Timepoint=43984.33630927084&Beer=Beer&Comment=Comment') + // BrewStatus ignors Beer, so we just set this to Undefined. + // BrewStatus will record Comment if it set, but just leave it blank. + // The Timepoint is Google Sheets time, which is fractional days since 12/30/1899 + // Using https://www.timeanddate.com/date/durationresult.html?m1=12&d1=30&y1=1899&m2=1&d2=1&y2=1970 gives + // us 25,569 days from the start of Google Sheets time to the start of the Unix epoch. // Loop through each of the tilt colors cached by tilt_scanner, sending data for each of the active tilts for(uint8_t i = 0;iis_loaded()) { - snprintf(payload, payload_size, "SG=%f&Temp=%f&Color=%s&Comment=''", + snprintf(payload, payload_size, "SG=%f&Temp=%f&Color=%s&Timepoint=%.11f&Beer=Undefined&Comment=", (float) tilt_scanner.tilt(i)->gravity / 1000, (float) tilt_scanner.tilt(i)->temp, - tilt_scanner.tilt(i)->color_name().c_str() + tilt_scanner.tilt(i)->color_name().c_str(), + (double) std::time(0) / 86400.0 + 25569.0 ); if(!send_to_url(app_config.config["brewstatusURL"].get().c_str(), "", payload, "application/x-www-form-urlencoded")) result = false; // There was an error with the previous send From 286d7aa5c50c4b2651c5bb965e2888e97b87d54a Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Tue, 2 Jun 2020 15:34:25 -0400 Subject: [PATCH 3/6] allow user to specify a timezone offset --- data/settings.htm | 10 ++++++++++ src/http_server.cpp | 20 ++++++++++++++++++++ src/jsonConfigHandler.cpp | 1 + src/sendData.cpp | 2 +- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/data/settings.htm b/data/settings.htm index d252d13..89dde6f 100644 --- a/data/settings.htm +++ b/data/settings.htm @@ -318,6 +318,16 @@
+
+
+ +
+ +
+
+
+
+ diff --git a/src/http_server.cpp b/src/http_server.cpp index c17350c..4f52496 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -164,6 +164,26 @@ void processConfig() { Serial.println("Updated brewstatusPushEvery"); } + if (server.hasArg("brewstatusTZoffset")) { + Serial.println("Has brewstatusTZoffset"); + if (server.arg("brewstatusTZoffset").length() > 3) + return processConfigError(); + else if (server.arg("brewstatusTZoffset").length() <= 0) + return processConfigError(); + } + + float tzoffset = strtof(server.arg("brewstatusTZoffset").c_str(), nullptr, 10); + if(tzoffset < -12.0) { + Serial.println("brewstatusTZoffset is less than -12!"); + return processConfigError(); + } else if(tzoffset > 12.0) { + Serial.println("brewstatusTZoffset is greater than 12!"); + return processConfigError(); + else + app_config.config["brewstatusTZoffset"] = tzoffset; + Serial.println("Updated brewstatusTZoffset"); + } + // Google Sheets Settings if (server.hasArg("scriptsURL")) { // TODO - Validate this begins with "https://scripts.google.com/" diff --git a/src/jsonConfigHandler.cpp b/src/jsonConfigHandler.cpp index d118075..9793350 100644 --- a/src/jsonConfigHandler.cpp +++ b/src/jsonConfigHandler.cpp @@ -31,6 +31,7 @@ void jsonConfigHandler::initialize() { // Brewstatus Settings {"brewstatusURL", ""}, {"brewstatusPushEvery", 60}, + {"brewstatusTZoffset", -5}, // Google Scripts Settings {"scriptsURL", ""}, diff --git a/src/sendData.cpp b/src/sendData.cpp index 680b850..5159a0b 100644 --- a/src/sendData.cpp +++ b/src/sendData.cpp @@ -107,7 +107,7 @@ bool dataSendHandler::send_to_brewstatus() { (float) tilt_scanner.tilt(i)->gravity / 1000, (float) tilt_scanner.tilt(i)->temp, tilt_scanner.tilt(i)->color_name().c_str(), - (double) std::time(0) / 86400.0 + 25569.0 + ((double) std::time(0) / 86400.0 + 25569.0) + (app_config.config["brewstatusTZoffset"].get() * 3600.0) ); if(!send_to_url(app_config.config["brewstatusURL"].get().c_str(), "", payload, "application/x-www-form-urlencoded")) result = false; // There was an error with the previous send From 27fd8d792b6a0699d7db0d22171da55ec3ec3ea0 Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Tue, 2 Jun 2020 15:39:28 -0400 Subject: [PATCH 4/6] fixed error in string conversion --- src/http_server.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/http_server.cpp b/src/http_server.cpp index 4f52496..37f315c 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -172,15 +172,16 @@ void processConfig() { return processConfigError(); } - float tzoffset = strtof(server.arg("brewstatusTZoffset").c_str(), nullptr, 10); + float tzoffset = strtof(server.arg("brewstatusTZoffset").c_str(), nullptr); if(tzoffset < -12.0) { Serial.println("brewstatusTZoffset is less than -12!"); return processConfigError(); } else if(tzoffset > 12.0) { Serial.println("brewstatusTZoffset is greater than 12!"); return processConfigError(); - else + } else { app_config.config["brewstatusTZoffset"] = tzoffset; + } Serial.println("Updated brewstatusTZoffset"); } From 99d21e193e1fcd45b787af0f94d2a2f60955f2af Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Tue, 2 Jun 2020 15:41:12 -0400 Subject: [PATCH 5/6] fixed typo --- src/http_server.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/http_server.cpp b/src/http_server.cpp index 37f315c..5338669 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -170,7 +170,6 @@ void processConfig() { return processConfigError(); else if (server.arg("brewstatusTZoffset").length() <= 0) return processConfigError(); - } float tzoffset = strtof(server.arg("brewstatusTZoffset").c_str(), nullptr); if(tzoffset < -12.0) { From 7f8a24a890e32a38323f17efd6df1a2a0f9cbdf5 Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Tue, 2 Jun 2020 15:48:33 -0400 Subject: [PATCH 6/6] fixed timezone calculation --- src/sendData.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sendData.cpp b/src/sendData.cpp index 5159a0b..7cab94a 100644 --- a/src/sendData.cpp +++ b/src/sendData.cpp @@ -99,6 +99,7 @@ bool dataSendHandler::send_to_brewstatus() { // The Timepoint is Google Sheets time, which is fractional days since 12/30/1899 // Using https://www.timeanddate.com/date/durationresult.html?m1=12&d1=30&y1=1899&m2=1&d2=1&y2=1970 gives // us 25,569 days from the start of Google Sheets time to the start of the Unix epoch. + // BrewStatus wants local time, so we allow the user to specify a time offset. // Loop through each of the tilt colors cached by tilt_scanner, sending data for each of the active tilts for(uint8_t i = 0;igravity / 1000, (float) tilt_scanner.tilt(i)->temp, tilt_scanner.tilt(i)->color_name().c_str(), - ((double) std::time(0) / 86400.0 + 25569.0) + (app_config.config["brewstatusTZoffset"].get() * 3600.0) - ); + ((double) std::time(0) + (app_config.config["brewstatusTZoffset"].get() * 3600.0)) + / 86400.0 + 25569.0); if(!send_to_url(app_config.config["brewstatusURL"].get().c_str(), "", payload, "application/x-www-form-urlencoded")) result = false; // There was an error with the previous send }