refactor device value rendering (to Web, Console or MQTT) to base class #632

This commit is contained in:
proddy
2020-12-13 22:52:34 +01:00
parent f72e549850
commit ffa313ebe4
60 changed files with 2579 additions and 3367 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -38,23 +38,14 @@ class Boiler : public EMSdevice {
public:
Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;
void register_mqtt_ha_config();
void register_mqtt_ha_config_ww();
void check_active(const bool force = false);
bool export_values_main(JsonObject & doc, const bool textformat = false);
bool export_values_ww(JsonObject & doc, const bool textformat = false);
bool changed_ = false;
bool mqtt_ha_config_ = false; // HA MQTT Discovery
bool mqtt_ha_config_ww_ = false; // HA MQTT Discovery
uint8_t boilerState_ = EMS_VALUE_UINT_NOTSET; // Boiler state flag - FOR INTERNAL USE
static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33;
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
@@ -68,120 +59,99 @@ class Boiler : public EMSdevice {
static constexpr uint8_t EMS_BOILER_SELFLOWTEMP_HEATING = 20; // was originally 70, changed to 30 for issue #193, then to 20 with issue #344
// UBAParameterWW
uint8_t wWActivated_ = EMS_VALUE_BOOL_NOTSET; // Warm Water activated
uint8_t wWSelTemp_ = EMS_VALUE_UINT_NOTSET; // Warm Water selected temperature
uint8_t wWCircPump_ = EMS_VALUE_BOOL_NOTSET; // Warm Water circulation pump available
uint8_t wWCircPumpMode_ = EMS_VALUE_UINT_NOTSET; // Warm Water circulation pump mode
uint8_t wWChargeType_ = EMS_VALUE_BOOL_NOTSET; // Warm Water charge type (pump or 3-way-valve)
uint8_t wWDisinfectionTemp_ = EMS_VALUE_UINT_NOTSET; // Warm Water disinfection temperature to prevent infection
uint8_t wWComfort_ = EMS_VALUE_UINT_NOTSET; // WW comfort mode
// ww
uint8_t wWSetTemp_; // Warm Water set temperature
uint8_t wWSelTemp_; // Warm Water selected temperature
uint8_t wWType_; // 0-off, 1-flow, 2-flowbuffer, 3-buffer, 4-layered buffer
uint8_t wWComfort_; // WW comfort mode
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 wWCirc_; // Circulation on/off
uint16_t wWCurTemp_; // Warm Water current temperature
uint16_t wWCurTemp2_; // Warm Water current temperature storage
uint8_t wWCurFlow_; // Warm Water current flow temp in l/min
uint16_t wwStorageTemp1_; // warm water storage temp 1
uint16_t wwStorageTemp2_; // warm water storage temp 2
uint8_t wWActivated_; // Warm Water activated
uint8_t wWOneTime_; // Warm Water one time function on/off
uint8_t wWDisinfecting_; // Warm Water disinfection on/off
uint8_t wWCharging_; // Warm Water charging on/off
uint8_t wWRecharging_; // Warm Water recharge on/off
uint8_t wWTempOK_; // Warm Water temperature ok on/off
uint8_t wWActive_;
uint8_t wWHeat_; // 3-way valve on WW
uint8_t wWSetPumpPower_; // ww pump speed/power?
uint16_t wwMixTemperature_; // mixing temperature
uint16_t wwBufferTemperature_; // buffertemperature
uint32_t wWStarts_; // Warm Water # starts
uint32_t wWStarts2_; // Warm water starts (control)
uint32_t wWWorkM_; // Warm Water # minutes
// MC10Status
uint16_t wwMixTemperature_ = EMS_VALUE_USHORT_NOTSET; // mengertemperatuur
uint16_t wwBufferTemperature_ = EMS_VALUE_USHORT_NOTSET; // buffertemperature
// UBAMonitorFast - 0x18 on EMS1
uint8_t selFlowTemp_ = EMS_VALUE_UINT_NOTSET; // Selected flow temperature
uint16_t curFlowTemp_ = EMS_VALUE_USHORT_NOTSET; // Current flow temperature
uint16_t wwStorageTemp1_ = EMS_VALUE_USHORT_NOTSET; // warm water storage temp 1
uint16_t wwStorageTemp2_ = EMS_VALUE_USHORT_NOTSET; // warm water storage temp 2
uint16_t retTemp_ = EMS_VALUE_USHORT_NOTSET; // Return temperature
uint8_t burnGas_ = EMS_VALUE_BOOL_NOTSET; // Gas on/off
uint8_t fanWork_ = EMS_VALUE_BOOL_NOTSET; // Fan on/off
uint8_t ignWork_ = EMS_VALUE_BOOL_NOTSET; // Ignition on/off
uint8_t heatPump_ = EMS_VALUE_BOOL_NOTSET; // Boiler pump on/off
uint8_t wWHeat_ = EMS_VALUE_BOOL_NOTSET; // 3-way valve on WW
uint8_t wWCirc_ = EMS_VALUE_BOOL_NOTSET; // Circulation on/off
uint8_t selBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner max power %
uint8_t curBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner current power %
uint16_t flameCurr_ = EMS_VALUE_USHORT_NOTSET; // Flame current in micro amps
uint8_t sysPress_ = EMS_VALUE_UINT_NOTSET; // System pressure
char serviceCode_[4] = {'\0'}; // 3 character status/service code
uint16_t serviceCodeNumber_ = EMS_VALUE_USHORT_NOTSET; // error/service code
uint8_t boilerState_ = EMS_VALUE_UINT_NOTSET; // Boiler state flag
char lastCode_[30] = {'\0'};
uint32_t lastCodeDate_ = 0;
// UBAMonitorSlow - 0x19 on EMS1
int16_t outdoorTemp_ = EMS_VALUE_SHORT_NOTSET; // Outside temperature
uint16_t boilTemp_ = EMS_VALUE_USHORT_NOTSET; // Boiler temperature
uint16_t exhaustTemp_ = EMS_VALUE_USHORT_NOTSET; // Exhaust temperature
uint8_t pumpMod_ = EMS_VALUE_UINT_NOTSET; // Pump modulation %
uint32_t burnStarts_ = EMS_VALUE_ULONG_NOTSET; // # burner restarts
uint32_t burnWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total burner operating time
uint32_t heatWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total heat operating time
uint16_t switchTemp_ = EMS_VALUE_USHORT_NOTSET; // Switch temperature
// UBAMonitorWW
uint8_t wWSetTemp_ = EMS_VALUE_UINT_NOTSET; // Warm Water set temperature
uint16_t wWCurTemp_ = EMS_VALUE_USHORT_NOTSET; // Warm Water current temperature
uint16_t wWCurTemp2_ = EMS_VALUE_USHORT_NOTSET; // Warm Water current temperature storage
uint32_t wWStarts_ = EMS_VALUE_ULONG_NOTSET; // Warm Water # starts
uint32_t wWWorkM_ = EMS_VALUE_ULONG_NOTSET; // Warm Water # minutes
uint8_t wWOneTime_ = EMS_VALUE_BOOL_NOTSET; // Warm Water one time function on/off
uint8_t wWDisinfecting_ = EMS_VALUE_BOOL_NOTSET; // Warm Water disinfection on/off
uint8_t wWCharging_ = EMS_VALUE_BOOL_NOTSET; // Warm Water charging on/off
uint8_t wWRecharging_ = EMS_VALUE_BOOL_NOTSET; // Warm Water recharge on/off
uint8_t wWTempOK_ = EMS_VALUE_BOOL_NOTSET; // Warm Water temperature ok on/off
uint8_t wWCurFlow_ = EMS_VALUE_UINT_NOTSET; // Warm Water current flow temp in l/min
uint8_t wWType_ = EMS_VALUE_UINT_NOTSET; // 0-off, 1-flow, 2-flowbuffer, 3-buffer, 4-layered buffer
uint8_t wWActive_ = EMS_VALUE_BOOL_NOTSET;
// UBATotalUptime
uint32_t UBAuptime_ = EMS_VALUE_ULONG_NOTSET; // Total UBA working hours
// UBAParameters
uint8_t heatingActivated_ = EMS_VALUE_BOOL_NOTSET; // Heating activated on the boiler
uint8_t heatingTemp_ = EMS_VALUE_UINT_NOTSET; // Heating temperature setting on the boiler
uint8_t pumpModMax_ = EMS_VALUE_UINT_NOTSET; // Boiler circuit pump modulation max. power %
uint8_t pumpModMin_ = EMS_VALUE_UINT_NOTSET; // Boiler circuit pump modulation min. power
uint8_t burnMinPower_ = EMS_VALUE_UINT_NOTSET;
uint8_t burnMaxPower_ = EMS_VALUE_UINT_NOTSET;
int8_t boilHystOff_ = EMS_VALUE_INT_NOTSET;
int8_t boilHystOn_ = EMS_VALUE_INT_NOTSET;
uint8_t burnMinPeriod_ = EMS_VALUE_UINT_NOTSET;
uint8_t pumpDelay_ = EMS_VALUE_UINT_NOTSET;
// UBASetPoint
uint8_t setFlowTemp_ = EMS_VALUE_UINT_NOTSET; // boiler setpoint temp
uint8_t setBurnPow_ = EMS_VALUE_UINT_NOTSET; // max output power in %
uint8_t wWSetPumpPower_ = EMS_VALUE_UINT_NOTSET; // ww pump speed/power?
// other internal calculated params
uint8_t tapwaterActive_ = EMS_VALUE_BOOL_NOTSET; // Hot tap water is on/off
uint8_t heatingActive_ = EMS_VALUE_BOOL_NOTSET; // Central heating is on/off
uint8_t pumpMod2_ = EMS_VALUE_UINT_NOTSET; // heatpump modulation from 0xE3 (heatpumps)
// UBAInformation
uint32_t upTimeControl_ = EMS_VALUE_ULONG_NOTSET; // Operating time control
uint32_t upTimeCompHeating_ = EMS_VALUE_ULONG_NOTSET; // Operating time compressor heating
uint32_t upTimeCompCooling_ = EMS_VALUE_ULONG_NOTSET; // Operating time compressor cooling
uint32_t upTimeCompWw_ = EMS_VALUE_ULONG_NOTSET; // Operating time compressor warm water
uint32_t heatingStarts_ = EMS_VALUE_ULONG_NOTSET; // Heating starts (control)
uint32_t coolingStarts_ = EMS_VALUE_ULONG_NOTSET; // Cooling starts (control)
uint32_t wWStarts2_ = EMS_VALUE_ULONG_NOTSET; // Warm water starts (control)
uint32_t nrgConsTotal_ = EMS_VALUE_ULONG_NOTSET; // Energy consumption total
uint32_t auxElecHeatNrgConsTotal_ = EMS_VALUE_ULONG_NOTSET; // Auxiliary electrical heater energy consumption total
uint32_t auxElecHeatNrgConsHeating_ = EMS_VALUE_ULONG_NOTSET; // Auxiliary electrical heater energy consumption heating
uint32_t auxElecHeatNrgConsDHW_ = EMS_VALUE_ULONG_NOTSET; // Auxiliary electrical heater energ consumption DHW
uint32_t nrgConsCompTotal_ = EMS_VALUE_ULONG_NOTSET; // Energy consumption compressor total
uint32_t nrgConsCompHeating_ = EMS_VALUE_ULONG_NOTSET; // Energy consumption compressor heating
uint32_t nrgConsCompWw_ = EMS_VALUE_ULONG_NOTSET; // Energy consumption compressor warm water
uint32_t nrgConsCompCooling_ = EMS_VALUE_ULONG_NOTSET; // Energy consumption compressor cooling
// UBAEnergySupplied
uint32_t nrgSuppTotal_ = EMS_VALUE_ULONG_NOTSET; // Energy supplied total
uint32_t nrgSuppHeating_ = EMS_VALUE_ULONG_NOTSET; // Energy supplied heating
uint32_t nrgSuppWw_ = EMS_VALUE_ULONG_NOTSET; // Energy supplied warm water
uint32_t nrgSuppCooling_ = EMS_VALUE_ULONG_NOTSET; // Energy supplied cooling
// _UBAMaintenanceData
uint8_t maintenanceMessage_ = EMS_VALUE_UINT_NOTSET;
uint8_t maintenanceType_ = EMS_VALUE_UINT_NOTSET;
uint8_t maintenanceTime_ = EMS_VALUE_UINT_NOTSET;
char maintenanceDate_[12] = {'\0'};
// main
uint8_t heatingActive_; // Central heating is on/off
uint8_t tapwaterActive_; // Hot tap water is on/off
uint8_t selFlowTemp_; // Selected flow temperature
uint8_t selBurnPow_; // Burner max power %
uint8_t pumpMod2_; // heatpump modulation from 0xE3 (heatpumps)
uint8_t pumpMod_; // Pump modulation %
int16_t outdoorTemp_; // Outside temperature
uint16_t curFlowTemp_; // Current flow temperature
uint16_t retTemp_; // Return temperature
uint16_t switchTemp_; // Switch temperature
uint8_t sysPress_; // System pressure
uint16_t boilTemp_; // Boiler temperature
uint16_t exhaustTemp_; // Exhaust temperature
uint8_t burnGas_; // Gas on/off
uint16_t flameCurr_; // Flame current in micro amps
uint8_t heatPump_; // Boiler pump on/off
uint8_t fanWork_; // Fan on/off
uint8_t ignWork_; // Ignition on/off
uint8_t heatingActivated_; // Heating activated on the boiler
uint8_t heatingTemp_; // Heating temperature setting on the boiler
uint8_t pumpModMax_; // Boiler circuit pump modulation max. power %
uint8_t pumpModMin_; // Boiler circuit pump modulation min. power
uint8_t pumpDelay_;
uint8_t burnMinPeriod_;
uint8_t burnMinPower_;
uint8_t burnMaxPower_;
int8_t boilHystOn_;
int8_t boilHystOff_;
uint8_t setFlowTemp_; // boiler setpoint temp
uint8_t curBurnPow_; // Burner current power %
uint8_t setBurnPow_; // max output power in %
uint32_t burnStarts_; // # burner restarts
uint32_t burnWorkMin_; // Total burner operating time
uint32_t heatWorkMin_; // Total heat operating time
uint32_t UBAuptime_; // Total UBA working hours
char lastCode_[30]; // last error code
char serviceCode_[4]; // 3 character status/service code
uint16_t serviceCodeNumber_; // error/service code
// info
uint32_t upTimeControl_; // Operating time control
uint32_t upTimeCompHeating_; // Operating time compressor heating
uint32_t upTimeCompCooling_; // Operating time compressor cooling
uint32_t upTimeCompWw_; // Operating time compressor warm water
uint32_t heatingStarts_; // Heating starts (control)
uint32_t coolingStarts_; // Cooling starts (control)
uint32_t nrgConsTotal_; // Energy consumption total
uint32_t nrgConsCompTotal_; // Energy consumption compressor total
uint32_t nrgConsCompHeating_; // Energy consumption compressor heating
uint32_t nrgConsCompWw_; // Energy consumption compressor warm water
uint32_t nrgConsCompCooling_; // Energy consumption compressor cooling
uint32_t nrgSuppTotal_; // Energy supplied total
uint32_t nrgSuppHeating_; // Energy supplied heating
uint32_t nrgSuppWw_; // Energy supplied warm water
uint32_t nrgSuppCooling_; // Energy supplied cooling
uint32_t auxElecHeatNrgConsTotal_; // Auxiliary electrical heater energy consumption total
uint32_t auxElecHeatNrgConsHeating_; // Auxiliary electrical heater energy consumption heating
uint32_t auxElecHeatNrgConsDHW_; // Auxiliary electrical heater energy consumption DHW
char maintenanceMessage_[4];
char maintenanceDate_[12];
uint8_t maintenanceType_;
uint8_t maintenanceTime_;
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);

View File

@@ -28,21 +28,9 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}
void Connect::device_info_web(JsonArray & root) {
}
// publish values via MQTT
void Connect::publish_values(JsonObject & json, bool force) {
}
// export values to JSON
bool Connect::export_values(JsonObject & json) {
// publish HA config
bool Connect::publish_ha_config() {
return true;
}
// check to see if values have been updated
bool Connect::updated_values() {
return false;
}
} // namespace emsesp

