Skip to content

Commit

Permalink
Add Brewfather support
Browse files Browse the repository at this point in the history
  • Loading branch information
lbussy committed Mar 26, 2020
1 parent 7bc2e4a commit 9b98d94
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 27 deletions.
110 changes: 110 additions & 0 deletions src/brewftarget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* Copyright (C) 2019-2020 Lee C. Bussy (@LBussy)

This file is part of Lee Bussy's Brew Bubbles (brew-bubbles).

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */

#include "brewftarget.h"

BrewfTarget* BrewfTarget::single = NULL;

BrewfTarget* BrewfTarget::getInstance() {
if (!single) {
single = new BrewfTarget();
single->target = new PushTarget;
single->target->ip = INADDR_NONE;

// Enable target and target name
single->target->target.enabled = (single->targeturl.length() > 3);
strlcpy(single->target->target.name, single->target_name.c_str(), single->target_name.length() + 1);
//
// Check return body for success
single->target->checkBody.enabled = single->checkbody_enabled;
strlcpy(single->target->checkBody.name, single->checkbody_name.c_str(), single->checkbody_name.length() + 1);
//
// Change JSON point enabled and name for target type
single->target->apiName.enabled = single->apiname_enabled;
strlcpy(single->target->apiName.name, single->apiname_name.c_str(), single->apiname_name.length() + 1);
//
single->target->bubName.enabled = single->bubname_enabled;
strlcpy(single->target->bubName.name, single->bubname_name.c_str(), single->bubname_name.length() + 1);
//
single->target->bpm.enabled = single->bpm_enabled;
strlcpy(single->target->bpm.name, single->bpm_name.c_str(), single->bpm_name.length() + 1);
//
single->target->ambientTemp.enabled = single->ambienttemp_enabled;
strlcpy(single->target->ambientTemp.name, single->ambienttemp_name.c_str(), single->ambienttemp_name.length() + 1);
//
single->target->vesselTemp.enabled = single->vesseltemp_enabled;
strlcpy(single->target->vesselTemp.name, single->vesseltemp_name.c_str(), single->vesseltemp_name.length() + 1);
//
single->target->tempFormat.enabled = single->tempformat_enabled;
strlcpy(single->target->tempFormat.name, single->tempformat_name.c_str(), single->tempformat_name.length() + 1);
//
// Grab correct URL for target type
strlcpy(single->target->url, single->targeturl.c_str(), single->targeturl.length() + 1);
//
// API Key handling parameters
single->target->key.enabled = single->apikey_enabled;
strlcpy(single->target->key.name, single->apikey_name.c_str(), single->apikey_name.length());
}
return single;
}

bool BrewfTarget::push() {
Log.verbose(F("Triggered %s push." CR), single->target->target.name);
if (single->target->apiName.enabled && strlen(config.brewfather.key)) { // Key target is unique per target (for now)
single->target->target.enabled = true;
strlcpy(single->target->key.name, config.brewfather.key, sizeof(config.brewfather.key)); // Key target is unique per target (for now)
} else {
single->target->target.enabled = false;
strlcpy(single->target->key.name, "", sizeof(""));
}
LCBUrl lcburl;
if (single->target->target.enabled) {
if (lcburl.setUrl(String(single->target->url))) {
IPAddress resolvedIP = resolveHost(lcburl.getHost().c_str());
if (resolvedIP == INADDR_NONE) {
if (single->target->ip == INADDR_NONE) {
Log.error(F("Unable to resolve host %s to IP address." CR), lcburl.getHost().c_str());
return false;
} else {
Log.verbose(F("Using cached information for host %s at IP %s." CR), lcburl.getHost().c_str(), single->target->ip.toString().c_str());
}
} else {
Log.verbose(F("Resolved host %s to IP %s." CR), lcburl.getHost().c_str(), resolvedIP.toString().c_str());
single->target->ip = resolvedIP;
}
} else {
Log.error(F("Invalid URL in %s configuration: %s" CR), single->target->target.name, single->target->url);
return false;
}
} else {
Log.verbose(F("%s not enabled, skipping." CR), single->target->target.name);
return true;
}

if (pushToTarget(single->target, target->ip, lcburl.getPort())) {
Log.notice(F("%s post ok." CR), single->target->target.name);
return true;
} else {
Log.error(F("%s post failed." CR), single->target->target.name);
return false;
}
}
100 changes: 100 additions & 0 deletions src/brewftarget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* Copyright (C) 2019-2020 Lee C. Bussy (@LBussy)

