From 03e43ba839d3b0d2f5cfe5625ad4155fb1f0d067 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 22 Mar 2021 17:12:09 +0100 Subject: [PATCH] add mqtt subscribe settings, thermostat switchtime, boiler heatingsources --- interface/src/mqtt/MqttSettingsForm.tsx | 11 +++ interface/src/mqtt/types.ts | 1 + lib/framework/MqttSettingsService.cpp | 6 ++ lib/framework/MqttSettingsService.h | 1 + src/command.cpp | 4 +- src/command.h | 2 +- src/devices/boiler.cpp | 50 +++++----- src/devices/boiler.h | 2 +- src/devices/thermostat.cpp | 119 ++++++++++++++++-------- src/devices/thermostat.h | 1 + src/emsdevice.cpp | 40 +++++++- src/emsdevice.h | 43 ++++++++- src/mqtt.cpp | 49 ++++++++-- src/mqtt.h | 3 +- 14 files changed, 249 insertions(+), 83 deletions(-) diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index bb9e5a8d5..6d0bacc7d 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -173,6 +173,17 @@ class MqttSettingsForm extends React.Component { 1/0 "ON"/"OFF" + + general device topic + individual topics, main heating circuit + individual topics, all heating circuits + t) { process_CascadeMessage(t); }); + uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source id, count from 0 + // Runtime of each heatingsource in 0x06DC, ff + register_telegram_type(0x6DC + hs, F("CascadeMessage"), false, MAKE_PF_CB(process_CascadeMessage)); + register_device_value(TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, nullptr, F("burnWorkMin"), F("total burner operating time"), DeviceValueUOM::MINUTES); // selBurnpower in D2 and E4 - // register_telegram_type(0xD2, F("CascadePowerMessage"), false, [&](std::shared_ptr t) { process_CascadePowerMessage(t); }); - // idividual Flowtemps and powervalues for each heatingsource in E4 - // register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, [&](std::shared_ptr t) { process_UBAMonitorFastPlus(t); }); - - // register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES); - // register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT); - // register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES); - // register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT); + // register_telegram_type(0xD2, F("CascadePowerMessage"), false, MAKE_PF_CB(process_CascadePowerMessage)); + // individual Flowtemps and powervalues for each heatingsource in E4 + register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus)); + register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT); + register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT); return; } // register values for master boiler/cascade module @@ -172,12 +172,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_device_value(TAG_BOILER_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, F("wWSetTemp"), F("set temperature"), DeviceValueUOM::DEGREES); register_device_value(TAG_BOILER_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), F("wWType"), F("type")); register_device_value(TAG_BOILER_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), F("wWComfort"), F("comfort")); - register_device_value(TAG_BOILER_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, F("wWFlowTempOffset"), F("flow offset temperature")); + register_device_value(TAG_BOILER_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, F("wWFlowTempOffset"), F("flow temperature offset")); register_device_value(TAG_BOILER_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, F("wWMaxPower"), F("max power"), DeviceValueUOM::PERCENT); register_device_value(TAG_BOILER_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, F("wWCircPump"), F("circulation pump available")); register_device_value(TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), F("wWChargeType"), F("charging type")); register_device_value(TAG_BOILER_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, nullptr, F("wWDisinfectionTemp"), F("disinfection temperature"), DeviceValueUOM::DEGREES); - register_device_value(TAG_BOILER_DATA_WW, &wWCircPumpMode_, DeviceValueType::ENUM, FL_(enum_freq), F("wWCircPumpMode"), F("circulation pump freq")); + register_device_value(TAG_BOILER_DATA_WW, &wWCircMode_, DeviceValueType::ENUM, FL_(enum_freq), F("wWCircMode"), F("circulation pump freq")); register_device_value(TAG_BOILER_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, F("wWCirc"), F("circulation active")); register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp"), F("current intern temperature"), DeviceValueUOM::DEGREES); register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp2"), F("current extern temperature"), DeviceValueUOM::DEGREES); @@ -276,7 +276,7 @@ void Boiler::check_active(const bool force) { void Boiler::process_UBAParameterWW(std::shared_ptr telegram) { has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on - has_update(telegram->read_value(wWCircPumpMode_, 7)); // 1=1x3min 6=6x3min 7=continuous + has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous has_update(telegram->read_value(wWChargeType_, 10)); // 0 = charge pump, 0xff = 3-way valve has_update(telegram->read_value(wWSelTemp_, 2)); has_update(telegram->read_value(wWDisinfectionTemp_, 8)); @@ -505,7 +505,7 @@ void Boiler::process_UBAParametersPlus(std::shared_ptr telegram) void Boiler::process_UBAParameterWWPlus(std::shared_ptr telegram) { has_update(telegram->read_value(wWActivated_, 5)); // 0x01 means on has_update(telegram->read_value(wWCircPump_, 10)); // 0x01 means yes - has_update(telegram->read_value(wWCircPumpMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous + has_update(telegram->read_value(wWCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous // has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9 // has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9 } @@ -600,19 +600,17 @@ void Boiler::process_UBASetPoints(std::shared_ptr telegram) { has_update(telegram->read_value(wWSetPumpPower_, 2)); // ww pump speed/power? } +// 0x6DC, ff for cascaded heatsources (hs) +void Boiler::process_CascadeMessage(std::shared_ptr telegram) { + // uint8_t hsActivated; + // has_update(telegram->read_value(hsActivated, 0)); + telegram->read_value(burnWorkMin_, 3); // this is in seconds + burnWorkMin_ /= 60; +} + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" -// 0x6DC, ff for cascaded heatsources (hs) -// not implemented yet, see #739 -void Boiler::process_CascadeMessage(std::shared_ptr telegram) { - uint8_t hs = telegram->dest - EMSdevice::EMS_DEVICE_ID_BOILER_1; - // uint8_t hsActivated; - // uint32_t hsRuntime; - // has_update(telegram->read_value(hsActivated, 0)); - // has_update(telegram->read_value(hsRuntime, 3)); -} - // 0x35 - not yet implemented void Boiler::process_UBAFlags(std::shared_ptr telegram) { } diff --git a/src/devices/boiler.h b/src/devices/boiler.h index b18836843..d2f21da6f 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -57,7 +57,7 @@ class Boiler : public EMSdevice { uint8_t wWCircPump_; // Warm Water circulation pump available uint8_t wWChargeType_; // Warm Water charge type (pump or 3-way-valve) uint8_t wWDisinfectionTemp_; // Warm Water disinfection temperature to prevent infection - uint8_t wWCircPumpMode_; // Warm Water circulation pump mode + uint8_t wWCircMode_; // Warm Water circulation pump mode uint8_t wWCirc_; // Circulation on/off uint16_t wWCurTemp_; // Warm Water current temperature uint16_t wWCurTemp2_; // Warm Water current temperature storage diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 41c223f76..d18a07916 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -1394,6 +1394,7 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) { data[5] = (dt[6] - '0') * 10 + (dt[7] - '0'); // sec data[6] = (dt[20] - '0'); // day of week data[7] = (dt[22] - '0') + 2; // DST and flag + LOG_INFO(F("Date and time: %02d.%02d.2%03d-%02d:%02d:%02d"),data[3], data[1], data[0], data[2], data[4], data[5]); } LOG_INFO(F("Setting date and time")); @@ -1608,6 +1609,49 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) { return false; } +// sets a single switchtime in the thermostat program for RC35 +// format "01:0,1,15:30" Number, day, on, time +bool Thermostat::set_switchtime(const char * value, const int8_t id) { + uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; + std::shared_ptr hc = heating_circuit(hc_num); + if (hc == nullptr) { + LOG_WARNING(F("Setting switchtime: Heating Circuit %d not found or activated"), hc_num); + return false; + } + if (strlen(value) != 12) { + LOG_WARNING(F("Setting switchtime: Invalid data")); + return false; + } + uint8_t no = (value[0] - '0') * 10 + (value[1] - '0'); + uint8_t day = value[3] - '0'; + uint8_t on = value[5] - '0'; + uint8_t time = 6 * ((value[7] - '0') * 10 + (value[8] - '0')) + (value[10] - '0'); + uint8_t data[2] = {0xE7, 0x90}; // unset switchtime + + if (day != 7 && on != 7) { + data[0] = (day << 5) + on; + data[1] = time; + } + + if (no > 41 || day > 7 || (on > 1 && on != 7) || time > 0x90) { + LOG_WARNING(F("Setting switchtime: Invalid data")); + return false; + } + + if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1)) { + write_command(timer_typeids[hc->hc_num() - 1], no * 2, (uint8_t *)&data, 2, timer_typeids[hc->hc_num() - 1]); + } else { + LOG_WARNING(F("Setting switchtime: thermostat not supported")); + return false; + } + if (data[0] == 0xE7) { + LOG_INFO(F("Setting switchtime no %d for heating circuit %d undefined"), no, hc->hc_num()); + } else { + LOG_INFO(F("Setting switchtime no %d for heating circuit %d to day %d, %s, %02d:%d0"), no, hc->hc_num(), day, (on == 1) ? "on" : "off", time / 6, time % 6); + } + return true; +} + // sets the thermostat program for RC35 and RC20 bool Thermostat::set_program(const char * value, const int8_t id) { uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; @@ -1986,33 +2030,33 @@ void Thermostat::add_commands() { } // common to all thermostats - register_mqtt_cmd(F("temp"), MAKE_CF_CB(set_temp)); - register_mqtt_cmd(F("mode"), MAKE_CF_CB(set_mode)); + register_mqtt_cmd(F("temp"), MAKE_CF_CB(set_temp), FLAG_HC); + register_mqtt_cmd(F("mode"), MAKE_CF_CB(set_mode), FLAG_HC); register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime)); switch (model()) { case EMS_DEVICE_FLAG_RC100: case EMS_DEVICE_FLAG_RC300: - register_mqtt_cmd(F("manualtemp"), MAKE_CF_CB(set_manualtemp)); - register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp)); - register_mqtt_cmd(F("comforttemp"), MAKE_CF_CB(set_comforttemp)); - register_mqtt_cmd(F("summermode"), MAKE_CF_CB(set_summermode)); - register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp)); + register_mqtt_cmd(F("manualtemp"), MAKE_CF_CB(set_manualtemp), FLAG_HC); + register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC); + register_mqtt_cmd(F("comforttemp"), MAKE_CF_CB(set_comforttemp), FLAG_HC); + register_mqtt_cmd(F("summermode"), MAKE_CF_CB(set_summermode), FLAG_HC); + register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC); register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode)); register_mqtt_cmd(F("wwsettemp"), MAKE_CF_CB(set_wwtemp)); register_mqtt_cmd(F("wwsettemplow"), MAKE_CF_CB(set_wwtemplow)); register_mqtt_cmd(F("wwonetime"), MAKE_CF_CB(set_wwonetime)); register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode)); register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building)); - register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp)); - register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp)); - register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp)); - register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp)); - register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp)); + register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC); + register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC); + register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC); + register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC); + register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC); register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp)); - register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence)); - register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program)); - register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode)); + register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC); + register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC); + register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC); break; case EMS_DEVICE_FLAG_RC20_2: register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp)); @@ -2025,35 +2069,36 @@ void Thermostat::add_commands() { register_mqtt_cmd(F("display"), MAKE_CF_CB(set_display)); break; case EMS_DEVICE_FLAG_RC35: // RC30 and RC35 - register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp)); - register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp)); - register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp)); - register_mqtt_cmd(F("remotetemp"), MAKE_CF_CB(set_remotetemp)); + register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp), FLAG_HC); + register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp), FLAG_HC); + register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC); + register_mqtt_cmd(F("remotetemp"), MAKE_CF_CB(set_remotetemp), FLAG_HC); register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp)); register_mqtt_cmd(F("calinttemp"), MAKE_CF_CB(set_calinttemp)); register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building)); - register_mqtt_cmd(F("control"), MAKE_CF_CB(set_control)); - register_mqtt_cmd(F("pause"), MAKE_CF_CB(set_pause)); - register_mqtt_cmd(F("party"), MAKE_CF_CB(set_party)); - register_mqtt_cmd(F("holiday"), MAKE_CF_CB(set_holiday)); - register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp)); - register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp)); - register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp)); - register_mqtt_cmd(F("holidaytemp"), MAKE_CF_CB(set_holidaytemp)); + register_mqtt_cmd(F("control"), MAKE_CF_CB(set_control), FLAG_HC); + register_mqtt_cmd(F("pause"), MAKE_CF_CB(set_pause), FLAG_HC); + register_mqtt_cmd(F("party"), MAKE_CF_CB(set_party), FLAG_HC); + register_mqtt_cmd(F("holiday"), MAKE_CF_CB(set_holiday), FLAG_HC); + register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC); + register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC); + register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC); + register_mqtt_cmd(F("holidaytemp"), MAKE_CF_CB(set_holidaytemp), FLAG_HC); register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode)); register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode)); - register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence)); - register_mqtt_cmd(F("flowtempoffset"), MAKE_CF_CB(set_flowtempoffset)); - register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp)); - register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp)); - register_mqtt_cmd(F("reducemode"), MAKE_CF_CB(set_reducemode)); - register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program)); - register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode)); + register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC); + register_mqtt_cmd(F("flowtempoffset"), MAKE_CF_CB(set_flowtempoffset), FLAG_HC); + register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC); + register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC); + register_mqtt_cmd(F("reducemode"), MAKE_CF_CB(set_reducemode), FLAG_HC); + register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC); + register_mqtt_cmd(F("switchtime"), MAKE_CF_CB(set_switchtime), FLAG_HC); + register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC); break; case EMS_DEVICE_FLAG_JUNKERS: - register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp)); - register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp)); - register_mqtt_cmd(F("heattemp"), MAKE_CF_CB(set_heattemp)); + register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC); + register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC); + register_mqtt_cmd(F("heattemp"), MAKE_CF_CB(set_heattemp), FLAG_HC); break; default: break; diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 0c4ff4390..758bde8e2 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -315,6 +315,7 @@ class Thermostat : public EMSdevice { bool set_minflowtemp(const char * value, const int8_t id); bool set_maxflowtemp(const char * value, const int8_t id); bool set_reducemode(const char * value, const int8_t id); + bool set_switchtime(const char * value, const int8_t id); bool set_program(const char * value, const int8_t id); bool set_controlmode(const char * value, const int8_t id); diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index b9403db0d..dacd1e851 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -53,7 +53,23 @@ static const __FlashStringHelper * const DeviceValueTAG_s[] PROGMEM = { F_(tag_wwc1), // "wwc1" F_(tag_wwc2), // "Wwc2" F_(tag_wwc3), // "wwc3" - F_(tag_wwc4) // "wwc4" + F_(tag_wwc4), // "wwc4" + F_(tag_hs1), // "hs1" + F_(tag_hs2), // "hs2" + F_(tag_hs3), // "hs3" + F_(tag_hs4), // "hs4" + F_(tag_hs5), // "hs5" + F_(tag_hs6), // "hs6" + F_(tag_hs7), // "hs7" + F_(tag_hs8), // "hs8" + F_(tag_hs9), // "hs9" + F_(tag_hs10), // "hs10" + F_(tag_hs11), // "hs11" + F_(tag_hs12), // "hs12" + F_(tag_hs13), // "hs13" + F_(tag_hs14), // "hs14" + F_(tag_hs15), // "hs15" + F_(tag_hs16) // "hs16" }; @@ -72,7 +88,23 @@ static const __FlashStringHelper * const DeviceValueTAG_mqtt[] PROGMEM = { F_(tag_wwc1), // "wwc1" F_(tag_wwc2), // "Wwc2" F_(tag_wwc3), // "wwc3" - F_(tag_wwc4) // "wwc4" + F_(tag_wwc4), // "wwc4" + F_(tag_hs1), // "hs1" + F_(tag_hs2), // "hs2" + F_(tag_hs3), // "hs3" + F_(tag_hs4), // "hs4" + F_(tag_hs5), // "hs5" + F_(tag_hs6), // "hs6" + F_(tag_hs7), // "hs7" + F_(tag_hs8), // "hs8" + F_(tag_hs9), // "hs9" + F_(tag_hs10), // "hs10" + F_(tag_hs11), // "hs11" + F_(tag_hs12), // "hs12" + F_(tag_hs13), // "hs13" + F_(tag_hs14), // "hs14" + F_(tag_hs15), // "hs15" + F_(tag_hs16) // "hs16" }; @@ -373,8 +405,8 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_ } // add command to library -void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f) { - Command::add(device_type_, cmd, f); +void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag) { + Command::add(device_type_, cmd, f, flag); } // register a call back function for a specific telegram type diff --git a/src/emsdevice.h b/src/emsdevice.h index d536b8841..cd59da034 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -83,6 +83,22 @@ MAKE_PSTR(tag_wwc1, "wwc1") MAKE_PSTR(tag_wwc2, "wwc2") MAKE_PSTR(tag_wwc3, "wwc3") MAKE_PSTR(tag_wwc4, "wwc4") +MAKE_PSTR(tag_hs1, "hs1") +MAKE_PSTR(tag_hs2, "hs2") +MAKE_PSTR(tag_hs3, "hs3") +MAKE_PSTR(tag_hs4, "hs4") +MAKE_PSTR(tag_hs5, "hs5") +MAKE_PSTR(tag_hs6, "hs6") +MAKE_PSTR(tag_hs7, "hs7") +MAKE_PSTR(tag_hs8, "hs8") +MAKE_PSTR(tag_hs9, "hs9") +MAKE_PSTR(tag_hs10, "hs10") +MAKE_PSTR(tag_hs11, "hs11") +MAKE_PSTR(tag_hs12, "hs12") +MAKE_PSTR(tag_hs13, "hs13") +MAKE_PSTR(tag_hs14, "hs14") +MAKE_PSTR(tag_hs15, "hs15") +MAKE_PSTR(tag_hs16, "hs16") // MQTT topic names MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat") @@ -102,10 +118,33 @@ enum DeviceValueTAG : uint8_t { TAG_WWC1, TAG_WWC2, TAG_WWC3, - TAG_WWC4 + TAG_WWC4, + TAG_HS1, + TAG_HS2, + TAG_HS3, + TAG_HS4, + TAG_HS5, + TAG_HS6, + TAG_HS7, + TAG_HS8, + TAG_HS9, + TAG_HS10, + TAG_HS11, + TAG_HS12, + TAG_HS13, + TAG_HS14, + TAG_HS15, + TAG_HS16 }; +// mqtt flags for command subscriptions +enum MqttSubFlag : uint8_t { + FLAG_NONE = 0, + FLAG_HC, + FLAG_WWC +}; + class EMSdevice { public: virtual ~EMSdevice() = default; // destructor of base class must always be virtual because it's a polymorphic class @@ -242,7 +281,7 @@ class EMSdevice { void read_command(const uint16_t type_id); void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f); - void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f); + void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0); void publish_mqtt_ha_sensor(); diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 405f77a79..cab227d11 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -40,6 +40,7 @@ uint8_t Mqtt::bool_format_; uint8_t Mqtt::ha_climate_format_; bool Mqtt::ha_enabled_; bool Mqtt::nested_format_; +uint8_t Mqtt::subscribes_; std::deque Mqtt::mqtt_messages_; std::vector Mqtt::mqtt_subfunctions_; @@ -83,12 +84,12 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_ } // register in our libary with the callback function. - // We store both the original topic and the fully-qualified one + // We store the original topic without base mqtt_subfunctions_.emplace_back(device_type, std::move(topic), std::move(cb)); } // subscribe to the command topic if it doesn't exist yet -void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb) { +void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) { std::string cmd_topic = EMSdevice::device_type_2_device_name(device_type); // thermostat, boiler, etc... // see if we have already a handler for the device type (boiler, thermostat). If not add it @@ -106,12 +107,28 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper LOG_DEBUG(F("Registering MQTT cmd %s with topic %s"), uuid::read_flash_string(cmd).c_str(), EMSdevice::device_type_2_device_name(device_type).c_str()); } + // only general device topics + if (subscribes_ == 0) { + return; + } + // register the individual commands too (e.g. ems-esp/boiler/wwonetime) // https://github.com/emsesp/EMS-ESP32/issues/31 if (device_type != EMSdevice::DeviceType::SYSTEM) { std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); - topic = cmd_topic + "/" + uuid::read_flash_string(cmd); - Mqtt::subscribe(device_type, topic, nullptr); + if (subscribes_ == 2 && flag == MqttSubFlag::FLAG_HC) { + topic = cmd_topic + "/hc1/" + uuid::read_flash_string(cmd); + queue_subscribe_message(topic); + topic = cmd_topic + "/hc2/" + uuid::read_flash_string(cmd); + queue_subscribe_message(topic); + topic = cmd_topic + "/hc3/" + uuid::read_flash_string(cmd); + queue_subscribe_message(topic); + topic = cmd_topic + "/hc4/" + uuid::read_flash_string(cmd); + queue_subscribe_message(topic); + } else { + topic = cmd_topic + "/" + uuid::read_flash_string(cmd); + queue_subscribe_message(topic); + } } } @@ -257,6 +274,12 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) char topic[100]; strlcpy(topic, &fulltopic[1 + strlen(mqtt_base_.c_str())], 100); + // strip the topic substrings + char * topic_end = strchr(topic,'/'); + if (topic_end != nullptr) { + topic_end[0] = '\0'; + } + // convert payload to a null-terminated char string char message[len + 2]; strlcpy(message, payload, len + 1); @@ -277,14 +300,22 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) // check if it's not json, then try and extract the command from the topic name if (message[0] != '{') { - char * cmd_only = strrchr(topic, '/'); + // get topic with substrings again + strlcpy(topic, &fulltopic[1 + strlen(mqtt_base_.c_str())], 100); + char * cmd_only = strchr(topic, '/'); if (cmd_only == NULL) { return; // invalid topic name } cmd_only++; // skip the / - // LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s"), mf.device_type_, topic, cmd_only, message); - if (!Command::call(mf.device_type_, cmd_only, message)) { - LOG_ERROR(F("No matching cmd (%s) in topic %s, or invalid data"), cmd_only, topic); + int8_t id = -1; + // check for hcx/ prefix + if (cmd_only[0] == 'h' && cmd_only[1] == 'c' && cmd_only[3] == '/') { + id = cmd_only[2] - '0'; + cmd_only += 4; + } + // LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s, id = %d"), mf.device_type_, topic, cmd_only, message, id); + if (!Command::call(mf.device_type_, cmd_only, message, id)) { + LOG_ERROR(F("No matching cmd (%s) in topic %s, id %d, or invalid data"), cmd_only, topic, id); } return; } @@ -411,6 +442,7 @@ void Mqtt::load_settings() { dallas_format_ = mqttSettings.dallas_format; bool_format_ = mqttSettings.bool_format; nested_format_ = mqttSettings.nested_format; + subscribes_ = mqttSettings.subscribes; // convert to milliseconds publish_time_boiler_ = mqttSettings.publish_time_boiler * 1000; @@ -573,7 +605,6 @@ void Mqtt::on_connect() { EMSESP::shower_.send_mqtt_stat(false); // Send shower_activated as false EMSESP::system_.send_heartbeat(); // send heatbeat - // } else { if (connectcount_ > 1) { // we doing a re-connect from a TCP break // only re-subscribe again to all MQTT topics diff --git a/src/mqtt.h b/src/mqtt.h index 9449acc50..85328f335 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -107,7 +107,7 @@ class Mqtt { static void publish_ha(const __FlashStringHelper * topic, const JsonObject & payload); static void publish_mqtt_ha_sensor(uint8_t type, uint8_t tag, const __FlashStringHelper * name, const uint8_t device_type, const __FlashStringHelper * entity, const uint8_t uom = 0); - static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb); + static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t tag = 0); static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type); static void show_mqtt(uuid::console::Shell & shell); @@ -275,6 +275,7 @@ class Mqtt { static uint8_t ha_climate_format_; static bool ha_enabled_; static bool nested_format_; + static uint8_t subscribes_; }; } // namespace emsesp