View File

@@ -35,10 +35,7 @@ class Connect : public EMSdevice {
public:
Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;

View File

@@ -28,21 +28,9 @@ Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_i
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}
void Controller::device_info_web(JsonArray & root) {
}
// publish values via MQTT
void Controller::publish_values(JsonObject & json, bool force) {
}
// export values to JSON
bool Controller::export_values(JsonObject & json) {
return true;
}
// check to see if values have been updated
bool Controller::updated_values() {
return false;
// publish HA config
bool Controller::publish_ha_config() {
return true;
}
} // namespace emsesp

View File

@@ -35,10 +35,7 @@ class Controller : public EMSdevice {
public:
Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;

View File

@@ -28,21 +28,9 @@ Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}
void Gateway::device_info_web(JsonArray & root) {
}
// publish values via MQTT
void Gateway::publish_values(JsonObject & json, bool force) {
}
// export values to JSON
bool Gateway::export_values(JsonObject & json) {
return true;
}
// check to see if values have been updated
bool Gateway::updated_values() {
return false;
// publish HA config
bool Gateway::publish_ha_config() {
return true;
}
} // namespace emsesp

View File

@@ -35,10 +35,7 @@ class Gateway : public EMSdevice {
public:
Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;

View File

@@ -28,21 +28,9 @@ Generic::Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}
void Generic::device_info_web(JsonArray & root) {
}
// publish values via MQTT
void Generic::publish_values(JsonObject & json, bool force) {
}
// export values to JSON
bool Generic::export_values(JsonObject & json) {
return true;
}
// check to see if values have been updated
bool Generic::updated_values() {
return false;
// publish HA config
bool Generic::publish_ha_config() {
return true;
}
} // namespace emsesp

View File

@@ -35,10 +35,7 @@ class Generic : public EMSdevice {
public:
Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;

View File

@@ -31,66 +31,23 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
// telegram handlers
register_telegram_type(0x042B, F("HP1"), true, [&](std::shared_ptr<const Telegram> t) { process_HPMonitor1(t); });
register_telegram_type(0x047B, F("HP2"), true, [&](std::shared_ptr<const Telegram> t) { process_HPMonitor2(t); });
std::string empty("");
register_device_value(empty, &airHumidity_, DeviceValueType::UINT, flash_string_vector{F("2")}, F("airHumidity"), F("Relative air humidity"), DeviceValueUOM::NONE);
register_device_value(empty, &dewTemperature_, DeviceValueType::UINT, {}, F("dewTemperature"), F("Dew point temperature"), DeviceValueUOM::NONE);
}
// creates JSON doc from values
// returns false if empty
bool Heatpump::export_values(JsonObject & json) {
if (Helpers::hasValue(airHumidity_)) {
json["airHumidity"] = (float)airHumidity_ / 2;
}
if (Helpers::hasValue(dewTemperature_)) {
json["dewTemperature"] = dewTemperature_;
}
return json.size();
}
void Heatpump::device_info_web(JsonArray & root) {
// fetch the values into a JSON document
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values(json)) {
return; // empty
}
create_value_json(root, F("airHumidity"), nullptr, F_(airHumidity), F_(percent), json);
create_value_json(root, F("dewTemperature"), nullptr, F_(dewTemperature), F_(degrees), json);
}
// publish values via MQTT
void Heatpump::publish_values(JsonObject & json, bool force) {
// handle HA first
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
if (!mqtt_ha_config_ || force) {
register_mqtt_ha_config();
return;
}
}
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json_data = doc.to<JsonObject>();
if (export_values(json_data)) {
Mqtt::publish(F("heatpump_data"), doc.as<JsonObject>());
}
}
void Heatpump::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
// Create the Master device
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_HA_CONFIG> doc;
doc["name"] = F_(EMSESP);
// publish HA config
bool Heatpump::publish_ha_config() {
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
doc["uniq_id"] = F_(heatpump);
doc["ic"] = F_(iconheatpump);
doc["ic"] = F_(iconvalve);
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["name"] = FJSON("Humidity");
doc["val_tpl"] = FJSON("{{value_json.airHumidity}}");
JsonObject dev = doc.createNestedObject("dev");
@@ -100,21 +57,12 @@ void Heatpump::register_mqtt_ha_config() {
dev["mdl"] = this->name();
JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp-heatpump");
Mqtt::publish_ha(F("homeassistant/sensor/ems-esp/heatpump/config"), doc.as<JsonObject>()); // publish the config payload with retain flag
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(airHumidity), device_type(), "airHumidity", F_(percent), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(dewTemperature), device_type(), "dewTemperature", F_(degrees), nullptr);
char topic[100];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/heatpump/config"), System::hostname().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
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;
return true;
}
/*
@@ -122,8 +70,8 @@ bool Heatpump::updated_values() {
* e.g. "38 10 FF 00 03 7B 08 24 00 4B"
*/
void Heatpump::process_HPMonitor2(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(dewTemperature_, 0);
changed_ |= telegram->read_value(airHumidity_, 1);
has_update(telegram->read_value(dewTemperature_, 0));
has_update(telegram->read_value(airHumidity_, 1));
}
#pragma GCC diagnostic push

