#include "mqtt.h" void mqttHA::merge(JsonVariant &dst, JsonVariantConst src) { if (src.is()) { for (JsonPairConst kvp : src.as()) { 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); }