From 9dcd1c0304a1e5bbdf84b317c905c6dc0addeb06 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 21 Oct 2020 13:41:48 +0200 Subject: [PATCH] heatpump support - #560 --- CHANGELOG.md | 1 + src/devices/heatpump.cpp | 115 +++++++++++++++++++++++++++++++++++---- src/devices/heatpump.h | 11 ++++ src/locale_EN.h | 7 +++ src/test/test.cpp | 23 +++++++- 5 files changed, 145 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fab761e99..10e8178ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - new command under system called `report`. http://ems-esp/api?device=system&cmd=report to generate a report log for troubleshooting - thermostat error codes - Console command `pulbish ha` to also force the creation of the Home Assistant MQTT Discovery topics +- Heatpump values ### Fixed - fix wwontime readback diff --git a/src/devices/heatpump.cpp b/src/devices/heatpump.cpp index d7222e624..0d9eb9a50 100644 --- a/src/devices/heatpump.cpp +++ b/src/devices/heatpump.cpp @@ -29,27 +29,128 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c LOG_DEBUG(F("Adding new Heat Pump module with device ID 0x%02X"), device_id); // telegram handlers - register_telegram_type(0x047B, F("HP1"), true, [&](std::shared_ptr t) { process_HPMonitor1(t); }); - register_telegram_type(0x042B, F("HP2"), true, [&](std::shared_ptr t) { process_HPMonitor2(t); }); + register_telegram_type(0x042B, F("HP1"), true, [&](std::shared_ptr t) { process_HPMonitor1(t); }); + register_telegram_type(0x047B, F("HP2"), true, [&](std::shared_ptr t) { process_HPMonitor2(t); }); + + // API call + Command::add_with_json(this->device_type(), F("info"), [&](const char * value, const int8_t id, JsonObject & object) { + return command_info(value, id, object); + }); +} + +bool Heatpump::command_info(const char * value, const int8_t id, JsonObject & output) { + return (export_values(output)); +} + +// creates JSON doc from values +// returns false if empty +bool Heatpump::export_values(JsonObject & output) { + if (Helpers::hasValue(airHumidity_)) { + output["airHumidity"] = (float)airHumidity_ / 2; + } + + if (Helpers::hasValue(dewTemperature_)) { + output["dewTemperature"] = dewTemperature_; + } + + return output.size(); } void Heatpump::device_info_web(JsonArray & root) { + // fetch the values into a JSON document + StaticJsonDocument doc; + JsonObject output = doc.to(); + if (!export_values(output)) { + return; // empty + } + + print_value_json(root, F("airHumidity"), nullptr, F_(airHumidity), F_(percent), output); + print_value_json(root, F("dewTemperature"), nullptr, F_(dewTemperature), F_(degrees), output); } // display all values into the shell console void Heatpump::show_values(uuid::console::Shell & shell) { - // EMSdevice::show_values(shell); // always call this to show header + EMSdevice::show_values(shell); // always call this to show header + + // fetch the values into a JSON document + StaticJsonDocument doc; + JsonObject output = doc.to(); + if (!export_values(output)) { + return; // empty + } + + print_value_json(shell, F("airHumidity"), nullptr, F_(airHumidity), F_(percent), output); + print_value_json(shell, F("dewTemperature"), nullptr, F_(dewTemperature), F_(degrees), output); } // publish values via MQTT void Heatpump::publish_values(JsonObject & data, bool force) { + // handle HA first + if (Mqtt::mqtt_format() == Mqtt::Format::HA) { + register_mqtt_ha_config(force); + } + + StaticJsonDocument doc; + JsonObject output = doc.to(); + if (export_values(output)) { + Mqtt::publish(F("heatpump_data"), doc.as()); + } +} + +void Heatpump::register_mqtt_ha_config(bool force) { + if ((mqtt_ha_config_ && !force)) { + return; + } + + if (!Mqtt::connected()) { + return; + } + + // Create the Master device + StaticJsonDocument doc; + doc["name"] = F_(EMSESP); + doc["uniq_id"] = F_(heatpump); + doc["ic"] = F_(iconheatpump); + + char stat_t[50]; + snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/heatpump_data"), System::hostname().c_str()); + doc["stat_t"] = stat_t; + + doc["val_tpl"] = F("{{value_json.airHumidity}}"); + + JsonObject dev = doc.createNestedObject("dev"); + dev["name"] = F("EMS-ESP Heat Pump"); + dev["sw"] = EMSESP_APP_VERSION; + dev["mf"] = this->brand_to_string(); + dev["mdl"] = this->name(); + JsonArray ids = dev.createNestedArray("ids"); + ids.add("ems-esp-heatpump"); + Mqtt::publish_retain(F("homeassistant/sensor/ems-esp/heatpump/config"), doc.as(), true); // publish the config payload with retain flag + + Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(airHumidity), this->device_type(), "airHumidity", F_(percent), nullptr); + Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(dewTemperature), this->device_type(), "dewTemperature", F_(degrees), nullptr); + + mqtt_ha_config_ = true; // done } // check to see if values have been updated bool Heatpump::updated_values() { + if (changed_) { + changed_ = false; + return true; + } return false; } +/* + * Type 0x47B - HeatPump Monitor 2 + * e.g. "38 10 FF 00 03 7B 08 24 00 4B" + */ +void Heatpump::process_HPMonitor2(std::shared_ptr telegram) { + changed_ |= telegram->read_value(dewTemperature_, 0); + changed_ |= telegram->read_value(airHumidity_, 1); +} + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -61,14 +162,6 @@ void Heatpump::process_HPMonitor1(std::shared_ptr telegram) { // still to implement } -/* - * Type 0x47B - HeatPump Monitor 2 - * e.g. "38 10 FF 00 03 7B 08 24 00 4B" - */ -void Heatpump::process_HPMonitor2(std::shared_ptr telegram) { - // still to implement -} - #pragma GCC diagnostic pop } // namespace emsesp \ No newline at end of file diff --git a/src/devices/heatpump.h b/src/devices/heatpump.h index c2e0e1ec5..0d97ea2a9 100644 --- a/src/devices/heatpump.h +++ b/src/devices/heatpump.h @@ -25,6 +25,7 @@ #include #include "emsdevice.h" +#include "emsesp.h" #include "telegram.h" #include "helpers.h" #include "mqtt.h" @@ -43,6 +44,16 @@ class Heatpump : public EMSdevice { private: static uuid::log::Logger logger_; + bool export_values(JsonObject & doc); + bool command_info(const char * value, const int8_t id, JsonObject & output); + void register_mqtt_ha_config(bool force); + + uint8_t airHumidity_ = EMS_VALUE_UINT_NOTSET; + uint8_t dewTemperature_ = EMS_VALUE_UINT_NOTSET; + + bool changed_ = false; + bool mqtt_ha_config_ = false; // for HA MQTT Discovery + void process_HPMonitor1(std::shared_ptr telegram); void process_HPMonitor2(std::shared_ptr telegram); }; diff --git a/src/locale_EN.h b/src/locale_EN.h index eddf1e89c..4aeadd032 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -99,6 +99,7 @@ MAKE_PSTR(1space, " ") MAKE_PSTR(2spaces, " ") MAKE_PSTR(kwh, "kWh") MAKE_PSTR(wh, "Wh") +MAKE_PSTR(EMSESP, "EMS-ESP") MAKE_PSTR(master_thermostat_fmt, "Master Thermostat Device ID = %s") MAKE_PSTR(host_fmt, "Host = %s") MAKE_PSTR(hostname_fmt, "WiFi Hostname = %s") @@ -260,6 +261,10 @@ MAKE_PSTR(roominfluence, "Room influence") MAKE_PSTR(mode, "Mode") MAKE_PSTR(modetype, "Mode type") +// heat pump +MAKE_PSTR(airHumidity, "Relative air humidity") +MAKE_PSTR(dewTemperature, "Dew point temperature") + // HA icons MAKE_PSTR(icontemperature, "mdi:coolant-temperature") MAKE_PSTR(iconpercent, "mdi:sine-wave") @@ -272,6 +277,8 @@ MAKE_PSTR(iconimport, "mdi:home-import-outline") MAKE_PSTR(iconcruise, "mdi:car-cruise-control") MAKE_PSTR(iconvalve, "mdi:valve") MAKE_PSTR(iconpower, "mdi:power-cycle") +MAKE_PSTR(iconthermostat, "mdi:home-thermometer-outline") +MAKE_PSTR(iconheatpump, "mdi:water-pump") // MQTT topic suffix MAKE_PSTR(mqtt_suffix_main, "_main") diff --git a/src/test/test.cpp b/src/test/test.cpp index 9ef8dcb5d..227836192 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -27,7 +27,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, "general"); // add the default test case here + run_test(shell, "heatpump"); // add the default test case here } if (command.empty()) { @@ -368,6 +368,27 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) { EMSESP::show_device_values(shell); } + if (command == "heatpump") { + shell.printfln(F("Testing Heat Pump")); + + EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); + + std::string version("1.2.3"); + + // add heatpump + EMSESP::add_device(0x38, 200, version, EMSdevice::Brand::BUDERUS); // Enviline module + + // add a thermostat + EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120 + uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24, + 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1 + + uart_telegram("38 0B FF 00 03 7B 0C 34 00 74"); + shell.invoke_command("call"); + shell.invoke_command("call heatpump info"); + EMSESP::show_device_values(shell); + } + if (command == "solar200") { shell.printfln(F("Testing Solar SM200"));