254 lines
8.0 KiB
C++
Executable File
254 lines
8.0 KiB
C++
Executable File
#include "mqtt.h"
|
|
|
|
void mqttHA::merge(JsonVariant &dst, JsonVariantConst src)
|
|
{
|
|
if (src.is<JsonObjectConst>())
|
|
{
|
|
for (JsonPairConst kvp : src.as<JsonObjectConst>())
|
|
{
|
|
if (dst[kvp.key()])
|
|
merge(dst[kvp.key()], kvp.value());
|
|
else
|
|
dst[kvp.key()] = kvp.value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dst.set(src);
|
|
}
|
|
}
|
|
|
|
String mqttHA::slugify(String input) {
|
|
return input.toLowerCase().replace(" ", "_");
|
|
}
|
|
|
|
void mqttHA::publishTemperature(DynamicJsonDocument &device, String &vessle) {
|
|
|
|
name_slug = slugify(device["name"]);
|
|
vessel_slug = slugify(vessel);
|
|
|
|
String topic = "homeassistant/sensor/" + name_slug + "_" + chipid + "/" + vessel_slug + "_temp/config";
|
|
|
|
DynamicJsonDocument entity(200);
|
|
entity["uniq_id"] = chipid + "_" + vessel_slug + "_temp";
|
|
entity["dev_cla"] = "temperature";
|
|
entity["name"] = vessle + " Temperature";
|
|
entity["unit_of_meas"] = "°" + TEMP_UNIT;
|
|
entity["val_tpl"] = "{{ value_json }}";
|
|
entity["stat_t"] = "damn_yankee/" + name_slug + "/" + vessel_slug + "_temp";
|
|
|
|
merge(entity, device)
|
|
|
|
String payload;
|
|
serializeJson(entity, payload)
|
|
|
|
mqtt_discovery(topic, payload);
|
|
}
|
|
|
|
void mqttHA::mqtt_discovery(const String topic, const String payload) {
|
|
|
|
bool response = ConnectMQTT(MQTT_BROKER, MQTT_NAME, MQTT_USER, MQTT_PASSWORD);
|
|
if (response)
|
|
{
|
|
mqtt_client.setBufferSize(512);
|
|
mqtt_client.publish(topic.c_str(), payload.c_str(), true);
|
|
|
|
/*
|
|
// Passive Sensors
|
|
mqtt_client.publish((topic + "boil_temperature/config").c_str(),
|
|
("{\"uniq_id\": \"" + chipid + "_boil_temp\"," +
|
|
"\"dev_cla\": \"temperature\"," +
|
|
"\"name\": \"Boil Temperature\"," +
|
|
"\"unit_of_meas\": \"°" + TEMP_UNIT + "\"," +
|
|
"\"val_tpl\": \"{{ value_json }}\"," +
|
|
"\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_temperature\"," +
|
|
device + "}")
|
|
.c_str(),
|
|
true);
|
|
mqtt_client.publish((topic + "mash_temperature/config").c_str(),
|
|
("{\"uniq_id\": \"" + chipid + "_mash_temp\"," +
|
|
"\"dev_cla\": \"temperature\"," +
|
|
"\"name\": \"Mash Temperature\"," +
|
|
"\"unit_of_meas\": \"°" + TEMP_UNIT + "\"," +
|
|
"\"val_tpl\": \"{{ value_json }}\"," +
|
|
"\"stat_t\": \"brewhouse/" + DEVNAME + "/mash_temperature\"," +
|
|
device + "}")
|
|
.c_str(),
|
|
true);
|
|
|
|
// User Changeable Settings
|
|
topic = "homeassistant/number/brewhouse_" + chipid + "/";
|
|
mqtt_client.publish((topic + "mash_setpoint/config").c_str(),
|
|
("{\"uniq_id\": \"" + chipid + "_mash_setpoint\"," +
|
|
"\"dev_cla\": \"temperature\"," +
|
|
"\"name\": \"mash Setpoint\"," +
|
|
"\"val_tpl\": \"{{ value_json }}\"," +
|
|
"\"cmd_tpl\": \"{{ value_json }}\"," +
|
|
"\"stat_t\": \"brewhouse/" + DEVNAME + "/mash_setpoint\"," +
|
|
"\"cmd_t\": \"brewhouse/" + DEVNAME + "/mash_set_temp\"," +
|
|
device + "}")
|
|
.c_str(),
|
|
true);
|
|
|
|
mqtt_client.publish((topic + "boil_setpoint/config").c_str(),
|
|
("{\"uniq_id\": \"" + chipid + "_boil_setpoint\"," +
|
|
"\"dev_cla\": \"temperature\"," +
|
|
"\"name\": \"Boil Setpoint\"," +
|
|
"\"val_tpl\": \"{{ value_json }}\"," +
|
|
"\"cmd_tpl\": \"{{ value_json }}\"," +
|
|
"\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_setpoint\"," +
|
|
"\"cmd_t\": \"brewhouse/" + DEVNAME + "/boil_set_temp\"," +
|
|
device + "}")
|
|
.c_str(),
|
|
true);
|
|
mqtt_client.publish((topic + "boil_pwm/config").c_str(),
|
|
("{\"uniq_id\": \"" + chipid + "_boil_pwm\"," +
|
|
"\"dev_cla\": \"temperature\"," +
|
|
"\"name\": \"Boil pwm\"," +
|
|
"\"val_tpl\": \"{{ value_json }}\"," +
|
|
"\"cmd_tpl\": \"{{ value_json }}\"," +
|
|
"\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_pwm\"," +
|
|
"\"cmd_t\": \"brewhouse/" + DEVNAME + "/boil_set_pwm\"," +
|
|
device + "}")
|
|
.c_str(),
|
|
true);
|
|
|
|
topic = "homeassistant/select/brewhouse_" + chipid + "/";
|
|
mqtt_client.publish((topic + "boil_mode/config").c_str(),
|
|
("{\"uniq_id\": \"" + chipid + "_boil_mode\"," +
|
|
"\"name\": \"Boil Mode\"," +
|
|
"\"ops\": [\"PWM\",\"PID\"]," +
|
|
"\"val_tpl\": \"{{ value_json }}\"," +
|
|
"\"cmd_tpl\": \"{{ value_json }}\"," +
|
|
"\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_mode\"," +
|
|
"\"cmd_t\": \"brewhouse/" + DEVNAME + "/boil_set_mode\"," +
|
|
device + "}")
|
|
.c_str(),
|
|
true);
|
|
*/
|
|
mqtt_client.loop();
|
|
}
|
|
|
|
bool mqttHA::ConnectMQTT(const String &server, const String &name, const String &user, const String &password) {
|
|
|
|
Serial.println("Setup MQTT client.");
|
|
mqtt_client.begin(server.c_str(), _net);
|
|
mqtt_client.onMessage(MessageReceived);
|
|
|
|
Serial.println("connecting MQTT...");
|
|
|
|
byte i = 0;
|
|
|
|
while (!mqtt_client.connected() && (i < 3)) {
|
|
boolean ret;
|
|
ret = mqtt_client.connect(name.c_str(), user.c_str(), password.c_str())
|
|
if (ret) {
|
|
Serial.println("Connected to MQTT");
|
|
|
|
mqtt_client.subscribe("brewery/setpoint/bk");
|
|
|
|
return true;
|
|
|
|
} else {
|
|
int Status = mqtt_client.state();
|
|
|
|
switch (Status)
|
|
{
|
|
case -4:
|
|
Serial.println(F("Connection timeout"));
|
|
break;
|
|
|
|
case -3:
|
|
Serial.println(F("Connection lost"));
|
|
break;
|
|
|
|
case -2:
|
|
Serial.println(F("Connect failed"));
|
|
break;
|
|
|
|
case -1:
|
|
Serial.println(F("Disconnected"));
|
|
break;
|
|
|
|
case 1:
|
|
Serial.println(F("Bad protocol"));
|
|
break;
|
|
|
|
case 2:
|
|
Serial.println(F("Bad client ID"));
|
|
break;
|
|
|
|
case 3:
|
|
Serial.println(F("Unavailable"));
|
|
break;
|
|
|
|
case 4:
|
|
Serial.println(F("Bad credentials"));
|
|
break;
|
|
|
|
case 5:
|
|
Serial.println(F("Unauthorized"));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
Serial.print(".");
|
|
i++;
|
|
delay(5000);
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
void mqttHA::MessageReceived(String &topic, String &payload) {
|
|
Serial.println("incoming: " + topic + " - " + payload);
|
|
|
|
/** JSON Parser Setup */
|
|
StaticJsonDocument<200> doc;
|
|
|
|
// Deserialize the JSON document
|
|
DeserializationError error = deserializeJson(doc, payload);
|
|
|
|
// Test if parsing succeeds.
|
|
if (error) {
|
|
Serial.print(F("deserializeJson() failed: "));
|
|
Serial.println(error.f_str());
|
|
return;
|
|
}
|
|
char buf[30];
|
|
strcpy(buf,TOPIC_PREFIX);
|
|
strcat(buf,BOIL_SETPOINT_TOPIC);
|
|
if (topic == buf) {
|
|
// Update PWM setpoint.
|
|
String name = doc["entity"];
|
|
String setting = doc["setpoint"];
|
|
|
|
KettleDuty = setting.toInt();
|
|
String unit = doc["units"];
|
|
|
|
Serial.println("Updating setpoint for " + name + " to " + setting + " " + unit);
|
|
}
|
|
}
|
|
|
|
static void mqttHA::SendSensorData() {
|
|
Serial.println("Sending data...");
|
|
|
|
// NOTE: max message length is 250 bytes.
|
|
StaticJsonDocument<200> doc;
|
|
|
|
doc["entity"] = "boil_kettle";
|
|
doc["setpoint"] = KettleDuty;
|
|
doc["units"] = "%";
|
|
|
|
String jstr;
|
|
serializeJson(doc, jstr);
|
|
|
|
String topic = TOPIC_PREFIX;
|
|
topic += "sensor/boil_kettle";
|
|
|
|
mqtt_client.publish(topic, jstr);
|
|
|
|
}
|