From a6bfe4f8d99332df49866cd97e694f6d161579d6 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:29:20 +0200 Subject: [PATCH 01/13] localize more strings --- src/locale_EN.h | 123 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/src/locale_EN.h b/src/locale_EN.h index 4207dbed5..af42f949e 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -104,7 +104,6 @@ MAKE_PSTR(mark_interval_fmt, "Mark interval = %lus") MAKE_PSTR(wifi_ssid_fmt, "WiFi SSID = %s") MAKE_PSTR(wifi_password_fmt, "WiFi Password = %S") MAKE_PSTR(mqtt_heartbeat_fmt, "MQTT Heartbeat is %s") -MAKE_PSTR(mqtt_format_fmt, "MQTT Format is %d") MAKE_PSTR(cmd_optional, "[cmd]") MAKE_PSTR(deep_optional, "[deep]") MAKE_PSTR(tx_mode_fmt, "Tx mode = %d") @@ -131,3 +130,125 @@ MAKE_PSTR(new_password_prompt1, "Enter new password: ") MAKE_PSTR(new_password_prompt2, "Retype new password: ") MAKE_PSTR(password_prompt, "Password: ") MAKE_PSTR(unset, "") + +// HA icons +MAKE_PSTR(icontemperature, "mdi:coolant-temperature") +MAKE_PSTR(iconpercent, "mdi:sine-wave") + +// boiler +MAKE_PSTR(heatingActive, "Heating Active") +MAKE_PSTR(tapwaterActive, "Warm water/DHW Active") +MAKE_PSTR(serviceCode, "Service Code") +MAKE_PSTR(serviceCodeNumber, "Service Code Number") +MAKE_PSTR(wWSelTemp, "Warm water selected temperature") +MAKE_PSTR(wWSetTemp, "Warm water set temperature") +MAKE_PSTR(wWDisinfectionTemp, "Warm water disinfection temperature") +MAKE_PSTR(selFlowTemp, "Selected flow temperature") +MAKE_PSTR(selBurnPow, "Burner selected max power") +MAKE_PSTR(curBurnPow, "Burner current power") +MAKE_PSTR(pumpMod, "Pump modulation") +MAKE_PSTR(pumpMod2, "Heat pump modulation") +MAKE_PSTR(wWType, "Warm water type") +MAKE_PSTR(wWChargeType, "Warm Water charging type") +MAKE_PSTR(wWCircPump, "Warm water circulation pump available") +MAKE_PSTR(wWCiPuMode, "Warm water circulation pump freq") +MAKE_PSTR(wWCirc, "Warm water circulation active") +MAKE_PSTR(outdoorTemp, "Outside temperature") +MAKE_PSTR(wWCurTmp, "Warm water current temperature (intern)") +MAKE_PSTR(wWCurTmp2, "Warm water current temperature (extern)") +MAKE_PSTR(wWCurFlow, "Warm water current tap water flow") +MAKE_PSTR(curFlowTemp, "Current flow temperature") +MAKE_PSTR(retTemp, "Return temperature") +MAKE_PSTR(switchTemp, "Mixing switch temperature") +MAKE_PSTR(sysPress, "System pressure") +MAKE_PSTR(boilTemp, "Max boiler temperature") +MAKE_PSTR(wwStorageTemp1, "Warm water storage temperature (intern)") +MAKE_PSTR(wwStorageTemp2, "Warm water storage temperature (extern)") +MAKE_PSTR(exhaustTemp, "Exhaust temperature") +MAKE_PSTR(wWActivated, "Warm water activated") +MAKE_PSTR(wWOnetime, "Warm water one time charging") +MAKE_PSTR(wWDisinfecting, "Warm water disinfecting") +MAKE_PSTR(wWCharge, "Warm water charging") +MAKE_PSTR(wWRecharge, "Warm water recharge") +MAKE_PSTR(wWTempOK, "Warm water temperature ok") +MAKE_PSTR(wWActive, "Warm water active") +MAKE_PSTR(burnGas, "Gas") +MAKE_PSTR(flameCurr, "Flame current") +MAKE_PSTR(heatPump, "Boiler pump") +MAKE_PSTR(fanWork, "Fan") +MAKE_PSTR(ignWork, "Ignition") +MAKE_PSTR(wWHeat, "Warm water charging") +MAKE_PSTR(heatingActivated, "Heating activated") +MAKE_PSTR(heatingTemp, "Heating temperature setting on the boiler") +MAKE_PSTR(pumpModMax, "Boiler circuit pump modulation max power") +MAKE_PSTR(pumpModMin, "Boiler circuit pump modulation min power") +MAKE_PSTR(pumpDelay, "Boiler circuit pump delay time") +MAKE_PSTR(burnMinPeriod, "Boiler burner min period") +MAKE_PSTR(burnMinPower, "Boiler burner min power") +MAKE_PSTR(burnMaxPower, "Boiler burner max power") +MAKE_PSTR(boilHystOn, "Boiler temp hysteresis on") +MAKE_PSTR(boilHystOff, "Boiler temp hysteresis off") +MAKE_PSTR(setFlowTemp, "Set Flow temperature") +MAKE_PSTR(wWSetPumpPower, "Warm water pump set power") +MAKE_PSTR(wwMixTemperature, "Warm water mix temperature") +MAKE_PSTR(wwBufferBoilerTemperature, "Warm water buffer boiler temperature") +MAKE_PSTR(wWStarts, "Warm water # starts") +MAKE_PSTR(wWWorkM, "Warm water active time") +MAKE_PSTR(setBurnPow, "Boiler burner set power") +MAKE_PSTR(burnStarts, "Burner # starts") + +// solar +MAKE_PSTR(collectorTemp, "Collector temperature (TS1)") +MAKE_PSTR(tankBottomTemp, "Bottom temperature (TS2)") +MAKE_PSTR(tankBottomTemp2, "Bottom temperature (TS5)") +MAKE_PSTR(heatExchangerTemp, "Heat exchanger temperature (TS6)") +MAKE_PSTR(solarPumpModulation, "Solar pump modulation (PS1)") +MAKE_PSTR(cylinderPumpModulation, "Cylinder pump modulation (PS5)") +MAKE_PSTR(pumpWorkMin, "Pump working time") +MAKE_PSTR(energyLastHour, "Energy last hour") +MAKE_PSTR(energyToday, "Energy today") +MAKE_PSTR(energyTotal, "Energy total") +MAKE_PSTR(solarPump, "Solar Pump (PS1) active") +MAKE_PSTR(valveStatus, "Valve status") +MAKE_PSTR(tankHeated, "Tank Heated") +MAKE_PSTR(collectorShutdown, "Collector shutdown") + +// mixing +MAKE_PSTR(ww_hc, "Warm Water Circuit") +MAKE_PSTR(wwTemp, "Current warm water temperature") +MAKE_PSTR(pumpStatus, "Current pump status") +MAKE_PSTR(tempStatus, "Current temperature status") +MAKE_PSTR(hc, "Heating Circuit") +MAKE_PSTR(flowTemp, "Current flow temperature") +MAKE_PSTR(flowSetTemp, "Setpoint flow temperature") + +// thermostat +MAKE_PSTR(display, "Display") +MAKE_PSTR(language, "Language") +MAKE_PSTR(offsetclock, "Offset clock") +MAKE_PSTR(dampedtemp, "Damped outdoor temperature") +MAKE_PSTR(inttemp1, "Temp sensor 1") +MAKE_PSTR(inttemp2, "Temp sensor 2") +MAKE_PSTR(intoffset, "Offset int. temperature") +MAKE_PSTR(minexttemp, "Min ext. temperature") +MAKE_PSTR(building, "Building") +MAKE_PSTR(wwmode, "Warm water mode") +MAKE_PSTR(wwcircmode, "Warm Water circulation mode") + +// thermostat - per heating circuit +MAKE_PSTR(seltemp, "Setpoint room temperature") +MAKE_PSTR(currtemp, "Current room temperature") +MAKE_PSTR(heattemp, "Heat temperature") +MAKE_PSTR(comforttemp, "Comfort temperature") +MAKE_PSTR(daytemp, "Day temperature") +MAKE_PSTR(ecotemp, "Eco temperature") +MAKE_PSTR(nighttemp, "Night temperature") +MAKE_PSTR(manualtemp, "Manual temperature") +MAKE_PSTR(holidaytemp, "Holiday temperature") +MAKE_PSTR(nofrosttemp, "Nofrost temperature") +MAKE_PSTR(targetflowtemp, "Target flow temperature") +MAKE_PSTR(offsettemp, "Offset temperature") +MAKE_PSTR(designtemp, "Design temperature") +MAKE_PSTR(summertemp, "Summer temperature") +MAKE_PSTR(mode, "Mode") +MAKE_PSTR(modetype, "Mode type") From 0623724d745476f24fcfb8aff3f8e96348dddd87 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:29:31 +0200 Subject: [PATCH 02/13] default mqtt clean is on --- factory_settings.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/factory_settings.ini b/factory_settings.ini index 12a99b948..b8c4c81ad 100644 --- a/factory_settings.ini +++ b/factory_settings.ini @@ -38,7 +38,7 @@ build_flags = -D FACTORY_MQTT_PASSWORD=\"\" -D FACTORY_MQTT_CLIENT_ID=\"ems-esp\" -D FACTORY_MQTT_KEEP_ALIVE=60 - -D FACTORY_MQTT_CLEAN_SESSION=false + -D FACTORY_MQTT_CLEAN_SESSION=true -D FACTORY_MQTT_MAX_TOPIC_LENGTH=128 ; JWT Secret From 3f632496a6f2a2194a676b97dc69d4d8094066d0 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:29:47 +0200 Subject: [PATCH 03/13] remove MQTT Custom format --- interface/src/mqtt/MqttSettingsForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index 9c4703636..80958ad0c 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -116,7 +116,6 @@ class MqttSettingsForm extends React.Component { Single Nested Home Assistant - Custom Date: Sat, 3 Oct 2020 16:30:04 +0200 Subject: [PATCH 04/13] update mqtt enabled for offline testing --- lib_standalone/ESP8266React.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index b38819c03..0ee3471c9 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -27,6 +27,7 @@ class DummySettings { uint8_t mqtt_format = 3; // 1=single, 2=nested, 3=ha, 4=custom uint8_t mqtt_qos = 0; bool mqtt_retain = false; + bool enabled = true; // MQTT String hostname = "ems-esp"; String jwtSecret = "ems-esp"; String ssid = "ems-esp"; From 551e85c720132c71aadd2cd9f48f68eab6abd0d8 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:30:30 +0200 Subject: [PATCH 05/13] show pretty json after 'info' call --- src/console.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/console.cpp b/src/console.cpp index 55a259410..c6fef6a84 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -398,7 +398,7 @@ void EMSESPShell::add_console_commands() { } if (ok && output.size()) { - serializeJson(doc, shell); + serializeJsonPretty(doc, shell); shell.println(); } }, From 51c181528c4e95a1a7a3af20f78098f361579d1e Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:30:53 +0200 Subject: [PATCH 06/13] render a value from a json string --- src/emsdevice.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 69cce714e..bbc47e4da 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -383,4 +383,36 @@ void EMSdevice::print_value(uuid::console::Shell & shell, uint8_t padding, const shell.printfln(PSTR("%s: %s"), uuid::read_flash_string(name).c_str(), value); } +// print value to shell from the json doc +void EMSdevice::print_value_json(uuid::console::Shell & shell, + const __FlashStringHelper * key, + const __FlashStringHelper * name, + const __FlashStringHelper * suffix, + JsonObject & json) { + JsonVariant data = json[uuid::read_flash_string(key)]; + if (data == nullptr) { + return; // doesn't exist + } + + shell.printf(PSTR(" %s: "), uuid::read_flash_string(name).c_str()); + + if (data.is()) { + shell.printf(PSTR("%s"), data.as()); + } else if (data.is()) { + shell.printf(PSTR("%d"), data.as()); + } else if (data.is()) { + char data_str[10]; + shell.printf(PSTR("%s"), Helpers::render_value(data_str, (float)data.as(), 1)); + } else if (data.is()) { + char data_str[10]; + shell.printf(PSTR("%s"), Helpers::render_boolean(data_str, data.as())); + } + + if (suffix != nullptr) { + shell.print(uuid::read_flash_string(suffix).c_str()); + } + + shell.println(); +} + } // namespace emsesp From 84c5c3faa2b99b4acf29ec822b17e61f591f66b0 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:31:06 +0200 Subject: [PATCH 07/13] render value from json string --- src/emsdevice.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/emsdevice.h b/src/emsdevice.h index f3d416226..65160f817 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -149,6 +149,12 @@ class EMSdevice { telegram_functions_.reserve(n); } + static void print_value_json(uuid::console::Shell & shell, + const __FlashStringHelper * key, + const __FlashStringHelper * name, + const __FlashStringHelper * suffix, + JsonObject & json); + // prints a ems device value to the console, handling the correct rendering of the type // padding is # white space // name is the name of the parameter @@ -192,7 +198,6 @@ class EMSdevice { Value & value, const __FlashStringHelper * suffix, const uint8_t format = 0) { - // create the value as a string using the render_value function char buffer[15]; if (Helpers::render_value(buffer, value, format) == nullptr) { From 644abdffb6a6c6baa593c41c272f206589716b2b Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:31:34 +0200 Subject: [PATCH 08/13] only publish MQTT if MQTT is enabled --- src/emsesp.cpp | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 44c4f9ef0..4b5c5b6ff 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -290,46 +290,46 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) { // MQTT publish everything, immediately void EMSESP::publish_all() { - publish_device_values(EMSdevice::DeviceType::BOILER); - publish_device_values(EMSdevice::DeviceType::THERMOSTAT); - publish_device_values(EMSdevice::DeviceType::SOLAR); - publish_device_values(EMSdevice::DeviceType::MIXING); - publish_other_values(); - publish_sensor_values(true); - system_.send_heartbeat(); + if (Mqtt::connected()) { + publish_device_values(EMSdevice::DeviceType::BOILER); + publish_device_values(EMSdevice::DeviceType::THERMOSTAT); + publish_device_values(EMSdevice::DeviceType::SOLAR); + publish_device_values(EMSdevice::DeviceType::MIXING); + publish_other_values(); + publish_sensor_values(true); + system_.send_heartbeat(); + } } void EMSESP::publish_device_values(uint8_t device_type) { - if (Mqtt::connected()) { - for (const auto & emsdevice : emsdevices) { - if (emsdevice && (emsdevice->device_type() == device_type)) { - emsdevice->publish_values(); - } + for (const auto & emsdevice : emsdevices) { + if (emsdevice && (emsdevice->device_type() == device_type)) { + emsdevice->publish_values(); } } } void EMSESP::publish_other_values() { - if (Mqtt::connected()) { - for (const auto & emsdevice : emsdevices) { - if (emsdevice && (emsdevice->device_type() != EMSdevice::DeviceType::BOILER) && (emsdevice->device_type() != EMSdevice::DeviceType::THERMOSTAT) - && (emsdevice->device_type() != EMSdevice::DeviceType::SOLAR) && (emsdevice->device_type() != EMSdevice::DeviceType::MIXING)) { - emsdevice->publish_values(); - } + for (const auto & emsdevice : emsdevices) { + if (emsdevice && (emsdevice->device_type() != EMSdevice::DeviceType::BOILER) && (emsdevice->device_type() != EMSdevice::DeviceType::THERMOSTAT) + && (emsdevice->device_type() != EMSdevice::DeviceType::SOLAR) && (emsdevice->device_type() != EMSdevice::DeviceType::MIXING)) { + emsdevice->publish_values(); } } } void EMSESP::publish_sensor_values(const bool force) { - if (Mqtt::connected()) { - if (sensor_.updated_values() || force) { - sensor_.publish_values(); - } + if (sensor_.updated_values() || force) { + sensor_.publish_values(); } } // MQTT publish a telegram as raw data void EMSESP::publish_response(std::shared_ptr telegram) { + if (!Mqtt::connected()) { + return; + } + StaticJsonDocument doc; char buffer[100]; @@ -553,8 +553,8 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { if (emsdevice) { if (emsdevice->is_device_id(telegram->src)) { found = emsdevice->handle_telegram(telegram); - // check to see if we need to follow up after the telegram has been processed - if (found) { + // if we correctly processes the telegram follow up with sending it via MQTT if needed + if (found && Mqtt::connected()) { if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->updated_values()) || telegram->type_id == publish_id_) { if (telegram->type_id == publish_id_) { publish_id_ = 0; From 677d0bd8085dad8c7d5bd5cf95e021ebdaf45fba Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:31:50 +0200 Subject: [PATCH 09/13] removed MQTT CUSTOM --- src/sensor.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sensor.cpp b/src/sensor.cpp index 684c9dfa1..1f961eb40 100644 --- a/src/sensor.cpp +++ b/src/sensor.cpp @@ -325,10 +325,7 @@ void Sensor::publish_values() { for (const auto & device : devices_) { char s[7]; - if (mqtt_format_ == Mqtt::Format::CUSTOM) { - // e.g. sensor_data = {28-EA41-9497-0E03-5F":23.30,"28-233D-9497-0C03-8B":24.0} - doc[device.to_string()] = Helpers::render_value(s, device.temperature_c, 1); - } else if ((mqtt_format_ == Mqtt::Format::NESTED) || (mqtt_format_ == Mqtt::Format::HA)) { + if ((mqtt_format_ == Mqtt::Format::NESTED) || (mqtt_format_ == Mqtt::Format::HA)) { // e.g. sensor_data = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":"23.30"},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":"24.0"}} char sensorID[20]; // sensor{1-n} strlcpy(sensorID, "sensor", 20); From 7d43af933fc2c4b93c3158771d63753ad638daff Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:31:59 +0200 Subject: [PATCH 10/13] bump to b4 --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index ee4c718db..24c9cc4eb 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "2.1.0b3" +#define EMSESP_APP_VERSION "2.1.0b4" From 402a80f43552ec37ca6cc0084798992438f2e549 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:32:21 +0200 Subject: [PATCH 11/13] updated tests --- src/test/test.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/test/test.cpp b/src/test/test.cpp index 608088b1d..ddaa28d92 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -28,7 +28,7 @@ namespace emsesp { // used with the 'test' command, under su/admin void Test::run_test(uuid::console::Shell & shell, const std::string & command) { if (command == "default") { - run_test(shell, "web"); // add the default test case here + run_test(shell, "thermostat"); // add the default test case here } if (command.empty()) { @@ -280,6 +280,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) { // HC3 uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + + shell.invoke_command("show"); } if (command == "tc100") { @@ -310,24 +312,19 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) { // B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80 rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80}); - EMSESP::show_device_values(shell); rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33}); - EMSESP::show_device_values(shell); rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8}); - EMSESP::show_device_values(shell); EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8"); uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on 1 uart_telegram("30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00"); // SM100 modulation - EMSESP::show_device_values(shell); uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0 - EMSESP::show_device_values(shell); } @@ -758,17 +755,18 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) { std::string version("1.2.3"); EMSESP::add_device(0x28, 160, version, EMSdevice::Brand::BUDERUS); // MM100, WWC EMSESP::add_device(0x29, 161, version, EMSdevice::Brand::BUDERUS); // MM200, WWC - - EMSESP::add_device(0x20, 160, version, EMSdevice::Brand::BOSCH); // MM100 + EMSESP::add_device(0x20, 160, version, EMSdevice::Brand::BOSCH); // MM100 // WWC1 on 0x29 - rx_telegram({0xA9, 0x00, 0xFF, 0x00, 0x02, 0x32, 0x02, 0x6C, 0x00, 0x3C, 0x00, 0x3C, 0x3C, 0x46, 0x02, 0x03, 0x03, 0x00, 0x3C}); + uart_telegram({0xA9, 0x00, 0xFF, 0x00, 0x02, 0x32, 0x02, 0x6C, 0x00, 0x3C, 0x00, 0x3C, 0x3C, 0x46, 0x02, 0x03, 0x03, 0x00, 0x3C}); // WWC2 on 0x28 - rx_telegram({0xA8, 0x00, 0xFF, 0x00, 0x02, 0x31, 0x02, 0x35, 0x00, 0x3C, 0x00, 0x3C, 0x3C, 0x46, 0x02, 0x03, 0x03, 0x00, 0x3C}); + uart_telegram({0xA8, 0x00, 0xFF, 0x00, 0x02, 0x31, 0x02, 0x35, 0x00, 0x3C, 0x00, 0x3C, 0x3C, 0x46, 0x02, 0x03, 0x03, 0x00, 0x3C}); - // check for error "[emsesp] No telegram type handler found for ID 0x255 (src 0x20, dest 0x00)" - rx_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A}); + // check for error "No telegram type handler found for ID 0x255 (src 0x20)" + uart_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A}); + + shell.invoke_command("show"); } // finally dump to console From 7020b41f557c0fb20415216ed0f254f9d20cf88d Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:32:52 +0200 Subject: [PATCH 12/13] added hostname, some formatting --- src/system.cpp | 60 +++++++++++++++++++++++++++----------------------- src/system.h | 23 +++++++++++++------ 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/system.cpp b/src/system.cpp index cfdada737..1109729c9 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -30,13 +30,14 @@ uuid::syslog::SyslogService System::syslog_; #endif // init statics -uint32_t System::heap_start_ = 0; -int System::reset_counter_ = 0; -bool System::upload_status_ = false; -bool System::hide_led_ = false; -uint8_t System::led_gpio_ = 0; -uint16_t System::analog_ = 0; -bool System::analog_enabled_ = false; +uint32_t System::heap_start_ = 0; +int System::reset_counter_ = 0; +bool System::upload_status_ = false; +bool System::hide_led_ = false; +uint8_t System::led_gpio_ = 0; +uint16_t System::analog_ = 0; +bool System::analog_enabled_ = false; +std::string System::hostname_; // send on/off to a gpio pin // value: true = HIGH, false = LOW @@ -168,6 +169,8 @@ void System::init() { analog_enabled_ = settings.analog_enabled; }); + EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & settings) { hostname(settings.hostname.c_str()); }); + EMSESP::init_tx(); // start UART } @@ -215,7 +218,7 @@ void System::loop() { uint32_t currentMillis = uuid::get_uptime(); if (!last_heartbeat_ || (currentMillis - last_heartbeat_ > SYSTEM_HEARTBEAT_INTERVAL)) { last_heartbeat_ = currentMillis; - send_heartbeat(); + send_heartbeat(); } #if defined(ESP8266) @@ -266,6 +269,9 @@ void System::send_heartbeat() { if (analog_enabled_) { doc["adc"] = analog_; } +#if defined(ESP8266) + doc["fragmentation"] = ESP.getHeapFragmentation(); +#endif Mqtt::publish_retain(F("heartbeat"), doc.as(), false); // send to MQTT with retain off. This will add to MQTT queue. } @@ -404,11 +410,6 @@ void System::show_system(uuid::console::Shell & shell) { shell.printfln(F("Flash chip: 0x%08X (%u bytes)"), ESP.getFlashChipId(), ESP.getFlashChipRealSize()); shell.printfln(F("Reset reason: %s"), ESP.getResetReason().c_str()); shell.printfln(F("Reset info: %s"), ESP.getResetInfo().c_str()); - shell.printfln(F("Free heap: %lu bytes"), (unsigned long)ESP.getFreeHeap()); - shell.printfln(F("Free mem: %d %%"), free_mem()); - shell.printfln(F("Maximum free block size: %lu bytes"), (unsigned long)ESP.getMaxFreeBlockSize()); - shell.printfln(F("Heap fragmentation: %u %%"), ESP.getHeapFragmentation()); - shell.printfln(F("Free continuations stack: %lu bytes"), (unsigned long)ESP.getFreeContStack()); #elif defined(ESP32) shell.printfln(F("SDK version: %s"), ESP.getSdkVersion()); shell.printfln(F("CPU frequency: %u MHz"), ESP.getCpuFreqMHz()); @@ -416,6 +417,11 @@ void System::show_system(uuid::console::Shell & shell) { shell.printfln(F("Sketch size: %u bytes (%u bytes free)"), ESP.getSketchSize(), ESP.getFreeSketchSpace()); shell.printfln(F("Free heap: %lu bytes"), (unsigned long)ESP.getFreeHeap()); shell.printfln(F("Free mem: %d %%"), free_mem()); +#if defined(ESP8266) + shell.printfln(F("Heap fragmentation: %u %%"), ESP.getHeapFragmentation()); + shell.printfln(F("Maximum free block size: %lu bytes"), (unsigned long)ESP.getMaxFreeBlockSize()); + shell.printfln(F("Free continuations stack: %lu bytes"), (unsigned long)ESP.getFreeContStack()); +#endif shell.println(); switch (WiFi.status()) { @@ -541,7 +547,7 @@ void System::console_commands(Shell & shell, unsigned int context) { EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::USER, - flash_string_vector{F_(show), F_(system)}, + flash_string_vector{F_(show)}, [=](Shell & shell, const std::vector & arguments __attribute__((unused))) { show_system(shell); shell.println(); @@ -635,7 +641,6 @@ void System::console_commands(Shell & shell, unsigned int context) { // the logic is bit abnormal (loading both filesystems and testing) but this was the only way I could get it to work reliably bool System::check_upgrade() { #if defined(ESP8266) - LittleFSConfig l_cfg; l_cfg.setAutoFormat(false); LittleFS.setConfig(l_cfg); // do not auto format if it can't find LittleFS @@ -733,18 +738,18 @@ bool System::check_upgrade() { EMSESP::esp8266React.getMqttSettingsService()->update( [&](MqttSettings & mqttSettings) { - mqttSettings.host = mqtt["ip"] | FACTORY_MQTT_HOST; - mqttSettings.mqtt_format = (mqtt["nestedjson"] ? Mqtt::Format::NESTED : Mqtt::Format::SINGLE); - mqttSettings.mqtt_qos = mqtt["qos"] | 0; - mqttSettings.mqtt_retain = mqtt["retain"]; - mqttSettings.username = mqtt["user"] | ""; - mqttSettings.password = mqtt["password"] | ""; - mqttSettings.port = mqtt["port"] | FACTORY_MQTT_PORT; - mqttSettings.clientId = FACTORY_MQTT_CLIENT_ID; - mqttSettings.enabled = mqtt["enabled"]; - mqttSettings.keepAlive = FACTORY_MQTT_KEEP_ALIVE; - mqttSettings.cleanSession = FACTORY_MQTT_CLEAN_SESSION; - mqttSettings.maxTopicLength = FACTORY_MQTT_MAX_TOPIC_LENGTH; + mqttSettings.host = mqtt["ip"] | FACTORY_MQTT_HOST; + mqttSettings.mqtt_format = (mqtt["nestedjson"] ? Mqtt::Format::NESTED : Mqtt::Format::SINGLE); + mqttSettings.mqtt_qos = mqtt["qos"] | 0; + mqttSettings.mqtt_retain = mqtt["retain"]; + mqttSettings.username = mqtt["user"] | ""; + mqttSettings.password = mqtt["password"] | ""; + mqttSettings.port = mqtt["port"] | FACTORY_MQTT_PORT; + mqttSettings.clientId = FACTORY_MQTT_CLIENT_ID; + mqttSettings.enabled = mqtt["enabled"]; + mqttSettings.keepAlive = FACTORY_MQTT_KEEP_ALIVE; + mqttSettings.cleanSession = FACTORY_MQTT_CLEAN_SESSION; + mqttSettings.maxTopicLength = FACTORY_MQTT_MAX_TOPIC_LENGTH; return StateUpdateResult::CHANGED; }, @@ -837,7 +842,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp #ifdef EMSESP_STANDALONE output["test"] = "testing"; #else - EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & settings) { char s[7]; JsonObject node = output.createNestedObject("WIFI"); diff --git a/src/system.h b/src/system.h index 4b5e38336..728ab7a3d 100644 --- a/src/system.h +++ b/src/system.h @@ -63,6 +63,14 @@ class System { void syslog_init(); void send_heartbeat(); + static std::string hostname() { + return hostname_; + } + + static void hostname(std::string hostname) { + hostname_ = hostname; + } + private: static uuid::log::Logger logger_; @@ -89,13 +97,14 @@ class System { static void wifi_reconnect(); static int8_t wifi_quality(); - bool system_healthy_ = false; - uint32_t led_flash_speed_ = LED_WARNING_BLINK_FAST; // default boot flashes quickly - static uint32_t heap_start_; - static int reset_counter_; - uint32_t last_heartbeat_ = 0; - static bool upload_status_; // true if we're in the middle of a OTA firmware upload - static uint16_t analog_; + bool system_healthy_ = false; + uint32_t led_flash_speed_ = LED_WARNING_BLINK_FAST; // default boot flashes quickly + static uint32_t heap_start_; + static int reset_counter_; + uint32_t last_heartbeat_ = 0; + static bool upload_status_; // true if we're in the middle of a OTA firmware upload + static uint16_t analog_; + static std::string hostname_; // settings static bool hide_led_; From eb98caa87a13e2aa2f09aeea1f0e8e7d692c8405 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Oct 2020 16:34:06 +0200 Subject: [PATCH 13/13] change show_info to read of json, updates to use HA MQTT Discovery --- src/devices/boiler.cpp | 490 ++++++++++++++++++++++++++---------- src/devices/boiler.h | 4 +- src/devices/mixing.cpp | 53 ++-- src/devices/mixing.h | 2 +- src/devices/solar.cpp | 63 ++--- src/devices/thermostat.cpp | 492 ++++++++++++++++++------------------- src/devices/thermostat.h | 5 +- src/mqtt.cpp | 67 +++-- src/mqtt.h | 28 ++- 9 files changed, 744 insertions(+), 460 deletions(-) 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