diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 08044775f..58444c916 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -87,7 +87,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const void Boiler::register_mqtt_ha_config() { // Create the Master device StaticJsonDocument doc; - doc["name"] = F("EMS-ESP"); + doc["name"] = F("Service Code"); doc["uniq_id"] = F("boiler"); doc["ic"] = F("mdi:home-thermometer-outline"); doc["stat_t"] = F("ems-esp/boiler_data"); @@ -101,39 +101,160 @@ void Boiler::register_mqtt_ha_config() { ids.add("ems-esp-boiler"); Mqtt::publish_retain(F("homeassistant/sensor/ems-esp/boiler/config"), doc.as(), true); // publish the config payload with retain flag - Mqtt::register_mqtt_ha_binary_sensor(F("Boiler DHW"), this->device_type(), "tapwater_active"); - Mqtt::register_mqtt_ha_binary_sensor(F("Boiler Heating"), this->device_type(), "heating_active"); + Mqtt::register_mqtt_ha_binary_sensor(F_(tapwaterActive), this->device_type(), "tapwaterActive"); + Mqtt::register_mqtt_ha_binary_sensor(F_(heatingActive), this->device_type(), "heatingActive"); - Mqtt::register_mqtt_ha_sensor(F("Service Code"), this->device_type(), "serviceCode", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Service Code number"), this->device_type(), "serviceCodeNumber", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Boiler WW Selected Temp"), this->device_type(), "wWSelTemp", "°C", "mdi:coolant-temperature"); - Mqtt::register_mqtt_ha_sensor(F("Selected flow temperature"), this->device_type(), "selFlowTemp", "°C", "mdi:coolant-temperature"); - Mqtt::register_mqtt_ha_sensor(F("Current flow temperature"), this->device_type(), "curFlowTemp", "°C", "mdi:coolant-temperature"); - Mqtt::register_mqtt_ha_sensor(F("Warm Water set temperature"), this->device_type(), "wWSetTemp", "°C", "mdi:coolant-temperature"); - Mqtt::register_mqtt_ha_sensor(F("Warm Water current temperature (intern)"), this->device_type(), "wWCurTmp", "°C", "mdi:coolant-temperature"); - Mqtt::register_mqtt_ha_sensor(F("Warm Water current temperature (extern)"), this->device_type(), "wWCurTmp2", "°C", "mdi:coolant-temperature"); - Mqtt::register_mqtt_ha_sensor(F("Pump modulation"), this->device_type(), "pumpMod", "%", "mdi:sine-wave"); - Mqtt::register_mqtt_ha_sensor(F("Heat Pump modulation"), this->device_type(), "pumpMod2", "%", "mdi:sine-wave"); - Mqtt::register_mqtt_ha_sensor(F("System Pressure"), this->device_type(), "sysPress", "bar", ""); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(serviceCodeNumber), this->device_type(), "serviceCodeNumber", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWSelTemp), this->device_type(), "wWSelTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWSetTemp), this->device_type(), "wWSetTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWDisinfectionTemp), this->device_type(), "wWDisinfectionTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(selFlowTemp), this->device_type(), "selFlowTemp", F_(degrees), F_(icontemperature)); + + Mqtt::register_mqtt_ha_sensor(nullptr, F_(selBurnPow), this->device_type(), "selBurnPow", F_(percent), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(curBurnPow), this->device_type(), "curBurnPow", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(pumpMod), this->device_type(), "pumpMod", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(pumpMod2), this->device_type(), "pumpMod2", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWType), this->device_type(), "wWType", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWChargeType), this->device_type(), "wWChargeType", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWCircPump), this->device_type(), "wWCircPump", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWCiPuMode), this->device_type(), "wWCiPuMode", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWCirc), this->device_type(), "wWCirc", nullptr, nullptr); + + Mqtt::register_mqtt_ha_sensor(nullptr, F_(outdoorTemp), this->device_type(), "outdoorTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWCurTmp), this->device_type(), "wWCurTmp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWCurTmp2), this->device_type(), "wWCurTmp2", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wWCurFlow), this->device_type(), "wWCurFlow", F("l/min"), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(curFlowTemp), this->device_type(), "curFlowTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(retTemp), this->device_type(), "retTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(switchTemp), this->device_type(), "switchTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(sysPress), this->device_type(), "sysPress", F_(bar), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(boilTemp), this->device_type(), "boilTemp", F_(degrees), nullptr); + + // TODO add remaining values to MQTT + +/* + + Mqtt::register_mqtt_ha_sensor(F_(burnGas), this->device_type(), "burnGas", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(flameCurr), this->device_type(), "flameCurr", F_(uA), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(heatPump), this->device_type(), "heatPump", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(fanWork), this->device_type(), "fanWork", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(ignWork), this->device_type(), "ignWork", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWHeat), this->device_type(), "wWHeat", nullptr, nullptr); + + Mqtt::register_mqtt_ha_sensor(F_(wwStorageTemp1), this->device_type(), "wwStorageTemp1", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wwStorageTemp2), this->device_type(), "wwStorageTemp2", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(exhaustTemp), this->device_type(), "exhaustTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWActivated), this->device_type(), "wWActivated", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWOnetime), this->device_type(), "wWOnetime", nullptr, nullptr); + + Mqtt::register_mqtt_ha_sensor(F_(wWDisinfecting), this->device_type(), "wWDisinfecting", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWCharge), this->device_type(), "wWCharge", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWRecharge), this->device_type(), "wWRecharge", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWTempOK), this->device_type(), "wWTempOK", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWActive), this->device_type(), "wWActive", nullptr, nullptr); + + Mqtt::register_mqtt_ha_sensor(F_(heatingActivated), this->device_type(), "heatingActivated", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(heatingTemp), this->device_type(), "heatingTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(pumpModMax), this->device_type(), "pumpModMax", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(pumpModMin), this->device_type(), "pumpModMin", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(pumpDelay), this->device_type(), "pumpDelay", F_(min), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(burnMinPeriod), this->device_type(), "burnMinPeriod", F_(min), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(burnMinPower), this->device_type(), "burnMinPower", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(burnMaxPower), this->device_type(), "burnMaxPower", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(boilHystOn), this->device_type(), "boilHystOn", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(boilHystOff), this->device_type(), "boilHystOff", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(setFlowTemp), this->device_type(), "setFlowTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWSetPumpPower), this->device_type(), "wWSetPumpPower", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(wwMixTemperature), this->device_type(), "wwMixTemperature", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wwBufferBoilerTemperature), this->device_type(), "wwBufferBoilerTemperature", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWStarts), this->device_type(), "wWStarts", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWWorkM), this->device_type(), "wWWorkM", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(setBurnPow), this->device_type(), "setBurnPow", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(burnStarts), this->device_type(), "burnStarts", nullptr, nullptr); + */ } // send stuff to the Web UI void Boiler::device_info_web(JsonArray & root) { JsonObject dataElement; - render_value_json(root, "", F("Service code"), serviceCodeChar_, nullptr); - render_value_json(root, "", F("Service code number"), serviceCode_, nullptr); - render_value_json(root, "", F("Hot tap water"), tap_water_active_, nullptr, EMS_VALUE_BOOL); - render_value_json(root, "", F("Central Heating"), heating_active_, nullptr, EMS_VALUE_BOOL); - render_value_json(root, "", F("Selected flow temperature"), selFlowTemp_, F_(degrees)); - render_value_json(root, "", F("Current flow temperature"), curFlowTemp_, F_(degrees), 10); - render_value_json(root, "", F("Warm Water selected temperature"), wWSelTemp_, F_(degrees)); - render_value_json(root, "", F("Warm Water set temperature"), wWSetTmp_, F_(degrees)); - render_value_json(root, "", F("Warm Water current temperature (intern)"), wWCurTmp_, F_(degrees), 10); - render_value_json(root, "", F("Warm Water current temperature (extern)"), wWCurTmp2_, F_(degrees), 10); - render_value_json(root, "", F("Pump modulation"), pumpMod_, F_(percent)); - render_value_json(root, "", F("Heat Pump modulation"), pumpMod2_, F_(percent)); - render_value_json(root, "", F("System pressure"), sysPress_, F_(bar), 10); + render_value_json(root, "", F_(serviceCode), serviceCodeChar_, nullptr); + render_value_json(root, "", F_(serviceCodeNumber), serviceCode_, nullptr); + render_value_json(root, "", F_(tapwaterActive), tap_water_active_, nullptr, EMS_VALUE_BOOL); + render_value_json(root, "", F_(heatingActive), heating_active_, nullptr, EMS_VALUE_BOOL); + render_value_json(root, "", F_(selFlowTemp), selFlowTemp_, F_(degrees)); + render_value_json(root, "", F_(curFlowTemp), curFlowTemp_, F_(degrees), 10); + render_value_json(root, "", F_(wWSelTemp), wWSelTemp_, F_(degrees)); + render_value_json(root, "", F_(wWSetTemp), wWSetTmp_, F_(degrees)); + render_value_json(root, "", F_(wWCurTmp), wWCurTmp_, F_(degrees), 10); + render_value_json(root, "", F_(wWCurTmp2), wWCurTmp2_, F_(degrees), 10); + render_value_json(root, "", F_(pumpMod), pumpMod_, F_(percent)); + render_value_json(root, "", F_(pumpMod2), pumpMod2_, F_(percent)); + render_value_json(root, "", F_(sysPress), sysPress_, F_(bar), 10); + + // TODO add remaining values to web + /* + Mqtt::register_mqtt_ha_sensor(F_(heatingActive), this->device_type(), "heatingActive", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(tapwaterActive), this->device_type(), "tapwaterActive", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(serviceCode), this->device_type(), "serviceCode", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(serviceCodeNumber), this->device_type(), "serviceCodeNumber", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWSelTemp), this->device_type(), "wWSelTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWSetTemp), this->device_type(), "wWSetTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWDisinfectionTemp), this->device_type(), "wWDisinfectionTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(selFlowTemp), this->device_type(), "selFlowTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(selBurnPow), this->device_type(), "selBurnPow", F_(percent), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(curBurnPow), this->device_type(), "curBurnPow", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(pumpMod), this->device_type(), "pumpMod", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(pumpMod2), this->device_type(), "pumpMod2", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(wWType), this->device_type(), "wWType", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWChargeType), this->device_type(), "wWChargeType", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWCircPump), this->device_type(), "wWCircPump", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWCiPuMode), this->device_type(), "wWCiPuMode", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWCirc), this->device_type(), "wWCirc", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(outdoorTemp), this->device_type(), "outdoorTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWCurTmp), this->device_type(), "wWCurTmp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWCurTmp2), this->device_type(), "wWCurTmp2", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWCurFlow), this->device_type(), "wWCurFlow", F("l/min"), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(curFlowTemp), this->device_type(), "curFlowTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(retTemp), this->device_type(), "retTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(switchTemp), this->device_type(), "switchTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(sysPress), this->device_type(), "sysPress", F_(bar), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(boilTemp), this->device_type(), "boilTemp", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wwStorageTemp1), this->device_type(), "wwStorageTemp1", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wwStorageTemp2), this->device_type(), "wwStorageTemp2", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(exhaustTemp), this->device_type(), "exhaustTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWActivated), this->device_type(), "wWActivated", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWOnetime), this->device_type(), "wWOnetime", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWDisinfecting), this->device_type(), "wWDisinfecting", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWCharge), this->device_type(), "wWCharge", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWRecharge), this->device_type(), "wWRecharge", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWTempOK), this->device_type(), "wWTempOK", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWActive), this->device_type(), "wWActive", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(burnGas), this->device_type(), "burnGas", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(flameCurr), this->device_type(), "flameCurr", F_(uA), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(heatPump), this->device_type(), "heatPump", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(fanWork), this->device_type(), "fanWork", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(ignWork), this->device_type(), "ignWork", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWHeat), this->device_type(), "wWHeat", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(heatingActivated), this->device_type(), "heatingActivated", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(heatingTemp), this->device_type(), "heatingTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(pumpModMax), this->device_type(), "pumpModMax", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(pumpModMin), this->device_type(), "pumpModMin", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(pumpDelay), this->device_type(), "pumpDelay", F_(min), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(burnMinPeriod), this->device_type(), "burnMinPeriod", F_(min), nullptr); + Mqtt::register_mqtt_ha_sensor(F_(burnMinPower), this->device_type(), "burnMinPower", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(burnMaxPower), this->device_type(), "burnMaxPower", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(boilHystOn), this->device_type(), "boilHystOn", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(boilHystOff), this->device_type(), "boilHystOff", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(setFlowTemp), this->device_type(), "setFlowTemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWSetPumpPower), this->device_type(), "wWSetPumpPower", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(wwMixTemperature), this->device_type(), "wwMixTemperature", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wwBufferBoilerTemperature), this->device_type(), "wwBufferBoilerTemperature", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(F_(wWStarts), this->device_type(), "wWStarts", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(wWWorkM), this->device_type(), "wWWorkM", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(F_(setBurnPow), this->device_type(), "setBurnPow", F_(percent), F_(iconpercent)); + Mqtt::register_mqtt_ha_sensor(F_(burnStarts), this->device_type(), "burnStarts", nullptr, nullptr); +*/ } bool Boiler::command_info(const char * value, const int8_t id, JsonObject & output) { @@ -145,14 +266,17 @@ bool Boiler::command_info(const char * value, const int8_t id, JsonObject & outp bool Boiler::export_values(JsonObject & output) { char s[10]; // for formatting strings + // Hot tap water bool if (Helpers::hasValue(heating_active_, EMS_VALUE_BOOL)) { - output["heating_active"] = Helpers::render_value(s, heating_active_, EMS_VALUE_BOOL); + output["heatingActive"] = Helpers::render_value(s, heating_active_, EMS_VALUE_BOOL); } + // Central heating bool if (Helpers::hasValue(tap_water_active_, EMS_VALUE_BOOL)) { - output["tap_water_active"] = Helpers::render_value(s, tap_water_active_, EMS_VALUE_BOOL); + output["tapwaterActive"] = Helpers::render_value(s, tap_water_active_, EMS_VALUE_BOOL); } + // Warm Water comfort setting if (Helpers::hasValue(wWComfort_)) { if (wWComfort_ == 0x00) { output["wWComfort"] = "Hot"; @@ -163,30 +287,47 @@ bool Boiler::export_values(JsonObject & output) { } } + // Warm Water selected temperature if (Helpers::hasValue(wWSelTemp_)) { output["wWSelTemp"] = wWSelTemp_; } + + // Warm Water set temperature if (Helpers::hasValue(wWSetTmp_)) { output["wWSetTemp"] = wWSetTmp_; } + + // Warm Water disinfection temperature if (Helpers::hasValue(wWDisinfectTemp_)) { output["wWDisinfectionTemp"] = wWDisinfectTemp_; } + + // Selected flow temperature deg if (Helpers::hasValue(selFlowTemp_)) { output["selFlowTemp"] = selFlowTemp_; } + + // Burner selected max power % if (Helpers::hasValue(selBurnPow_)) { output["selBurnPow"] = selBurnPow_; } + + // Burner current power % if (Helpers::hasValue(curBurnPow_)) { output["curBurnPow"] = curBurnPow_; } + + // Pump modulation % if (Helpers::hasValue(pumpMod_)) { output["pumpMod"] = pumpMod_; } + + // Heat Pump modulation % if (Helpers::hasValue(pumpMod2_)) { output["pumpMod2"] = pumpMod2_; } + + // Warm Water type if (wWType_ == 0) { // no output if not set output["wWType"] = F("off"); } else if (wWType_ == 1) { @@ -198,147 +339,269 @@ bool Boiler::export_values(JsonObject & output) { } else if (wWType_ == 4) { output["wWType"] = F("layered buffer"); } + + // Warm Water charging type if (Helpers::hasValue(wWChargeType_, EMS_VALUE_BOOL)) { - output["wWChargeType"] = wWChargeType_ ? "valve" : "pump"; + output["wWChargeType"] = wWChargeType_ ? "3-way valve" : "charge pump"; } + + // Warm Water circulation pump available bool if (Helpers::hasValue(wWCircPump_, EMS_VALUE_BOOL)) { output["wWCircPump"] = Helpers::render_value(s, wWCircPump_, EMS_VALUE_BOOL); } + + // Warm Water circulation pump freq if (Helpers::hasValue(wWCircPumpMode_)) { - output["wWCiPuMode"] = wWCircPumpMode_; + if (wWCircPumpMode_ == 7) { + output["wWCiPuMode"] = F("continuous"); + } else { + char s[7]; + char buffer[2]; + buffer[0] = (wWCircPumpMode_ % 10) + '0'; + buffer[1] = '\0'; + strlcpy(s, buffer, 7); + strlcat(s, "x3min", 7); + output["wWCiPuMode"] = s; + } } + + // Warm Water circulation active bool if (Helpers::hasValue(wWCirc_, EMS_VALUE_BOOL)) { output["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL); } + + // Outside temperature if (Helpers::hasValue(extTemp_)) { output["outdoorTemp"] = (float)extTemp_ / 10; } + + // Warm Water current temperature (intern) if (Helpers::hasValue(wWCurTmp_)) { output["wWCurTmp"] = (float)wWCurTmp_ / 10; } + + // Warm Water current temperature (extern) if (Helpers::hasValue(wWCurTmp2_)) { output["wWCurTmp2"] = (float)wWCurTmp2_ / 10; } + + // Warm Water current tap water flow l/min if (Helpers::hasValue(wWCurFlow_)) { output["wWCurFlow"] = (float)wWCurFlow_ / 10; } + + // Current flow temperature if (Helpers::hasValue(curFlowTemp_)) { output["curFlowTemp"] = (float)curFlowTemp_ / 10; } + + // Return temperature if (Helpers::hasValue(retTemp_)) { output["retTemp"] = (float)retTemp_ / 10; } + + // Mixing switch temperature if (Helpers::hasValue(switchTemp_)) { output["switchTemp"] = (float)switchTemp_ / 10; } + + // System pressure if (Helpers::hasValue(sysPress_)) { output["sysPress"] = (float)sysPress_ / 10; } + + // Max boiler temperature if (Helpers::hasValue(boilTemp_)) { output["boilTemp"] = (float)boilTemp_ / 10; } + + // Warm water storage temperature (intern) if (Helpers::hasValue(wwStorageTemp1_)) { output["wwStorageTemp1"] = (float)wwStorageTemp1_ / 10; } + + // Warm water storage temperature (extern) if (Helpers::hasValue(wwStorageTemp2_)) { output["wwStorageTemp2"] = (float)wwStorageTemp2_ / 10; } + + // Exhaust temperature if (Helpers::hasValue(exhaustTemp_)) { output["exhaustTemp"] = (float)exhaustTemp_ / 10; } + + // Warm Water activated bool if (Helpers::hasValue(wWActivated_, EMS_VALUE_BOOL)) { output["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL); } + + // Warm Water one time charging bool if (Helpers::hasValue(wWOneTime_, EMS_VALUE_BOOL)) { output["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL); } + + // Warm Water disinfecting bool if (Helpers::hasValue(wWDisinfecting_, EMS_VALUE_BOOL)) { output["wWDisinfecting"] = Helpers::render_value(s, wWDisinfecting_, EMS_VALUE_BOOL); } + + // Warm water charging bool if (Helpers::hasValue(wWCharging_, EMS_VALUE_BOOL)) { output["wWCharge"] = Helpers::render_value(s, wWCharging_, EMS_VALUE_BOOL); } + + // Warm water recharge bool if (Helpers::hasValue(wWRecharging_, EMS_VALUE_BOOL)) { output["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL); } + + // Warm water temperature ok bool if (Helpers::hasValue(wWTemperatureOK_, EMS_VALUE_BOOL)) { output["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL); } + + // Warm water active bool if (Helpers::hasValue(wWActive_, EMS_VALUE_BOOL)) { output["wWActive"] = Helpers::render_value(s, wWActive_, EMS_VALUE_BOOL); } + + // Gas bool if (Helpers::hasValue(burnGas_, EMS_VALUE_BOOL)) { output["burnGas"] = Helpers::render_value(s, burnGas_, EMS_VALUE_BOOL); } + + // Flame current uA if (Helpers::hasValue(flameCurr_)) { output["flameCurr"] = (float)(int16_t)flameCurr_ / 10; } + + // Boiler pump bool if (Helpers::hasValue(heatPmp_, EMS_VALUE_BOOL)) { output["heatPump"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL); } + + // Fan bool if (Helpers::hasValue(fanWork_, EMS_VALUE_BOOL)) { output["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL); } + + // Ignition bool if (Helpers::hasValue(ignWork_, EMS_VALUE_BOOL)) { output["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL); } + + // Warm Water charging bool if (Helpers::hasValue(wWHeat_, EMS_VALUE_BOOL)) { output["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL); } + + // heating activated bool if (Helpers::hasValue(heating_activated_, EMS_VALUE_BOOL)) { output["heatingActivated"] = Helpers::render_value(s, heating_activated_, EMS_VALUE_BOOL); } + + // Heating temperature setting on the boiler if (Helpers::hasValue(heating_temp_)) { output["heatingTemp"] = heating_temp_; } + + // Boiler circuit pump modulation max power % if (Helpers::hasValue(pump_mod_max_)) { output["pumpModMax"] = pump_mod_max_; } + + // Boiler circuit pump modulation min power % if (Helpers::hasValue(pump_mod_min_)) { output["pumpModMin"] = pump_mod_min_; } + + // Boiler circuit pump delay time min if (Helpers::hasValue(pumpDelay_)) { output["pumpDelay"] = pumpDelay_; } + + // Boiler burner min period min if (Helpers::hasValue(burnPeriod_)) { output["burnMinPeriod"] = burnPeriod_; } + + // Boiler burner min power % if (Helpers::hasValue(burnPowermin_)) { output["burnMinPower"] = burnPowermin_; } + + // Boiler burner max power % if (Helpers::hasValue(burnPowermax_)) { output["burnMaxPower"] = burnPowermax_; } + + // Boiler temp hysteresis on degrees if (Helpers::hasValue(boilTemp_on_)) { output["boilHystOn"] = boilTemp_on_; } + + // Boiler temp hysteresis off degrees if (Helpers::hasValue(boilTemp_off_)) { output["boilHystOff"] = boilTemp_off_; } + + // Set Flow temperature if (Helpers::hasValue(setFlowTemp_)) { output["setFlowTemp"] = setFlowTemp_; } + + // Warm Water pump set power % if (Helpers::hasValue(setWWPumpPow_)) { output["wWSetPumpPower"] = setWWPumpPow_; } + + // Warm water mix temperature + if (Helpers::hasValue(wwMixTemperature_)) { + output["wwMixTemperature"] = wwMixTemperature_; + } + + // Warm water buffer boiler temperature + if (Helpers::hasValue(wwBufferBoilerTemperature_)) { + output["wwBufferBoilerTemperature"] = wwBufferBoilerTemperature_; + } + + // Warm Water # starts if (Helpers::hasValue(wWStarts_)) { output["wWStarts"] = wWStarts_; } + + // Warm Water active time if (Helpers::hasValue(wWWorkM_)) { output["wWWorkM"] = wWWorkM_; } + + // Total UBA working time if (Helpers::hasValue(UBAuptime_)) { output["UBAuptime"] = UBAuptime_; } + + // Boiler burner set power % + if (Helpers::hasValue(setBurnPow_)) { + output["setBurnPow_"] = setBurnPow_; + } + + // Burner # starts if (Helpers::hasValue(burnStarts_)) { output["burnStarts"] = burnStarts_; } + + // Total burner operating time if (Helpers::hasValue(burnWorkMin_)) { output["burnWorkMin"] = burnWorkMin_; } + + // Total heat operating time if (Helpers::hasValue(heatWorkMin_)) { output["heatWorkMin"] = heatWorkMin_; } + + // Service Code + // Service Code Number if (Helpers::hasValue(serviceCode_)) { output["serviceCode"] = serviceCodeChar_; output["serviceCodeNumber"] = serviceCode_; @@ -372,113 +635,78 @@ bool Boiler::updated_values() { void Boiler::show_values(uuid::console::Shell & shell) { EMSdevice::show_values(shell); // for showing the header - print_value(shell, 2, F("Hot tap water"), tap_water_active_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Central heating"), heating_active_, nullptr, EMS_VALUE_BOOL); - - print_value(shell, 2, F("Warm Water activated"), wWActivated_, nullptr, EMS_VALUE_BOOL); - if (wWType_ == 0) { - print_value(shell, 2, F("Warm Water type"), F("off")); - } else if (wWType_ == 1) { - print_value(shell, 2, F("Warm Water type"), F("flow")); - } else if (wWType_ == 2) { - print_value(shell, 2, F("Warm Water type"), F("buffered flow")); - } else if (wWType_ == 3) { - print_value(shell, 2, F("Warm Water type"), F("buffer")); - } else if (wWType_ == 4) { - print_value(shell, 2, F("Warm Water type"), F("layered buffer")); + // fetch the values into a JSON document + StaticJsonDocument doc; + JsonObject output = doc.to(); + if (!export_values(output)) { + return; // empty } - if (Helpers::hasValue(wWChargeType_, EMS_VALUE_BOOL)) { - print_value(shell, 2, F("Warm Water charging type"), wWChargeType_ ? F("3-way valve") : F("charge pump")); - } + print_value_json(shell, F("heatingActive"), F_(heatingActive), nullptr, output); + print_value_json(shell, F("tapwaterActive"), F_(tapwaterActive), nullptr, output); + print_value_json(shell, F("serviceCode"), F_(serviceCode), nullptr, output); + print_value_json(shell, F("serviceCodeNumber"), F_(serviceCodeNumber), nullptr, output); + print_value_json(shell, F("wWSelTemp"), F_(wWSelTemp), F_(degrees), output); + print_value_json(shell, F("wWSetTemp"), F_(wWSetTemp), F_(degrees), output); + print_value_json(shell, F("wWDisinfectionTemp"), F_(wWDisinfectionTemp), F_(degrees), output); + print_value_json(shell, F("selFlowTemp"), F_(selFlowTemp), F_(degrees), output); + print_value_json(shell, F("selBurnPow"), F_(selBurnPow), F_(percent), output); + print_value_json(shell, F("curBurnPow"), F_(curBurnPow), F_(percent), output); + print_value_json(shell, F("pumpMod"), F_(pumpMod), F_(percent), output); + print_value_json(shell, F("pumpMod2"), F_(pumpMod2), F_(percent), output); + print_value_json(shell, F("wWType"), F_(wWType), nullptr, output); + print_value_json(shell, F("wWChargeType"), F_(wWChargeType), nullptr, output); + print_value_json(shell, F("wWCircPump"), F_(wWCircPump), nullptr, output); + print_value_json(shell, F("wWCiPuMode"), F_(wWCiPuMode), nullptr, output); + print_value_json(shell, F("wWCirc"), F_(wWCirc), nullptr, output); + print_value_json(shell, F("outdoorTemp"), F_(outdoorTemp), F_(degrees), output); + print_value_json(shell, F("wWCurTmp"), F_(wWCurTmp), F_(degrees), output); + print_value_json(shell, F("wWCurTmp2"), F_(wWCurTmp2), F_(degrees), output); + print_value_json(shell, F("wWCurFlow"), F_(wWCurFlow), F("l/min"), output); + print_value_json(shell, F("curFlowTemp"), F_(curFlowTemp), F_(degrees), output); + print_value_json(shell, F("retTemp"), F_(retTemp), F_(degrees), output); + print_value_json(shell, F("switchTemp"), F_(switchTemp), F_(degrees), output); + print_value_json(shell, F("sysPress"), F_(sysPress), nullptr, output); + print_value_json(shell, F("boilTemp"), F_(boilTemp), F_(degrees), output); + print_value_json(shell, F("wwStorageTemp1"), F_(wwStorageTemp1), F_(degrees), output); + print_value_json(shell, F("wwStorageTemp2"), F_(wwStorageTemp2), F_(degrees), output); + print_value_json(shell, F("exhaustTemp"), F_(exhaustTemp), F_(degrees), output); + print_value_json(shell, F("wWActivated"), F_(wWActivated), nullptr, output); + print_value_json(shell, F("wWOnetime"), F_(wWOnetime), nullptr, output); + print_value_json(shell, F("wWDisinfecting"), F_(wWDisinfecting), nullptr, output); + print_value_json(shell, F("wWCharge"), F_(wWCharge), nullptr, output); + print_value_json(shell, F("wWRecharge"), F_(wWRecharge), nullptr, output); + print_value_json(shell, F("wWTempOK"), F_(wWTempOK), nullptr, output); + print_value_json(shell, F("wWActive"), F_(wWActive), nullptr, output); + print_value_json(shell, F("burnGas"), F_(burnGas), nullptr, output); + print_value_json(shell, F("flameCurr"), F_(flameCurr), F_(uA), output); + print_value_json(shell, F("heatPump"), F_(heatPump), nullptr, output); + print_value_json(shell, F("fanWork"), F_(fanWork), nullptr, output); + print_value_json(shell, F("ignWork"), F_(ignWork), nullptr, output); + print_value_json(shell, F("wWHeat"), F_(wWHeat), nullptr, output); + print_value_json(shell, F("heatingActivated"), F_(heatingActivated), nullptr, output); + print_value_json(shell, F("heatingTemp"), F_(heatingTemp), F_(degrees), output); + print_value_json(shell, F("pumpModMax"), F_(pumpModMax), F_(percent), output); + print_value_json(shell, F("pumpModMin"), F_(pumpModMin), F_(percent), output); + print_value_json(shell, F("pumpDelay"), F_(pumpDelay), F_(min), output); + print_value_json(shell, F("burnMinPeriod"), F_(burnMinPeriod), F_(min), output); + print_value_json(shell, F("burnMinPower"), F_(burnMinPower), F_(percent), output); + print_value_json(shell, F("burnMaxPower"), F_(burnMaxPower), F_(percent), output); + print_value_json(shell, F("boilHystOn"), F_(boilHystOn), F_(degrees), output); + print_value_json(shell, F("boilHystOff"), F_(boilHystOff), F_(degrees), output); + print_value_json(shell, F("setFlowTemp"), F_(setFlowTemp), F_(degrees), output); + print_value_json(shell, F("wWSetPumpPower"), F_(wWSetPumpPower), F_(percent), output); + print_value_json(shell, F("wwMixTemperature"), F_(wwMixTemperature), F_(degrees), output); + print_value_json(shell, F("wwBufferBoilerTemperature"), F_(wwBufferBoilerTemperature), F_(degrees), output); + print_value_json(shell, F("wWStarts"), F_(wWStarts), nullptr, output); + print_value_json(shell, F("wWWorkM"), F_(wWWorkM), nullptr, output); + print_value_json(shell, F("setBurnPow"), F_(setBurnPow), F_(percent), output); + print_value_json(shell, F("burnStarts"), F_(burnStarts), nullptr, output); - print_value(shell, 2, F("Warm Water circulation pump available"), wWCircPump_, nullptr, EMS_VALUE_BOOL); - - if (Helpers::hasValue(wWCircPumpMode_)) { - if (wWCircPumpMode_ == 7) { - print_value(shell, 2, F("Warm Water circulation pump freq"), F("continuous")); - } else { - char s[7]; - char buffer[2]; - buffer[0] = (wWCircPumpMode_ % 10) + '0'; - buffer[1] = '\0'; - strlcpy(s, buffer, 7); - strlcat(s, "x3min", 7); - print_value(shell, 2, F("Warm Water circulation pump freq"), s); - } - } - print_value(shell, 2, F("Warm Water circulation active"), wWCirc_, nullptr, EMS_VALUE_BOOL); - - if (wWComfort_ == 0x00) { - print_value(shell, 2, F("Warm Water comfort setting"), F("Hot")); - } else if (wWComfort_ == 0xD8) { - print_value(shell, 2, F("Warm Water comfort setting"), F("Eco")); - } else if (wWComfort_ == 0xEC) { - print_value(shell, 2, F("Warm Water comfort setting"), F("Intelligent")); - } - - print_value(shell, 2, F("Warm water mix temperature"), wwMixTemperature_, F_(degrees), 10); - print_value(shell, 2, F("Warm water buffer boiler temperature"), wwBufferBoilerTemperature_, F_(degrees), 10); - print_value(shell, 2, F("Warm Water disinfection temperature"), wWDisinfectTemp_, F_(degrees)); - print_value(shell, 2, F("Warm Water selected temperature"), wWSelTemp_, F_(degrees)); - print_value(shell, 2, F("Warm Water set temperature"), wWSetTmp_, F_(degrees)); - print_value(shell, 2, F("Warm Water current temperature (intern)"), wWCurTmp_, F_(degrees), 10); - print_value(shell, 2, F("Warm water storage temperature (intern)"), wwStorageTemp1_, F_(degrees), 10); - print_value(shell, 2, F("Warm Water current temperature (extern)"), wWCurTmp2_, F_(degrees), 10); - print_value(shell, 2, F("Warm water storage temperature (extern)"), wwStorageTemp2_, F_(degrees), 10); - print_value(shell, 2, F("Warm Water current tap water flow"), wWCurFlow_, F("l/min"), 10); - print_value(shell, 2, F("Warm Water # starts"), wWStarts_, nullptr); if (Helpers::hasValue(wWWorkM_)) { shell.printfln(F(" Warm Water active time: %d days %d hours %d minutes"), wWWorkM_ / 1440, (wWWorkM_ % 1440) / 60, wWWorkM_ % 60); } - print_value(shell, 2, F("Warm Water one time charging"), wWOneTime_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Warm Water charging"), wWHeat_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Warm Water disinfecting"), wWDisinfecting_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Selected flow temperature"), selFlowTemp_, F_(degrees)); - print_value(shell, 2, F("Current flow temperature"), curFlowTemp_, F_(degrees), 10); - print_value(shell, 2, F("Max boiler temperature"), boilTemp_, F_(degrees), 10); - print_value(shell, 2, F("Return temperature"), retTemp_, F_(degrees), 10); - print_value(shell, 2, F("Gas"), burnGas_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Boiler pump"), heatPmp_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Fan"), fanWork_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Ignition"), ignWork_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Burner selected max power"), selBurnPow_, F_(percent)); - print_value(shell, 2, F("Burner current power"), curBurnPow_, F_(percent)); - print_value(shell, 2, F("Flame current"), flameCurr_, F_(uA), 10); - print_value(shell, 2, F("System pressure"), sysPress_, F_(bar), 10); - if (Helpers::hasValue(serviceCode_)) { - shell.printfln(F(" System service code: %s (%d)"), serviceCodeChar_, serviceCode_); - } else if (serviceCodeChar_[0] != '\0') { - print_value(shell, 2, F("System service code"), serviceCodeChar_); - } - - // UBAParameters - print_value(shell, 2, F("Heating activated"), heating_activated_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Heating temperature setting on the boiler"), heating_temp_, F_(degrees)); - print_value(shell, 2, F("Boiler circuit pump modulation max power"), pump_mod_max_, F_(percent)); - print_value(shell, 2, F("Boiler circuit pump modulation min power"), pump_mod_min_, F_(percent)); - print_value(shell, 2, F("Boiler circuit pump delay time"), pumpDelay_, F_(min)); - print_value(shell, 2, F("Boiler temp hysteresis on"), boilTemp_on_, F_(degrees)); - print_value(shell, 2, F("Boiler temp hysteresis off"), boilTemp_off_, F_(degrees)); - print_value(shell, 2, F("Boiler burner min period"), burnPeriod_, F_(min)); - print_value(shell, 2, F("Boiler burner min power"), burnPowermin_, F_(percent)); - print_value(shell, 2, F("Boiler burner max power"), burnPowermax_, F_(percent)); - - // UBASetPoint - these may differ from the above - print_value(shell, 2, F("Set Flow temperature"), setFlowTemp_, F_(degrees)); - print_value(shell, 2, F("Boiler burner set power"), setBurnPow_, F_(percent)); - print_value(shell, 2, F("Warm Water pump set power"), setWWPumpPow_, F_(percent)); - - // UBAMonitorSlow - if (Helpers::hasValue(extTemp_)) { - print_value(shell, 2, F("Outside temperature"), extTemp_, F_(degrees), 10); - } - - print_value(shell, 2, F("Exhaust temperature"), exhaustTemp_, F_(degrees), 10); - print_value(shell, 2, F("Pump modulation"), pumpMod_, F_(percent)); - print_value(shell, 2, F("Heat Pump modulation"), pumpMod2_, F_(percent)); - print_value(shell, 2, F("Burner # starts"), burnStarts_, nullptr); if (Helpers::hasValue(burnWorkMin_)) { shell.printfln(F(" Total burner operating time: %d days %d hours %d minutes"), burnWorkMin_ / 1440, (burnWorkMin_ % 1440) / 60, burnWorkMin_ % 60); } diff --git a/src/devices/boiler.h b/src/devices/boiler.h index 377667843..f58ca3a17 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -47,11 +47,11 @@ class Boiler : public EMSdevice { static uuid::log::Logger logger_; void register_mqtt_ha_config(); - void register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, const char * entity); - void register_mqtt_ha_sensor(const __FlashStringHelper * name, const char * entity, const char * uom, const char * icon); void check_active(); bool export_values(JsonObject & doc); + void print_value2(uuid::console::Shell & shell, const char * param, const __FlashStringHelper * name, const __FlashStringHelper * suffix, JsonObject & json); + uint8_t last_boilerState = 0xFF; // remember last state of heating and warm water on/off bool changed_ = false; diff --git a/src/devices/mixing.cpp b/src/devices/mixing.cpp index c083acea2..5033adb86 100644 --- a/src/devices/mixing.cpp +++ b/src/devices/mixing.cpp @@ -98,17 +98,24 @@ void Mixing::show_values(uuid::console::Shell & shell) { return; // don't have any values yet } + // fetch the values into a JSON document + StaticJsonDocument doc; + JsonObject output = doc.to(); + if (!export_values(output)) { + return; // empty + } + if (type() == Type::WWC) { - print_value(shell, 2, F("Warm Water Circuit"), hc_, nullptr); - print_value(shell, 4, F("Current warm water temperature"), flowTemp_, F_(degrees), 10); - print_value(shell, 4, F("Current pump status"), pump_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 4, F("Current temperature status"), status_, nullptr); + shell.println(F_(ww_hc)); + print_value_json(shell, F("wwTemp"), F_(wwTemp), F_(degrees), output); + print_value_json(shell, F("pumpStatus"), F_(pumpStatus), nullptr, output); + print_value_json(shell, F("tempStatus"), F_(tempStatus), nullptr, output); } else { - print_value(shell, 2, F("Heating Circuit"), hc_, nullptr); - print_value(shell, 4, F("Current flow temperature"), flowTemp_, F_(degrees), 10); - print_value(shell, 4, F("Setpoint flow temperature"), flowSetTemp_, F_(degrees)); - print_value(shell, 4, F("Current pump status"), pump_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 4, F("Current valve status"), status_, F_(percent)); + shell.println(F_(hc)); + print_value_json(shell, F("flowTemp"), F_(flowTemp), F_(degrees), output); + print_value_json(shell, F("flowSetTemp"), F_(flowSetTemp), F_(degrees), output); + print_value_json(shell, F("pumpStatus"), F_(pumpStatus), nullptr, output); + print_value_json(shell, F("valveStatus"), F_(valveStatus), F_(percent), output); } shell.println(); @@ -119,7 +126,7 @@ bool Mixing::command_info(const char * value, const int8_t id, JsonObject & outp } // publish values via MQTT -// ideally we should group up all the mixing units together into a nested JSON but for now we'll send them individually +// topic is mixing_data void Mixing::publish_values() { StaticJsonDocument doc; JsonObject output = doc.to(); @@ -127,24 +134,28 @@ void Mixing::publish_values() { char topic[30]; char s[5]; strlcpy(topic, "mixing_data", 30); - strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append hc to topic + strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append device_id to topic Mqtt::publish(topic, doc.as()); // if we're using Home Assistant and haven't created the MQTT Discovery topics, do it now if ((Mqtt::mqtt_format() == Mqtt::Format::HA) && (!ha_created_)) { - register_mqtt_ha_config(); + register_mqtt_ha_config(topic); } } } // publish config topic for HA MQTT Discovery -void Mixing::register_mqtt_ha_config() { +void Mixing::register_mqtt_ha_config(const char * topic) { // Create the Master device StaticJsonDocument doc; doc["name"] = F("EMS-ESP"); doc["uniq_id"] = F("mixing"); doc["ic"] = F("mdi:home-thermometer-outline"); - doc["stat_t"] = F("ems-esp/mixing_data"); + + std::string stat_t(50, '\0'); + snprintf_P(&stat_t[0], stat_t.capacity() + 1, PSTR("%s/%s"), System::hostname().c_str(), topic); + doc["stat_t"] = stat_t; + doc["val_tpl"] = F("{{value_json.pumpStatus}}"); JsonObject dev = doc.createNestedObject("dev"); dev["name"] = F("EMS-ESP Mixing"); @@ -156,15 +167,15 @@ void Mixing::register_mqtt_ha_config() { Mqtt::publish_retain(F("homeassistant/sensor/ems-esp/mixing/config"), doc.as(), true); // publish the config payload with retain flag if (this->type() == Type::HC) { - Mqtt::register_mqtt_ha_sensor(F("Current flow temperature"), this->device_type(), "flowTemp", "°C", ""); - Mqtt::register_mqtt_ha_sensor(F("Setpoint flow temperature"), this->device_type(), "flowSetTemp", "°C", ""); - Mqtt::register_mqtt_ha_sensor(F("Current pump status"), this->device_type(), "pumpStatus", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Current valve status"), this->device_type(), "valveStatus", "", ""); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(flowTemp), this->device_type(), "flowTemp", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(flowSetTemp), this->device_type(), "flowSetTemp", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(pumpStatus), this->device_type(), "pumpStatus", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(valveStatus), this->device_type(), "valveStatus", nullptr, nullptr); } else { // WWC - Mqtt::register_mqtt_ha_sensor(F("Current flow temperature"), this->device_type(), "wwTemp", "°C", ""); - Mqtt::register_mqtt_ha_sensor(F("Current pump status"), this->device_type(), "pumpStatus", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Current temperature status"), this->device_type(), "tempStatus", "", ""); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(wwTemp), this->device_type(), "wwTemp", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(pumpStatus), this->device_type(), "pumpStatus", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(tempStatus), this->device_type(), "tempStatus", nullptr, nullptr); } ha_created_ = true; } diff --git a/src/devices/mixing.h b/src/devices/mixing.h index a308c3f13..28b145241 100644 --- a/src/devices/mixing.h +++ b/src/devices/mixing.h @@ -45,7 +45,7 @@ class Mixing : public EMSdevice { static uuid::log::Logger logger_; bool export_values(JsonObject & doc); - void register_mqtt_ha_config(); + void register_mqtt_ha_config(const char * topic); bool command_info(const char * value, const int8_t id, JsonObject & output); void process_MMPLUSStatusMessage_HC(std::shared_ptr telegram); diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index b62fb4919..0920b6ee6 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -90,25 +90,30 @@ void Solar::device_info_web(JsonArray & root) { void Solar::show_values(uuid::console::Shell & shell) { EMSdevice::show_values(shell); // always call this to show header - print_value(shell, 2, F("Collector temperature (TS1)"), collectorTemp_, F_(degrees), 10); - print_value(shell, 2, F("Bottom temperature (TS2)"), tankBottomTemp_, F_(degrees), 10); - print_value(shell, 2, F("Bottom temperature (TS5)"), tankBottomTemp2_, F_(degrees), 10); - print_value(shell, 2, F("Heat exchanger temperature (TS6)"), heatExchangerTemp_, F_(degrees), 10); - print_value(shell, 2, F("Solar pump modulation (PS1)"), solarPumpModulation_, F_(percent)); - print_value(shell, 2, F("Cylinder pump modulation (PS5)"), cylinderPumpModulation_, F_(percent)); - print_value(shell, 2, F("Valve (VS2) status"), valveStatus_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Solar Pump (PS1) active"), solarPump_, nullptr, EMS_VALUE_BOOL); + // fetch the values into a JSON document + StaticJsonDocument doc; + JsonObject output = doc.to(); + if (!export_values(output)) { + return; // empty + } + + print_value_json(shell, F("collectorTemp"), F_(collectorTemp), F_(degrees), output); + print_value_json(shell, F("tankBottomTemp"), F_(tankBottomTemp), F_(degrees), output); + print_value_json(shell, F("tankBottomTemp2"), F_(tankBottomTemp2), F_(degrees), output); + print_value_json(shell, F("heatExchangerTemp"), F_(heatExchangerTemp), F_(degrees), output); + print_value_json(shell, F("solarPumpModulation"), F_(solarPumpModulation), F_(percent), output); + print_value_json(shell, F("cylinderPumpModulation"), F_(cylinderPumpModulation), F_(percent), output); + print_value_json(shell, F("valveStatus"), F_(valveStatus), nullptr, output); + print_value_json(shell, F("solarPump"), F_(solarPump), nullptr, output); + print_value_json(shell, F("tankHeated"), F_(tankHeated), nullptr, output); + print_value_json(shell, F("collectorShutdown"), F_(collectorShutdown), nullptr, output); + print_value_json(shell, F("energyLastHour"), F_(energyLastHour), F_(wh), output); + print_value_json(shell, F("energyToday"), F_(energyToday), F_(wh), output); + print_value_json(shell, F("energyTotal"), F_(energyTotal), F_(kwh), output); if (Helpers::hasValue(pumpWorkMin_)) { shell.printfln(F(" Pump working time: %d days %d hours %d minutes"), pumpWorkMin_ / 1440, (pumpWorkMin_ % 1440) / 60, pumpWorkMin_ % 60); } - - print_value(shell, 2, F("Tank Heated"), tankHeated_, nullptr, EMS_VALUE_BOOL); - print_value(shell, 2, F("Collector shutdown"), collectorShutdown_, nullptr, EMS_VALUE_BOOL); - - print_value(shell, 2, F("Energy last hour"), energyLastHour_, F_(wh), 10); - print_value(shell, 2, F("Energy today"), energyToday_, F_(wh)); - print_value(shell, 2, F("Energy total"), energyTotal_, F_(kwh), 10); } // publish values via MQTT @@ -143,20 +148,20 @@ void Solar::register_mqtt_ha_config() { ids.add("ems-esp-solar"); Mqtt::publish_retain(F("homeassistant/sensor/ems-esp/solar/config"), doc.as(), true); // publish the config payload with retain flag - Mqtt::register_mqtt_ha_sensor(F("Collector temperature (TS1)"), this->device_type(), "collectorTemp", "°C", ""); - Mqtt::register_mqtt_ha_sensor(F("Bottom temperature (TS2)"), this->device_type(), "tankBottomTemp", "°C", ""); - Mqtt::register_mqtt_ha_sensor(F("Bottom temperature (TS5)"), this->device_type(), "tankBottomTemp2", "°C", ""); - Mqtt::register_mqtt_ha_sensor(F("Heat exchanger temperature (TS6)"), this->device_type(), "heatExchangerTemp", "°C", ""); - Mqtt::register_mqtt_ha_sensor(F("Solar pump modulation (PS1)"), this->device_type(), "solarPumpModulation", "%", ""); - Mqtt::register_mqtt_ha_sensor(F("Cylinder pump modulation (PS5)"), this->device_type(), "cylinderPumpModulation", "%", ""); - Mqtt::register_mqtt_ha_sensor(F("Pump working time"), this->device_type(), "pumpWorkMin", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Energy last hour"), this->device_type(), "energyLastHour", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Energy today"), this->device_type(), "energyToday", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Energy total"), this->device_type(), "energyTotal", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Solar Pump (PS1) active"), this->device_type(), "solarPump", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Valve (VS2) status"), this->device_type(), "valveStatus", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Tank Heated"), this->device_type(), "tankHeated", "", ""); - Mqtt::register_mqtt_ha_sensor(F("Collector shutdown"), this->device_type(), "collectorShutdown", "", ""); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(collectorTemp), this->device_type(), "collectorTemp", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(tankBottomTemp), this->device_type(), "tankBottomTemp", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(tankBottomTemp2), this->device_type(), "tankBottomTemp2", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(heatExchangerTemp), this->device_type(), "heatExchangerTemp", F_(degrees), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(solarPumpModulation), this->device_type(), "solarPumpModulation", F_(percent), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(cylinderPumpModulation), this->device_type(), "cylinderPumpModulation", F_(percent), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(pumpWorkMin), this->device_type(), "pumpWorkMin", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(energyLastHour), this->device_type(), "energyLastHour", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(energyToday), this->device_type(), "energyToday", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(energyTotal), this->device_type(), "energyTotal", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(solarPump), this->device_type(), "solarPump", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(valveStatus), this->device_type(), "valveStatus", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(tankHeated), this->device_type(), "tankHeated", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, F_(collectorShutdown), this->device_type(), "collectorShutdown", nullptr, nullptr); ha_created_ = true; } diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index e53fd4f81..6a1dc2bf7 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -194,14 +194,16 @@ void Thermostat::device_info_web(JsonArray & root) { std::string hc_str(5, '\0'); snprintf_P(&hc_str[0], hc_str.capacity() + 1, PSTR("hc%d: "), hc->hc_num()); - render_value_json(root, hc_str, F("Current room temperature"), hc->curr_roomTemp, F_(degrees), format_curr); - render_value_json(root, hc_str, F("Setpoint room temperature"), hc->setpoint_roomTemp, F_(degrees), format_setpoint); + render_value_json(root, hc_str, F_(currtemp), hc->curr_roomTemp, F_(degrees), format_curr); + render_value_json(root, hc_str, F_(seltemp), hc->setpoint_roomTemp, F_(degrees), format_setpoint); if (Helpers::hasValue(hc->mode)) { JsonObject dataElement; dataElement = root.createNestedObject(); + std::string mode_str(15, '\0'); snprintf_P(&mode_str[0], mode_str.capacity() + 1, PSTR("%sMode"), hc_str.c_str()); dataElement["name"] = mode_str; + std::string modetype_str(20, '\0'); if (Helpers::hasValue(hc->summer_mode) && hc->summer_mode) { snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, PSTR("%s - summer"), mode_tostring(hc->get_mode(flags)).c_str()); @@ -238,7 +240,66 @@ bool Thermostat::updated_values() { // info API command // returns the same MQTT publish payload in Nested format bool Thermostat::command_info(const char * value, const int8_t id, JsonObject & output) { - return (export_values(Mqtt::Format::NESTED, output)); + return (export_values_hc(Mqtt::Format::NESTED, output)); +} + +// display all thermostat values into the shell console +void Thermostat::show_values(uuid::console::Shell & shell) { + EMSdevice::show_values(shell); // always call this to show header + + StaticJsonDocument doc_main; + JsonObject output_main = doc_main.to(); + if (export_values_main(output_main)) { + print_value_json(shell, F("display"), F_(display), nullptr, output_main); + print_value_json(shell, F("language"), F_(language), nullptr, output_main); + print_value_json(shell, F("offsetclock"), F_(offsetclock), nullptr, output_main); + print_value_json(shell, F("dampedtemp"), F_(dampedtemp), F_(degrees), output_main); + print_value_json(shell, F("inttemp1"), F_(inttemp1), F_(degrees), output_main); + print_value_json(shell, F("inttemp2"), F_(inttemp2), F_(degrees), output_main); + print_value_json(shell, F("intoffset"), F_(intoffset), nullptr, output_main); + print_value_json(shell, F("minexttemp"), F_(minexttemp), F_(degrees), output_main); + print_value_json(shell, F("building"), F_(building), nullptr, output_main); + print_value_json(shell, F("wwmode"), F_(wwmode), nullptr, output_main); + print_value_json(shell, F("wwcircmode"), F_(wwcircmode), nullptr, output_main); + } + + StaticJsonDocument doc_hc; + JsonObject output_hc = doc_hc.to(); + // e.g. {"hc1":{"seltemp":849.4,"currtemp":819.2,"mode":"unknown","modetype":"day"},"hc2":{"seltemp":875.1,"currtemp":409.6,"mode":"unknown","modetype":"day"},"hc3":{"seltemp":0,"currtemp":0,"mode":"unknown","modetype":"day"}} + + if (export_values_hc(Mqtt::Format::NESTED, output_hc)) { + // display for each active heating circuit + for (const auto & hc : heating_circuits_) { + if (hc->is_active()) { + shell.printfln("Heating Circuit %d:", hc->hc_num()); + + char hc_name[10]; // hc{1-4} + strlcpy(hc_name, "hc", 10); + char s[3]; + strlcat(hc_name, Helpers::itoa(s, hc->hc_num()), 10); + JsonObject output = output_hc[hc_name]; + + print_value_json(shell, F("seltemp"), F_(seltemp), F_(degrees), output); + print_value_json(shell, F("currtemp"), F_(currtemp), F_(degrees), output); + print_value_json(shell, F("heattemp"), F_(heattemp), F_(degrees), output); + print_value_json(shell, F("comforttemp"), F_(comforttemp), F_(degrees), output); + print_value_json(shell, F("daytemp"), F_(daytemp), F_(degrees), output); + print_value_json(shell, F("ecotemp"), F_(ecotemp), F_(degrees), output); + print_value_json(shell, F("nighttemp"), F_(nighttemp), F_(degrees), output); + print_value_json(shell, F("manualtemp"), F_(manualtemp), F_(degrees), output); + print_value_json(shell, F("holidaytemp"), F_(holidaytemp), F_(degrees), output); + print_value_json(shell, F("nofrosttemp"), F_(nofrosttemp), F_(degrees), output); + print_value_json(shell, F("targetflowtemp"), F_(targetflowtemp), F_(degrees), output); + print_value_json(shell, F("offsettemp"), F_(offsettemp), F_(degrees), output); + print_value_json(shell, F("designtemp"), F_(designtemp), F_(degrees), output); + print_value_json(shell, F("summertemp"), F_(summertemp), F_(degrees), output); + print_value_json(shell, F("mode"), F_(mode), nullptr, output); + print_value_json(shell, F("modetype"), F_(modetype), nullptr, output); + + shell.println(); + } + } + } } // publish values via MQTT @@ -247,77 +308,137 @@ void Thermostat::publish_values() { return; } - StaticJsonDocument doc; - JsonObject output = doc.to(); + StaticJsonDocument doc_main; + JsonObject output_main = doc_main.to(); + if (export_values_main(output_main)) { + Mqtt::publish(F("thermostat_system_data"), output_main); + } - export_values(Mqtt::mqtt_format(), output); - - // if we're in SINGLE mode the MQTT would have been published on the export_values() function for each hc - if (Mqtt::mqtt_format() != Mqtt::Format::SINGLE) { - Mqtt::publish(F("thermostat_data"), output); + StaticJsonDocument doc_hc; + JsonObject output_hc = doc_hc.to(); + if (export_values_hc(Mqtt::mqtt_format(), output_hc)) { + // if we're in SINGLE mode the MQTT would have been published on the export_values() function for each hc + if (Mqtt::mqtt_format() != Mqtt::Format::SINGLE) { + Mqtt::publish(F("thermostat_data"), output_hc); + } } } // creates JSON doc from values // returns false if empty -bool Thermostat::export_values(uint8_t mqtt_format, JsonObject & rootThermostat) { - uint8_t flags = this->model(); - JsonObject dataThermostat; +bool Thermostat::export_values_main(JsonObject & rootThermostat) { + // Clock time + if (datetime_.size()) { + rootThermostat["time"] = datetime_.c_str(); + } - // add external temp and other stuff specific to the RC30 and RC35 - if (flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) { - if (datetime_.size()) { - rootThermostat["time"] = datetime_.c_str(); - } - if (Helpers::hasValue(dampedoutdoortemp_)) { - rootThermostat["dampedtemp"] = dampedoutdoortemp_; - } - if (Helpers::hasValue(tempsensor1_)) { - rootThermostat["inttemp1"] = (float)tempsensor1_ / 10; - } - if (Helpers::hasValue(tempsensor2_)) { - rootThermostat["inttemp2"] = (float)tempsensor2_ / 10; - } - if (Helpers::hasValue(ibaCalIntTemperature_)) { - rootThermostat["intoffset"] = (float)ibaCalIntTemperature_ / 2; - } - if (Helpers::hasValue(ibaMinExtTemperature_)) { - rootThermostat["minexttemp"] = (float)ibaMinExtTemperature_; // min ext temp for heating curve, in deg. - } - if (Helpers::hasValue(ibaBuildingType_)) { - if (ibaBuildingType_ == 0) { - rootThermostat["building"] = "light"; - } else if (ibaBuildingType_ == 1) { - rootThermostat["building"] = "medium"; - } else if (ibaBuildingType_ == 2) { - rootThermostat["building"] = "heavy"; - } - } - if (Helpers::hasValue(wwMode_)) { - if (wwMode_ == 2) { - rootThermostat["wwmode"] = "auto"; - } else { - char s[7]; - rootThermostat["wwmode"] = Helpers::render_boolean(s, (wwMode_ == 1)); - } - } - - if (Helpers::hasValue(wwCircMode_)) { - if (wwCircMode_ == 2) { - rootThermostat["wwcircmode"] = "auto"; - } else { - char s[7]; - rootThermostat["wwcircmode"] = Helpers::render_boolean(s, (wwCircMode_ == 1)); - } - } - - // send this specific data using the thermostat_data topic - if (mqtt_format != Mqtt::Format::NESTED) { - Mqtt::publish(F("thermostat_data"), rootThermostat); - rootThermostat.clear(); // clear object + // Display + if (Helpers::hasValue(ibaMainDisplay_)) { + if (ibaMainDisplay_ == 0) { + rootThermostat["display"] = F("internal temperature"); + } else if (ibaMainDisplay_ == 1) { + rootThermostat["display"] = F("internal setpoint"); + } else if (ibaMainDisplay_ == 2) { + rootThermostat["display"] = F("external temperature"); + } else if (ibaMainDisplay_ == 3) { + rootThermostat["display"] = F("burner temperature"); + } else if (ibaMainDisplay_ == 4) { + rootThermostat["display"] = F("WW temperature"); + } else if (ibaMainDisplay_ == 5) { + rootThermostat["display"] = F("functioning mode"); + } else if (ibaMainDisplay_ == 6) { + rootThermostat["display"] = F("time"); + } else if (ibaMainDisplay_ == 7) { + rootThermostat["display"] = F("date"); + } else if (ibaMainDisplay_ == 8) { + rootThermostat["display"] = F("smoke temperature"); } } + // Language + if (Helpers::hasValue(ibaLanguage_)) { + if (ibaLanguage_ == 0) { + rootThermostat["language"] = F("German"); + } else if (ibaLanguage_ == 1) { + rootThermostat["language"] = F("Dutch"); + } else if (ibaLanguage_ == 2) { + rootThermostat["language"] = F("French"); + } else if (ibaLanguage_ == 3) { + rootThermostat["language"] = F("Italian"); + } + } + + // Offset clock + if (Helpers::hasValue(ibaClockOffset_)) { + rootThermostat["offsetclock"] = ibaClockOffset_; // offset (in sec) to clock, 0xff=-1s, 0x02=2s + } + + // Damped outdoor temperature + if (Helpers::hasValue(dampedoutdoortemp_)) { + rootThermostat["dampedtemp"] = dampedoutdoortemp_; + } + + // Temp sensor 1 + if (Helpers::hasValue(tempsensor1_)) { + rootThermostat["inttemp1"] = (float)tempsensor1_ / 10; + } + + // Temp sensor 2 + if (Helpers::hasValue(tempsensor2_)) { + rootThermostat["inttemp2"] = (float)tempsensor2_ / 10; + } + + // Offset int. temperature + if (Helpers::hasValue(ibaCalIntTemperature_)) { + rootThermostat["intoffset"] = (float)ibaCalIntTemperature_ / 2; + } + + // Min ext. temperature + if (Helpers::hasValue(ibaMinExtTemperature_)) { + rootThermostat["minexttemp"] = (float)ibaMinExtTemperature_; // min ext temp for heating curve, in deg. + } + + // Building + if (Helpers::hasValue(ibaBuildingType_)) { + if (ibaBuildingType_ == 0) { + rootThermostat["building"] = F("light"); + } else if (ibaBuildingType_ == 1) { + rootThermostat["building"] = F("medium"); + } else if (ibaBuildingType_ == 2) { + rootThermostat["building"] = F("heavy"); + } + } + + // Warm water mode + if (Helpers::hasValue(wwMode_)) { + if (wwMode_ == 2) { + rootThermostat["wwmode"] = "auto"; + } else { + char s[7]; + rootThermostat["wwmode"] = Helpers::render_boolean(s, (wwMode_ == 1)); + } + } + + // Warm Water circulation mode + if (Helpers::hasValue(wwCircMode_)) { + if (wwCircMode_ == 2) { + rootThermostat["wwcircmode"] = "auto"; + } else { + char s[7]; + rootThermostat["wwcircmode"] = Helpers::render_boolean(s, (wwCircMode_ == 1)); + } + } + + return (rootThermostat.size()); +} + +// creates JSON doc from values, for each heating circuit +// if the mqtt_format is 0 then it will not perform the MQTT publish +// returns false if empty +bool Thermostat::export_values_hc(uint8_t mqtt_format, JsonObject & rootThermostat) { + uint8_t flags = this->model(); + JsonObject dataThermostat; + // go through all the heating circuits bool has_data = false; for (const auto & hc : heating_circuits_) { @@ -349,64 +470,80 @@ bool Thermostat::export_values(uint8_t mqtt_format, JsonObject & rootThermostat) curr_temp_divider = 10; } + // Setpoint room temperature if (Helpers::hasValue(hc->setpoint_roomTemp)) { dataThermostat["seltemp"] = Helpers::round2((float)hc->setpoint_roomTemp / setpoint_temp_divider); } + // Current room temperature if (Helpers::hasValue(hc->curr_roomTemp)) { dataThermostat["currtemp"] = Helpers::round2((float)hc->curr_roomTemp / curr_temp_divider); } if (Helpers::hasValue(hc->daytemp)) { if (flags == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) { + // Heat temperature dataThermostat["heattemp"] = (float)hc->daytemp / 2; } else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC300 || flags == EMSdevice::EMS_DEVICE_FLAG_RC100) { + // Comfort temperature dataThermostat["comforttemp"] = (float)hc->daytemp / 2; } else { + // Day temperature dataThermostat["daytemp"] = (float)hc->daytemp / 2; } } if (Helpers::hasValue(hc->nighttemp)) { if (flags == EMSdevice::EMS_DEVICE_FLAG_JUNKERS || flags == EMSdevice::EMS_DEVICE_FLAG_RC300 || flags == EMSdevice::EMS_DEVICE_FLAG_RC100) { + // Eco temperature dataThermostat["ecotemp"] = (float)hc->nighttemp / 2; } else { + // Night temperature dataThermostat["nighttemp"] = (float)hc->nighttemp / 2; } } + // Manual temperature if (Helpers::hasValue(hc->manualtemp)) { dataThermostat["manualtemp"] = (float)hc->manualtemp / 2; } + // Holiday temperature if (Helpers::hasValue(hc->holidaytemp)) { dataThermostat["holidaytemp"] = (float)hc->holidaytemp / 2; } + // Nofrost temperature if (Helpers::hasValue(hc->nofrosttemp)) { dataThermostat["nofrosttemp"] = (float)hc->nofrosttemp / 2; } + // Heating Type if (Helpers::hasValue(hc->heatingtype)) { dataThermostat["heatingtype"] = hc->heatingtype; } + // Target flow temperature if (Helpers::hasValue(hc->targetflowtemp)) { dataThermostat["targetflowtemp"] = hc->targetflowtemp; } + // Offset temperature if (Helpers::hasValue(hc->offsettemp)) { dataThermostat["offsettemp"] = hc->offsettemp / 2; } + // Design temperature if (Helpers::hasValue(hc->designtemp)) { dataThermostat["designtemp"] = hc->designtemp; } + // Summer temperature if (Helpers::hasValue(hc->summertemp)) { dataThermostat["summertemp"] = hc->summertemp; } + // Summer mode if (Helpers::hasValue(hc->summer_setmode)) { if (hc->summer_setmode == 1) { dataThermostat["summermode"] = "auto"; @@ -429,11 +566,13 @@ bool Thermostat::export_values(uint8_t mqtt_format, JsonObject & rootThermostat) hc_mode = HeatingCircuit::Mode::AUTO; } } + // Mode dataThermostat["mode"] = mode_tostring(hc_mode); } // special handling of mode type, for the RC35 replace with summer/holiday if set // https://github.com/proddy/EMS-ESP/issues/373#issuecomment-619810209 + // Mode Type if (Helpers::hasValue(hc->summer_mode) && hc->summer_mode) { dataThermostat["modetype"] = F("summer"); } else if (Helpers::hasValue(hc->holiday_mode) && hc->holiday_mode) { @@ -444,7 +583,7 @@ bool Thermostat::export_values(uint8_t mqtt_format, JsonObject & rootThermostat) // if format is single, send immediately and clear object for next hc // the topic will have the hc number appended - if ((mqtt_format == Mqtt::Format::SINGLE) || (mqtt_format == Mqtt::Format::CUSTOM)) { + if (mqtt_format == Mqtt::Format::SINGLE) { char topic[30]; char s[3]; strlcpy(topic, "thermostat_data", 30); @@ -633,6 +772,45 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) { // enable the thermostat topic to take both mode strings and floats register_mqtt_topic("thermostat", [&](const char * m) { return thermostat_ha_cmd(m); }); + + char hc_name[10]; // hc{1-4} + strlcpy(hc_name, "hc", 10); + char s[3]; + strlcat(hc_name, Helpers::itoa(s, hc_num), 10); + + Mqtt::register_mqtt_ha_sensor(hc_name, F_(mode), this->device_type(), "mode", nullptr, nullptr); + + uint8_t model = this->model(); + switch (model) { + case EMS_DEVICE_FLAG_RC100: + case EMS_DEVICE_FLAG_RC300: + Mqtt::register_mqtt_ha_sensor(hc_name, F_(modetype), this->device_type(), "modetype", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(ecotemp), this->device_type(), "ecotemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(manualtemp), this->device_type(), "manualtemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(comforttemp), this->device_type(), "comforttemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(summertemp), this->device_type(), "summertemp", F_(degrees), F_(icontemperature)); + break; + case EMS_DEVICE_FLAG_RC20_2: + case EMS_DEVICE_FLAG_RC35: + Mqtt::register_mqtt_ha_sensor(hc_name, F_(modetype), this->device_type(), "modetype", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(nighttemp), this->device_type(), "nighttemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(daytemp), this->device_type(), "daytemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(designtemp), this->device_type(), "designtemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(offsettemp), this->device_type(), "offsettemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(holidaytemp), this->device_type(), "holidaytemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(targetflowtemp), this->device_type(), "targetflowtemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(summertemp), this->device_type(), "summertemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(nofrosttemp), this->device_type(), "nofrosttemp", F_(degrees), F_(icontemperature)); + break; + case EMS_DEVICE_FLAG_JUNKERS: + Mqtt::register_mqtt_ha_sensor(hc_name, F_(modetype), this->device_type(), "modetype", nullptr, nullptr); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(heattemp), this->device_type(), "heattemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(ecotemp), this->device_type(), "ecotemp", F_(degrees), F_(icontemperature)); + Mqtt::register_mqtt_ha_sensor(hc_name, F_(nofrosttemp), this->device_type(), "nofrosttemp", F_(degrees), F_(icontemperature)); + break; + default: + break; + } } // for HA specifically when receiving over MQTT in the thermostat topic @@ -780,187 +958,6 @@ std::string Thermostat::mode_tostring(uint8_t mode) { } } -// display all thermostat values into the shell console -void Thermostat::show_values(uuid::console::Shell & shell) { - EMSdevice::show_values(shell); // always call this to show header - - uint8_t flags = this->model(); - - if (datetime_.size()) { - shell.printfln(F(" Clock: %s"), datetime_.c_str()); - if (Helpers::hasValue(ibaClockOffset_) && flags == EMS_DEVICE_FLAG_RC30_1) { - print_value(shell, 2, F("Offset clock"), ibaClockOffset_, nullptr); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s - } - } - - if (Helpers::hasValue(wwMode_)) { - if (wwMode_ == 2) { - print_value(shell, 2, F("Warm Water mode"), F("auto")); - } else { - print_value(shell, 2, F("Warm Water mode"), wwMode_, nullptr, EMS_VALUE_BOOL); - } - } - - if (Helpers::hasValue(wwCircMode_)) { - if (wwCircMode_ == 2) { - print_value(shell, 2, F("Warm Water circulation mode"), F("auto")); - } else { - print_value(shell, 2, F("Warm Water circulation mode"), wwCircMode_, nullptr, EMS_VALUE_BOOL); - } - } - - if (flags == EMS_DEVICE_FLAG_RC35) { - print_value(shell, 2, F("Damped Outdoor temperature"), dampedoutdoortemp_, F_(degrees)); - print_value(shell, 2, F("Temp sensor 1"), tempsensor1_, F_(degrees), 10); - print_value(shell, 2, F("Temp sensor 2"), tempsensor2_, F_(degrees), 10); - } - - if (flags == EMS_DEVICE_FLAG_RC30_1) { - // settings parameters - if (Helpers::hasValue(ibaMainDisplay_)) { - if (ibaMainDisplay_ == 0) { - shell.printfln(F(" Display: internal temperature")); - } else if (ibaMainDisplay_ == 1) { - shell.printfln(F(" Display: internal setpoint")); - } else if (ibaMainDisplay_ == 2) { - shell.printfln(F(" Display: external temperature")); - } else if (ibaMainDisplay_ == 3) { - shell.printfln(F(" Display: burner temperature")); - } else if (ibaMainDisplay_ == 4) { - shell.printfln(F(" Display: WW temperature")); - } else if (ibaMainDisplay_ == 5) { - shell.printfln(F(" Display: functioning mode")); - } else if (ibaMainDisplay_ == 6) { - shell.printfln(F(" Display: time")); - } else if (ibaMainDisplay_ == 7) { - shell.printfln(F(" Display: date")); - } else if (ibaMainDisplay_ == 8) { - shell.printfln(F(" Display: smoke temperature")); - } - } - - if (Helpers::hasValue(ibaLanguage_)) { - if (ibaLanguage_ == 0) { - shell.printfln(F(" Language: German")); - } else if (ibaLanguage_ == 1) { - shell.printfln(F(" Language: Dutch")); - } else if (ibaLanguage_ == 2) { - shell.printfln(F(" Language: French")); - } else if (ibaLanguage_ == 3) { - shell.printfln(F(" Language: Italian")); - } - } - } - - if (flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) { - if (Helpers::hasValue(ibaCalIntTemperature_)) { - print_value(shell, 2, F("Offset int. temperature"), ibaCalIntTemperature_, F_(degrees), 2); - } - - if (Helpers::hasValue(ibaMinExtTemperature_)) { - print_value(shell, 2, F("Min ext. temperature"), ibaMinExtTemperature_, F_(degrees)); // min ext temp for heating curve, in deg. - } - - if (Helpers::hasValue(ibaBuildingType_)) { - if (ibaBuildingType_ == 0) { - shell.printfln(F(" Building: light")); - } else if (ibaBuildingType_ == 1) { - shell.printfln(F(" Building: medium")); - } else if (ibaBuildingType_ == 2) { - shell.printfln(F(" Building: heavy")); - } - } - } - - for (const auto & hc : heating_circuits_) { - if (!hc->is_active()) { - break; // skip this HC - } - - shell.printfln(F(" Heating Circuit %d:"), hc->hc_num()); - - // different thermostat types store their temperature values differently - uint8_t format_setpoint, format_curr; - switch (flags) { - case EMS_DEVICE_FLAG_EASY: - format_setpoint = 100; // *100 - format_curr = 100; // *100 - break; - case EMS_DEVICE_FLAG_JUNKERS: - format_setpoint = 10; // *10 - format_curr = 10; // *10 - break; - default: // RC30, RC35 etc... - format_setpoint = 2; // *2 - format_curr = 10; // *10 - break; - } - - print_value(shell, 4, F("Current room temperature"), hc->curr_roomTemp, F_(degrees), format_curr); - print_value(shell, 4, F("Setpoint room temperature"), hc->setpoint_roomTemp, F_(degrees), format_setpoint); - if (Helpers::hasValue(hc->mode)) { - print_value(shell, 4, F("Mode"), mode_tostring(hc->get_mode(flags)).c_str()); - } - if (Helpers::hasValue(hc->mode_type)) { - print_value(shell, 4, F("Mode Type"), mode_tostring(hc->get_mode_type(flags)).c_str()); - } - - if (Helpers::hasValue(hc->summer_mode) && hc->summer_mode) { - shell.printfln(F(" Program is set to Summer mode")); - } else if (Helpers::hasValue(hc->holiday_mode) && hc->holiday_mode) { - shell.printfln(F(" Program is set to Holiday mode")); - } - - if (Helpers::hasValue(hc->daytemp)) { - if (flags == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) { - print_value(shell, 4, F("Heat temperature"), hc->daytemp, F_(degrees), 2); - } else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC300 || flags == EMSdevice::EMS_DEVICE_FLAG_RC100) { - print_value(shell, 4, F("Comfort temperature"), hc->daytemp, F_(degrees), 2); - } else { - print_value(shell, 4, F("Day temperature"), hc->daytemp, F_(degrees), 2); - } - } - if (Helpers::hasValue(hc->nighttemp)) { - if (flags == EMSdevice::EMS_DEVICE_FLAG_JUNKERS || flags == EMSdevice::EMS_DEVICE_FLAG_RC300 || flags == EMSdevice::EMS_DEVICE_FLAG_RC100) { - print_value(shell, 4, F("Eco temperature"), hc->nighttemp, F_(degrees), 2); - } else { - print_value(shell, 4, F("Night temperature"), hc->nighttemp, F_(degrees), 2); - } - } - if (Helpers::hasValue(hc->manualtemp)) { - print_value(shell, 4, F("Manual temperature"), hc->manualtemp, F_(degrees), 2); - } - if (Helpers::hasValue(hc->nofrosttemp)) { - print_value(shell, 4, F("Nofrost temperature"), hc->nofrosttemp, F_(degrees), 2); - } - if (Helpers::hasValue(hc->holidaytemp)) { - print_value(shell, 4, F("Holiday temperature"), hc->holidaytemp, F_(degrees), 2); - } - if (Helpers::hasValue(hc->offsettemp)) { - print_value(shell, 4, F("Offset temperature"), hc->offsettemp, F_(degrees), 2); - } - if (Helpers::hasValue(hc->designtemp)) { - print_value(shell, 4, F("Design temperature"), hc->designtemp, F_(degrees)); - } - if (Helpers::hasValue(hc->summertemp)) { - print_value(shell, 4, F("Summer temperature"), hc->summertemp, F_(degrees)); - } - if (Helpers::hasValue(hc->summer_setmode)) { - if (hc->summer_setmode == 1) { - print_value(shell, 4, F("Summer mode"), F("auto")); - } else { - char s[7]; - print_value(shell, 4, F("Summer mode"), Helpers::render_boolean(s, (hc->summer_setmode == 0))); - } - } - if (Helpers::hasValue(hc->targetflowtemp)) { - print_value(shell, 4, F("Target flow temperature"), hc->targetflowtemp, F_(degrees)); - } - } - - shell.println(); -} - // 0xA8 - for reading the mode from the RC20 thermostat (0x17) void Thermostat::process_RC20Set(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); @@ -1026,6 +1023,9 @@ void Thermostat::process_JunkersSet2(std::shared_ptr telegram) { // type 0xA3 - for external temp settings from the the RC* thermostats (e.g. RC35) void Thermostat::process_RCOutdoorTemp(std::shared_ptr telegram) { changed_ |= telegram->read_value(dampedoutdoortemp_, 0); + if (dampedoutdoortemp_ == 0) { + dampedoutdoortemp_ = EMS_VALUE_INT_NOTSET; // special case for RC20's where the value is always 0 + } changed_ |= telegram->read_value(tempsensor1_, 3); // sensor 1 - is * 10 changed_ |= telegram->read_value(tempsensor2_, 5); // sensor 2 - is * 10 } diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 2dd3c170f..5e39f814e 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -107,7 +107,8 @@ class Thermostat : public EMSdevice { static uuid::log::Logger logger_; void add_commands(); - bool export_values(uint8_t mqtt_format, JsonObject & doc); + bool export_values_main(JsonObject & doc); + bool export_values_hc(uint8_t mqtt_format, JsonObject & doc); // specific thermostat characteristics, stripping the option bits at pos 6 and 7 inline uint8_t model() const { @@ -122,7 +123,7 @@ class Thermostat : public EMSdevice { std::string datetime_; // date and time stamp - bool changed_ = false; + bool changed_ = false; // Installation parameters uint8_t ibaMainDisplay_ = diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 1c1582b37..4f6c75be9 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -35,6 +35,7 @@ uint32_t Mqtt::publish_time_mixing_; uint32_t Mqtt::publish_time_other_; uint32_t Mqtt::publish_time_sensor_; uint8_t Mqtt::mqtt_format_; +bool Mqtt::mqtt_enabled_; std::vector Mqtt::mqtt_subfunctions_; @@ -49,6 +50,10 @@ uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON}; // subscribe to an MQTT topic, and store the associated callback function // only if it already hasn't been added void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_subfunction_p cb) { + if (!enabled()) { + return; + } + // check if we already have the topic subscribed, if so don't add it again if (!mqtt_subfunctions_.empty()) { for (auto & mqtt_subfunction : mqtt_subfunctions_) { @@ -159,8 +164,6 @@ void Mqtt::loop() { void Mqtt::show_mqtt(uuid::console::Shell & shell) { shell.printfln(F("MQTT is %s"), connected() ? uuid::read_flash_string(F_(connected)).c_str() : uuid::read_flash_string(F_(disconnected)).c_str()); - EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { shell.printfln(F_(mqtt_format_fmt), settings.mqtt_format); }); - shell.printfln(F("MQTT publish fails count: %lu"), mqtt_publish_fails_); shell.println(); @@ -352,6 +355,7 @@ void Mqtt::start() { mqtt_qos_ = mqttSettings.mqtt_qos; mqtt_retain_ = mqttSettings.mqtt_retain; mqtt_format_ = mqttSettings.mqtt_format; + mqtt_enabled_ = mqttSettings.enabled; }); mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); }); @@ -476,6 +480,7 @@ void Mqtt::on_connect() { // Home Assistant Discovery - the main master Device // homeassistant/sensor/ems-esp/status/config +// all the values from the heartbeat payload will be added as attributes to the entity state void Mqtt::ha_status() { StaticJsonDocument doc; doc["name"] = F("EMS-ESP status"); @@ -500,7 +505,7 @@ void Mqtt::ha_status() { // add sub or pub task to the queue. // a fully-qualified topic is created by prefixing the hostname, unless it's HA // returns a pointer to the message created -std::shared_ptr Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain) { +std::shared_ptr Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain) { if (topic.empty()) { return nullptr; } @@ -509,16 +514,14 @@ std::shared_ptr Mqtt::queue_message(const uint8_t operation, std::shared_ptr message; if ((strncmp(topic.c_str(), "homeassistant/", 13) == 0)) { // leave topic as it is - // message = std::make_shared(operation, topic, std::move(payload), retain); message = std::make_shared(operation, topic, std::move(payload), retain); - } else { // prefix the hostname std::string full_topic(100, '\0'); snprintf_P(&full_topic[0], full_topic.capacity() + 1, PSTR("%s/%s"), hostname_.c_str(), topic.c_str()); - // message = std::make_shared(operation, full_topic, std::move(payload), retain); message = std::make_shared(operation, full_topic, std::move(payload), retain); } + // TODO use && and resize() to fix mem defrag issues // if the queue is full, make room but removing the last one if (mqtt_messages_.size() >= maximum_mqtt_messages_) { @@ -530,7 +533,10 @@ std::shared_ptr Mqtt::queue_message(const uint8_t operation, } // add MQTT message to queue, payload is a string -std::shared_ptr Mqtt::queue_publish_message(const std::string & topic, const std::string & payload, const bool retain) { +std::shared_ptr Mqtt::queue_publish_message(const std::string & topic, const std::string & payload, bool retain) { + if (!enabled()) { + return nullptr; + }; return queue_message(Operation::PUBLISH, topic, payload, retain); } @@ -549,7 +555,6 @@ void Mqtt::publish(const __FlashStringHelper * topic, const char * payload) { queue_publish_message(uuid::read_flash_string(topic), payload, mqtt_retain_); } - // MQTT Publish, using a specific retain flag, topic is a flash string void Mqtt::publish(const __FlashStringHelper * topic, const std::string & payload) { queue_publish_message(uuid::read_flash_string(topic), payload, mqtt_retain_); @@ -561,7 +566,7 @@ void Mqtt::publish(const __FlashStringHelper * topic, const JsonObject & payload // publish json doc, only if its not empty void Mqtt::publish(const std::string & topic, const JsonObject & payload) { - if (payload.size()) { + if (enabled() && payload.size()) { std::string payload_text; serializeJson(payload, payload_text); // convert json to string queue_publish_message(topic, payload_text, mqtt_retain_); @@ -569,11 +574,11 @@ void Mqtt::publish(const std::string & topic, const JsonObject & payload) { } // for booleans, which get converted to string values 1 and 0 -void Mqtt::publish(const std::string & topic, const bool value) { +void Mqtt::publish(const std::string & topic, bool value) { queue_publish_message(topic, value ? "1" : "0", false); } -void Mqtt::publish(const __FlashStringHelper * topic, const bool value) { +void Mqtt::publish(const __FlashStringHelper * topic, bool value) { queue_publish_message(uuid::read_flash_string(topic), value ? "1" : "0", false); } @@ -589,7 +594,7 @@ void Mqtt::publish_retain(const __FlashStringHelper * topic, const std::string & // publish json doc, only if its not empty, using the retain flag void Mqtt::publish_retain(const std::string & topic, const JsonObject & payload, bool retain) { - if (payload.size()) { + if (enabled() && payload.size()) { std::string payload_text; serializeJson(payload, payload_text); // convert json to string queue_publish_message(topic, payload_text, retain); @@ -721,41 +726,61 @@ void Mqtt::register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, cons } // HA config for a normal 'sensor' type -void Mqtt::register_mqtt_ha_sensor(const __FlashStringHelper * name, const uint8_t device_type, const char * entity, const char * uom, const char * icon) { +// entity must match the key/value pair in the _data topic +void Mqtt::register_mqtt_ha_sensor(const char * prefix, + const __FlashStringHelper * name, + const uint8_t device_type, + const char * entity, + const __FlashStringHelper * uom, + const __FlashStringHelper * icon) { if (mqtt_format() != Format::HA) { return; } StaticJsonDocument doc; + std::string device_name = EMSdevice::device_type_2_device_name(device_type); + + char new_entity[20]; + // add prefix to entity if its specified + if (prefix != nullptr) { + snprintf_P(&new_entity[0], 20, PSTR("%s.%s"), prefix, entity); + } else { + strcpy(new_entity, entity); + } + doc["name"] = name; + // build unique identifier, replacing all . with _ as not to break HA std::string uniq(50, '\0'); - snprintf_P(&uniq[0], uniq.capacity() + 1, PSTR("%s"), entity); + snprintf_P(&uniq[0], uniq.capacity() + 1, PSTR("%s_%s"), device_name.c_str(), new_entity); + std::replace(uniq.begin(), uniq.end(), '.', '_'); + doc["uniq_id"] = uniq; - doc["uniq_id"] = uniq; - doc["unit_of_meas"] = uom; + if (uom != nullptr) { + doc["unit_of_meas"] = uom; + } std::string state_t(50, '\0'); - snprintf_P(&state_t[0], state_t.capacity() + 1, PSTR("%s/%s_data"), hostname_.c_str(), EMSdevice::device_type_2_device_name(device_type).c_str()); + snprintf_P(&state_t[0], state_t.capacity() + 1, PSTR("%s/%s_data"), hostname_.c_str(), device_name.c_str()); doc["stat_t"] = state_t; std::string tpl(50, '\0'); - snprintf_P(&tpl[0], tpl.capacity() + 1, PSTR("{{value_json.%s}}"), entity); + snprintf_P(&tpl[0], tpl.capacity() + 1, PSTR("{{value_json.%s}}"), new_entity); doc["val_tpl"] = tpl; - if (strlen(icon)) { + if (icon != nullptr) { doc["ic"] = icon; } JsonObject dev = doc.createNestedObject(F("dev")); JsonArray ids = dev.createNestedArray(F("ids")); std::string ha_device(40, '\0'); - snprintf_P(&ha_device[0], ha_device.capacity() + 1, PSTR("ems-esp-%s"), EMSdevice::device_type_2_device_name(device_type).c_str()); + snprintf_P(&ha_device[0], ha_device.capacity() + 1, PSTR("ems-esp-%s"), device_name.c_str()); ids.add(ha_device); std::string topic(100, '\0'); - snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/%s/config"), entity); + snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/%s/config"), uniq.c_str()); Mqtt::publish_retain(topic, doc.as(), true); // publish the config payload with retain flag } diff --git a/src/mqtt.h b/src/mqtt.h index 6084fa55f..2ddbb07e2 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -81,7 +81,7 @@ class Mqtt { enum Operation { PUBLISH, SUBSCRIBE }; - enum Format : uint8_t { SINGLE = 1, NESTED, HA, CUSTOM }; + enum Format : uint8_t { NONE = 0, SINGLE, NESTED, HA }; static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 100; @@ -96,8 +96,8 @@ class Mqtt { static void publish(const std::string & topic, const JsonObject & payload); static void publish(const __FlashStringHelper * topic, const JsonObject & payload); static void publish(const __FlashStringHelper * topic, const std::string & payload); - static void publish(const std::string & topic, const bool value); - static void publish(const __FlashStringHelper * topi, const bool value); + static void publish(const std::string & topic, bool value); + static void publish(const __FlashStringHelper * topic, bool value); static void publish(const std::string & topic); static void publish_retain(const std::string & topic, const JsonObject & payload, bool retain); @@ -105,7 +105,12 @@ class Mqtt { static void publish_retain(const __FlashStringHelper * topic, const JsonObject & payload, bool retain); static void register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, const uint8_t device_type, const char * entity); - static void register_mqtt_ha_sensor(const __FlashStringHelper * name, const uint8_t device_type, const char * entity, const char * uom, const char * icon); + static void register_mqtt_ha_sensor(const char * prefix, + const __FlashStringHelper * name, + const uint8_t device_type, + const char * entity, + const __FlashStringHelper * uom, + const __FlashStringHelper * icon); static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type); static void show_mqtt(uuid::console::Shell & shell); @@ -122,7 +127,15 @@ class Mqtt { #endif static bool connected() { +#if defined(EMSESP_STANDALONE) + return true; +#else return mqttClient_->connected(); +#endif + } + + static bool enabled() { + return mqtt_enabled_; } static uint32_t publish_fails() { @@ -163,11 +176,11 @@ class Mqtt { static uint16_t mqtt_message_id_; static constexpr size_t MAX_MQTT_MESSAGES = 70; // size of queue - static constexpr uint32_t MQTT_PUBLISH_WAIT = 200; // delay between sending publishes, to account for large payloads + static constexpr uint32_t MQTT_PUBLISH_WAIT = 100; // delay between sending publishes, to account for large payloads static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing - static std::shared_ptr queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain); - static std::shared_ptr queue_publish_message(const std::string & topic, const std::string & payload, const bool retain); + static std::shared_ptr queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain); + static std::shared_ptr queue_publish_message(const std::string & topic, const std::string & payload, bool retain); static std::shared_ptr queue_subscribe_message(const std::string & topic); void on_publish(uint16_t packetId); @@ -213,6 +226,7 @@ class Mqtt { static uint32_t publish_time_other_; static uint32_t publish_time_sensor_; static uint8_t mqtt_format_; + static bool mqtt_enabled_; }; } // namespace emsesp