This file is part of Lee Bussy's Brew Bubbbles (brew-bubbles).

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */

#ifndef _BREWFTARGET_H
#define _BREWFTARGET_H

#include "pushtarget.h"
#include "pushhelper.h"
#include "jsonconfig.h"
#include <LCBUrl.h>
#include <ArduinoLog.h>
#include <Arduino.h>

class BrewfTarget {
private:
// Singleton Declarations
BrewfTarget() {}
static BrewfTarget *single;
// External Declarations
PushTarget *target;
// Private Methods

// Private Properties

/////////////////////////////////////////////////////////////////////
// Configure Target - Below are configuration items per target type
/////////////////////////////////////////////////////////////////////

// Enable target and target name
const bool target_enabled = true;
String target_name = "Brewerfather";
//
// Check return body for success
const bool checkbody_enabled = true;
String checkbody_name = "200";
//
// Turn JSON points on/off and provide JSON field name per target type
//
const bool apiname_enabled = false;
String apiname_name = "";
//
const bool bubname_enabled = true;
String bubname_name = "name";
//
const bool bpm_enabled = true;
String bpm_name = "bpm";
//
const bool ambienttemp_enabled = true;
String ambienttemp_name = "ext_temp";
//
const bool vesseltemp_enabled = true;
String vesseltemp_name = "temp";
//
const bool tempformat_enabled = true;
String tempformat_name = "temp_unit";
//
// Main URL for endpoint
String targeturl = "http://log.brewfather.net/stream?id=";
//
const bool apikey_enabled = true;
String apikey_name = ""; // Will pick this up from config

/////////////////////////////////////////////////////////////////////
// Configure Target - Above are configuration items per target type
/////////////////////////////////////////////////////////////////////

public:
// Singleton Declarations
static BrewfTarget* getInstance();
~BrewfTarget() {single = NULL;}
// External Declarations

// Public Methods
bool push();
// Public Properties

};

extern struct Config config;