View File

@@ -36,21 +36,13 @@ class Heatpump : public EMSdevice {
public:
Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;
void register_mqtt_ha_config();
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
uint8_t airHumidity_;
uint8_t dewTemperature_;
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
void process_HPMonitor2(std::shared_ptr<const Telegram> telegram);

View File

@@ -55,103 +55,50 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
}
}
// output json to web UI
void Mixer::device_info_web(JsonArray & root) {
if (type() == Type::NONE) {
return; // don't have any values yet
// register values, depending on type (hc or wwc)
void Mixer::register_values(const Type type, uint16_t hc) {
if (type == Type::NONE) {
return; // already done
}
// fetch the values into a JSON document
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
// store the heating circuit and type
hc_ = hc + 1;
type_ = type;
if (!export_values_format(Mqtt::Format::SINGLE, json)) {
return; // empty
}
std::string prefix(10, '\0');
snprintf_P(&prefix[0], sizeof(prefix), PSTR("%s%d"), (type_ == Type::HC) ? "hc" : "wwc", hc + 1);
char prefix_str[10];
if (type() == Type::HC) {
snprintf_P(prefix_str, sizeof(prefix_str), PSTR("(hc %d) "), hc_);
create_value_json(root, F("flowTemp"), FPSTR(prefix_str), F_(flowTemp), F_(degrees), json);
create_value_json(root, F("flowSetTemp"), FPSTR(prefix_str), F_(flowSetTemp), F_(degrees), json);
create_value_json(root, F("pumpStatus"), FPSTR(prefix_str), F_(pumpStatus), nullptr, json);
create_value_json(root, F("valveStatus"), FPSTR(prefix_str), F_(valveStatus), F_(percent), json);
} else {
snprintf_P(prefix_str, sizeof(prefix_str), PSTR("(wwc %d) "), hc_);
create_value_json(root, F("wwTemp"), FPSTR(prefix_str), F_(wwTemp), F_(degrees), json);
create_value_json(root, F("pumpStatus"), FPSTR(prefix_str), F_(pumpStatus), nullptr, json);
create_value_json(root, F("tempStatus"), FPSTR(prefix_str), F_(tempStatus), nullptr, json);
}
register_device_value(
prefix, &flowTemp_, DeviceValueType::USHORT, flash_string_vector{F("10")}, F("flowTemp"), F("Current flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(prefix, &flowSetTemp_, DeviceValueType::UINT, {}, F("flowSetTemp"), F("Setpoint flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(prefix, &pumpStatus_, DeviceValueType::BOOL, {}, F("pumpStatus"), F("Pump/Valve status"), DeviceValueUOM::NONE);
register_device_value(prefix, &status_, DeviceValueType::INT, {}, F("status"), F("Current status"), DeviceValueUOM::NONE);
}
// check to see if values have been updated
bool Mixer::updated_values() {
if (changed_) {
changed_ = false;
return true;
}
return false;
}
// publish values via MQTT
// topic is mixer_data<id>
void Mixer::publish_values(JsonObject & json, bool force) {
// handle HA first
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
if (!mqtt_ha_config_ || force) {
register_mqtt_ha_config();
return;
}
}
if (Mqtt::mqtt_format() == Mqtt::Format::SINGLE) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json_data = doc.to<JsonObject>();
if (export_values_format(Mqtt::mqtt_format(), json_data)) {
char topic[30];
if (type() == Type::HC) {
snprintf_P(topic, 30, PSTR("mixer_data_hc%d"), hc_);
} else {
snprintf_P(topic, 30, PSTR("mixer_data_wwc%d"), hc_);
}
Mqtt::publish(topic, doc.as<JsonObject>());
}
} else {
// format is HA or Nested. This is bundled together and sent in emsesp.cpp
export_values_format(Mqtt::mqtt_format(), json);
}
}
// publish config topic for HA MQTT Discovery
void Mixer::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
// publish HA config
bool Mixer::publish_ha_config() {
// if we don't have valid values for this HC don't add it ever again
if (!Helpers::hasValue(pumpStatus_)) {
return;
return false;
}
// Create the Master device
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_HA_CONFIG> doc;
char name[20];
snprintf_P(name, sizeof(name), PSTR("Mixer %02X"), device_id() - 0x20 + 1);
doc["name"] = name;
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
char uniq_id[20];
snprintf_P(uniq_id, sizeof(uniq_id), PSTR("mixer%02X"), device_id() - 0x20 + 1);
snprintf_P(uniq_id, sizeof(uniq_id), PSTR("Mixer%02X"), device_id() - 0x20 + 1);
doc["uniq_id"] = uniq_id;
doc["ic"] = FJSON("mdi:home-thermometer-outline");
char stat_t[50];
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/mixer_data"), System::hostname().c_str());
doc["stat_t"] = stat_t;
doc["val_tpl"] = FJSON("{{value_json.type}}"); // HA needs a single value. We take the type which is wwc or hc
char name[20];
snprintf_P(name, sizeof(name), PSTR("Mixer %02X Type"), device_id() - 0x20 + 1);
doc["name"] = name;
doc["val_tpl"] = FJSON("{{value_json.type}}"); // HA needs a single value. We take the type which is wwc or hc
JsonObject dev = doc.createNestedObject("dev");
dev["name"] = FJSON("EMS-ESP Mixer");
dev["sw"] = EMSESP_APP_VERSION;
@@ -160,127 +107,45 @@ void Mixer::register_mqtt_ha_config() {
JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp-mixer");
// determine the topic, if its HC and WWC. This is determined by the incoming telegram types.
std::string topic(100, '\0');
if (type() == Type::HC) {
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/mixer_hc%d/config"), hc_);
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
char hc_name[10];
snprintf_P(hc_name, sizeof(hc_name), PSTR("hc%d"), hc_);
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(flowTemp), device_type(), "flowTemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(flowSetTemp), device_type(), "flowSetTemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(pumpStatus), device_type(), "pumpStatus", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(valveStatus), device_type(), "valveStatus", F_(percent), F_(iconpercent));
if (type_ == Type::HC) {
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/%s/mixer_hc%d/config"), System::hostname().c_str(), hc_);
} else {
// WWC
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/mixer_wwc%d/config"), hc_);
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
char wwc_name[10];
snprintf_P(wwc_name, sizeof(wwc_name), PSTR("wwc%d"), hc_);
Mqtt::register_mqtt_ha_sensor(wwc_name, nullptr, F_(wwTemp), device_type(), "wwTemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(wwc_name, nullptr, F_(pumpStatus), device_type(), "pumpStatus", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(wwc_name, nullptr, F_(tempStatus), device_type(), "tempStatus", nullptr, nullptr);
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/%s/mixer_wwc%d/config"), System::hostname().c_str(), hc_); // WWC
}
mqtt_ha_config_ = true; // done
}
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
bool Mixer::export_values(JsonObject & json) {
return export_values_format(Mqtt::Format::NESTED, json);
}
// creates JSON doc from values
// returns false if empty
bool Mixer::export_values_format(uint8_t mqtt_format, JsonObject & json) {
// check if there is data for the mixer unit
if (type() == Type::NONE) {
return 0;
}
JsonObject json_hc;
char hc_name[10]; // hc{1-4}
if (type() == Type::HC) {
snprintf_P(hc_name, sizeof(hc_name), PSTR("hc%d"), hc_);
if (mqtt_format == Mqtt::Format::SINGLE) {
json_hc = json;
json["type"] = FJSON("hc");
} else if (mqtt_format == Mqtt::Format::HA) {
json_hc = json.createNestedObject(hc_name);
json_hc["type"] = FJSON("hc");
} else {
json_hc = json.createNestedObject(hc_name);
}
if (Helpers::hasValue(flowTemp_)) {
json_hc["flowTemp"] = (float)flowTemp_ / 10;
}
if (Helpers::hasValue(flowSetTemp_)) {
json_hc["flowSetTemp"] = flowSetTemp_;
}
if (Helpers::hasValue(pumpStatus_)) {
char s[7];
json_hc["pumpStatus"] = Helpers::render_value(s, pumpStatus_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(status_)) {
json_hc["valveStatus"] = status_;
}
return json_hc.size();
}
// WWC
snprintf_P(hc_name, sizeof(hc_name), PSTR("wwc%d"), hc_);
if (mqtt_format == Mqtt::Format::SINGLE) {
json_hc = json;
json["type"] = FJSON("wwc");
} else if (mqtt_format == Mqtt::Format::HA) {
json_hc = json.createNestedObject(hc_name);
json_hc["type"] = FJSON("wwc");
} else {
json_hc = json.createNestedObject(hc_name);
}
if (Helpers::hasValue(flowTemp_)) {
json_hc["wwTemp"] = (float)flowTemp_ / 10;
}
if (Helpers::hasValue(pumpStatus_)) {
char s[7];
json_hc["pumpStatus"] = Helpers::render_value(s, pumpStatus_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(status_)) {
json_hc["tempStatus"] = status_;
}
return json_hc.size();
return true;
}
// heating circuits 0x02D7, 0x02D8 etc...
// e.g. A0 00 FF 00 01 D7 00 00 00 80 00 00 00 00 03 C5
// A0 0B FF 00 01 D7 00 00 00 80 00 00 00 00 03 80
void Mixer::process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram) {
type(Type::HC);
hc_ = telegram->type_id - 0x02D7 + 1; // determine which circuit this is
changed_ |= telegram->read_value(flowTemp_, 3); // is * 10
changed_ |= telegram->read_value(flowSetTemp_, 5);
changed_ |= telegram->read_bitvalue(pumpStatus_, 0, 0);
changed_ |= telegram->read_value(status_, 2); // valve status
register_values(Type::HC, telegram->type_id - 0x02D7);
has_update(telegram->read_value(flowTemp_, 3)); // is * 10
has_update(telegram->read_value(flowSetTemp_, 5));
has_update(telegram->read_bitvalue(pumpStatus_, 0, 0));
has_update(telegram->read_value(status_, 2)); // valve status
}
// Mixer warm water loading/DHW - 0x0331, 0x0332
// e.g. A9 00 FF 00 02 32 02 6C 00 3C 00 3C 3C 46 02 03 03 00 3C // on 0x28
// A8 00 FF 00 02 31 02 35 00 3C 00 3C 3C 46 02 03 03 00 3C // in 0x29
void Mixer::process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram) {
type(Type::WWC);
hc_ = telegram->type_id - 0x0331 + 1; // determine which circuit this is. There are max 2.
changed_ |= telegram->read_value(flowTemp_, 0); // is * 10
changed_ |= telegram->read_bitvalue(pumpStatus_, 2, 0);
changed_ |= telegram->read_value(status_, 11); // temp status
register_values(Type::WWC, telegram->type_id - 0x0331);
has_update(telegram->read_value(flowTemp_, 0)); // is * 10
has_update(telegram->read_bitvalue(pumpStatus_, 2, 0));
has_update(telegram->read_value(status_, 11)); // temp status
}
// Mixer IMP - 0x010C
// e.g. A0 00 FF 00 00 0C 01 00 00 00 00 00 54
// A1 00 FF 00 00 0C 02 04 00 01 1D 00 82
void Mixer::process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram) {
type(Type::HC);
hc_ = device_id() - 0x20 + 1;
register_values(Type::HC, device_id() - 0x20);
// check if circuit is active, 0-off, 1-unmixed, 2-mixed
uint8_t ismixed = 0;
@@ -291,28 +156,27 @@ void Mixer::process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram) {
// do we have a mixed circuit
if (ismixed == 2) {
changed_ |= telegram->read_value(flowTemp_, 3); // is * 10
changed_ |= telegram->read_value(flowSetTemp_, 5);
changed_ |= telegram->read_value(status_, 2); // valve status
has_update(telegram->read_value(flowTemp_, 3)); // is * 10
has_update(telegram->read_value(flowSetTemp_, 5));
has_update(telegram->read_value(status_, 2)); // valve status
}
changed_ |= telegram->read_bitvalue(pumpStatus_, 1, 0); // pump is also in unmixed circuits
has_update(telegram->read_bitvalue(pumpStatus_, 1, 0)); // pump is also in unmixed circuits
}
// Mixer on a MM10 - 0xAB
// e.g. Mixer Module -> All, type 0xAB, telegram: 21 00 AB 00 2D 01 BE 64 04 01 00 (CRC=15) #data=7
// see also https://github.com/proddy/EMS-ESP/issues/386
void Mixer::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
type(Type::HC);
// the heating circuit is determine by which device_id it is, 0x20 - 0x23
// 0x21 is position 2. 0x20 is typically reserved for the WM10 switch module
// see https://github.com/proddy/EMS-ESP/issues/270 and https://github.com/proddy/EMS-ESP/issues/386#issuecomment-629610918
hc_ = device_id() - 0x20 + 1;
changed_ |= telegram->read_value(flowTemp_, 1); // is * 10
changed_ |= telegram->read_bitvalue(pumpStatus_, 3, 2); // is 0 or 0x64 (100%), check only bit 2
changed_ |= telegram->read_value(flowSetTemp_, 0);
changed_ |= telegram->read_value(status_, 4); // valve status -100 to 100
register_values(Type::HC, device_id() - 0x20);
has_update(telegram->read_value(flowTemp_, 1)); // is * 10
has_update(telegram->read_bitvalue(pumpStatus_, 3, 2)); // is 0 or 0x64 (100%), check only bit 2
has_update(telegram->read_value(flowSetTemp_, 0));
has_update(telegram->read_value(status_, 4)); // valve status -100 to 100
}
#pragma GCC diagnostic push
@@ -321,7 +185,7 @@ void Mixer::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
// Mixer on a MM10 - 0xAA
// e.g. Thermostat -> Mixer Module, type 0xAA, telegram: 10 21 AA 00 FF 0C 0A 11 0A 32 xx
void Mixer::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
hc_ = device_id() - 0x20 + 1;
register_values(Type::HC, device_id() - 0x20);
// pos 0: active FF = on
// pos 1: valve runtime 0C = 120 sec in units of 10 sec
}
@@ -329,7 +193,7 @@ void Mixer::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
// Mixer on a MM10 - 0xAC
// e.g. Thermostat -> Mixer Module, type 0xAC, telegram: 10 21 AC 00 1E 64 01 AB
void Mixer::process_MMSetMessage(std::shared_ptr<const Telegram> telegram) {
hc_ = device_id() - 0x20 + 1;
register_values(Type::HC, device_id() - 0x20);
// pos 0: flowtemp setpoint 1E = 30°C
// pos 1: position in %
}

View File

@@ -36,17 +36,11 @@ class Mixer : public EMSdevice {
public:
Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;
bool export_values_format(uint8_t mqtt_format, JsonObject & doc);
void register_mqtt_ha_config();
void process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram);
void process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram);
void process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram);
@@ -60,25 +54,15 @@ class Mixer : public EMSdevice {
WWC // warm water circuit
};
Type type() const {
return type_;
}
void type(Type new_type) {
type_ = new_type;
}
private:
uint16_t hc_ = EMS_VALUE_USHORT_NOTSET;
uint16_t flowTemp_ = EMS_VALUE_USHORT_NOTSET;
uint8_t pumpStatus_ = EMS_VALUE_UINT_NOTSET;
int8_t status_ = EMS_VALUE_INT_NOTSET;
uint8_t flowSetTemp_ = EMS_VALUE_UINT_NOTSET;
uint16_t flowTemp_;
uint8_t pumpStatus_;
int8_t status_;
uint8_t flowSetTemp_;
Type type_ = Type::NONE;
bool changed_ = false;
bool mqtt_ha_config_ = false; // for HA MQTT Discovery
void register_values(const Type type, const uint16_t hc);
Type type_ = Type::NONE;
uint16_t hc_ = EMS_VALUE_USHORT_NOTSET;
};
} // namespace emsesp

