Skip to content

Commit

Permalink
Merge pull request #27 from duncan-brown/brewstatus
Browse files Browse the repository at this point in the history
Implement pushing to BrewStatus
  • Loading branch information
thorrak authored Jun 7, 2020
2 parents 9dc2997 + 7f8a24a commit 1db4077
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 16 deletions.
48 changes: 47 additions & 1 deletion data/settings.htm
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,52 @@ <h5 class="card-header">
</div>


<div class="row row-padded">
<div class="col-12">
<div class="card">
<h5 class="card-header">
BrewStatus Settings
</h5>
<div class="card-body">
<p class="card-text">These settings control how TiltBridge talks to
<a href="https://www.brewstat.us/">BrewStatus</a>. To disable pushing to BrewStatus, delete
the BrewStatus URL.
</p>
<form action="/settings/update/" method="POST">
<div class="form-group row">
<label for="brewstatusURL" class="col-sm-2 col-form-label" data-toggle="tooltip" title="Target BrewStatus tiltbridge URL. Delete to disable pushing to BrewStatus">BrewStatus URL</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="brewstatusURL" id="brewstatusURL" placeholder="https://www.brewstat.us/tilt/xxxxxxxxxx/log" :value="settings.brewstatusURL">
</div>
<div class="col-sm-2"><button type="submit" class="btn btn-primary">Update</button></div>
</div>
</form>

<form action="/settings/update/" method="POST">
<div class="form-group row">
<label for="brewstatusPushEvery" class="col-sm-2 col-form-label" data-toggle="tooltip" title="How often (in seconds) to push data to Brewstatus">Push Frequency</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="brewstatusPushEvery" id="brewstatusPushEvery" placeholder="30" :value="settings.brewstatusPushEvery">
</div>
<div class="col-sm-2"><button type="submit" class="btn btn-primary">Update</button></div>
</div>
</form>

<form action="/settings/update/" method="POST">
<div class="form-group row">
<label for="brewstatusTZoffset" class="col-sm-2 col-form-label" data-toggle="tooltip" title="Time zone offset from UTC (in hours)">Time Zone Offset</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="brewstatusTZoffset" id="brewstatusTZoffset" placeholder="-5" :value="settings.brewstatusTZoffset">
</div>
<div class="col-sm-2"><button type="submit" class="btn btn-primary">Update</button></div>
</div>
</form>

</div>
</div>
</div>
</div>

</div>


Expand Down Expand Up @@ -327,4 +373,4 @@ <h5 class="card-header">


</body>
</html>
</html>
7 changes: 4 additions & 3 deletions src/SecureWithRedirects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <WiFiClientSecure.h>


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;
Expand All @@ -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;
}


Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -169,4 +170,4 @@ bool SecureWithRedirects::send_with_redirects() {
return false;
}

}
}
3 changes: 2 additions & 1 deletion src/SecureWithRedirects.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -26,6 +26,7 @@ class SecureWithRedirects {
String url;
const char *apiKey;
const char *dataToSend;
const char *contentType;

};

Expand Down
53 changes: 53 additions & 0 deletions src/http_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,59 @@ 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");
}

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);
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")) {
Expand Down
5 changes: 5 additions & 0 deletions src/jsonConfigHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ void jsonConfigHandler::initialize() {
{"fermentrackURL", ""},
{"fermentrackPushEvery", 30},

// Brewstatus Settings
{"brewstatusURL", ""},
{"brewstatusPushEvery", 60},
{"brewstatusTZoffset", -5},

// Google Scripts Settings
{"scriptsURL", ""},
{"scriptsEmail", ""},
Expand Down
68 changes: 59 additions & 9 deletions src/sendData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// Created by John Beeler on 2/18/19.
//

#include<ctime>

#include <nlohmann/json.hpp>

// for convenience
using json = nlohmann::json;


#include "tiltBridge.h"
#include "wifi_setup.h"
#include "sendData.h"
Expand All @@ -26,6 +27,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
Expand Down Expand Up @@ -77,17 +79,49 @@ bool dataSendHandler::send_to_fermentrack() {
j["tilts"] = tilt_scanner.tilt_to_json();


if(!send_to_url(app_config.config["fermentrackURL"].get<std::string>().c_str(), "", j.dump().c_str()))
if(!send_to_url(app_config.config["fermentrackURL"].get<std::string>().c_str(), "", j.dump().c_str(), "application/json"))
result = false; // There was an error with the previous send

j.clear();
return result;
}


bool dataSendHandler::send_to_brewstatus() {
bool result = true;
const int payload_size = 512;
char payload[payload_size];

// 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.
// 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;i<TILT_COLORS;i++) {
if(tilt_scanner.tilt(i)->is_loaded()) {
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(),
((double) std::time(0) + (app_config.config["brewstatusTZoffset"].get<double>() * 3600.0))
/ 86400.0 + 25569.0);
if(!send_to_url(app_config.config["brewstatusURL"].get<std::string>().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;

Expand All @@ -109,7 +143,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();

Expand Down Expand Up @@ -174,7 +208,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<std::string>().c_str(), "", payload.dump().c_str()))
if(!send_to_url_https(app_config.config["scriptsURL"].get<std::string>().c_str(), "", payload.dump().c_str(), "application/json"))
result = false; // There was an error with the previous send
payload.clear();
#else
Expand All @@ -183,7 +217,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();
Expand Down Expand Up @@ -257,7 +291,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();
Expand All @@ -268,7 +302,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;
Expand All @@ -285,7 +319,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
}
Expand Down Expand Up @@ -360,6 +394,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<std::string>().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<int>() * 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<std::string>().length() > GSCRIPTS_MIN_URL_LENGTH) {
Expand Down
7 changes: 5 additions & 2 deletions src/sendData.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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;
Expand All @@ -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

};
Expand Down

0 comments on commit 1db4077

Please sign in to comment.