#endif // _BREWFTARGET_H
8 changes: 6 additions & 2 deletions src/jsonconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ bool printFile(const char *filename)
bool serializeConfig(const Config &config, Print &dst)
{
// Serialize configuration
const size_t capacity = 5 * JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(9);
const size_t capacity = 3*JSON_OBJECT_SIZE(2) + 4*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(8);
DynamicJsonDocument doc(capacity);

// Create an object at the root
Expand All @@ -123,7 +123,7 @@ bool serializeConfig(const Config &config, Print &dst)
bool deserializeConfig(Stream &src, Config &config)
{
// Deserialize configuration
const size_t capacity = 5 * JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(9) + 210;
const size_t capacity = 3*JSON_OBJECT_SIZE(2) + 4*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(8) + 690;
DynamicJsonDocument doc(capacity);

// Parse the JSON object in the file
Expand Down Expand Up @@ -295,6 +295,8 @@ void Config::load(JsonObjectConst obj)
urltarget.load(obj["urltarget"]);
// Load Brewer's Friend config object
brewersfriend.load(obj["brewersfriend"]);
// Load Brewfather config object
brewfather.load(obj["brewfather"]);
// Load dospiffs1, check for null
JsonVariantConst ds1 = obj["dospiffs1"];
if (!ds1.isNull())
Expand Down Expand Up @@ -329,6 +331,8 @@ void Config::save(JsonObject obj) const
urltarget.save(obj.createNestedObject("urltarget"));
// Add Brewer's Friend object
brewersfriend.save(obj.createNestedObject("brewersfriend"));
// Add Brewfather object
brewfather.save(obj.createNestedObject("brewfather"));
// Add dospiffs1 object
obj["dospiffs1"] = dospiffs1;
// Add dospiffs2 object
Expand Down
1 change: 1 addition & 0 deletions src/jsonconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ struct Config
Calibrate calibrate;
URLTarget urltarget;
KeyTarget brewersfriend;
KeyTarget brewfather;
bool dospiffs1;
bool dospiffs2;
bool didupdate;
Expand Down
10 changes: 10 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ void loop() {
Ticker bfTimer;
bfTimer.attach(config.brewersfriend.freq * 60, setDoBFTarget);

// Brewer's friend timer
Ticker brewfTimer;
brewfTimer.attach(config.brewfather.freq * 60, setDoBrewfTarget);

// mDNS Reset Timer - Helps avoid the host not found issues
Ticker mDNSTimer;
mDNSTimer.attach(MDNSTIMER, mdnsreset);
Expand Down Expand Up @@ -105,6 +109,12 @@ void loop() {
bfTimer.attach(config.brewersfriend.freq * 60, setDoBFTarget);
config.brewersfriend.update = false;
}
if (config.brewfather.update) {
Log.notice(F("Resetting Brewer's Friend frequency timer to %l minutes." CR), config.brewersfriend.freq);
bfTimer.detach();
bfTimer.attach(config.brewfather.freq * 60, setDoBrewfTarget);
config.brewfather.update = false;
}

_delay(50); // Required to "loosen up" the loop so mDNS and webpages are responsive

Expand Down
1 change: 1 addition & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ SOFTWARE. */
#include "pushtarget.h"
#include "target.h"
#include "bftarget.h"
#include "brewftarget.h"
#include "pushhelper.h"
#include "bubbles.h"
#include "mdns.h"
Expand Down
12 changes: 6 additions & 6 deletions src/pushhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,15 @@ void setDoBFTarget() {
doBFTarget = true; // Semaphore required for Ticker + radio event
}

void setDoBRFTarget() {
doBRFTarget = true; // Semaphore required for Ticker + radio event
void setDoBrewfTarget() {
doBrewfTarget = true; // Semaphore required for Ticker + radio event
}

void tickerLoop() {
Bubbles *bubble = Bubbles::getInstance();
Target *target = Target::getInstance();
BFTarget *bfTarget = BFTarget::getInstance();
// BRFTarget *brfTarget = BRFTarget::getInstance();
BrewfTarget *brewfTarget = BrewfTarget::getInstance();

// Handle JSON posts
//
Expand All @@ -180,9 +180,9 @@ void tickerLoop() {
}
//
// Do Brewfather Post
if (doBRFTarget) { // Do BF post
doBRFTarget = false;
// brfTarget->push(); // TODO - Attach BF Target
if (doBrewfTarget) { // Do BF post
doBrewfTarget = false;
brewfTarget->push();
}

// Handle the board LED status
Expand Down
6 changes: 3 additions & 3 deletions src/pushhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ SOFTWARE. */
#include "pushtarget.h"
#include "target.h"
#include "bftarget.h"
//#include "brftarget.h"
#include "brewftarget.h"
#include <ESP8266WiFi.h>

IPAddress resolveHost(const char hostname[129]);
bool pushToTarget(PushTarget*, IPAddress, int);
void tickerLoop();
void setDoURLTarget();
void setDoBFTarget();
void setDoBRFTarget();
void setDoBrewfTarget();

static bool __attribute__((unused)) doURLTarget = false; // Semaphore for Target timer
static bool __attribute__((unused)) doBFTarget = false; // Semaphore for BF timer
static bool __attribute__((unused)) doBRFTarget = false; // Semaphore for BRF timer
static bool __attribute__((unused)) doBrewfTarget = false; // Semaphore for BRF timer

#endif // _PUSHHELPER_H
Loading

0 comments on commit 9b98d94

Please sign in to comment.