View File

@@ -59,67 +59,62 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, [&](std::shared_ptr<const Telegram> t) { process_ISM1StatusMessage(t); });
register_telegram_type(0x0101, F("ISM1Set"), false, [&](std::shared_ptr<const Telegram> t) { process_ISM1Set(t); });
}
std::string empty("");
// special case for a device_id with 0x2A where it's not actual a solar module
if (device_id == 0x2A) {
register_device_value(empty, &type_, DeviceValueType::TEXT, {}, F("type"), F("Type"), DeviceValueUOM::NONE);
strncpy(type_, "warm water circuit", sizeof(type_));
}
register_device_value(empty,
&collectorTemp_,
DeviceValueType::SHORT,
flash_string_vector{F("10")},
F("collectorTemp"),
F("Collector temperature (TS1)"),
DeviceValueUOM::DEGREES);
register_device_value(empty,
&tankBottomTemp_,
DeviceValueType::SHORT,
flash_string_vector{F("10")},
F("tankBottomTemp"),
F("Bottom temperature (TS2)"),
DeviceValueUOM::DEGREES);
register_device_value(empty,
&tankBottomTemp2_,
DeviceValueType::SHORT,
flash_string_vector{F("10")},
F("tankBottomTemp2"),
F("Bottom temperature (TS5)"),
DeviceValueUOM::DEGREES);
register_device_value(
empty, &heatExchangerTemp_, DeviceValueType::SHORT, {F("10")}, F("heatExchangerTemp"), F("Heat exchanger temperature (TS6)"), DeviceValueUOM::DEGREES);
register_device_value(empty, &tank1MaxTempCurrent_, DeviceValueType::UINT, {}, F("tank1MaxTempCurrent"), F("Maximum Tank temperature"), DeviceValueUOM::NONE);
register_device_value(empty, &solarPumpModulation_, DeviceValueType::UINT, {}, F("solarPumpModulation"), F("Solar pump modulation (PS1)"), DeviceValueUOM::PERCENT);
register_device_value(
empty, &cylinderPumpModulation_, DeviceValueType::UINT, {}, F("cylinderPumpModulation"), F("Cylinder pump modulation (PS5)"), DeviceValueUOM::PERCENT);
register_device_value(empty, &solarPump_, DeviceValueType::BOOL, {}, F("solarPump"), F("Solar pump (PS1) active"), DeviceValueUOM::NONE);
register_device_value(empty, &valveStatus_, DeviceValueType::BOOL, {}, F("valveStatus"), F("Valve status"), DeviceValueUOM::NONE);
register_device_value(empty, &tankHeated_, DeviceValueType::BOOL, {}, F("tankHeated"), F("Tank heated"), DeviceValueUOM::NONE);
register_device_value(empty, &collectorShutdown_, DeviceValueType::BOOL, {}, F("collectorShutdown"), F("Collector shutdown"), DeviceValueUOM::NONE);
register_device_value(empty, &pumpWorkMin_, DeviceValueType::TIME, {}, F("pumpWorkMin"), F("Pump working time"), DeviceValueUOM::MINUTES);
register_device_value(
empty, &energyLastHour_, DeviceValueType::ULONG, flash_string_vector{F("10")}, F("energyLastHour"), F("Energy last hour"), DeviceValueUOM::WH);
register_device_value(empty, &energyTotal_, DeviceValueType::ULONG, flash_string_vector{F("10")}, F("energyTotal"), F("Energy total"), DeviceValueUOM::KWH);
register_device_value(empty, &energyToday_, DeviceValueType::ULONG, {}, F("energyToday"), F("Energy today"), DeviceValueUOM::WH);
}
// print to web
void Solar::device_info_web(JsonArray & root) {
// fetch the values into a JSON document
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values(json)) {
return; // empty
}
create_value_json(root, F("collectorTemp"), nullptr, F_(collectorTemp), F_(degrees), json);
create_value_json(root, F("tankBottomTemp"), nullptr, F_(tankBottomTemp), F_(degrees), json);
create_value_json(root, F("tankBottomTemp2"), nullptr, F_(tankBottomTemp2), F_(degrees), json);
create_value_json(root, F("tank1MaxTempCurrent"), nullptr, F_(tank1MaxTempCurrent), F_(degrees), json);
create_value_json(root, F("heatExchangerTemp"), nullptr, F_(heatExchangerTemp), F_(degrees), json);
create_value_json(root, F("solarPumpModulation"), nullptr, F_(solarPumpModulation), F_(percent), json);
create_value_json(root, F("cylinderPumpModulation"), nullptr, F_(cylinderPumpModulation), F_(percent), json);
create_value_json(root, F("valveStatus"), nullptr, F_(valveStatus), nullptr, json);
create_value_json(root, F("solarPump"), nullptr, F_(solarPump), nullptr, json);
create_value_json(root, F("tankHeated"), nullptr, F_(tankHeated), nullptr, json);
create_value_json(root, F("collectorShutdown"), nullptr, F_(collectorShutdown), nullptr, json);
create_value_json(root, F("energyLastHour"), nullptr, F_(energyLastHour), F_(wh), json);
create_value_json(root, F("energyToday"), nullptr, F_(energyToday), F_(wh), json);
create_value_json(root, F("energyTotal"), nullptr, F_(energyTotal), F_(kwh), json);
create_value_json(root, F("pumpWorkMin"), nullptr, F_(pumpWorkMin), F_(min), json);
create_value_json(root, F("pumpWorkMintxt"), nullptr, F_(pumpWorkMintxt), F_(min), json);
}
// publish values via MQTT
void Solar::publish_values(JsonObject & json, bool force) {
// handle HA first
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
if ((!mqtt_ha_config_ || force)) {
register_mqtt_ha_config();
return;
}
}
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
JsonObject json_payload = doc.to<JsonObject>();
if (export_values(json_payload)) {
if (device_id() == 0x2A) {
Mqtt::publish(F("ww_data"), doc.as<JsonObject>());
} else {
Mqtt::publish(F("solar_data"), doc.as<JsonObject>());
}
}
}
// publish config topic for HA MQTT Discovery
void Solar::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
// Create the Master device
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_HA_CONFIG> doc;
doc["name"] = F_(EMSESP);
// publish HA config
bool Solar::publish_ha_config() {
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
doc["name"] = FJSON("Solar Status");
doc["uniq_id"] = F_(solar);
doc["ic"] = F_(iconthermostat);
char stat_t[50];
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/solar_data"), System::hostname().c_str());
@@ -133,113 +128,21 @@ void Solar::register_mqtt_ha_config() {
dev["mdl"] = name();
JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp-solar");
Mqtt::publish_ha(F("homeassistant/sensor/ems-esp/solar/config"), doc.as<JsonObject>()); // publish the config payload with retain flag
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(collectorTemp), device_type(), "collectorTemp", F_(degrees), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(tankBottomTemp), device_type(), "tankBottomTemp", F_(degrees), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(tankBottomTemp2), device_type(), "tankBottomTemp2", F_(degrees), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(tank1MaxTempCurrent), device_type(), "tank1MaxTempCurrent", F_(degrees), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(heatExchangerTemp), device_type(), "heatExchangerTemp", F_(degrees), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(solarPumpModulation), device_type(), "solarPumpModulation", F_(percent), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(cylinderPumpModulation), device_type(), "cylinderPumpModulation", F_(percent), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(pumpWorkMin), device_type(), "pumpWorkMin", F_(min), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyLastHour), device_type(), "energyLastHour", F_(wh), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyToday), device_type(), "energyToday", F_(wh), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyTotal), device_type(), "energyTotal", F_(kwh), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(solarPump), device_type(), "solarPump", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(valveStatus), device_type(), "valveStatus", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(tankHeated), device_type(), "tankHeated", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(collectorShutdown), device_type(), "collectorShutdown", nullptr, nullptr);
char topic[100];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/solar/config"), System::hostname().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
mqtt_ha_config_ = true; // done
}
// creates JSON doc from values
// returns false if empty
bool Solar::export_values(JsonObject & json) {
char s[10]; // for formatting strings
if (Helpers::hasValue(collectorTemp_)) {
json["collectorTemp"] = (float)collectorTemp_ / 10;
}
if (Helpers::hasValue(tankBottomTemp_)) {
json["tankBottomTemp"] = (float)tankBottomTemp_ / 10;
}
if (Helpers::hasValue(tankBottomTemp2_)) {
json["tankBottomTemp2"] = (float)tankBottomTemp2_ / 10;
}
if (Helpers::hasValue(tank1MaxTempCurrent_)) {
json["tank1MaxTempCurrent"] = tank1MaxTempCurrent_;
}
if (Helpers::hasValue(heatExchangerTemp_)) {
json["heatExchangerTemp"] = (float)heatExchangerTemp_ / 10;
}
if (Helpers::hasValue(solarPumpModulation_)) {
json["solarPumpModulation"] = solarPumpModulation_;
}
if (Helpers::hasValue(cylinderPumpModulation_)) {
json["cylinderPumpModulation"] = cylinderPumpModulation_;
}
if (Helpers::hasValue(solarPump_, EMS_VALUE_BOOL)) {
json["solarPump"] = Helpers::render_value(s, solarPump_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(valveStatus_, EMS_VALUE_BOOL)) {
json["valveStatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(pumpWorkMin_)) {
json["pumpWorkMin"] = pumpWorkMin_;
char slong[40];
json["pumpWorkMintxt"] = Helpers::render_value(slong, pumpWorkMin_, EMS_VALUE_TIME);
}
if (Helpers::hasValue(tankHeated_, EMS_VALUE_BOOL)) {
json["tankHeated"] = Helpers::render_value(s, tankHeated_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(collectorShutdown_, EMS_VALUE_BOOL)) {
json["collectorShutdown"] = Helpers::render_value(s, collectorShutdown_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(energyLastHour_)) {
json["energyLastHour"] = (float)energyLastHour_ / 10;
}
if (Helpers::hasValue(energyToday_)) {
json["energyToday"] = energyToday_;
}
if (Helpers::hasValue(energyTotal_)) {
json["energyTotal"] = (float)energyTotal_ / 10;
}
return json.size();
}
// check to see if values have been updated
bool Solar::updated_values() {
if (changed_) {
changed_ = false;
return true;
}
return false;
return true;
}
// SM10Monitor - type 0x97
void Solar::process_SM10Monitor(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(collectorTemp_, 2); // collector temp from SM10, is *10
changed_ |= telegram->read_value(tankBottomTemp_, 5); // bottom temp from SM10, is *10
changed_ |= telegram->read_value(solarPumpModulation_, 4); // modulation solar pump
changed_ |= telegram->read_bitvalue(solarPump_, 7, 1);
changed_ |= telegram->read_value(pumpWorkMin_, 8, 3);
has_update(telegram->read_value(collectorTemp_, 2)); // collector temp from SM10, is *10
has_update(telegram->read_value(tankBottomTemp_, 5)); // bottom temp from SM10, is *10
has_update(telegram->read_value(solarPumpModulation_, 4)); // modulation solar pump
has_update(telegram->read_bitvalue(solarPump_, 7, 1));
has_update(telegram->read_value(pumpWorkMin_, 8, 3));
}
/*
@@ -247,11 +150,11 @@ void Solar::process_SM10Monitor(std::shared_ptr<const Telegram> telegram) {
* e.g. B0 0B FF 00 02 58 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 00 FF 01 00 00
*/
void Solar::process_SM100SystemConfig(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(heatTransferSystem_, 5, 1);
changed_ |= telegram->read_value(externalTank_, 9, 1);
changed_ |= telegram->read_value(thermalDisinfect_, 10, 1);
changed_ |= telegram->read_value(heatMetering_, 14, 1);
changed_ |= telegram->read_value(solarIsEnabled_, 19, 1);
has_update(telegram->read_value(heatTransferSystem_, 5, 1));
has_update(telegram->read_value(externalTank_, 9, 1));
has_update(telegram->read_value(thermalDisinfect_, 10, 1));
has_update(telegram->read_value(heatMetering_, 14, 1));
has_update(telegram->read_value(solarIsEnabled_, 19, 1));
}
/*
@@ -259,16 +162,16 @@ void Solar::process_SM100SystemConfig(std::shared_ptr<const Telegram> telegram)
* e.g. B0 0B FF 00 02 5A 64 05 00 58 14 01 01 32 64 00 00 00 5A 0C
*/
void Solar::process_SM100SolarCircuitConfig(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(collectorTempMax_, 0, 1);
changed_ |= telegram->read_value(tank1MaxTempCurrent_, 3, 1);
changed_ |= telegram->read_value(collectorTempMin_, 4, 1);
changed_ |= telegram->read_value(solarPumpMode_, 5, 1);
changed_ |= telegram->read_value(solarPumpMinRPM_, 6, 1);
changed_ |= telegram->read_value(solarPumpTurnoffDiff_, 7, 1);
changed_ |= telegram->read_value(solarPumpTurnonDiff_, 8, 1);
changed_ |= telegram->read_value(solarPumpKick_, 9, 1);
changed_ |= telegram->read_value(plainWaterMode_, 10, 1);
changed_ |= telegram->read_value(doubleMatchFlow_, 11, 1);
has_update(telegram->read_value(collectorTempMax_, 0, 1));
has_update(telegram->read_value(tank1MaxTempCurrent_, 3, 1));
has_update(telegram->read_value(collectorTempMin_, 4, 1));
has_update(telegram->read_value(solarPumpMode_, 5, 1));
has_update(telegram->read_value(solarPumpMinRPM_, 6, 1));
has_update(telegram->read_value(solarPumpTurnoffDiff_, 7, 1));
has_update(telegram->read_value(solarPumpTurnonDiff_, 8, 1));
has_update(telegram->read_value(solarPumpKick_, 9, 1));
has_update(telegram->read_value(plainWaterMode_, 10, 1));
has_update(telegram->read_value(doubleMatchFlow_, 11, 1));
}
/* process_SM100ParamCfg - type 0xF9 EMS 1.0
@@ -290,14 +193,14 @@ void Solar::process_SM100ParamCfg(std::shared_ptr<const Telegram> telegram) {
uint16_t t_id;
uint8_t of;
int32_t min, def, max, cur;
telegram->read_value(t_id, 1);
telegram->read_value(of, 3);
telegram->read_value(min, 5);
telegram->read_value(def, 9);
telegram->read_value(max, 13);
telegram->read_value(cur, 17);
has_update(telegram->read_value(t_id, 1));
has_update(telegram->read_value(of, 3));
has_update(telegram->read_value(min, 5));
has_update(telegram->read_value(def, 9));
has_update(telegram->read_value(max, 13));
has_update(telegram->read_value(cur, 17));
// LOG_DEBUG(F("SM100ParamCfg param=0x%04X, offset=%d, min=%d, default=%d, max=%d, current=%d"), t_id, of, min, def, max, cur);
// LOG_DEBUG(F("SM100ParamCfg param=0x%04X, offset=%d, min=%d, default=%d, max=%d, current=%d"), t_id, of, min, def, max, cur));
}
/*
@@ -312,10 +215,10 @@ void Solar::process_SM100ParamCfg(std::shared_ptr<const Telegram> telegram) {
* bytes 20+21 = TS6 Temperature sensor external heat exchanger
*/
void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(collectorTemp_, 0); // is *10 - TS1: Temperature sensor for collector array 1
changed_ |= telegram->read_value(tankBottomTemp_, 2); // is *10 - TS2: Temperature sensor 1 cylinder, bottom
changed_ |= telegram->read_value(tankBottomTemp2_, 16); // is *10 - TS5: Temperature sensor 2 cylinder, bottom, or swimming pool
changed_ |= telegram->read_value(heatExchangerTemp_, 20); // is *10 - TS6: Heat exchanger temperature sensor
has_update(telegram->read_value(collectorTemp_, 0)); // is *10 - TS1: Temperature sensor for collector array 1
has_update(telegram->read_value(tankBottomTemp_, 2)); // is *10 - TS2: Temperature sensor 1 cylinder, bottom
has_update(telegram->read_value(tankBottomTemp2_, 16)); // is *10 - TS5: Temperature sensor 2 cylinder, bottom, or swimming pool
has_update(telegram->read_value(heatExchangerTemp_, 20)); // is *10 - TS6: Heat exchanger temperature sensor
}
#pragma GCC diagnostic push
@@ -330,17 +233,17 @@ void Solar::process_SM100Monitor2(std::shared_ptr<const Telegram> telegram) {
// SM100wwTemperatur - 0x07D6
// Solar Module(0x2A) -> (0x00), (0x7D6), data: 01 C1 00 00 02 5B 01 AF 01 AD 80 00 01 90
void Solar::process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram) {
// changed_ |= telegram->read_value(wwTemp_1_, 0);
// changed_ |= telegram->read_value(wwTemp_3_, 4);
// changed_ |= telegram->read_value(wwTemp_4_, 6);
// changed_ |= telegram->read_value(wwTemp_5_, 8);
// changed_ |= telegram->read_value(wwTemp_7_, 12);
// has_update(telegram->read_value(wwTemp_1_, 0));
// has_update(telegram->read_value(wwTemp_3_, 4));
// has_update(telegram->read_value(wwTemp_4_, 6));
// has_update(telegram->read_value(wwTemp_5_, 8));
// has_update(telegram->read_value(wwTemp_7_, 12));
}
// SM100wwStatus - 0x07AA
// Solar Module(0x2A) -> (0x00), (0x7AA), data: 64 00 04 00 03 00 28 01 0F
void Solar::process_SM100wwStatus(std::shared_ptr<const Telegram> telegram) {
// changed_ |= telegram->read_value(wwPump_, 0);
// has_update(telegram->read_value(wwPump_, 0));
}
// SM100wwCommand - 0x07AB
@@ -349,15 +252,14 @@ void Solar::process_SM100wwCommand(std::shared_ptr<const Telegram> telegram) {
// not implemented yet
}
#pragma GCC diagnostic pop
// SM100Config - 0x0366
// e.g. B0 00 FF 00 02 66 01 62 00 13 40 14
void Solar::process_SM100Config(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(availabilityFlag_, 0);
changed_ |= telegram->read_value(configFlag_, 1);
changed_ |= telegram->read_value(userFlag_, 2);
has_update(telegram->read_value(availabilityFlag_, 0));
has_update(telegram->read_value(configFlag_, 1));
has_update(telegram->read_value(userFlag_, 2));
}
/*
@@ -369,8 +271,8 @@ void Solar::process_SM100Config(std::shared_ptr<const Telegram> telegram) {
void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
uint8_t solarpumpmod = solarPumpModulation_;
uint8_t cylinderpumpmod = cylinderPumpModulation_;
changed_ |= telegram->read_value(cylinderPumpModulation_, 8);
changed_ |= telegram->read_value(solarPumpModulation_, 9);
has_update(telegram->read_value(cylinderPumpModulation_, 8));
has_update(telegram->read_value(solarPumpModulation_, 9));
if (solarpumpmod == 0 && solarPumpModulation_ == 100) { // mask out boosts
solarPumpModulation_ = 15; // set to minimum
@@ -379,8 +281,8 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
if (cylinderpumpmod == 0 && cylinderPumpModulation_ == 100) { // mask out boosts
cylinderPumpModulation_ = 15; // set to minimum
}
changed_ |= telegram->read_bitvalue(tankHeated_, 3, 1); // issue #422
changed_ |= telegram->read_bitvalue(collectorShutdown_, 3, 0); // collector shutdown
has_update(telegram->read_bitvalue(tankHeated_, 3, 1)); // issue #422
has_update(telegram->read_bitvalue(collectorShutdown_, 3, 0)); // collector shutdown
}
/*
@@ -390,8 +292,8 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
* byte 10 = PS1 Solar circuit pump for collector array 1: test=b0001(1), on=b0100(4) and off=b0011(3)
*/
void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_bitvalue(valveStatus_, 4, 2); // on if bit 2 set
changed_ |= telegram->read_bitvalue(solarPump_, 10, 2); // on if bit 2 set
has_update(telegram->read_bitvalue(valveStatus_, 4, 2)); // on if bit 2 set
has_update(telegram->read_bitvalue(solarPump_, 10, 2)); // on if bit 2 set
}
/*
@@ -399,9 +301,9 @@ void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
* e.g. B0 0B FF 00 02 80 50 64 00 00 29 01 00 00 01
*/
void Solar::process_SM100CollectorConfig(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(climateZone_, 0, 1);
changed_ |= telegram->read_value(collector1Area_, 3, 2);
changed_ |= telegram->read_value(collector1Type_, 5, 1);
has_update(telegram->read_value(climateZone_, 0, 1));
has_update(telegram->read_value(collector1Area_, 3, 2));
has_update(telegram->read_value(collector1Type_, 5, 1));
}
/*
@@ -409,16 +311,16 @@ void Solar::process_SM100CollectorConfig(std::shared_ptr<const Telegram> telegra
* e.g. 30 00 FF 00 02 8E 00 00 00 00 00 00 06 C5 00 00 76 35
*/
void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(energyLastHour_, 0); // last hour / 10 in Wh
changed_ |= telegram->read_value(energyToday_, 4); // todays in Wh
changed_ |= telegram->read_value(energyTotal_, 8); // total / 10 in kWh
has_update(telegram->read_value(energyLastHour_, 0)); // last hour / 10 in Wh
has_update(telegram->read_value(energyToday_, 4)); // todays in Wh
has_update(telegram->read_value(energyTotal_, 8)); // total / 10 in kWh
}
/*
* SM100Time - type 0x0391 EMS+ for pump working time
*/
void Solar::process_SM100Time(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(pumpWorkMin_, 1, 3);
has_update(telegram->read_value(pumpWorkMin_, 1, 3));
}
/*
@@ -426,26 +328,26 @@ void Solar::process_SM100Time(std::shared_ptr<const Telegram> telegram) {
* e.g. B0 00 FF 00 00 03 32 00 00 00 00 13 00 D6 00 00 00 FB D0 F0
*/
void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(collectorTemp_, 4); // Collector Temperature
changed_ |= telegram->read_value(tankBottomTemp_, 6); // Temperature Bottom of Solar Boiler
has_update(telegram->read_value(collectorTemp_, 4)); // Collector Temperature
has_update(telegram->read_value(tankBottomTemp_, 6)); // Temperature Bottom of Solar Boiler
uint16_t Wh = 0xFFFF;
changed_ |= telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10
has_update(telegram->read_value(Wh, 2)); // Solar Energy produced in last hour only ushort, is not * 10
if (Wh != 0xFFFF) {
energyLastHour_ = Wh * 10; // set to *10
}
changed_ |= telegram->read_bitvalue(solarPump_, 8, 0); // PS1 Solar pump on (1) or off (0)
changed_ |= telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes
changed_ |= telegram->read_bitvalue(collectorShutdown_, 9, 0); // collector shutdown on/off
changed_ |= telegram->read_bitvalue(tankHeated_, 9, 2); // tank full
has_update(telegram->read_bitvalue(solarPump_, 8, 0)); // PS1 Solar pump on (1) or off (0)
has_update(telegram->read_value(pumpWorkMin_, 10, 3)); // force to 3 bytes
has_update(telegram->read_bitvalue(collectorShutdown_, 9, 0)); // collector shutdown on/off
has_update(telegram->read_bitvalue(tankHeated_, 9, 2)); // tank full
}
/*
* Junkers ISM1 Solar Module - type 0x0101 EMS+ for setting values
*/
void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(setpoint_maxBottomTemp_, 6);
has_update(telegram->read_value(setpoint_maxBottomTemp_, 6));
}
// set temperature for tank

