diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 1f2ee87d0..8e854164d 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -11,6 +11,9 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/). - boiler auxheatersource [#2489](https://github.com/emsesp/EMS-ESP32/discussions/2489) - thermostat last error for RC100/300 [#2501](https://github.com/emsesp/EMS-ESP32/issues/2501) - boiler 0xC6 telegram [#1963](https://github.com/emsesp/EMS-ESP32/issues/1963) +- HA optimistic [#2551](https://github.com/emsesp/EMS-ESP32/issues/2551) +- CS6800i changes [#2448](https://github.com/emsesp/EMS-ESP32/issues/2448), [#2449](https://github.com/emsesp/EMS-ESP32/issues/2449) +- charging pump [#2544](https://github.com/emsesp/EMS-ESP32/issues/2544) ## Fixed @@ -21,6 +24,7 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/). - also rebuild HA config on mqtt connect for scheduler, custom and shower - FB100 controls the hc, not the master [#2510](https://github.com/emsesp/EMS-ESP32/issues/2510) - IPM DHW module, [#2524](https://github.com/emsesp/EMS-ESP32/issues/2524) +- charge optimization [#2543](https://github.com/emsesp/EMS-ESP32/issues/2543) ## Changed diff --git a/interface/src/api/app.ts b/interface/src/api/app.ts index a56f4db53..d670a5749 100644 --- a/interface/src/api/app.ts +++ b/interface/src/api/app.ts @@ -143,7 +143,8 @@ export const readCustomEntities = () => o_name: ei.name, o_writeable: ei.writeable, o_value: ei.value, - o_deleted: ei.deleted + o_deleted: ei.deleted, + o_hide: ei.hide })); } }); diff --git a/interface/src/app/main/CustomEntities.tsx b/interface/src/app/main/CustomEntities.tsx index 73e109827..fac37a376 100644 --- a/interface/src/app/main/CustomEntities.tsx +++ b/interface/src/app/main/CustomEntities.tsx @@ -76,6 +76,7 @@ const CustomEntities = () => { ei.factor !== ei.o_factor || ei.value_type !== ei.o_value_type || ei.writeable !== ei.o_writeable || + ei.hide !== ei.o_hide || ei.deleted !== ei.o_deleted || (ei.value || '') !== (ei.o_value || '') ); @@ -147,6 +148,7 @@ const CustomEntities = () => { factor: condensed_ei.factor, uom: condensed_ei.uom, writeable: condensed_ei.writeable, + hide: condensed_ei.hide, value_type: condensed_ei.value_type, value: condensed_ei.value })) @@ -209,6 +211,7 @@ const CustomEntities = () => { value_type: item.value_type, writeable: item.writeable, deleted: false, + hide: item.hide, value: item.value }); setDialogOpen(true); diff --git a/interface/src/app/main/CustomEntitiesDialog.tsx b/interface/src/app/main/CustomEntitiesDialog.tsx index 32550cf6c..152dd2c80 100644 --- a/interface/src/app/main/CustomEntitiesDialog.tsx +++ b/interface/src/app/main/CustomEntitiesDialog.tsx @@ -2,7 +2,11 @@ import { useEffect, useState } from 'react'; import AddIcon from '@mui/icons-material/Add'; import CancelIcon from '@mui/icons-material/Cancel'; +import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; import DoneIcon from '@mui/icons-material/Done'; +import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import { Box, @@ -128,6 +132,20 @@ const CustomEntitiesDialog = ({ onChange={updateFormValue} /> + + } + checkedIcon={} + checked={editItem.hide} + onChange={updateFormValue} + name="hide" + /> + } + label="API/Mqtt" + /> + - + } + checkedIcon={} checked={editItem.writeable} onChange={updateFormValue} name="writeable" diff --git a/interface/src/app/main/types.ts b/interface/src/app/main/types.ts index 38b521563..2f967eacd 100644 --- a/interface/src/app/main/types.ts +++ b/interface/src/app/main/types.ts @@ -384,6 +384,7 @@ export interface EntityItem { value_type: number; value?: unknown; writeable: boolean; + hide: boolean; deleted?: boolean; o_id?: number; o_ram?: number; @@ -397,6 +398,7 @@ export interface EntityItem { o_deleted?: boolean; o_writeable?: boolean; o_value?: unknown; + o_hide?: boolean; } export interface Entities { diff --git a/interface/src/app/settings/MqttSettings.tsx b/interface/src/app/settings/MqttSettings.tsx index 8cbac5d9e..8ec9c30ad 100644 --- a/interface/src/app/settings/MqttSettings.tsx +++ b/interface/src/app/settings/MqttSettings.tsx @@ -254,102 +254,112 @@ const MqttSettings = () => { } label={LL.MQTT_RESPONSE()} /> - {!data.ha_enabled && ( - + + + + } + label={LL.MQTT_PUBLISH_TEXT_1()} + /> + + {data.publish_single && ( } - label={LL.MQTT_PUBLISH_TEXT_1()} + label={LL.MQTT_PUBLISH_TEXT_2()} /> - {data.publish_single && ( + )} + + + + + } + label={LL.MQTT_PUBLISH_TEXT_3()} + /> + {data.ha_enabled && data.discovery_type === 0 && ( + + } + label="Optimistic" + /> + )} + + {data.ha_enabled && ( + - - } - label={LL.MQTT_PUBLISH_TEXT_2()} + + Home Assistant + Domoticz + Domoticz (latest) + + + + - )} - - )} - {!data.publish_single && ( - - - - } - label={LL.MQTT_PUBLISH_TEXT_3()} - /> - - {data.ha_enabled && ( - - - - Home Assistant - Domoticz - Domoticz (latest) - - - - - - - - {LL.MQTT_ENTITY_FORMAT_0()} - - {LL.MQTT_ENTITY_FORMAT_1()} (v3.6) - - - {LL.MQTT_ENTITY_FORMAT_2()} (v3.6) - - {LL.MQTT_ENTITY_FORMAT_1()} - {LL.MQTT_ENTITY_FORMAT_2()} - - + + + {LL.MQTT_ENTITY_FORMAT_0()} + + {LL.MQTT_ENTITY_FORMAT_1()} (v3.6) + + + {LL.MQTT_ENTITY_FORMAT_2()} (v3.6) + + {LL.MQTT_ENTITY_FORMAT_1()} + {LL.MQTT_ENTITY_FORMAT_2()} + - )} - - )} + + )} + {LL.MQTT_PUBLISH_INTERVALS()} (0=auto) diff --git a/interface/src/types/mqtt.ts b/interface/src/types/mqtt.ts index 738dafa75..f930a12bf 100644 --- a/interface/src/types/mqtt.ts +++ b/interface/src/types/mqtt.ts @@ -43,6 +43,7 @@ export interface MqttSettingsType { mqtt_qos: number; mqtt_retain: boolean; ha_enabled: boolean; + ha_optimistic: boolean; nested_format: number; send_response: boolean; publish_single: boolean; diff --git a/mock-api/rest_server.ts b/mock-api/rest_server.ts index 8a873c385..08714dc99 100644 --- a/mock-api/rest_server.ts +++ b/mock-api/rest_server.ts @@ -567,6 +567,7 @@ let mqtt_settings = { rootCA: '', mqtt_retain: false, ha_enabled: true, + ha_optimistic: false, nested_format: 1, discovery_type: 0, discovery_prefix: 'homeassistant', diff --git a/src/ESP32React/MqttSettingsService.cpp b/src/ESP32React/MqttSettingsService.cpp index f6fb78872..55d5a5881 100644 --- a/src/ESP32React/MqttSettingsService.cpp +++ b/src/ESP32React/MqttSettingsService.cpp @@ -244,6 +244,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject root) { root["mqtt_qos"] = settings.mqtt_qos; root["mqtt_retain"] = settings.mqtt_retain; root["ha_enabled"] = settings.ha_enabled; + root["ha_optimistic"] = settings.ha_optimistic; root["nested_format"] = settings.nested_format; root["discovery_prefix"] = settings.discovery_prefix; root["discovery_type"] = settings.discovery_type; @@ -284,6 +285,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings) newSettings.publish_time_heartbeat = static_cast(root["publish_time_heartbeat"] | EMSESP_DEFAULT_PUBLISH_HEARTBEAT); newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED; + newSettings.ha_optimistic = root["ha_optimistic"] | EMSESP_DEFAULT_HA_OPTIMISTIC; newSettings.nested_format = static_cast(root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT); newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX; newSettings.discovery_type = static_cast(root["discovery_type"] | EMSESP_DEFAULT_DISCOVERY_TYPE); @@ -345,6 +347,14 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings) changed = true; } + if (newSettings.discovery_type != 0) { + newSettings.ha_optimistic = false; + } + if (newSettings.ha_optimistic != settings.ha_optimistic) { + emsesp::EMSESP::mqtt_.ha_optimistic(newSettings.ha_optimistic); + changed = true; + } + if (newSettings.mqtt_retain != settings.mqtt_retain) { emsesp::EMSESP::mqtt_.set_retain(newSettings.mqtt_retain); changed = true; diff --git a/src/ESP32React/MqttSettingsService.h b/src/ESP32React/MqttSettingsService.h index ef448af0d..760507ef2 100644 --- a/src/ESP32React/MqttSettingsService.h +++ b/src/ESP32React/MqttSettingsService.h @@ -76,6 +76,7 @@ class MqttSettings { uint8_t mqtt_qos; bool mqtt_retain; bool ha_enabled; + bool ha_optimistic; uint8_t nested_format; String discovery_prefix; uint8_t discovery_type; diff --git a/src/core/analogsensor.cpp b/src/core/analogsensor.cpp index ff3d7baf8..faca9c471 100644 --- a/src/core/analogsensor.cpp +++ b/src/core/analogsensor.cpp @@ -682,6 +682,9 @@ void AnalogSensor::publish_values(const bool force) { Mqtt::add_ha_sections_to_doc("analog", stat_t, config, !is_ha_device_created, val_cond); + if (Mqtt::ha_optimistic()) { + config["optimistic"] = true; + } sensor.ha_registered = Mqtt::queue_ha(topic, config.as()); } } diff --git a/src/core/default_settings.h b/src/core/default_settings.h index 3a0e7a269..5a32df6af 100644 --- a/src/core/default_settings.h +++ b/src/core/default_settings.h @@ -185,6 +185,10 @@ #define EMSESP_DEFAULT_HA_ENABLED false #endif +#ifndef EMSESP_DEFAULT_HA_OPTIMISTIC +#define EMSESP_DEFAULT_HA_OPTIMISTIC false +#endif + #ifndef EMSESP_DEFAULT_PUBLISH_TIME #define EMSESP_DEFAULT_PUBLISH_TIME 10 #endif diff --git a/src/core/device_library.h b/src/core/device_library.h index 43a0f24ce..0e7818df6 100644 --- a/src/core/device_library.h +++ b/src/core/device_library.h @@ -24,7 +24,7 @@ */ // Boilers - 0x08 -{ 8, DeviceType::BOILER, "CS*800i, Logatherm WLW*", DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP}, +{ 8, DeviceType::BOILER, "CS5800i, CS6800i, WLW176i, WLW186i", DeviceFlags::EMS_DEVICE_FLAG_CS6800}, { 12, DeviceType::BOILER, "C1200W", DeviceFlags::EMS_DEVICE_FLAG_NONE}, { 64, DeviceType::BOILER, "BK13/BK15, Smartline, GB1*2", DeviceFlags::EMS_DEVICE_FLAG_NONE}, { 72, DeviceType::BOILER, "Logano GB1*5, Logamatic MC10", DeviceFlags::EMS_DEVICE_FLAG_EMS}, diff --git a/src/core/emsdevice.h b/src/core/emsdevice.h index 36bdecf23..bfe3560e8 100644 --- a/src/core/emsdevice.h +++ b/src/core/emsdevice.h @@ -197,6 +197,19 @@ class EMSdevice { } } + void has_enumupdate(std::shared_ptr telegram, + uint8_t & value, + const uint8_t index, + const std::vector & maskIn, + const std::vector & maskOut) { + uint8_t val = value < maskIn.size() ? maskIn[value] : EMS_VALUE_UINT8_NOTSET; + if (telegram->read_value(val, index)) { + value = val < maskOut.size() ? maskOut[val] : EMS_VALUE_UINT8_NOTSET; + has_update_ = true; + publish_value((void *)&value); + } + } + template void has_update(std::shared_ptr telegram, Value & value, const uint8_t index, uint8_t s = 0) { if (telegram->read_value(value, index, s)) { @@ -434,9 +447,10 @@ class EMSdevice { static constexpr uint8_t EMS_DEVICE_FLAG_EMS = 1; static constexpr uint8_t EMS_DEVICE_FLAG_EMSPLUS = 2; static constexpr uint8_t EMS_DEVICE_FLAG_HT3 = 3; - static constexpr uint8_t EMS_DEVICE_FLAG_HEATPUMP = 4; - static constexpr uint8_t EMS_DEVICE_FLAG_HYBRID = 5; - static constexpr uint8_t EMS_DEVICE_FLAG_HIU = 6; + static constexpr uint8_t EMS_DEVICE_FLAG_HYBRID = 4; + static constexpr uint8_t EMS_DEVICE_FLAG_HIU = 5; + static constexpr uint8_t EMS_DEVICE_FLAG_HEATPUMP = 8; // use bit for subtypes + static constexpr uint8_t EMS_DEVICE_FLAG_CS6800 = 9; // subtype of heatpump // Solar Module static constexpr uint8_t EMS_DEVICE_FLAG_SM10 = 1; diff --git a/src/core/helpers.cpp b/src/core/helpers.cpp index 95b2b366e..685181bce 100644 --- a/src/core/helpers.cpp +++ b/src/core/helpers.cpp @@ -627,10 +627,18 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co } } value_ui = 0; - return false; } +bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * const ** strs, const std::vector & mask) { + uint8_t v = value_ui; + if (!value2enum(value, v, strs) || v >= mask.size()) { + return false; + } + value_ui = mask[v]; + return true; +} + // finds the string (value) of a list vector (strs) // returns true if found, and sets the value_ui to the index, else false // also allow true/false for on/off @@ -658,6 +666,15 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co return false; } +bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * const * strs, const std::vector & mask) { + uint8_t v = value_ui; + if (!value2enum(value, v, strs) || v >= mask.size()) { + return false; + } + value_ui = mask[v]; + return true; +} + // https://stackoverflow.com/questions/313970/how-to-convert-stdstring-to-lower-case std::string Helpers::toLower(std::string const & s) { std::string lc = s; diff --git a/src/core/helpers.h b/src/core/helpers.h index 144653018..7a9dc92dd 100644 --- a/src/core/helpers.h +++ b/src/core/helpers.h @@ -72,7 +72,9 @@ class Helpers { static bool value2bool(const char * value, bool & value_b); static bool value2string(const char * value, std::string & value_s); static bool value2enum(const char * value, uint8_t & value_ui, const char * const ** strs); + static bool value2enum(const char * value, uint8_t & value_ui, const char * const ** strs, const std::vector & mask); static bool value2enum(const char * value, uint8_t & value_ui, const char * const * strs); + static bool value2enum(const char * value, uint8_t & value_ui, const char * const * strs, const std::vector & mask); static bool value2temperature(const char * value, float & value_f, bool relative = false); static bool value2temperature(const char * value, int & value_i, const bool relative = false, const int min = -2147483648, const int max = 2147483647); diff --git a/src/core/locale_common.h b/src/core/locale_common.h index 2718db83f..fd06c6a61 100644 --- a/src/core/locale_common.h +++ b/src/core/locale_common.h @@ -298,6 +298,8 @@ MAKE_ENUM(enum_comfort2, FL_(eco), FL_(high_comfort)) MAKE_ENUM(enum_flow, FL_(off), FL_(flow), FL_(bufferedflow), FL_(buffer), FL_(layeredbuffer)) MAKE_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error), FL_(history), FL_(message)) MAKE_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW)) +MAKE_ENUM(enum_maxHeat1, FL_(0kW), FL_(3kW), FL_(6kW), FL_(9kW)) +MAKE_ENUM(enum_maxHeat2, FL_(3kW), FL_(6kW), FL_(9kW)) MAKE_ENUM(enum_pumpMode, FL_(proportional), FL_(deltaP1), FL_(deltaP2), FL_(deltaP3), FL_(deltaP4)) MAKE_ENUM(enum_pumpCharacter, FL_(proportional), FL_(pressure1), FL_(pressure2), FL_(pressure3), FL_(pressure4), FL_(pressure5), FL_(pressure6)) MAKE_ENUM(enum_hpPumpMode, FL_(auto), FL_(continuous)) diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index 42bbfc59a..280649357 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -40,6 +40,7 @@ uint32_t Mqtt::publish_time_heartbeat_; bool Mqtt::mqtt_enabled_; uint8_t Mqtt::entity_format_; bool Mqtt::ha_enabled_; +bool Mqtt::ha_optimistic_; uint8_t Mqtt::nested_format_; std::string Mqtt::discovery_prefix_; uint8_t Mqtt::discovery_type_; @@ -340,6 +341,7 @@ void Mqtt::load_settings() { mqtt_retain_ = mqttSettings.mqtt_retain; mqtt_enabled_ = mqttSettings.enabled; ha_enabled_ = mqttSettings.ha_enabled; + ha_optimistic_ = mqttSettings.ha_optimistic; nested_format_ = mqttSettings.nested_format; publish_single_ = mqttSettings.publish_single; publish_single2cmd_ = mqttSettings.publish_single2cmd; @@ -1114,6 +1116,9 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev doc["dev"] = dev_json; + if (ha_optimistic_) { + doc["optimistic"] = true; + } return queue_ha(topic, doc.as()); } @@ -1343,6 +1348,9 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, // device name must be different to the entity name, take the ids value we just created add_ha_sections_to_doc("thermostat", topic_t, doc, false, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); + if (ha_optimistic_) { + doc["optimistic"] = true; + } return queue_ha(topic, doc.as()); // publish the config payload with retain flag } diff --git a/src/core/mqtt.h b/src/core/mqtt.h index 2a7fadf0f..89d572b96 100644 --- a/src/core/mqtt.h +++ b/src/core/mqtt.h @@ -206,6 +206,14 @@ class Mqtt { ha_enabled_ = ha_enabled; } + static bool ha_optimistic() { + return ha_optimistic_; + } + + static void ha_optimistic(bool ha_optimistic) { + ha_optimistic_ = ha_optimistic; + } + static bool ha_climate_reset() { return ha_climate_reset_; } @@ -309,6 +317,7 @@ class Mqtt { static uint32_t publish_time_heartbeat_; static bool mqtt_enabled_; static bool ha_enabled_; + static bool ha_optimistic_; static uint8_t nested_format_; static uint8_t entity_format_; static std::string discovery_prefix_; diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index e742a4884..37d29971b 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -68,7 +68,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_telegram_type(0x28, "WeatherComp", true, MAKE_PF_CB(process_WeatherComp)); } - if (model() == EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) { + if (!isHeatPump()) { register_telegram_type(0x494, "UBAEnergySupplied", false, MAKE_PF_CB(process_UBAEnergySupplied)); register_telegram_type(0x495, "UBAInformation", false, MAKE_PF_CB(process_UBAInformation)); register_telegram_type(0x48D, "HpPower", true, MAKE_PF_CB(process_HpPower)); @@ -96,7 +96,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const } // some gas boilers, see #1701 - if (model() != EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) { + if (!isHeatPump()) { register_telegram_type(0x2E, "Meters", false, MAKE_PF_CB(process_Meters)); register_telegram_type(0x3B, "Energy", false, MAKE_PF_CB(process_Energy)); } @@ -187,7 +187,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const DeviceValueUOM::DEGREES); // exclude burner related entities from heatpump and HIU - if (model() != EMSdevice::EMS_DEVICE_FLAG_HEATPUMP && model() != EMSdevice::EMS_DEVICE_FLAG_HIU) { + if (!isHeatPump() && model() != EMSdevice::EMS_DEVICE_FLAG_HIU) { register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &exhaustTemp_, DeviceValueType::UINT16, @@ -411,7 +411,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const 99); } */ - if (model() != EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) { + if (!isHeatPump()) { register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &meterHeat_, DeviceValueType::UINT24, @@ -436,7 +436,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const } // heatpump info - if (model() == EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) { + if (isHeatPump()) { register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nrgTotal_, DeviceValueType::UINT24, @@ -655,27 +655,51 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const FL_(hpIn4Opt), DeviceValueUOM::NONE, MAKE_CF_CB(set_HpIn4Logic)); - register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, - &maxHeatComp_, - DeviceValueType::ENUM, - FL_(enum_maxHeat), - FL_(maxHeatComp), - DeviceValueUOM::NONE, - MAKE_CF_CB(set_maxHeatComp)); - register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, - &maxHeatHeat_, - DeviceValueType::ENUM, - FL_(enum_maxHeat), - FL_(maxHeatHeat), - DeviceValueUOM::NONE, - MAKE_CF_CB(set_maxHeatHeat)); - register_device_value(DeviceValueTAG::TAG_DHW1, - &maxHeatDhw_, - DeviceValueType::ENUM, - FL_(enum_maxHeat), - FL_(maxHeatDhw), - DeviceValueUOM::NONE, - MAKE_CF_CB(set_maxHeatDhw)); + if (model() == EMSdevice::EMS_DEVICE_FLAG_CS6800) { + register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, + &maxHeatComp_, + DeviceValueType::ENUM, + FL_(enum_maxHeat1), + FL_(maxHeatComp), + DeviceValueUOM::NONE, + MAKE_CF_CB(set_maxHeatComp)); + register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, + &maxHeatHeat_, + DeviceValueType::ENUM, + FL_(enum_maxHeat2), + FL_(maxHeatHeat), + DeviceValueUOM::NONE, + MAKE_CF_CB(set_maxHeatHeat)); + register_device_value(DeviceValueTAG::TAG_DHW1, + &maxHeatDhw_, + DeviceValueType::ENUM, + FL_(enum_maxHeat2), + FL_(maxHeatDhw), + DeviceValueUOM::NONE, + MAKE_CF_CB(set_maxHeatDhw)); + } else { + register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, + &maxHeatComp_, + DeviceValueType::ENUM, + FL_(enum_maxHeat), + FL_(maxHeatComp), + DeviceValueUOM::NONE, + MAKE_CF_CB(set_maxHeatComp)); + register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, + &maxHeatHeat_, + DeviceValueType::ENUM, + FL_(enum_maxHeat), + FL_(maxHeatHeat), + DeviceValueUOM::NONE, + MAKE_CF_CB(set_maxHeatHeat)); + register_device_value(DeviceValueTAG::TAG_DHW1, + &maxHeatDhw_, + DeviceValueType::ENUM, + FL_(enum_maxHeat), + FL_(maxHeatDhw), + DeviceValueUOM::NONE, + MAKE_CF_CB(set_maxHeatDhw)); + } register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &auxHeaterSource_, DeviceValueType::ENUM, @@ -1068,7 +1092,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const EMSESP::send_read_request(0xC6, device_id, 0, 21); // read last errorcode on start (not broadcasted) - if (model() != EMSdevice::EMS_DEVICE_FLAG_HEATPUMP && model() != EMSdevice::EMS_DEVICE_FLAG_HIU) { + if (!isHeatPump() && model() != EMSdevice::EMS_DEVICE_FLAG_HIU) { register_telegram_type(0x04, "UBAFactory", true, MAKE_PF_CB(process_UBAFactory)); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nomPower_, DeviceValueType::UINT8, FL_(nomPower), DeviceValueUOM::KW, MAKE_CF_CB(set_nomPower)); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, @@ -1189,7 +1213,7 @@ void Boiler::check_active() { } // calculate energy for boiler 0x08 from stored modulation an time in units of 0.01 Wh - if (model() != EMSdevice::EMS_DEVICE_FLAG_HEATPUMP && model() != EMSdevice::EMS_DEVICE_FLAG_HIU) { + if (!isHeatPump() && model() != EMSdevice::EMS_DEVICE_FLAG_HIU) { // remember values from last call static uint32_t powLastReadTime_ = uuid::get_uptime(); static uint8_t heatBurnPow = 0; @@ -1780,6 +1804,12 @@ void Boiler::process_HpCooling(std::shared_ptr telegram) { // Boiler(0x08) -W-> Me(0x0B), HpHeaterConfig(0x0492), data: 03 00 00 04 00 void Boiler::process_HpHeaterConfig(std::shared_ptr telegram) { + if (model() == EMSdevice::EMS_DEVICE_FLAG_CS6800) { + has_enumupdate(telegram, maxHeatComp_, 2, {0, 2, 4, 5}, {0, 0, 1, 0, 2, 3}); + has_enumupdate(telegram, maxHeatHeat_, 3, {2, 4, 5}, {0, 0, 0, 0, 1, 2}); + has_enumupdate(telegram, maxHeatDhw_, 4, {2, 4, 5}, {0, 0, 0, 0, 1, 2}); + return; + } has_update(telegram, maxHeatComp_, 2); has_update(telegram, maxHeatHeat_, 3); has_update(telegram, maxHeatDhw_, 4); @@ -2080,7 +2110,7 @@ void Boiler::process_HpPump2(std::shared_ptr telegram) { // Boiler(0x08) -> All(0x00), ?(0x0491), data: 03 01 00 00 00 02 64 00 00 14 01 2C 00 0A 00 1E 00 1E 00 00 1E 0A 1E 05 05 void Boiler::process_HpAdditionalHeater(std::shared_ptr telegram) { has_update(telegram, auxHeaterSource_, 0); // https://github.com/emsesp/EMS-ESP32/discussions/2489 - has_update(telegram, auxHeaterOnly_, 1); + has_update(telegram, auxHeaterOnly_, model() == EMSdevice::EMS_DEVICE_FLAG_CS6800 ? 3 : 1); has_update(telegram, auxHeaterOff_, 2); has_update(telegram, auxHeatMode_, 4); // eco/comfort has_update(telegram, tempParMode_, 5); @@ -3061,8 +3091,20 @@ bool Boiler::set_HpInLogic(const char * value, const int8_t id) { bool Boiler::set_maxHeat(const char * value, const int8_t id) { uint8_t v; - if (!Helpers::value2enum(value, v, FL_(enum_maxHeat))) { - return false; + if (model() == EMSdevice::EMS_DEVICE_FLAG_CS6800) { + if (id == 0) { + if (!Helpers::value2enum(value, v, FL_(enum_maxHeat1), {0, 2, 4, 5})) { + return false; + } + } else { + if (!Helpers::value2enum(value, v, FL_(enum_maxHeat2), {2, 4, 5})) { + return false; + } + } + } else { + if (!Helpers::value2enum(value, v, FL_(enum_maxHeat))) { + return false; + } } write_command(0x492, id, v, 0x492); return true; @@ -3107,7 +3149,7 @@ bool Boiler::set_minTempSilent(const char * value, const int8_t id) { bool Boiler::set_additionalHeaterOnly(const char * value, const int8_t id) { bool v; if (Helpers::value2bool(value, v)) { - write_command(0x491, 1, v ? 1 : 0, 0x491); + write_command(0x491, model() == EMSdevice::EMS_DEVICE_FLAG_CS6800 ? 3 : 1, v ? 1 : 0, 0x491); return true; } return false; diff --git a/src/devices/boiler.h b/src/devices/boiler.h index 1665f8c4e..48061d255 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -34,6 +34,9 @@ class Boiler : public EMSdevice { inline uint8_t model() const { return (flags() & 0x0F); } + inline bool isHeatPump() const { + return flags() & 0x08; + } void check_active(); void store_energy(); @@ -45,6 +48,7 @@ class Boiler : public EMSdevice { static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D; static constexpr uint8_t EMS_TYPE_UBAFlags = 0x35; static constexpr uint8_t EMS_TYPE_UBASetPoints = 0x1A; + static constexpr uint16_t EMS_TYPE_UBASetPoints2 = 0x2E0; static constexpr uint8_t EMS_TYPE_UBAParameters = 0x16; static constexpr uint8_t EMS_TYPE_UBAParametersPlus = 0xE6; static constexpr uint8_t EMS_TYPE_UBAParameterWWPlus = 0xEA; @@ -82,12 +86,12 @@ class Boiler : public EMSdevice { uint8_t wwActive_; // uint8_t ww3wayValve_; // 3-way valve on WW uint8_t wwChargePump_; - uint8_t wwFlowTempOffset_; // Boiler offset for ww heating - uint8_t wwMaxPower_; // DHW maximum power - uint8_t wwMaxTemp_; // DHW maximum temperature - uint32_t wwStarts_; // DHW starts - uint32_t wwStartsHp_; // DHW starts Heatpump - uint32_t wwWorkM_; // DHW minutes + uint8_t wwFlowTempOffset_; // Boiler offset for ww heating + uint8_t wwMaxPower_; // DHW maximum power + uint8_t wwMaxTemp_; // DHW maximum temperature + uint32_t wwStarts_; // DHW starts + uint32_t wwStartsHp_; // DHW starts Heatpump + uint32_t wwWorkM_; // DHW minutes int8_t wwHystOn_; int8_t wwHystOff_; uint16_t wwMixerTemp_; // mixing temperature diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 3ef575291..edb028a10 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -1275,30 +1275,14 @@ void Thermostat::process_RC300WWmode(std::shared_ptr telegram) { has_update(telegram, dhw->wwCircPump_, 1); // FF=off, 0=on ? if (model() == EMSdevice::EMS_DEVICE_FLAG_BC400 || model() == EMSdevice::EMS_DEVICE_FLAG_HMC310) { - const uint8_t modes[] = {0, 5, 1, 2, 4}; // off, eco+, eco, comfort, auto - uint8_t wwmode = dhw->wwMode_ < sizeof(modes) ? modes[dhw->wwMode_] : EMS_VALUE_UINT8_NOTSET; - telegram->read_value(wwmode, 2); - const uint8_t modes1[] = {0, 2, 3, 0, 4, 1}; - has_update(dhw->wwMode_, wwmode < sizeof(modes1) ? modes1[wwmode] : EMS_VALUE_UINT8_NOTSET); + has_enumupdate(telegram, dhw->wwMode_, 2, {0, 5, 1, 2, 4}, {0, 2, 3, 0, 4, 1}); } else if (model() == EMSdevice::EMS_DEVICE_FLAG_R3000) { // https://github.com/emsesp/EMS-ESP32/pull/1722#discussion_r1582823521 - const uint8_t modes[] = {1, 2, 5}; // normal, comfort, eco+ - uint8_t wwmode = dhw->wwMode_ < sizeof(modes) ? modes[dhw->wwMode_] : EMS_VALUE_UINT8_NOTSET; - telegram->read_value(wwmode, 2); - const uint8_t modes1[] = {0, 0, 1, 0, 0, 2}; // 0=normal (1), 1=comfort(2), 2=eco+(5) - has_update(dhw->wwMode_, wwmode < sizeof(modes1) ? modes1[wwmode] : EMS_VALUE_UINT8_NOTSET); + has_enumupdate(telegram, dhw->wwMode_, 2, {1, 2, 5}, {0, 0, 1, 0, 0, 2}); // normal, comfort, eco+ } else if (model() == EMSdevice::EMS_DEVICE_FLAG_CR120) { - const uint8_t modes[] = {1, 2, 4}; //// 0=normal (1), 1=comfort(2), 2=auto(4) - uint8_t wwmode = dhw->wwMode_ < sizeof(modes) ? modes[dhw->wwMode_] : EMS_VALUE_UINT8_NOTSET; - telegram->read_value(wwmode, 2); - const uint8_t modes1[] = {0, 0, 1, 0, 2, 0}; - has_update(dhw->wwMode_, wwmode < sizeof(modes1) ? modes1[wwmode] : EMS_VALUE_UINT8_NOTSET); + has_enumupdate(telegram, dhw->wwMode_, 2, {1, 2, 4}, {0, 0, 1, 0, 2, 0}); // normal, comfort, auto } else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) { - const uint8_t modes[] = {0, 2, 3}; //// 0=off(0), 1=on(2), 2=auto(3) - uint8_t wwmode = dhw->wwMode_ < sizeof(modes) ? modes[dhw->wwMode_] : EMS_VALUE_UINT8_NOTSET; - telegram->read_value(wwmode, 2); - const uint8_t modes1[] = {0, 0, 1, 2, 0, 0}; - has_update(dhw->wwMode_, wwmode < sizeof(modes1) ? modes1[wwmode] : EMS_VALUE_UINT8_NOTSET); + has_enumupdate(telegram, dhw->wwMode_, 2, {0, 2, 3}, {0, 0, 1, 2, 0, 0}); // normal, on, auto } else { has_update(telegram, dhw->wwMode_, 2); // 0=off, 1=low, 2=high, 3=auto, 4=own prog } @@ -2294,29 +2278,25 @@ bool Thermostat::set_wwmode(const char * value, const int8_t id) { } write_command(0xB0, 2, set, 0xB0); } else if (model() == EMSdevice::EMS_DEVICE_FLAG_BC400 || model() == EMSdevice::EMS_DEVICE_FLAG_HMC310) { - if (!Helpers::value2enum(value, set, FL_(enum_wwMode4))) { // off, eco+, eco, comfort, auto + if (!Helpers::value2enum(value, set, FL_(enum_wwMode4), {0, 5, 1, 2, 4})) { // off, eco+, eco, comfort, auto return false; } - const uint8_t modes[] = {0, 5, 1, 2, 4}; - write_command(0x02F5 + dhw->offset(), 2, modes[set], 0x02F5 + dhw->offset()); + write_command(0x02F5 + dhw->offset(), 2, set, 0x02F5 + dhw->offset()); } else if (model() == EMSdevice::EMS_DEVICE_FLAG_CR120) { - if (!Helpers::value2enum(value, set, FL_(enum_wwMode6))) { // normal, comfort, auto + if (!Helpers::value2enum(value, set, FL_(enum_wwMode6), {0, 2, 4})) { // normal, comfort, auto return false; } - const uint8_t modes[] = {0, 2, 4}; - write_command(0x02F5 + dhw->offset(), 2, modes[set], 0x02F5 + dhw->offset()); + write_command(0x02F5 + dhw->offset(), 2, set, 0x02F5 + dhw->offset()); } else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) { - if (!Helpers::value2enum(value, set, FL_(enum_wwMode2))) { // off, on, auto + if (!Helpers::value2enum(value, set, FL_(enum_wwMode2), {0, 2, 3})) { // off, on, auto return false; } - const uint8_t modes[] = {0, 2, 3}; - write_command(0x02F5 + dhw->offset(), 2, modes[set], 0x02F5 + dhw->offset()); + write_command(0x02F5 + dhw->offset(), 2, set, 0x02F5 + dhw->offset()); } else if (model() == EMSdevice::EMS_DEVICE_FLAG_R3000) { // Rego3000 - https://github.com/emsesp/EMS-ESP32/issues/1692 - if (!Helpers::value2enum(value, set, FL_(enum_wwMode5))) { + if (!Helpers::value2enum(value, set, FL_(enum_wwMode5), {1, 2, 5})) { return false; } - const uint8_t modes[] = {1, 2, 5}; - write_command(0x02F5 + dhw->offset(), 2, modes[set], 0x02F5 + dhw->offset()); + write_command(0x02F5 + dhw->offset(), 2, set, 0x02F5 + dhw->offset()); } else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC300) { if (!Helpers::value2enum(value, set, FL_(enum_wwMode))) { return false; diff --git a/src/emsesp_version.h b/src/emsesp_version.h index ca6316e7b..3fdcb5c15 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.7.3-dev.10" +#define EMSESP_APP_VERSION "3.7.3-dev.11" diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 19e0f6fe0..69d9ad2b9 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -59,6 +59,7 @@ void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject root) { ei["uom"] = entityItem.value_type == DeviceValueType::BOOL ? 0 : entityItem.uom; ei["value_type"] = entityItem.value_type; ei["writeable"] = entityItem.writeable; + ei["hide"] = entityItem.hide; EMSESP::webCustomEntityService.render_value(ei, entityItem, true, true); } } @@ -94,6 +95,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web entityItem.uom = ei["uom"]; entityItem.value_type = ei["value_type"]; entityItem.writeable = ei["writeable"]; + entityItem.hide = ei["hide"] | false; entityItem.data = ei["value"].as(); if (entityItem.ram == 1) { entityItem.device_id = 0; @@ -295,7 +297,9 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) if (!strlen(cmd) || !strcmp(cmd, F_(values)) || !strcmp(cmd, F_(info))) { // list all names for (CustomEntityItem & entity : *customEntityItems_) { - render_value(output, entity); + if (!entity.hide) { + render_value(output, entity); + } } return true; } @@ -346,7 +350,7 @@ void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem // publish single value void WebCustomEntityService::publish_single(CustomEntityItem & entity) { - if (!Mqtt::enabled() || !Mqtt::publish_single()) { + if (!Mqtt::enabled() || !Mqtt::publish_single() || entity.hide) { return; } @@ -387,6 +391,9 @@ void WebCustomEntityService::publish(const bool force) { bool ha_created = ha_registered_; for (CustomEntityItem & entityItem : *customEntityItems_) { + if (entityItem.hide) { + continue; + } render_value(output, entityItem); // create HA config if (Mqtt::ha_enabled() && !ha_registered_) { @@ -445,6 +452,9 @@ void WebCustomEntityService::publish(const bool force) { Mqtt::add_ha_sections_to_doc(F_(custom), stat_t, config, !ha_created, val_cond); + if (Mqtt::ha_optimistic()) { + config["optimistic"] = true; + } ha_created |= Mqtt::queue_ha(topic, config.as()); } } @@ -470,7 +480,7 @@ uint8_t WebCustomEntityService::count_entities() { for (CustomEntityItem & entity : *customEntityItems_) { render_value(output, entity); - if (output[entity.name].is() || entity.writeable) { + if (!entity.hide && (output[entity.name].is() || entity.writeable)) { count++; } } @@ -484,6 +494,9 @@ void WebCustomEntityService::generate_value_web(JsonObject output, const bool is uint8_t index = 0; for (const CustomEntityItem & entity : *customEntityItems_) { + if (entity.hide) { + continue; + } bool include = false; JsonObject root_obj = nodes.add(); // create the object, we know there is a value diff --git a/src/web/WebCustomEntityService.h b/src/web/WebCustomEntityService.h index 1aa4faf30..4a298c547 100644 --- a/src/web/WebCustomEntityService.h +++ b/src/web/WebCustomEntityService.h @@ -40,6 +40,7 @@ class CustomEntityItem { std::string data; uint8_t ram; uint8_t * raw; + bool hide; }; class WebCustomEntity { diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 599672659..e8fc309d8 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -154,8 +154,6 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { return true; } - const char * attribute_s = Command::get_attribute(cmd); - if (!strcmp(cmd, F_(entities))) { uint8_t i = 0; char name[30]; @@ -166,6 +164,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { return true; } + const char * attribute_s = Command::get_attribute(cmd); for (const ScheduleItem & scheduleItem : *scheduleItems_) { if (Helpers::toLower(scheduleItem.name) == cmd) { get_value_json(output, scheduleItem); @@ -291,6 +290,9 @@ void WebSchedulerService::publish(const bool force) { Mqtt::add_ha_bool(config); Mqtt::add_ha_sections_to_doc(F_(scheduler), stat_t, config, !ha_created, val_cond); + if (Mqtt::ha_optimistic()) { + config["optimistic"] = true; + } ha_created |= Mqtt::queue_ha(topic, config.as()); } }