View File

@@ -36,61 +36,56 @@ class Solar : public EMSdevice {
public:
Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;
void register_mqtt_ha_config();
int16_t collectorTemp_ = EMS_VALUE_SHORT_NOTSET; // TS1: Temperature sensor for collector array 1
int16_t tankBottomTemp_ = EMS_VALUE_SHORT_NOTSET; // TS2: Temperature sensor 1 cylinder, bottom (solar thermal system)
int16_t tankBottomTemp2_ = EMS_VALUE_SHORT_NOTSET; // TS5: Temperature sensor 2 cylinder, bottom, or swimming pool (solar thermal system)
int16_t heatExchangerTemp_ = EMS_VALUE_SHORT_NOTSET; // TS6: Heat exchanger temperature sensor
uint8_t solarPumpModulation_ = EMS_VALUE_UINT_NOTSET; // PS1: modulation solar pump
uint8_t cylinderPumpModulation_ = EMS_VALUE_UINT_NOTSET; // PS5: modulation cylinder pump
uint8_t solarPump_ = EMS_VALUE_BOOL_NOTSET; // PS1: solar pump active
uint8_t valveStatus_ = EMS_VALUE_BOOL_NOTSET; // VS2: status 3-way valve for cylinder 2 (solar thermal system) with valve
int16_t setpoint_maxBottomTemp_ = EMS_VALUE_SHORT_NOTSET; // setpoint for maximum collector temp
uint32_t energyLastHour_ = EMS_VALUE_ULONG_NOTSET;
uint32_t energyToday_ = EMS_VALUE_ULONG_NOTSET;
uint32_t energyTotal_ = EMS_VALUE_ULONG_NOTSET;
uint32_t pumpWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total solar pump operating time
uint8_t tankHeated_ = EMS_VALUE_BOOL_NOTSET;
uint8_t collectorShutdown_ = EMS_VALUE_BOOL_NOTSET; // Collector shutdown on/off
int16_t collectorTemp_; // TS1: Temperature sensor for collector array 1
int16_t tankBottomTemp_; // TS2: Temperature sensor 1 cylinder, bottom (solar thermal system)
int16_t tankBottomTemp2_; // TS5: Temperature sensor 2 cylinder, bottom, or swimming pool (solar thermal system)
int16_t heatExchangerTemp_; // TS6: Heat exchanger temperature sensor
uint8_t solarPumpModulation_; // PS1: modulation solar pump
uint8_t cylinderPumpModulation_; // PS5: modulation cylinder pump
uint8_t solarPump_; // PS1: solar pump active
uint8_t valveStatus_; // VS2: status 3-way valve for cylinder 2 (solar thermal system) with valve
int16_t setpoint_maxBottomTemp_; // setpoint for maximum collector temp
uint32_t energyLastHour_;
uint32_t energyToday_;
uint32_t energyTotal_;
uint32_t pumpWorkMin_; // Total solar pump operating time
uint8_t tankHeated_;
uint8_t collectorShutdown_; // Collector shutdown on/off
uint8_t availabilityFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t configFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t userFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t availabilityFlag_;
uint8_t configFlag_;
uint8_t userFlag_;
// telegram 0x0358
uint8_t heatTransferSystem_ = EMS_VALUE_UINT_NOTSET; // Umladesystem, 00=no
uint8_t externalTank_ = EMS_VALUE_UINT_NOTSET; // Heat exchanger, 00=no
uint8_t thermalDisinfect_ = EMS_VALUE_UINT_NOTSET; // Daily heatup for disinfection, 00=no
uint8_t heatMetering_ = EMS_VALUE_UINT_NOTSET; // Wärmemengenzählung, 00=no
uint8_t solarIsEnabled_ = EMS_VALUE_UINT_NOTSET; // System enable, 00=no
uint8_t heatTransferSystem_; // Umladesystem, 00=no
uint8_t externalTank_; // Heat exchanger, 00=no
uint8_t thermalDisinfect_; // Daily heatup for disinfection, 00=no
uint8_t heatMetering_; // Wärmemengenzählung, 00=no
uint8_t solarIsEnabled_; // System enable, 00=no
// telegram 0x035A
uint8_t collectorTempMax_ = EMS_VALUE_UINT_NOTSET; // maximum allowable temperature for collector
uint8_t tank1MaxTempCurrent_ = EMS_VALUE_UINT_NOTSET; // Current value for max tank temp
uint8_t collectorTempMin_ = EMS_VALUE_UINT_NOTSET; // minimum allowable temperature for collector
uint8_t solarPumpMode_ = EMS_VALUE_UINT_NOTSET; // 00=off, 01=PWM, 02=10V
uint8_t solarPumpMinRPM_ = EMS_VALUE_UINT_NOTSET; // minimum RPM setting, *5 %
uint8_t solarPumpTurnoffDiff_ = EMS_VALUE_UINT_NOTSET; // solar pump turnoff collector/tank diff
uint8_t solarPumpTurnonDiff_ = EMS_VALUE_UINT_NOTSET; // solar pump turnon collector/tank diff
uint8_t solarPumpKick_ = EMS_VALUE_UINT_NOTSET; // pump kick for vacuum collector, 00=off
uint8_t plainWaterMode_ = EMS_VALUE_UINT_NOTSET; // system does not use antifreeze, 00=off
uint8_t doubleMatchFlow_ = EMS_VALUE_UINT_NOTSET; // double Match Flow, 00=off
uint8_t collectorTempMax_; // maximum allowable temperature for collector
uint8_t tank1MaxTempCurrent_; // Current value for max tank temp
uint8_t collectorTempMin_; // minimum allowable temperature for collector
uint8_t solarPumpMode_; // 00=off, 01=PWM, 02=10V
uint8_t solarPumpMinRPM_; // minimum RPM setting, *5 %
uint8_t solarPumpTurnoffDiff_; // solar pump turnoff collector/tank diff
uint8_t solarPumpTurnonDiff_; // solar pump turnon collector/tank diff
uint8_t solarPumpKick_; // pump kick for vacuum collector, 00=off
uint8_t plainWaterMode_; // system does not use antifreeze, 00=off
uint8_t doubleMatchFlow_; // double Match Flow, 00=off
// telegram 0x380
uint8_t climateZone_ = EMS_VALUE_UINT_NOTSET; // climate zone identifier
uint16_t collector1Area_ = EMS_VALUE_USHORT_NOTSET; // Area of collector field 1
uint8_t collector1Type_ = EMS_VALUE_UINT_NOTSET; // Type of collector field 1, 01=flat, 02=vacuum
uint8_t climateZone_; // climate zone identifier
uint16_t collector1Area_; // Area of collector field 1
uint8_t collector1Type_; // Type of collector field 1, 01=flat, 02=vacuum
bool changed_ = false;
bool mqtt_ha_config_ = false; // for HA MQTT Discovery
char type_[20]; // Solar of WWC
void process_SM10Monitor(std::shared_ptr<const Telegram> telegram);
void process_SM100SystemConfig(std::shared_ptr<const Telegram> telegram);

View File

@@ -32,92 +32,30 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
register_telegram_type(0x9C, F("WM10MonitorMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_WM10MonitorMessage(t); });
register_telegram_type(0x9B, F("WM10SetMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_WM10SetMessage(t); });
std::string empty("");
register_device_value(empty, &activated_, DeviceValueType::BOOL, {}, F("activated"), F("Activated"), DeviceValueUOM::NONE);
register_device_value(
empty, &flowTemp_, DeviceValueType::USHORT, flash_string_vector{F("10")}, F("flowTemp"), F("Current flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(empty, &status_, DeviceValueType::INT, {}, F("status"), F("Status"), DeviceValueUOM::NONE);
}
// fetch the values into a JSON document for display in the web
void Switch::device_info_web(JsonArray & root) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (export_values(json)) {
create_value_json(root, F("activated"), nullptr, F_(activated), nullptr, json);
create_value_json(root, F("flowTemp"), nullptr, F_(flowTemp), F_(degrees), json);
create_value_json(root, F("status"), nullptr, F_(status), nullptr, json);
}
}
// publish values via MQTT
void Switch::publish_values(JsonObject & json, bool force) {
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
if (!mqtt_ha_config_ || force) {
register_mqtt_ha_config();
return;
}
}
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json_data = doc.to<JsonObject>();
if (export_values(json_data)) {
Mqtt::publish(F("switch_data"), doc.as<JsonObject>());
}
}
// export values to JSON
bool Switch::export_values(JsonObject & json) {
if (Helpers::hasValue(flowTemp_)) {
char s[7];
json["activated"] = Helpers::render_value(s, activated_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(flowTemp_)) {
json["flowTemp"] = (float)flowTemp_ / 10;
}
if (Helpers::hasValue(flowTemp_)) {
json["status"] = status_;
}
return true;
}
// check to see if values have been updated
bool Switch::updated_values() {
if (changed_) {
changed_ = false;
return true;
}
return false;
}
// publish config topic for HA MQTT Discovery
void Switch::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
// if we don't have valid values for this HC don't add it ever again
// publish HA config
bool Switch::publish_ha_config() {
// if we don't have valid values don't add it ever again
if (!Helpers::hasValue(flowTemp_)) {
return;
return false;
}
// Create the Master device
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_HA_CONFIG> doc;
char name[10];
snprintf_P(name, sizeof(name), PSTR("Switch"));
doc["name"] = name;
char uniq_id[10];
snprintf_P(uniq_id, sizeof(uniq_id), PSTR("switch"));
doc["uniq_id"] = uniq_id;
doc["ic"] = FJSON("mdi:home-thermometer-outline");
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
doc["uniq_id"] = F_(switch);
char stat_t[50];
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/switch_data"), System::hostname().c_str());
doc["stat_t"] = stat_t;
doc["name"] = FJSON("Type");
doc["val_tpl"] = FJSON("{{value_json.type}}"); // HA needs a single value. We take the type which is wwc or hc
JsonObject dev = doc.createNestedObject("dev");
dev["name"] = FJSON("EMS-ESP Switch");
dev["sw"] = EMSESP_APP_VERSION;
@@ -126,24 +64,22 @@ void Switch::register_mqtt_ha_config() {
JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp-switch");
Mqtt::publish_ha(F("homeassistant/sensor/ems-esp/switch/config"), doc.as<JsonObject>()); // publish the config payload with retain flag
char topic[100];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/switch/config"), System::hostname().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(activated), device_type(), "activated", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(flowTemp), device_type(), "flowTemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(status), device_type(), "status", nullptr, nullptr);
mqtt_ha_config_ = true; // done
return true;
}
// message 0x9B switch on/off
void Switch::process_WM10SetMessage(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(activated_, 0);
has_update(telegram->read_value(activated_, 0));
}
// message 0x9C holds flowtemp and unknown status value
void Switch::process_WM10MonitorMessage(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(flowTemp_, 0); // is * 10
changed_ |= telegram->read_value(status_, 2);
has_update(telegram->read_value(flowTemp_, 0)); // is * 10
has_update(telegram->read_value(status_, 2));
}
} // namespace emsesp

View File

@@ -36,23 +36,17 @@ class Switch : public EMSdevice {
public:
Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;
void process_WM10SetMessage(std::shared_ptr<const Telegram> telegram);
void process_WM10MonitorMessage(std::shared_ptr<const Telegram> telegram);
void register_mqtt_ha_config();
uint16_t flowTemp_ = EMS_VALUE_USHORT_NOTSET;
uint8_t status_ = EMS_VALUE_UINT_NOTSET;
uint8_t activated_ = EMS_VALUE_BOOL_NOTSET;
bool changed_ = false;
bool mqtt_ha_config_ = false; // for HA MQTT Discovery
uint16_t flowTemp_;
uint8_t status_;
uint8_t activated_;
};
} // namespace emsesp

File diff suppressed because it is too large Load Diff

View File

@@ -40,44 +40,43 @@ class Thermostat : public EMSdevice {
Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
class HeatingCircuit {
public:
HeatingCircuit(const uint8_t hc_num)
HeatingCircuit(const uint8_t hc_num, const uint8_t model)
: hc_num_(hc_num)
, ha_registered_(false) {
, model_(model) {
}
~HeatingCircuit() = default;
int16_t setpoint_roomTemp = EMS_VALUE_SHORT_NOTSET;
int16_t curr_roomTemp = EMS_VALUE_SHORT_NOTSET;
uint8_t mode = EMS_VALUE_UINT_NOTSET;
uint8_t mode_type = EMS_VALUE_UINT_NOTSET;
uint8_t summer_mode = EMS_VALUE_UINT_NOTSET;
uint8_t holiday_mode = EMS_VALUE_UINT_NOTSET;
uint8_t daytemp = EMS_VALUE_UINT_NOTSET;
uint8_t nighttemp = EMS_VALUE_UINT_NOTSET;
uint8_t holidaytemp = EMS_VALUE_UINT_NOTSET;
uint8_t heatingtype = EMS_VALUE_UINT_NOTSET; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
uint8_t targetflowtemp = EMS_VALUE_UINT_NOTSET;
uint8_t summertemp = EMS_VALUE_UINT_NOTSET;
int8_t nofrosttemp = EMS_VALUE_INT_NOTSET; // signed -20°C to +10°C
uint8_t designtemp = EMS_VALUE_UINT_NOTSET; // heating curve design temp at MinExtTemp
int8_t offsettemp = EMS_VALUE_INT_NOTSET; // heating curve offest temp at roomtemp signed!
uint8_t manualtemp = EMS_VALUE_UINT_NOTSET;
uint8_t summer_setmode = EMS_VALUE_UINT_NOTSET;
uint8_t roominfluence = EMS_VALUE_UINT_NOTSET;
uint8_t flowtempoffset = EMS_VALUE_UINT_NOTSET;
uint8_t minflowtemp = EMS_VALUE_UINT_NOTSET;
uint8_t maxflowtemp = EMS_VALUE_UINT_NOTSET;
int16_t setpoint_roomTemp;
int16_t curr_roomTemp;
uint8_t mode;
uint8_t modetype;
uint8_t summermode;
uint8_t holidaymode;
uint8_t daytemp;
uint8_t nighttemp;
uint8_t holidaytemp;
uint8_t heatingtype; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
uint8_t targetflowtemp;
uint8_t summertemp;
int8_t nofrosttemp; // signed -20°C to +10°C
uint8_t designtemp; // heating curve design temp at MinExtTemp
int8_t offsettemp; // heating curve offest temp at roomtemp signed!
uint8_t manualtemp;
uint8_t summer_setmode;
uint8_t roominfluence;
uint8_t flowtempoffset;
uint8_t minflowtemp;
uint8_t maxflowtemp;
uint8_t reducemode;
uint8_t program;
uint8_t controlmode;
uint8_t hc_num() const {
return hc_num_;
}
bool ha_registered() const {
return ha_registered_;
}
void ha_registered(bool b) {
ha_registered_ = b;
uint8_t get_model() const {
return model_;
}
// determines if the heating circuit is actually present and has data
@@ -85,11 +84,10 @@ class Thermostat : public EMSdevice {
return Helpers::hasValue(setpoint_roomTemp);
}
uint8_t get_mode(uint8_t model) const;
uint8_t get_mode_type(uint8_t model) const;
uint8_t get_mode() const;
uint8_t get_mode_type() const;
enum Mode : uint8_t {
UNKNOWN,
OFF,
MANUAL,
AUTO,
@@ -106,7 +104,8 @@ class Thermostat : public EMSdevice {
FLOWOFFSET,
MINFLOW,
MAXFLOW,
ROOMINFLUENCE
ROOMINFLUENCE,
UNKNOWN
};
// for sorting based on hc number
@@ -115,31 +114,21 @@ class Thermostat : public EMSdevice {
}
private:
uint8_t hc_num_; // heating circuit number 1..10
bool ha_registered_; // whether it has been registered for HA MQTT Discovery
uint8_t hc_num_; // heating circuit number 1..10
uint8_t model_; // the model type
};
static std::string mode_tostring(uint8_t mode);
virtual void publish_values(JsonObject & json, bool force);
virtual bool export_values(JsonObject & json);
virtual void device_info_web(JsonArray & root);
virtual bool updated_values();
virtual bool publish_ha_config();
private:
static uuid::log::Logger logger_;
void add_commands();
bool export_values_main(JsonObject & doc);
bool export_values_hc(uint8_t mqtt_format, JsonObject & doc);
bool ha_registered() const {
return ha_registered_;
}
void ha_registered(bool b) {
ha_registered_ = b;
}
void register_device_values();
void register_device_values(uint8_t hc_num);
// specific thermostat characteristics, stripping the top 4 bits
inline uint8_t model() const {
@@ -153,40 +142,38 @@ class Thermostat : public EMSdevice {
std::vector<uint16_t> summer_typeids;
std::vector<uint16_t> curve_typeids;
std::string datetime_; // date and time stamp
std::string errorCode_; // code from 0xA2 as string i.e. "A22(816)"
bool changed_ = false;
bool ha_registered_ = false;
char dateTime_[25]; // date and time stamp
char errorCode_[15]; // code from 0xA2 as string i.e. "A22(816)"
// Installation parameters
uint8_t ibaMainDisplay_ =
EMS_VALUE_UINT_NOTSET; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
uint8_t ibaLanguage_ = EMS_VALUE_UINT_NOTSET; // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian
int8_t ibaCalIntTemperature_ = EMS_VALUE_INT_NOTSET; // offset int. temperature sensor, by * 0.1 Kelvin (-5.0 to 5.0K)
int8_t ibaMinExtTemperature_ = EMS_VALUE_INT_NOTSET; // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1
uint8_t ibaBuildingType_ = EMS_VALUE_UINT_NOTSET; // building type: 0 = light, 1 = medium, 2 = heavy
uint8_t ibaClockOffset_ = EMS_VALUE_UINT_NOTSET; // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
uint8_t ibaLanguage_; // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian
int8_t ibaCalIntTemperature_; // offset int. temperature sensor, by * 0.1 Kelvin (-5.0 to 5.0K)
int8_t ibaMinExtTemperature_; // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1
uint8_t ibaBuildingType_; // building type: 0 = light, 1 = medium, 2 = heavy
uint8_t ibaClockOffset_; // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
uint16_t errorNumber_ = EMS_VALUE_USHORT_NOTSET;
char lastCode_[30] = {'\0'};
int8_t dampedoutdoortemp_ = EMS_VALUE_INT_NOTSET;
uint16_t tempsensor1_ = EMS_VALUE_USHORT_NOTSET;
uint16_t tempsensor2_ = EMS_VALUE_USHORT_NOTSET;
int16_t dampedoutdoortemp2_ = EMS_VALUE_SHORT_NOTSET;
uint8_t floordrystatus_ = EMS_VALUE_UINT_NOTSET;
uint8_t floordrytemp_ = EMS_VALUE_UINT_NOTSET;
uint16_t errorNumber_;
char lastCode_[30];
int8_t dampedoutdoortemp_;
uint16_t tempsensor1_;
uint16_t tempsensor2_;
int16_t dampedoutdoortemp2_;
uint8_t floordrystatus_;
uint8_t floordrytemp_;
uint8_t wwExtra1_ = EMS_VALUE_UINT_NOTSET; // wwExtra active for wwSystem 1
uint8_t wwExtra2_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwMode_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwCircPump_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwCircMode_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwTemp_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwTempLow_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwExtra1_; // wwExtra active for wwSystem 1
uint8_t wwExtra2_;
uint8_t wwMode_;
uint8_t wwCircPump_;
uint8_t wwCircMode_;
uint8_t wwTemp_;
uint8_t wwTempLow_;
std::vector<std::shared_ptr<HeatingCircuit>> heating_circuits_; // each thermostat can have multiple heating circuits
uint8_t zero_value_ = 0; // for fixing current room temperature to 0 for HA
// Generic Types
static constexpr uint16_t EMS_TYPE_RCTime = 0x06; // time
static constexpr uint16_t EMS_TYPE_RCOutdoorTemp = 0xA3; // is an automatic thermostat broadcast, outdoor external temp
@@ -266,9 +253,9 @@ class Thermostat : public EMSdevice {
std::shared_ptr<Thermostat::HeatingCircuit> heating_circuit(std::shared_ptr<const Telegram> telegram);
std::shared_ptr<Thermostat::HeatingCircuit> heating_circuit(const uint8_t hc_num);
void register_mqtt_ha_config();
void register_mqtt_ha_config(uint8_t hc_num);
bool ha_config(bool force = false);
void register_mqtt_ha_config_hc(uint8_t hc_num);
void register_device_values_hc(std::shared_ptr<emsesp::Thermostat::HeatingCircuit> hc);
bool thermostat_ha_cmd(const char * message, uint8_t hc_num);
void process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram);
@@ -279,6 +266,7 @@ class Thermostat : public EMSdevice {
void process_RC35wwSettings(std::shared_ptr<const Telegram> telegram);
void process_RC35Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC35Set(std::shared_ptr<const Telegram> telegram);
void process_RC35Timer(std::shared_ptr<const Telegram> telegram);
void process_RC30Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC30Set(std::shared_ptr<const Telegram> telegram);
void process_RC20Monitor(std::shared_ptr<const Telegram> telegram);
@@ -335,6 +323,9 @@ class Thermostat : public EMSdevice {
bool set_flowtempoffset(const char * value, const int8_t id);
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_program(const char * value, const int8_t id);
bool set_controlmode(const char * value, const int8_t id);
// set functions - these don't use the id/hc, the parameters are ignored
bool set_wwmode(const char * value, const int8_t id);
@@ -353,4 +344,4 @@ class Thermostat : public EMSdevice {
} // namespace emsesp
#endif
#endif