From d4603aa0de7dab240fad4fd3b895effa7ece4d86 Mon Sep 17 00:00:00 2001 From: Yurii Date: Sun, 2 Nov 2025 23:43:02 +0300 Subject: [PATCH 01/20] fix: compatibility with HA 2025.10+ fixed #189 --- lib/HomeAssistantHelper/strings.h | 2 +- src/HaHelper.h | 78 +++++++++++++++---------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/lib/HomeAssistantHelper/strings.h b/lib/HomeAssistantHelper/strings.h index da99954..6c0eb3b 100644 --- a/lib/HomeAssistantHelper/strings.h +++ b/lib/HomeAssistantHelper/strings.h @@ -12,6 +12,7 @@ const char HA_ENTITY_SELECT[] PROGMEM = "select"; const char HA_ENTITY_SENSOR[] PROGMEM = "sensor"; const char HA_ENTITY_SWITCH[] PROGMEM = "switch"; +const char HA_DEFAULT_ENTITY_ID[] PROGMEM = "def_ent_id"; const char HA_DEVICE[] PROGMEM = "device"; const char HA_IDENTIFIERS[] PROGMEM = "identifiers"; const char HA_SW_VERSION[] PROGMEM = "sw_version"; @@ -23,7 +24,6 @@ const char HA_COMMAND_TOPIC[] PROGMEM = "command_topic"; const char HA_COMMAND_TEMPLATE[] PROGMEM = "command_template"; const char HA_ENABLED_BY_DEFAULT[] PROGMEM = "enabled_by_default"; const char HA_UNIQUE_ID[] PROGMEM = "unique_id"; -const char HA_OBJECT_ID[] PROGMEM = "object_id"; const char HA_ENTITY_CATEGORY[] PROGMEM = "entity_category"; const char HA_ENTITY_CATEGORY_DIAGNOSTIC[] PROGMEM = "diagnostic"; const char HA_ENTITY_CATEGORY_CONFIG[] PROGMEM = "config"; diff --git a/src/HaHelper.h b/src/HaHelper.h index 69ba800..311bcbb 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -262,7 +262,7 @@ public: // object id's doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; const String& configTopic = this->makeConfigTopic( sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR), @@ -324,7 +324,7 @@ public: // object id's doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; // state topic doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic( @@ -371,7 +371,7 @@ public: // object id's doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; // state topic doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic( @@ -422,7 +422,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_turbo")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Turbo heating"); doc[FPSTR(HA_ICON)] = F("mdi:rocket-launch-outline"); @@ -444,7 +444,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_hysteresis")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -476,7 +476,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_turbo_factor")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = F("power_factor"); doc[FPSTR(HA_NAME)] = F("Heating turbo factor"); @@ -500,7 +500,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_min_temp")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -534,7 +534,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_max_temp")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -569,7 +569,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_min_temp")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -603,7 +603,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_max_temp")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -638,7 +638,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID"); doc[FPSTR(HA_ICON)] = F("mdi:chart-bar-stacked"); @@ -660,7 +660,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_p")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID factor P"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-p-circle-outline"); @@ -683,7 +683,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_i")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID factor I"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-i-circle-outline"); @@ -706,7 +706,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_d")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID factor D"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-d-circle-outline"); @@ -729,7 +729,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_dt")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = F("duration"); doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("s"); @@ -754,7 +754,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_min_temp")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -788,7 +788,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_max_temp")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -823,7 +823,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm"); doc[FPSTR(HA_ICON)] = F("mdi:sun-snowflake-variant"); @@ -845,7 +845,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_n")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm factor N"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-n-circle-outline"); @@ -868,7 +868,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_k")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm factor K"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-k-circle-outline"); @@ -894,7 +894,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_t")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm factor T"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-t-circle-outline"); @@ -917,7 +917,7 @@ public: JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("status")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Status"); @@ -935,7 +935,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("emergency")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Emergency"); @@ -953,7 +953,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("ot_status")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("connectivity"); doc[FPSTR(HA_NAME)] = F("Opentherm status"); @@ -974,7 +974,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; //doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("Heating"); @@ -995,7 +995,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; //doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("DHW"); @@ -1016,7 +1016,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("flame")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; //doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("Flame"); @@ -1037,7 +1037,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("fault")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Fault"); @@ -1058,7 +1058,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC)); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Diagnostic"); @@ -1076,7 +1076,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("ext_pump")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("External pump"); @@ -1097,7 +1097,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("fault_code")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_NAME)] = F("Fault code"); doc[FPSTR(HA_ICON)] = F("mdi:cog-box"); @@ -1117,7 +1117,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("diagnostic_code")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_NAME)] = F("Diagnostic code"); doc[FPSTR(HA_ICON)] = F("mdi:information-box"); @@ -1134,7 +1134,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RSSI)); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength"); doc[FPSTR(HA_STATE_CLASS)] = FPSTR(HA_STATE_CLASS_MEASUREMENT); @@ -1154,7 +1154,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("uptime")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("duration"); doc[FPSTR(HA_STATE_CLASS)] = F("total_increasing"); @@ -1175,7 +1175,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_NAME)] = F("Heating"); doc[FPSTR(HA_ICON)] = F("mdi:radiator"); @@ -1227,7 +1227,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_NAME)] = F("DHW"); doc[FPSTR(HA_ICON)] = F("mdi:faucet"); @@ -1272,7 +1272,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RESTART)); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Restart"); @@ -1292,7 +1292,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_fault")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Reset fault"); @@ -1312,7 +1312,7 @@ public: doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_diagnostic")); - doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Reset diagnostic"); From 0b60734a3b78800c63e158049b6f6b5622cf8ed6 Mon Sep 17 00:00:00 2001 From: Yurii Date: Wed, 12 Nov 2025 20:18:16 +0300 Subject: [PATCH 02/20] refactor: using abbreviations for HA --- lib/HomeAssistantHelper/strings.h | 91 ++++++++++++++++--------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/lib/HomeAssistantHelper/strings.h b/lib/HomeAssistantHelper/strings.h index 6c0eb3b..6b95d01 100644 --- a/lib/HomeAssistantHelper/strings.h +++ b/lib/HomeAssistantHelper/strings.h @@ -12,66 +12,67 @@ const char HA_ENTITY_SELECT[] PROGMEM = "select"; const char HA_ENTITY_SENSOR[] PROGMEM = "sensor"; const char HA_ENTITY_SWITCH[] PROGMEM = "switch"; -const char HA_DEFAULT_ENTITY_ID[] PROGMEM = "def_ent_id"; -const char HA_DEVICE[] PROGMEM = "device"; -const char HA_IDENTIFIERS[] PROGMEM = "identifiers"; -const char HA_SW_VERSION[] PROGMEM = "sw_version"; -const char HA_MANUFACTURER[] PROGMEM = "manufacturer"; -const char HA_MODEL[] PROGMEM = "model"; +// https://www.home-assistant.io/integrations/mqtt/#supported-abbreviations-in-mqtt-discovery-messages +const char HA_DEFAULT_ENTITY_ID[] PROGMEM = "def_ent_id"; // "default_entity_id " +const char HA_DEVICE[] PROGMEM = "dev"; // "device" +const char HA_IDENTIFIERS[] PROGMEM = "ids"; // "identifiers" +const char HA_SW_VERSION[] PROGMEM = "sw"; // "sw_version" +const char HA_MANUFACTURER[] PROGMEM = "mf"; // "manufacturer" +const char HA_MODEL[] PROGMEM = "mdl"; // "model" const char HA_NAME[] PROGMEM = "name"; -const char HA_CONF_URL[] PROGMEM = "configuration_url"; -const char HA_COMMAND_TOPIC[] PROGMEM = "command_topic"; -const char HA_COMMAND_TEMPLATE[] PROGMEM = "command_template"; -const char HA_ENABLED_BY_DEFAULT[] PROGMEM = "enabled_by_default"; -const char HA_UNIQUE_ID[] PROGMEM = "unique_id"; -const char HA_ENTITY_CATEGORY[] PROGMEM = "entity_category"; +const char HA_CONF_URL[] PROGMEM = "cu"; // "configuration_url" +const char HA_COMMAND_TOPIC[] PROGMEM = "cmd_t"; // "command_topic" +const char HA_COMMAND_TEMPLATE[] PROGMEM = "cmd_tpl"; // "command_template" +const char HA_ENABLED_BY_DEFAULT[] PROGMEM = "en"; // "enabled_by_default" +const char HA_UNIQUE_ID[] PROGMEM = "uniq_id"; // "unique_id" +const char HA_ENTITY_CATEGORY[] PROGMEM = "ent_cat"; // "entity_category" const char HA_ENTITY_CATEGORY_DIAGNOSTIC[] PROGMEM = "diagnostic"; const char HA_ENTITY_CATEGORY_CONFIG[] PROGMEM = "config"; -const char HA_STATE_TOPIC[] PROGMEM = "state_topic"; -const char HA_VALUE_TEMPLATE[] PROGMEM = "value_template"; -const char HA_OPTIONS[] PROGMEM = "options"; -const char HA_AVAILABILITY[] PROGMEM = "availability"; -const char HA_AVAILABILITY_MODE[] PROGMEM = "availability_mode"; -const char HA_TOPIC[] PROGMEM = "topic"; -const char HA_DEVICE_CLASS[] PROGMEM = "device_class"; -const char HA_UNIT_OF_MEASUREMENT[] PROGMEM = "unit_of_measurement"; +const char HA_STATE_TOPIC[] PROGMEM = "stat_t"; // "state_topic" +const char HA_VALUE_TEMPLATE[] PROGMEM = "val_tpl"; // "value_template" +const char HA_OPTIONS[] PROGMEM = "ops"; // "options" +const char HA_AVAILABILITY[] PROGMEM = "avty"; // "availability" +const char HA_AVAILABILITY_MODE[] PROGMEM = "avty_mode"; // "availability_mode" +const char HA_TOPIC[] PROGMEM = "t"; // "topic" +const char HA_DEVICE_CLASS[] PROGMEM = "dev_cla"; // "device_class" +const char HA_UNIT_OF_MEASUREMENT[] PROGMEM = "unit_of_meas"; // "unit_of_measurement" const char HA_UNIT_OF_MEASUREMENT_C[] PROGMEM = "°C"; const char HA_UNIT_OF_MEASUREMENT_F[] PROGMEM = "°F"; const char HA_UNIT_OF_MEASUREMENT_PERCENT[] PROGMEM = "%"; const char HA_UNIT_OF_MEASUREMENT_L_MIN[] PROGMEM = "L/min"; const char HA_UNIT_OF_MEASUREMENT_GAL_MIN[] PROGMEM = "gal/min"; -const char HA_ICON[] PROGMEM = "icon"; +const char HA_ICON[] PROGMEM = "ic"; // "icon" const char HA_MIN[] PROGMEM = "min"; const char HA_MAX[] PROGMEM = "max"; const char HA_STEP[] PROGMEM = "step"; const char HA_MODE[] PROGMEM = "mode"; const char HA_MODE_BOX[] PROGMEM = "box"; -const char HA_STATE_ON[] PROGMEM = "state_on"; -const char HA_STATE_OFF[] PROGMEM = "state_off"; -const char HA_PAYLOAD_ON[] PROGMEM = "payload_on"; -const char HA_PAYLOAD_OFF[] PROGMEM = "payload_off"; -const char HA_STATE_CLASS[] PROGMEM = "state_class"; +const char HA_STATE_ON[] PROGMEM = "stat_on"; // "state_on" +const char HA_STATE_OFF[] PROGMEM = "stat_off"; // "state_off" +const char HA_PAYLOAD_ON[] PROGMEM = "pl_on"; // "payload_on" +const char HA_PAYLOAD_OFF[] PROGMEM = "pl_off"; // "payload_off" +const char HA_STATE_CLASS[] PROGMEM = "stat_cla"; // "state_class" const char HA_STATE_CLASS_MEASUREMENT[] PROGMEM = "measurement"; -const char HA_EXPIRE_AFTER[] PROGMEM = "expire_after"; -const char HA_CURRENT_TEMPERATURE_TOPIC[] PROGMEM = "current_temperature_topic"; -const char HA_CURRENT_TEMPERATURE_TEMPLATE[] PROGMEM = "current_temperature_template"; -const char HA_TEMPERATURE_COMMAND_TOPIC[] PROGMEM = "temperature_command_topic"; -const char HA_TEMPERATURE_COMMAND_TEMPLATE[] PROGMEM = "temperature_command_template"; -const char HA_TEMPERATURE_STATE_TOPIC[] PROGMEM = "temperature_state_topic"; -const char HA_TEMPERATURE_STATE_TEMPLATE[] PROGMEM = "temperature_state_template"; -const char HA_TEMPERATURE_UNIT[] PROGMEM = "temperature_unit"; -const char HA_MODE_COMMAND_TOPIC[] PROGMEM = "mode_command_topic"; -const char HA_MODE_COMMAND_TEMPLATE[] PROGMEM = "mode_command_template"; -const char HA_MODE_STATE_TOPIC[] PROGMEM = "mode_state_topic"; -const char HA_MODE_STATE_TEMPLATE[] PROGMEM = "mode_state_template"; +const char HA_EXPIRE_AFTER[] PROGMEM = "exp_aft"; // "expire_after" +const char HA_CURRENT_TEMPERATURE_TOPIC[] PROGMEM = "curr_temp_t"; // "current_temperature_topic" +const char HA_CURRENT_TEMPERATURE_TEMPLATE[] PROGMEM = "curr_temp_tpl"; // "current_temperature_template" +const char HA_TEMPERATURE_COMMAND_TOPIC[] PROGMEM = "temp_cmd_t"; // "temperature_command_topic" +const char HA_TEMPERATURE_COMMAND_TEMPLATE[] PROGMEM = "temp_cmd_tpl"; // "temperature_command_template" +const char HA_TEMPERATURE_STATE_TOPIC[] PROGMEM = "temp_stat_t"; // "temperature_state_topic" +const char HA_TEMPERATURE_STATE_TEMPLATE[] PROGMEM = "temp_stat_tpl"; // "temperature_state_template" +const char HA_TEMPERATURE_UNIT[] PROGMEM = "temp_unit"; // "temperature_unit" +const char HA_MODE_COMMAND_TOPIC[] PROGMEM = "mode_cmd_t"; // "mode_command_topic" +const char HA_MODE_COMMAND_TEMPLATE[] PROGMEM = "mode_cmd_tpl"; // "mode_command_template" +const char HA_MODE_STATE_TOPIC[] PROGMEM = "mode_stat_t"; // "mode_state_topic" +const char HA_MODE_STATE_TEMPLATE[] PROGMEM = "mode_stat_tpl"; // "mode_state_template" const char HA_MODES[] PROGMEM = "modes"; -const char HA_ACTION_TOPIC[] PROGMEM = "action_topic"; -const char HA_ACTION_TEMPLATE[] PROGMEM = "action_template"; +const char HA_ACTION_TOPIC[] PROGMEM = "act_t"; // "action_topic" +const char HA_ACTION_TEMPLATE[] PROGMEM = "act_tpl"; // "action_template" const char HA_MIN_TEMP[] PROGMEM = "min_temp"; const char HA_MAX_TEMP[] PROGMEM = "max_temp"; const char HA_TEMP_STEP[] PROGMEM = "temp_step"; -const char HA_PRESET_MODE_COMMAND_TOPIC[] PROGMEM = "preset_mode_command_topic"; -const char HA_PRESET_MODE_COMMAND_TEMPLATE[] PROGMEM = "preset_mode_command_template"; -const char HA_PRESET_MODE_STATE_TOPIC[] PROGMEM = "preset_mode_state_topic"; -const char HA_PRESET_MODE_VALUE_TEMPLATE[] PROGMEM = "preset_mode_value_template"; -const char HA_PRESET_MODES[] PROGMEM = "preset_modes"; +const char HA_PRESET_MODE_COMMAND_TOPIC[] PROGMEM = "pr_mode_cmd_t"; // "preset_mode_command_topic" +const char HA_PRESET_MODE_COMMAND_TEMPLATE[] PROGMEM = "pr_mode_cmd_tpl"; // "preset_mode_command_template" +const char HA_PRESET_MODE_STATE_TOPIC[] PROGMEM = "pr_mode_stat_t"; // "preset_mode_state_topic" +const char HA_PRESET_MODE_VALUE_TEMPLATE[] PROGMEM = "pr_mode_val_tpl"; // "preset_mode_value_template" +const char HA_PRESET_MODES[] PROGMEM = "pr_modes"; // "preset_modes" From e573ce582f4bb3d630713c5a822890acc7e7f051 Mon Sep 17 00:00:00 2001 From: Yurii Date: Wed, 12 Nov 2025 20:21:39 +0300 Subject: [PATCH 03/20] fix: generation of `default_entity_id` for HA fixed --- lib/HomeAssistantHelper/HomeAssistantHelper.h | 13 +- src/HaHelper.h | 165 +++++++++--------- 2 files changed, 95 insertions(+), 83 deletions(-) diff --git a/lib/HomeAssistantHelper/HomeAssistantHelper.h b/lib/HomeAssistantHelper/HomeAssistantHelper.h index c7417c8..990cda1 100644 --- a/lib/HomeAssistantHelper/HomeAssistantHelper.h +++ b/lib/HomeAssistantHelper/HomeAssistantHelper.h @@ -147,8 +147,19 @@ public: return topic; } + template + String getEntityIdWithPrefix(DT domain, VT value, char separator = '_') { + String topic = ""; + topic.concat(domain); + topic.concat('.'); + topic.concat(this->devicePrefix); + topic.concat(separator); + topic.concat(value); + return topic; + } + template - String getObjectIdWithPrefix(T value, char separator = '_') { + String getUniqueIdWithPrefix(T value, char separator = '_') { String topic = ""; topic.concat(this->devicePrefix); topic.concat(separator); diff --git a/src/HaHelper.h b/src/HaHelper.h index 311bcbb..a41454b 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -261,8 +261,13 @@ public: } // object id's - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(objId.c_str()); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix( + sSensor.type == Sensors::Type::MANUAL + ? FPSTR(HA_ENTITY_NUMBER) + : FPSTR(HA_ENTITY_SENSOR), + objId.c_str() + ); const String& configTopic = this->makeConfigTopic( sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR), @@ -323,8 +328,8 @@ public: String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected")); // object id's - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(objId.c_str()); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), objId.c_str()); // state topic doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic( @@ -370,8 +375,8 @@ public: String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality")); // object id's - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(objId.c_str()); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SENSOR), objId.c_str()); // state topic doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic( @@ -407,7 +412,6 @@ public: } bool deleteSignalQualityDynamicSensor(Sensors::Settings& sSensor) { - JsonDocument doc; const String& configTopic = this->makeConfigTopic( FPSTR(HA_ENTITY_SENSOR), Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality")).c_str() @@ -421,8 +425,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_turbo")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("heating_turbo")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SWITCH), F("heating_turbo")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Turbo heating"); doc[FPSTR(HA_ICON)] = F("mdi:rocket-launch-outline"); @@ -443,8 +447,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_hysteresis")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("heating_hysteresis")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("heating_hysteresis")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -475,8 +479,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_turbo_factor")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("heating_turbo_factor")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("heating_turbo_factor")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = F("power_factor"); doc[FPSTR(HA_NAME)] = F("Heating turbo factor"); @@ -499,8 +503,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_min_temp")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("heating_min_temp")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("heating_min_temp")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -533,8 +537,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_max_temp")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("heating_max_temp")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("heating_max_temp")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -568,8 +572,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_min_temp")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("dhw_min_temp")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("dhw_min_temp")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -602,8 +606,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_max_temp")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("dhw_max_temp")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("dhw_max_temp")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -637,8 +641,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("pid")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SWITCH), F("pid")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID"); doc[FPSTR(HA_ICON)] = F("mdi:chart-bar-stacked"); @@ -659,8 +663,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_p")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("pid_p")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("pid_p")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID factor P"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-p-circle-outline"); @@ -682,8 +686,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_i")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("pid_i")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("pid_i")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID factor I"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-i-circle-outline"); @@ -705,8 +709,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_d")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("pid_d")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("pid_d")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("PID factor D"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-d-circle-outline"); @@ -728,8 +732,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_dt")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("pid_dt")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("pid_dt")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = F("duration"); doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("s"); @@ -753,8 +757,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_min_temp")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("pid_min_temp")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("pid_min_temp")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -787,8 +791,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_max_temp")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("pid_max_temp")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("pid_max_temp")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); @@ -822,8 +826,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SWITCH), F("equitherm")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm"); doc[FPSTR(HA_ICON)] = F("mdi:sun-snowflake-variant"); @@ -844,8 +848,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_n")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_n")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_n")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm factor N"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-n-circle-outline"); @@ -867,8 +871,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_k")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_k")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_k")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm factor K"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-k-circle-outline"); @@ -893,8 +897,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.pid.enabled, 'offline', 'online') }}"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_t")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_t")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_t")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_NAME)] = F("Equitherm factor T"); doc[FPSTR(HA_ICON)] = F("mdi:alpha-t-circle-outline"); @@ -916,8 +920,8 @@ public: bool publishStatusState(bool enabledByDefault = true) { JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("status")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("status")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("status")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Status"); @@ -934,8 +938,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("emergency")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("emergency")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("emergency")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Emergency"); @@ -952,8 +956,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("ot_status")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("ot_status")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("ot_status")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("connectivity"); doc[FPSTR(HA_NAME)] = F("Opentherm status"); @@ -973,9 +977,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = JsonString(AVAILABILITY_OT_CONN, true); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; - //doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("heating")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("heating")); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("Heating"); doc[FPSTR(HA_ICON)] = F("mdi:radiator"); @@ -994,9 +997,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = JsonString(AVAILABILITY_OT_CONN, true); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; - //doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("dhw")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("dhw")); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("DHW"); doc[FPSTR(HA_ICON)] = F("mdi:faucet"); @@ -1015,9 +1017,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = JsonString(AVAILABILITY_OT_CONN, true); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("flame")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; - //doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("flame")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("flame")); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("Flame"); doc[FPSTR(HA_ICON)] = F("mdi:gas-burner"); @@ -1036,8 +1037,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = JsonString(AVAILABILITY_OT_CONN, true); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("fault")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("fault")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("fault")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Fault"); @@ -1057,8 +1058,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = JsonString(AVAILABILITY_OT_CONN, true); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC)); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC)); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC)); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("problem"); doc[FPSTR(HA_NAME)] = F("Diagnostic"); @@ -1075,8 +1076,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("ext_pump")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("ext_pump")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BINARY_SENSOR), F("ext_pump")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("running"); doc[FPSTR(HA_NAME)] = F("External pump"); @@ -1096,8 +1097,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.slave.connected and value_json.slave.fault.active, 'online', 'offline') }}"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("fault_code")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("fault_code")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SENSOR), F("fault_code")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_NAME)] = F("Fault code"); doc[FPSTR(HA_ICON)] = F("mdi:cog-box"); @@ -1116,8 +1117,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.slave.connected and value_json.slave.fault.active or value_json.slave.diag.active, 'online', 'offline') }}"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("diagnostic_code")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("diagnostic_code")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SENSOR), F("diagnostic_code")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_NAME)] = F("Diagnostic code"); doc[FPSTR(HA_ICON)] = F("mdi:information-box"); @@ -1133,8 +1134,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RSSI)); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(FPSTR(S_RSSI)); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SENSOR), FPSTR(S_RSSI)); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength"); doc[FPSTR(HA_STATE_CLASS)] = FPSTR(HA_STATE_CLASS_MEASUREMENT); @@ -1153,8 +1154,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("uptime")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("uptime")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_SENSOR), F("uptime")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("duration"); doc[FPSTR(HA_STATE_CLASS)] = F("total_increasing"); @@ -1174,8 +1175,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("heating")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_CLIMATE), F("heating")); doc[FPSTR(HA_NAME)] = F("Heating"); doc[FPSTR(HA_ICON)] = F("mdi:radiator"); @@ -1226,8 +1227,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("dhw")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_CLIMATE), F("dhw")); doc[FPSTR(HA_NAME)] = F("DHW"); doc[FPSTR(HA_ICON)] = F("mdi:faucet"); @@ -1271,8 +1272,8 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RESTART)); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(FPSTR(S_RESTART)); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BUTTON), FPSTR(S_RESTART)); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Restart"); @@ -1291,8 +1292,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.slave.fault.active, 'online', 'offline') }}"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_fault")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("reset_fault")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BUTTON), F("reset_fault")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Reset fault"); @@ -1311,8 +1312,8 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.slave.diag.active, 'online', 'offline') }}"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_diagnostic")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("reset_diagnostic")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_BUTTON), F("reset_diagnostic")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Reset diagnostic"); From 6f8c8567a084a835f7ea0a3e524a3432b0c81f3a Mon Sep 17 00:00:00 2001 From: Yurii Date: Sat, 15 Nov 2025 13:08:43 +0300 Subject: [PATCH 04/20] chore: bump `pioarduino/platform-espressif32` from 3.3.2 to 3.3.4 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 2cdeed2..ce6ad17 100644 --- a/platformio.ini +++ b/platformio.ini @@ -92,7 +92,7 @@ check_flags = ${env.check_flags} ;platform_packages = ; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5 ; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.32/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.34/platform-espressif32.zip platform_packages = ${env.platform_packages} board_build.partitions = esp32_partitions.csv lib_deps = From 00baf10b9faedc143c6067d7de0e482841530fe7 Mon Sep 17 00:00:00 2001 From: Yurii Date: Tue, 9 Dec 2025 18:18:10 +0300 Subject: [PATCH 05/20] chore: removed `platformio_dependabot` --- .github/workflows/pio-dependabot.yaml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/workflows/pio-dependabot.yaml diff --git a/.github/workflows/pio-dependabot.yaml b/.github/workflows/pio-dependabot.yaml deleted file mode 100644 index 7f1513d..0000000 --- a/.github/workflows/pio-dependabot.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: PlatformIO Dependabot -on: - workflow_dispatch: # option to manually trigger the workflow - schedule: - # Runs every day at 00:00 - - cron: "0 0 * * *" - -permissions: - contents: write - pull-requests: write - -jobs: - dependabot: - runs-on: ubuntu-latest - name: run PlatformIO Dependabot - steps: - - name: Checkout - uses: actions/checkout@v5 - - name: run PlatformIO Dependabot - uses: peterus/platformio_dependabot@v1.2.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} From cb8251dd40bb130533f694576a8af2ae6a6b21d8 Mon Sep 17 00:00:00 2001 From: Yurii <34578544+Laxilef@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:27:12 +0300 Subject: [PATCH 06/20] Implementation of the new Equitherm algorithm (#146) * feat: new equitherm algorithm and chart for it (#144) * refactor: refactoring after #144 * refactor: cosmetic changes (equitherm chart) * chore: fix typo * refactor: cosmetic changes * chore: remove unused files * chore: resolve conflicts * refactor: added notes for equitherm parameters * fix: decimation for Equitherm chart fixed; chartjs updated * style: HTML code formatting * chore: added additional description of the ``T`` parameter for Equitherm * flx: typo * refactor: after merge --------- Co-authored-by: P43YM --- gulpfile.js | 3 + lib/Equitherm/Equitherm.h | 63 -------- src/HaHelper.h | 78 ++++++---- src/MqttTask.h | 7 +- src/RegulatorTask.h | 55 +++---- src/Settings.h | 7 +- src/strings.h | 7 +- src/utils.h | 40 +++-- src_data/locales/en.json | 23 ++- src_data/locales/it.json | 23 ++- src_data/locales/ru.json | 23 ++- src_data/pages/sensors.html | 4 +- src_data/pages/settings.html | 279 ++++++++++++++++++++++++++++++++--- src_data/scripts/chart.js | 14 ++ src_data/scripts/utils.js | 38 ++++- 15 files changed, 483 insertions(+), 181 deletions(-) delete mode 100644 lib/Equitherm/Equitherm.h create mode 100644 src_data/scripts/chart.js diff --git a/gulpfile.js b/gulpfile.js index 8463487..fa7ff6d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -27,6 +27,9 @@ let paths = { 'src_data/scripts/i18n.min.js', 'src_data/scripts/lang.js', 'src_data/scripts/utils.js' + ], + 'chart.js': [ + 'src_data/scripts/chart.js' ] } }, diff --git a/lib/Equitherm/Equitherm.h b/lib/Equitherm/Equitherm.h deleted file mode 100644 index 287b4f2..0000000 --- a/lib/Equitherm/Equitherm.h +++ /dev/null @@ -1,63 +0,0 @@ -#include - -#if defined(EQUITHERM_INTEGER) -// расчёты с целыми числами -typedef int datatype; -#else -// расчёты с float числами -typedef float datatype; -#endif - -class Equitherm { -public: - datatype targetTemp = 0; - datatype indoorTemp = 0; - datatype outdoorTemp = 0; - float Kn = 0.0; - float Kk = 0.0; - float Kt = 0.0; - - Equitherm() = default; - - // kn, kk, kt - Equitherm(float new_kn, float new_kk, float new_kt) { - Kn = new_kn; - Kk = new_kk; - Kt = new_kt; - } - - // лимит выходной величины - void setLimits(unsigned short min_output, unsigned short max_output) { - _minOut = min_output; - _maxOut = max_output; - } - - // возвращает новое значение при вызове - datatype getResult() { - datatype output = getResultN() + getResultK() + getResultT(); - output = constrain(output, _minOut, _maxOut); // ограничиваем выход - return output; - } - -private: - unsigned short _minOut = 20, _maxOut = 90; - - // температура контура отопления в зависимости от наружной температуры - datatype getResultN() { - float a = (-0.21 * Kn) - 0.06; // a = -0,21k — 0,06 - float b = (6.04 * Kn) + 1.98; // b = 6,04k + 1,98 - float c = (-5.06 * Kn) + 18.06; // с = -5,06k + 18,06 - float x = (-0.2 * outdoorTemp) + 5; // x = -0.2*t1 + 5 - return (a * x * x) + (b * x) + c; // Tn = ax2 + bx + c - } - - // поправка на желаемую комнатную температуру - datatype getResultK() { - return (targetTemp - 20) * Kk; - } - - // Расчет поправки (ошибки) термостата - datatype getResultT() { - return constrain((targetTemp - indoorTemp), -3, 3) * Kt; - } -}; \ No newline at end of file diff --git a/src/HaHelper.h b/src/HaHelper.h index a41454b..3b6f9ba 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -844,19 +844,19 @@ public: return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SWITCH), F("equitherm")).c_str(), doc); } - bool publishInputEquithermFactorN(bool enabledByDefault = true) { + bool publishInputEquithermSlope(bool enabledByDefault = true) { JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_n")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_n")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_slope")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_slope")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_NAME)] = F("Equitherm factor N"); - doc[FPSTR(HA_ICON)] = F("mdi:alpha-n-circle-outline"); + doc[FPSTR(HA_NAME)] = F("Equitherm slope"); + doc[FPSTR(HA_ICON)] = F("mdi:slope-uphill"); doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); - doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.n_factor|float(0)|round(3) }}"); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.slope|float(0)|round(3) }}"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); - doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"n_factor\" : {{ value }}}}"); + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"slope\" : {{ value }}}}"); doc[FPSTR(HA_MIN)] = 0.001f; doc[FPSTR(HA_MAX)] = 10; doc[FPSTR(HA_STEP)] = 0.001f; @@ -864,56 +864,80 @@ public: doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_n_factor")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_slope")).c_str(), doc); } - bool publishInputEquithermFactorK(bool enabledByDefault = true) { + bool publishInputEquithermExponent(bool enabledByDefault = true) { JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_k")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_k")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_exponent")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_exponent")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_NAME)] = F("Equitherm factor K"); - doc[FPSTR(HA_ICON)] = F("mdi:alpha-k-circle-outline"); + doc[FPSTR(HA_NAME)] = F("Equitherm exponent"); + doc[FPSTR(HA_ICON)] = F("mdi:exponent"); doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); - doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.k_factor|float(0)|round(2) }}"); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.exponent|float(0)|round(3) }}"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); - doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"k_factor\" : {{ value }}}}"); - doc[FPSTR(HA_MIN)] = 0; - doc[FPSTR(HA_MAX)] = 10; + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"exponent\" : {{ value }}}}"); + doc[FPSTR(HA_MIN)] = 0.1; + doc[FPSTR(HA_MAX)] = 2; + doc[FPSTR(HA_STEP)] = 0.001f; + doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX); + doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; + doc.shrinkToFit(); + + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_exponent")).c_str(), doc); + } + + bool publishInputEquithermShift(bool enabledByDefault = true) { + JsonDocument doc; + doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); + doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_shift")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_shift")); + doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); + doc[FPSTR(HA_NAME)] = F("Equitherm shift"); + doc[FPSTR(HA_ICON)] = F("mdi:chart-areaspline"); + doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.shift|float(0)|round(2) }}"); + doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"shift\" : {{ value }}}}"); + doc[FPSTR(HA_MIN)] = -15; + doc[FPSTR(HA_MAX)] = 15; doc[FPSTR(HA_STEP)] = 0.01f; doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX); doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_k_factor")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_shift")).c_str(), doc); } - bool publishInputEquithermFactorT(bool enabledByDefault = true) { + bool publishInputEquithermTargetDiffFactor(bool enabledByDefault = true) { JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->settingsTopic.c_str(); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.pid.enabled, 'offline', 'online') }}"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_t")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_t")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_target_diff_factor")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_target_diff_factor")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_NAME)] = F("Equitherm factor T"); - doc[FPSTR(HA_ICON)] = F("mdi:alpha-t-circle-outline"); + doc[FPSTR(HA_NAME)] = F("Equitherm target diff factor"); + doc[FPSTR(HA_ICON)] = F("mdi:chart-timeline-variant-shimmer"); doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); - doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.t_factor|float(0)|round(2) }}"); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.targetDiffFactor|float(0)|round(3) }}"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); - doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"t_factor\" : {{ value }}}}"); + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"targetDiffFactor\" : {{ value }}}}"); doc[FPSTR(HA_MIN)] = 0; doc[FPSTR(HA_MAX)] = 10; - doc[FPSTR(HA_STEP)] = 0.01f; + doc[FPSTR(HA_STEP)] = 0.001f; doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX); doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_t_factor")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_target_diff_factor")).c_str(), doc); } diff --git a/src/MqttTask.h b/src/MqttTask.h index 7578f72..b629d78 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -502,9 +502,10 @@ protected: // equitherm this->haHelper->publishSwitchEquitherm(); - this->haHelper->publishInputEquithermFactorN(false); - this->haHelper->publishInputEquithermFactorK(false); - this->haHelper->publishInputEquithermFactorT(false); + this->haHelper->publishInputEquithermSlope(false); + this->haHelper->publishInputEquithermExponent(false); + this->haHelper->publishInputEquithermShift(false); + this->haHelper->publishInputEquithermTargetDiffFactor(false); // states this->haHelper->publishStatusState(); diff --git a/src/RegulatorTask.h b/src/RegulatorTask.h index f768a54..4334df7 100644 --- a/src/RegulatorTask.h +++ b/src/RegulatorTask.h @@ -1,7 +1,5 @@ -#include #include -Equitherm etRegulator; GyverPID pidRegulator(0, 0, 0); @@ -146,39 +144,32 @@ protected: // if use equitherm if (settings.equitherm.enabled) { - unsigned short minTemp = settings.heating.minTemp; - unsigned short maxTemp = settings.heating.maxTemp; - float targetTemp = settings.heating.target; - float indoorTemp = vars.master.heating.indoorTemp; - float outdoorTemp = vars.master.heating.outdoorTemp; + float tempDelta = settings.heating.target - vars.master.heating.outdoorTemp; + float maxPoint = settings.heating.target - ( + settings.heating.maxTemp - settings.heating.target + ) / settings.equitherm.slope; - if (settings.system.unitSystem == UnitSystem::IMPERIAL) { - minTemp = f2c(minTemp); - maxTemp = f2c(maxTemp); - targetTemp = f2c(targetTemp); - indoorTemp = f2c(indoorTemp); - outdoorTemp = f2c(outdoorTemp); + float sf = (settings.heating.maxTemp - settings.heating.target) / pow( + settings.heating.target - maxPoint, + 1.0f / settings.equitherm.exponent + ); + float etResult = settings.heating.target + settings.equitherm.shift + sf * ( + tempDelta >= 0 + ? pow(tempDelta, 1.0f / settings.equitherm.exponent) + : -(pow(-(tempDelta), 1.0f / settings.equitherm.exponent)) + ); + + // add diff + if (this->indoorSensorsConnected && !settings.pid.enabled && !settings.heating.turbo) { + etResult += constrain( + settings.heating.target - vars.master.heating.indoorTemp, + -3.0f, + 3.0f + ) * settings.equitherm.targetDiffFactor; } - if (!this->indoorSensorsConnected || settings.pid.enabled) { - etRegulator.Kt = 0.0f; - etRegulator.indoorTemp = 0.0f; - - } else { - etRegulator.Kt = settings.heating.turbo ? 0.0f : settings.equitherm.t_factor; - etRegulator.indoorTemp = indoorTemp; - } - - etRegulator.setLimits(minTemp, maxTemp); - etRegulator.Kn = settings.equitherm.n_factor; - etRegulator.Kk = settings.equitherm.k_factor; - etRegulator.targetTemp = targetTemp; - etRegulator.outdoorTemp = outdoorTemp; - float etResult = etRegulator.getResult(); - - if (settings.system.unitSystem == UnitSystem::IMPERIAL) { - etResult = c2f(etResult); - } + // limit + etResult = constrain(etResult, settings.heating.minTemp, settings.heating.maxTemp); if (fabsf(prevEtResult - etResult) > 0.09f) { prevEtResult = etResult; diff --git a/src/Settings.h b/src/Settings.h index ecb4d15..c09fb89 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -154,9 +154,10 @@ struct Settings { struct { bool enabled = false; - float n_factor = 0.7f; - float k_factor = 3.0f; - float t_factor = 2.0f; + float slope = 0.7f; + float exponent = 1.3f; + float shift = 0.0f; + float targetDiffFactor = 2.0f; } equitherm; struct { diff --git a/src/strings.h b/src/strings.h index 268d73e..961721e 100644 --- a/src/strings.h +++ b/src/strings.h @@ -81,6 +81,7 @@ const char S_ENABLED[] PROGMEM = "enabled"; const char S_ENV[] PROGMEM = "env"; const char S_EPC[] PROGMEM = "epc"; const char S_EQUITHERM[] PROGMEM = "equitherm"; +const char S_EXPONENT[] PROGMEM = "exponent"; const char S_EXTERNAL_PUMP[] PROGMEM = "externalPump"; const char S_FACTOR[] PROGMEM = "factor"; const char S_FAULT[] PROGMEM = "fault"; @@ -117,7 +118,6 @@ const char S_INVERT_STATE[] PROGMEM = "invertState"; const char S_IP[] PROGMEM = "ip"; const char S_I_FACTOR[] PROGMEM = "i_factor"; const char S_I_MULTIPLIER[] PROGMEM = "i_multiplier"; -const char S_K_FACTOR[] PROGMEM = "k_factor"; const char S_LOGIN[] PROGMEM = "login"; const char S_LOG_LEVEL[] PROGMEM = "logLevel"; const char S_LOW_TEMP[] PROGMEM = "lowTemp"; @@ -143,7 +143,6 @@ const char S_NAME[] PROGMEM = "name"; const char S_NATIVE_HEATING_CONTROL[] PROGMEM = "nativeHeatingControl"; const char S_NETWORK[] PROGMEM = "network"; const char S_NTP[] PROGMEM = "ntp"; -const char S_N_FACTOR[] PROGMEM = "n_factor"; const char S_OFFSET[] PROGMEM = "offset"; const char S_ON_ENABLED_HEATING[] PROGMEM = "onEnabledHeating"; const char S_ON_FAULT[] PROGMEM = "onFault"; @@ -182,9 +181,11 @@ const char S_SERIAL[] PROGMEM = "serial"; const char S_SERVER[] PROGMEM = "server"; const char S_SETTINGS[] PROGMEM = "settings"; const char S_SET_DATE_AND_TIME[] PROGMEM = "setDateAndTime"; +const char S_SHIFT[] PROGMEM = "shift"; const char S_SIGNAL_QUALITY[] PROGMEM = "signalQuality"; const char S_SIZE[] PROGMEM = "size"; const char S_SLAVE[] PROGMEM = "slave"; +const char S_SLOPE[] PROGMEM = "slope"; const char S_SSID[] PROGMEM = "ssid"; const char S_STA[] PROGMEM = "sta"; const char S_STATE[] PROGMEM = "state"; @@ -196,6 +197,7 @@ const char S_SUBNET[] PROGMEM = "subnet"; const char S_SUMMER_WINTER_MODE[] PROGMEM = "summerWinterMode"; const char S_SYSTEM[] PROGMEM = "system"; const char S_TARGET[] PROGMEM = "target"; +const char S_TARGET_DIFF_FACTOR[] PROGMEM = "targetDiffFactor"; const char S_TARGET_TEMP[] PROGMEM = "targetTemp"; const char S_TELNET[] PROGMEM = "telnet"; const char S_TEMPERATURE[] PROGMEM = "temperature"; @@ -208,7 +210,6 @@ const char S_TRESHOLD_TIME[] PROGMEM = "tresholdTime"; const char S_TURBO[] PROGMEM = "turbo"; const char S_TURBO_FACTOR[] PROGMEM = "turboFactor"; const char S_TYPE[] PROGMEM = "type"; -const char S_T_FACTOR[] PROGMEM = "t_factor"; const char S_UNIT_SYSTEM[] PROGMEM = "unitSystem"; const char S_UPTIME[] PROGMEM = "uptime"; const char S_USE[] PROGMEM = "use"; diff --git a/src/utils.h b/src/utils.h index 8442dbf..2c7b64a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -517,9 +517,10 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { auto equitherm = dst[FPSTR(S_EQUITHERM)].to(); equitherm[FPSTR(S_ENABLED)] = src.equitherm.enabled; - equitherm[FPSTR(S_N_FACTOR)] = roundf(src.equitherm.n_factor, 3); - equitherm[FPSTR(S_K_FACTOR)] = roundf(src.equitherm.k_factor, 3); - equitherm[FPSTR(S_T_FACTOR)] = roundf(src.equitherm.t_factor, 3); + equitherm[FPSTR(S_SLOPE)] = roundf(src.equitherm.slope, 3); + equitherm[FPSTR(S_EXPONENT)] = roundf(src.equitherm.exponent, 3); + equitherm[FPSTR(S_SHIFT)] = roundf(src.equitherm.shift, 2); + equitherm[FPSTR(S_TARGET_DIFF_FACTOR)] = roundf(src.equitherm.targetDiffFactor, 3); auto pid = dst[FPSTR(S_PID)].to(); pid[FPSTR(S_ENABLED)] = src.pid.enabled; @@ -1127,29 +1128,38 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } - if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_N_FACTOR)].isNull()) { - float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_N_FACTOR)].as(); + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].as(); - if (value > 0 && value <= 10 && fabsf(value - dst.equitherm.n_factor) > 0.0001f) { - dst.equitherm.n_factor = roundf(value, 3); + if (value > 0.0f && value <= 10.0f && fabsf(value - dst.equitherm.slope) > 0.0001f) { + dst.equitherm.slope = roundf(value, 3); changed = true; } } - if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_K_FACTOR)].isNull()) { - float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_K_FACTOR)].as(); + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].as(); - if (value >= 0 && value <= 10 && fabsf(value - dst.equitherm.k_factor) > 0.0001f) { - dst.equitherm.k_factor = roundf(value, 3); + if (value > 0.0f && value <= 2.0f && fabsf(value - dst.equitherm.exponent) > 0.0001f) { + dst.equitherm.exponent = roundf(value, 3); changed = true; } } - if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_T_FACTOR)].isNull()) { - float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_T_FACTOR)].as(); + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].as(); - if (value >= 0 && value <= 10 && fabsf(value - dst.equitherm.t_factor) > 0.0001f) { - dst.equitherm.t_factor = roundf(value, 3); + if (value >= -15.0f && value <= 15.0f && fabsf(value - dst.equitherm.shift) > 0.0001f) { + dst.equitherm.shift = roundf(value, 2); + changed = true; + } + } + + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].as(); + + if (value >= 0.0f && value <= 10.0f && fabsf(value - dst.equitherm.targetDiffFactor) > 0.0001f) { + dst.equitherm.targetDiffFactor = roundf(value, 3); changed = true; } } diff --git a/src_data/locales/en.json b/src_data/locales/en.json index 24ac153..f460b79 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -371,11 +371,26 @@ }, "equitherm": { - "n": "N factor", - "k": "K factor", - "t": { + "slope": { + "title": "Slope", + "note": "Heat loss compensation. Main tuning parameter." + }, + "exponent": { + "title": "Exponent", + "note": "Radiator efficiency. Typical values: 1.1 - Floor heating, 1.2 - Cast iron, 1.3 - Panel radiators, 1.4 - Convectors." + }, + "shift": { + "title": "Shift", + "note": "Compensates for additional heat losses (e.g., in pipes) or extra heat sources." + }, + "targetDiffFactor": { "title": "T factor", - "note": "Not used if PID is enabled" + "note": "Not used if PID is enabled. Adds to the setpoint the difference between the target and current indoor temp: setpoint = setpoint + ((target - indoor) * T)." + }, + "chart": { + "targetTemp": "Target indoor temperature", + "setpointTemp": "Heat carrier temperature", + "outdoorTemp": "Outdoor temperature" } }, diff --git a/src_data/locales/it.json b/src_data/locales/it.json index 2e4f15c..a9a0227 100644 --- a/src_data/locales/it.json +++ b/src_data/locales/it.json @@ -371,11 +371,26 @@ }, "equitherm": { - "n": "Fattore N", - "k": "Fattore K", - "t": { + "slope": { + "title": "Pendenza", + "note": "Compensazione delle perdite di calore. Principale parametro di regolazione." + }, + "exponent": { + "title": "Esponente", + "note": "Efficienza del radiatore. Valori tipici: 1.1 - Riscaldamento a pavimento, 1.2 - Radiatori in ghisa, 1.3 - Radiatori a pannelli, 1.4 - Convetttori." + }, + "shift": { + "title": "Spostare", + "note": "Compensa le perdite di calore aggiuntive (ad esempio, nelle tubature) o fonti di calore extra." + }, + "targetDiffFactor": { "title": "Fattore T", - "note": "Non usato se PID è attivato" + "note": "Non usato se PID è attivato. Aggiunge al setpoint la differenza tra la temperatura interna target e quella attuale: setpoint = setpoint + ((target - indoor) * T)." + }, + "chart": { + "targetTemp": "Temperatura interna target", + "setpointTemp": "Temperatura del portatore di calore", + "outdoorTemp": "Temperatura esterna" } }, diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index 7a6e627..09b1b4d 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -371,11 +371,26 @@ }, "equitherm": { - "n": "Коэффициент N", - "k": "Коэффициент K", - "t": { + "slope": { + "title": "Наклон", + "note": "Компенсация теплопотерь. Основной параметр настройки." + }, + "exponent": { + "title": "Экспонента", + "note": "Эффективность радиатора. Типичные значения: 1.1 - Тёплый пол, 1.2 - Чугунные радиаторы, 1.3 - Панельные радиаторы, 1.4 - Конвекторы." + }, + "shift": { + "title": "Смещение", + "note": "Компенсирует дополнительные теплопотери (например, в трубах) или дополнительные источники тепла." + }, + "targetDiffFactor": { "title": "Коэффициент T", - "note": "Не используется, если ПИД включен" + "note": "Не используется, если ПИД включен. Добавляет разницу между целевой и текущей температурой в помещении: setpoint = setpoint + ((target - indoor) * T)." + }, + "chart": { + "targetTemp": "Целевая внутренняя температура", + "setpointTemp": "Температура теплоносителя", + "outdoorTemp": "Наружная температура" } }, diff --git a/src_data/pages/sensors.html b/src_data/pages/sensors.html index c36dbb7..b9496f6 100644 --- a/src_data/pages/sensors.html +++ b/src_data/pages/sensors.html @@ -241,7 +241,9 @@ setCheckboxValue("[name='filtering']", data.filtering, sensorForm); setInputValue("[name='filteringFactor']", data.filteringFactor, {}, sensorForm); - sensorForm.querySelector("[name='type']").dispatchEvent(new Event("change")); + setTimeout(() => { + sensorForm.querySelector("[name='type']").dispatchEvent(new Event("change")); + }, 10); setBusy(".form-busy", "form", false, sensorNode); }; diff --git a/src_data/pages/settings.html b/src_data/pages/settings.html index f5c4494..04f3dbc 100644 --- a/src_data/pages/settings.html +++ b/src_data/pages/settings.html @@ -106,7 +106,7 @@ - +
@@ -354,21 +354,44 @@
+
+
+ +
+ + +
+
+
+ +
+
@@ -424,7 +447,7 @@ settings.temp.min - + - + - + - + + +
@@ -1117,8 +1122,9 @@ setCheckboxValue("[name='opentherm[options][autoFaultReset]']", data.opentherm.options.autoFaultReset); setCheckboxValue("[name='opentherm[options][autoDiagReset]']", data.opentherm.options.autoDiagReset); setCheckboxValue("[name='opentherm[options][setDateAndTime]']", data.opentherm.options.setDateAndTime); - setCheckboxValue("[name='opentherm[options][nativeHeatingControl]']", data.opentherm.options.nativeHeatingControl); + setCheckboxValue("[name='opentherm[options][nativeOTC]']", data.opentherm.options.nativeOTC); setCheckboxValue("[name='opentherm[options][immergasFix]']", data.opentherm.options.immergasFix); + setCheckboxValue("[name='opentherm[options][alwaysSendIndoorTemp]']", data.opentherm.options.alwaysSendIndoorTemp); setBusy('#ot-settings-busy', '#ot-settings', false); // MQTT @@ -1206,7 +1212,7 @@ setBusy('#dhw-settings-busy', '#dhw-settings', false); // Emergency mode - if (data.opentherm.options.nativeHeatingControl) { + if (data.opentherm.options.nativeOTC) { setInputValue("[name='emergency[target]']", data.emergency.target, { "min": data.system.unitSystem == 0 ? 5 : 41, "max": data.system.unitSystem == 0 ? 40 : 104 From 348fab39bb5518cce79a2ba5cd9454f9a79ebb5b Mon Sep 17 00:00:00 2001 From: Yurii Date: Fri, 12 Dec 2025 15:21:02 +0300 Subject: [PATCH 13/20] refactor: refactoring after #204 --- src/OpenThermTask.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index 53eed3b..235ae07 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -1209,8 +1209,8 @@ protected: } } - // NativeOTC or if the AlwaysSendIndoorTemp option is enabled. - if ((settings.opentherm.options.nativeOTC) || (settings.opentherm.options.alwaysSendIndoorTemp)) { + // Send indoor temp if AlwaysSendIndoorTemp option is enabled. + if (settings.opentherm.options.nativeOTC || settings.opentherm.options.alwaysSendIndoorTemp) { // Converted current indoor temp float convertedTemp = convertTemp(vars.master.heating.indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem); @@ -1237,9 +1237,12 @@ protected: Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set current CH2 indoor temp")); } } + } + // NativeOTC + if (settings.opentherm.options.nativeOTC) { // Converted target indoor temp - convertedTemp = convertTemp(vars.master.heating.targetTemp, settings.system.unitSystem, settings.opentherm.unitSystem); + float convertedTemp = convertTemp(vars.master.heating.targetTemp, settings.system.unitSystem, settings.opentherm.unitSystem); // Set target indoor temp if (this->needSetHeatingTemp(convertedTemp)) { From 09c50d5df8c40e2cdf0d754f1979e58a300e3090 Mon Sep 17 00:00:00 2001 From: Yurii Date: Fri, 12 Dec 2025 15:27:42 +0300 Subject: [PATCH 14/20] refactor: fixed localization after #204 --- src_data/locales/cn.json | 6 +++--- src_data/locales/en.json | 6 +++--- src_data/locales/it.json | 6 +++--- src_data/locales/nl.json | 7 ++++--- src_data/locales/ru.json | 6 +++--- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src_data/locales/cn.json b/src_data/locales/cn.json index 4d60218..2f9cd01 100644 --- a/src_data/locales/cn.json +++ b/src_data/locales/cn.json @@ -458,12 +458,12 @@ "autoDiagReset": "自动诊断复位 (不推荐!)", "setDateAndTime": "同步设置锅炉日期与时间", "immergasFix": "针对Immergas锅炉的兼容性修复", - "alwaysSendIndoorTemp": "一定要给锅炉输送电流和目标温度" + "alwaysSendIndoorTemp": "向锅炉发送当前室内温度" }, "nativeOTC": { - "title": "本地(由锅炉处理)外部温度补偿", - "note": "只有 锅炉能调节冷却液温度时才有效。未经测试。固件中不兼容PID和Equitherm的调压器。" + "title": "原生热载体温度计算模式", + "note": "仅在锅炉处于 OTC 模式时工作:需要并接受目标室内温度,并基于内置曲线模式自行调节热载体温度。与 PID 和 Equitherm 不兼容。" } }, diff --git a/src_data/locales/en.json b/src_data/locales/en.json index 6bc0152..f0e3548 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -458,12 +458,12 @@ "autoDiagReset": "Auto diag reset (not recommended!)", "setDateAndTime": "Set date & time on boiler", "immergasFix": "Fix for Immergas boilers", - "alwaysSendIndoorTemp": "Always send current and target indoor temp to boiler" + "alwaysSendIndoorTemp": "Send current indoor temp to boiler" }, "nativeOTC": { - "title": "Native (handled by boiler) Outside Temp Compensation", - "note": "Works ONLY if the boiler regulates the temperature of the coolant itself. Untested. Not compatible with PID and Equitherm regulators in firmware." + "title": "Native OTC mode", + "note": "Works ONLY if the boiler is in OTC mode: requires and accepts the target indoor temperature and self-regulates the heat carrier temperature based on the built-in curves mode. Incompatible with PID and Equitherm." } }, diff --git a/src_data/locales/it.json b/src_data/locales/it.json index 7792efb..eb3eb74 100644 --- a/src_data/locales/it.json +++ b/src_data/locales/it.json @@ -458,12 +458,12 @@ "autoDiagReset": "Ripristino diagnostico automatica (sconsigliato!)", "setDateAndTime": "Imposta data e ora sulla caldaia", "immergasFix": "Fix per caldiaie Immergas", - "alwaysSendIndoorTemp": "Invia sempre corrente e la temperatura interna target alla caldaia" + "alwaysSendIndoorTemp": "Invia la temp attuale interna alla caldaia" }, "nativeOTC": { - "title": "Compensazione della temperatura esterna nativa (gestita dalla caldaia)", - "note": "Lavora SOLO se la caldaia regola la temperatura del liquido refrigerante stesso. Testate. Non compatibile con i regolatori PID ed Equitherm nel firmware." + "title": "Modalità nativa di calcolo della temperatura del vettore termico", + "note": "Funziona SOLO se la caldaia è in modalità OTC: richiede e accetta la temperatura interna target e regola autonomamente la temperatura del vettore termico basata sulla modalità curve integrata. Incompatibile con PID e Equitherm." } }, diff --git a/src_data/locales/nl.json b/src_data/locales/nl.json index af4e063..d982f21 100644 --- a/src_data/locales/nl.json +++ b/src_data/locales/nl.json @@ -423,11 +423,12 @@ "autoDiagReset": "Automatische diagnosereset (niet aanbevolen!)", "setDateAndTime": "Stel datum & tijd in op ketel", "immergasFix": "Fix voor Immergas-ketels", - "alwaysSendIndoorTemp": "Stuur altijd de stroom- en streeftemperatuur binnen naar de ketel" + "alwaysSendIndoorTemp": "Stuur huidige binnentemp naar ketel" }, + "nativeOTC": { - "title": "Native (afgehandeld door de ketel) Buitentemperatuurcompensatie", - "note": "Werkt ALLEEN als de ketel de temperatuur van de koelvloeistof zelf regelt. Geteste. Niet compatibel met PID- en Equitherm-regelaars in firmware" + "title": "Native warmtedrager temperatuur berekeningsmodus", + "note": "Werkt ALLEEN als de ketel in OTC-modus is: vereist en accepteert de doel binnentemperatuur en regelt zelf de warmtedrager temperatuur op basis van de ingebouwde curves modus. Incompatibel met PID en Equitherm." } }, "mqtt": { diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index 6bce482..cd91c0c 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -458,12 +458,12 @@ "autoDiagReset": "Автоматический сброс диагностики (не рекомендуется!)", "setDateAndTime": "Устанавливать время и дату на котле", "immergasFix": "Фикс для котлов Immergas", - "alwaysSendIndoorTemp": "Всегда отправляйте ток и целевую температуру в помещении в котёл" + "alwaysSendIndoorTemp": "Передавать текущую темп. в помещении котлу" }, "nativeOTC": { - "title": "Нативная (обрабатывается котлом) Внешняя компенсация температуры", - "note": "Работает ТОЛЬКО если котёл регулирует температуру самого охлаждающей жидкости. Непроверенных. Несовместимо с регуляторами PID и Equitherm в прошивке." + "title": "Нативный режим OTC (расчёт температуры теплоносителя)", + "note": "Работает ТОЛЬКО если котел в режиме OTC: требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД и ПЗА." } }, From 6c3b79bda188fbae0a2763cb07734bf690e94d29 Mon Sep 17 00:00:00 2001 From: Yurii Date: Sat, 13 Dec 2025 04:15:48 +0300 Subject: [PATCH 15/20] fix: OT protocol version in JSON of slave fixed --- src/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.h b/src/utils.h index f3db18d..1a79642 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2131,7 +2131,7 @@ void varsToJson(const Variables& src, JsonVariant dst) { slave[FPSTR(S_FLAGS)] = src.slave.flags; slave[FPSTR(S_TYPE)] = src.slave.type; slave[FPSTR(S_APP_VERSION)] = src.slave.appVersion; - slave[FPSTR(S_PROTOCOL_VERSION)] = src.slave.appVersion; + slave[FPSTR(S_PROTOCOL_VERSION)] = src.slave.protocolVersion; slave[FPSTR(S_CONNECTED)] = src.slave.connected; slave[FPSTR(S_FLAME)] = src.slave.flame; From e487c789215ab5b443a4086f53a9742ca310048e Mon Sep 17 00:00:00 2001 From: Yurii Date: Sat, 13 Dec 2025 06:17:15 +0300 Subject: [PATCH 16/20] feat: added sensor type "OT, Cooling hours" --- src/OpenThermTask.h | 35 +++++++++++++++++++++++++++++++++++ src/Sensors.h | 1 + src/Settings.h | 1 + src/utils.h | 1 + src_data/locales/cn.json | 1 + src_data/locales/en.json | 1 + src_data/locales/it.json | 1 + src_data/locales/nl.json | 2 ++ src_data/locales/ru.json | 1 + src_data/pages/sensors.html | 1 + 10 files changed, 45 insertions(+) diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index 235ae07..5101597 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -305,6 +305,7 @@ protected: Sensors::setConnectionStatusByType(Sensors::Type::OT_DHW_BURNER_HOURS, false); Sensors::setConnectionStatusByType(Sensors::Type::OT_HEATING_PUMP_HOURS, false); Sensors::setConnectionStatusByType(Sensors::Type::OT_DHW_PUMP_HOURS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_COOLING_HOURS, false); this->initialized = false; this->disconnectedTime = millis(); @@ -677,6 +678,21 @@ protected: } } + // Update cooling hours + if (Sensors::getAmountByType(Sensors::Type::OT_COOLING_HOURS, true)) { + if (this->updateCoolingHours()) { + Log.snoticeln(FPSTR(L_OT), F("Received cooling hours: %hu"), vars.slave.stats.coolingHours); + + Sensors::setValueByType( + Sensors::Type::OT_COOLING_HOURS, vars.slave.stats.coolingHours, + Sensors::ValueType::PRIMARY, true, true + ); + + } else { + Log.swarningln(FPSTR(L_OT), F("Failed receive cooling hours")); + } + } + // Auto fault reset if (settings.opentherm.options.autoFaultReset && vars.slave.fault.active && !vars.actions.resetFault) { vars.actions.resetFault = true; @@ -2171,6 +2187,25 @@ protected: return true; } + bool updateCoolingHours() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::CoolingOperationHours, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::CoolingOperationHours)) { + return false; + } + + vars.slave.stats.coolingHours = CustomOpenTherm::getUInt(response); + + return true; + } + bool updateModulationLevel() { const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( OpenThermRequestType::READ_DATA, diff --git a/src/Sensors.h b/src/Sensors.h index 170829e..71b9ac3 100644 --- a/src/Sensors.h +++ b/src/Sensors.h @@ -34,6 +34,7 @@ public: OT_DHW_BURNER_HOURS = 24, OT_HEATING_PUMP_HOURS = 25, OT_DHW_PUMP_HOURS = 26, + OT_COOLING_HOURS = 27, NTC_10K_TEMP = 50, DALLAS_TEMP = 51, diff --git a/src/Settings.h b/src/Settings.h index 13da7c2..580c9d5 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -389,6 +389,7 @@ struct Variables { uint16_t dhwBurnerStarts = 0; uint16_t heatingPumpStarts = 0; uint16_t dhwPumpStarts = 0; + uint16_t coolingHours = 0; uint16_t burnerHours = 0; uint16_t dhwBurnerHours = 0; uint16_t heatingPumpHours = 0; diff --git a/src/utils.h b/src/utils.h index 1a79642..f52f2ba 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1932,6 +1932,7 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se case static_cast(Sensors::Type::OT_DHW_BURNER_HOURS): case static_cast(Sensors::Type::OT_HEATING_PUMP_HOURS): case static_cast(Sensors::Type::OT_DHW_PUMP_HOURS): + case static_cast(Sensors::Type::OT_COOLING_HOURS): case static_cast(Sensors::Type::NTC_10K_TEMP): case static_cast(Sensors::Type::DALLAS_TEMP): diff --git a/src_data/locales/cn.json b/src_data/locales/cn.json index 2f9cd01..aafbac7 100644 --- a/src_data/locales/cn.json +++ b/src_data/locales/cn.json @@ -243,6 +243,7 @@ "otDhwBurnerHours": "OpenTherm, number of burner operating hours (DHW)", "otHeatingPumpHours": "OpenTherm, number of pump operating hours (heating)", "otDhwPumpHours": "OpenTherm, number of pump operating hours (DHW)", + "otCoolingHours": "OpenTherm, number of cooling hours", "ntcTemp": "NTC 传感器", "dallasTemp": "DALLAS 传感器", diff --git a/src_data/locales/en.json b/src_data/locales/en.json index f0e3548..1514791 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -243,6 +243,7 @@ "otDhwBurnerHours": "OpenTherm, number of burner operating hours (DHW)", "otHeatingPumpHours": "OpenTherm, number of pump operating hours (heating)", "otDhwPumpHours": "OpenTherm, number of pump operating hours (DHW)", + "otCoolingHours": "OpenTherm, number of cooling hours", "ntcTemp": "NTC sensor", "dallasTemp": "DALLAS sensor", diff --git a/src_data/locales/it.json b/src_data/locales/it.json index eb3eb74..7469f64 100644 --- a/src_data/locales/it.json +++ b/src_data/locales/it.json @@ -243,6 +243,7 @@ "otDhwBurnerHours": "OpenTherm, numero di ore di funzionamento del bruciatore (ACS)", "otHeatingPumpHours": "OpenTherm, numero di ore di funzionamento della pompa (riscaldamento)", "otDhwPumpHours": "OpenTherm, numero di ore di funzionamento della pompa (ACS)", + "otCoolingHours": "OpenTherm, numero di ore di funzionamento della cooling", "ntcTemp": "Sensore NTC", "dallasTemp": "Sensore DALLAS", diff --git a/src_data/locales/nl.json b/src_data/locales/nl.json index d982f21..b670292 100644 --- a/src_data/locales/nl.json +++ b/src_data/locales/nl.json @@ -222,6 +222,8 @@ "otDhwBurnerHours": "OpenTherm, aantal branderuren (warm water)", "otHeatingPumpHours": "OpenTherm, aantal pompuren (verwarming)", "otDhwPumpHours": "OpenTherm, aantal pompuren (warm water)", + "otCoolingHours": "OpenTherm, aantal cooling", + "ntcTemp": "NTC-sensor", "dallasTemp": "DALLAS-sensor", "bluetooth": "BLE-sensor", diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index cd91c0c..98532a6 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -243,6 +243,7 @@ "otDhwBurnerHours": "OpenTherm, кол-во часов работы горелки (ГВС)", "otHeatingPumpHours": "OpenTherm, кол-во часов работы насоса (отопление)", "otDhwPumpHours": "OpenTherm, кол-во часов работы насоса (ГВС)", + "otCoolingHours": "OpenTherm, кол-во часов работы охлаждения", "ntcTemp": "NTC датчик", "dallasTemp": "DALLAS датчик", diff --git a/src_data/pages/sensors.html b/src_data/pages/sensors.html index b9496f6..47f9986 100644 --- a/src_data/pages/sensors.html +++ b/src_data/pages/sensors.html @@ -113,6 +113,7 @@ + From 5a14857f52340ab38ce948920d1657a65896e0ef Mon Sep 17 00:00:00 2001 From: Yurii Date: Sat, 13 Dec 2025 06:21:21 +0300 Subject: [PATCH 17/20] refactor: minor changes --- lib/CustomOpenTherm/CustomOpenTherm.h | 103 ++++++++++++++++++-------- src/OpenThermTask.h | 59 +++++++++------ 2 files changed, 107 insertions(+), 55 deletions(-) diff --git a/lib/CustomOpenTherm/CustomOpenTherm.h b/lib/CustomOpenTherm/CustomOpenTherm.h index 6fd6db1..3bef65f 100644 --- a/lib/CustomOpenTherm/CustomOpenTherm.h +++ b/lib/CustomOpenTherm/CustomOpenTherm.h @@ -70,50 +70,89 @@ public: } } - bool sendBoilerReset() { - unsigned int data = 1; - data <<= 8; - unsigned long response = this->sendRequest(buildRequest( - OpenThermMessageType::WRITE_DATA, - OpenThermMessageID::RemoteRequest, - data - )); - - return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest); + inline auto sendBoilerReset() { + return this->sendRequestCode(1); } - bool sendServiceReset() { - unsigned int data = 10; - data <<= 8; - unsigned long response = this->sendRequest(buildRequest( - OpenThermMessageType::WRITE_DATA, - OpenThermMessageID::RemoteRequest, - data - )); - - return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest); + inline auto sendServiceReset() { + return this->sendRequestCode(10); } - bool sendWaterFilling() { - unsigned int data = 2; - data <<= 8; + inline auto sendWaterFilling() { + return this->sendRequestCode(2); + } + + bool sendRequestCode(const uint8_t requestCode) { unsigned long response = this->sendRequest(buildRequest( OpenThermMessageType::WRITE_DATA, OpenThermMessageID::RemoteRequest, - data + static_cast(requestCode) << 8 )); - return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest); + if (!isValidResponse(response) || !isValidResponseId(response, OpenThermMessageID::RemoteRequest)) { + return false; + } + + const uint8_t responseRequestCode = (response & 0xFFFF) >> 8; + const uint8_t responseCode = response & 0xFF; + if (responseRequestCode != requestCode || responseCode < 128) { + return false; + } + + // reset + this->sendRequest(buildRequest( + OpenThermMessageType::WRITE_DATA, + OpenThermMessageID::RemoteRequest, + 0u << 8 + )); + + return true; + } + + bool getStr(OpenThermMessageID id, char* buffer, uint16_t length = 50) { + if (buffer == nullptr || length == 0) { + return false; + } + + unsigned long response; + uint8_t index = 0; + uint8_t maxIndex = 255; + + while (index <= maxIndex && index < length) { + response = this->sendRequest(buildRequest( + OpenThermMessageType::READ_DATA, + id, + static_cast(index) << 8 + )); + + if (!isValidResponse(response) || !isValidResponseId(response, id)) { + break; + } + + const uint8_t character = response & 0xFF; + if (character == 0) { + break; + } + + if (index == 0) { + maxIndex = (response & 0xFFFF) >> 8; + } + + buffer[index++] = static_cast(character); + } + + buffer[index] = '\0'; + return index > 0; } static bool isCh2Active(unsigned long response) { - return response & 0x20; + return response & 0x20; } static bool isValidResponseId(unsigned long response, OpenThermMessageID id) { - uint8_t responseId = (response >> 16) & 0xFF; + const uint8_t responseId = (response >> 16) & 0xFF; - return (uint8_t)id == responseId; + return static_cast(id) == responseId; } static uint8_t getResponseMessageTypeId(unsigned long response) { @@ -124,10 +163,10 @@ public: uint8_t msgType = getResponseMessageTypeId(response); switch (msgType) { - case (uint8_t) OpenThermMessageType::READ_ACK: - case (uint8_t) OpenThermMessageType::WRITE_ACK: - case (uint8_t) OpenThermMessageType::DATA_INVALID: - case (uint8_t) OpenThermMessageType::UNKNOWN_DATA_ID: + case static_cast(OpenThermMessageType::READ_ACK): + case static_cast(OpenThermMessageType::WRITE_ACK): + case static_cast(OpenThermMessageType::DATA_INVALID): + case static_cast(OpenThermMessageType::UNKNOWN_DATA_ID): return CustomOpenTherm::messageTypeToString( static_cast(msgType) ); diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index 5101597..131f5ef 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -808,7 +808,7 @@ protected: bool result = this->updateDhwTemp(); if (result) { - float convertedDhwTemp = convertTemp( + const float convertedDhwTemp = convertTemp( vars.slave.dhw.currentTemp, settings.opentherm.unitSystem, settings.system.unitSystem @@ -832,7 +832,7 @@ protected: // Update DHW temp 2 if (settings.opentherm.options.dhwSupport && Sensors::getAmountByType(Sensors::Type::OT_DHW_TEMP2, true)) { if (this->updateDhwTemp2()) { - float convertedDhwTemp2 = convertTemp( + const float convertedDhwTemp2 = convertTemp( vars.slave.dhw.currentTemp2, settings.opentherm.unitSystem, settings.system.unitSystem @@ -856,7 +856,7 @@ protected: // Update DHW flow rate if (settings.opentherm.options.dhwSupport && Sensors::getAmountByType(Sensors::Type::OT_DHW_FLOW_RATE, true)) { if (this->updateDhwFlowRate()) { - float convertedDhwFlowRate = convertVolume( + const float convertedDhwFlowRate = convertVolume( vars.slave.dhw.flowRate, settings.opentherm.unitSystem, settings.system.unitSystem @@ -880,7 +880,7 @@ protected: // Update heating temp if (Sensors::getAmountByType(Sensors::Type::OT_HEATING_TEMP, true)) { if (this->updateHeatingTemp()) { - float convertedHeatingTemp = convertTemp( + const float convertedHeatingTemp = convertTemp( vars.slave.heating.currentTemp, settings.opentherm.unitSystem, settings.system.unitSystem @@ -904,7 +904,7 @@ protected: // Update heating return temp if (Sensors::getAmountByType(Sensors::Type::OT_HEATING_RETURN_TEMP, true)) { if (this->updateHeatingReturnTemp()) { - float convertedHeatingReturnTemp = convertTemp( + const float convertedHeatingReturnTemp = convertTemp( vars.slave.heating.returnTemp, settings.opentherm.unitSystem, settings.system.unitSystem @@ -929,7 +929,7 @@ protected: if (Sensors::getAmountByType(Sensors::Type::OT_CH2_TEMP, true)) { if (vars.master.ch2.enabled && !settings.opentherm.options.nativeOTC) { if (this->updateCh2Temp()) { - float convertedCh2Temp = convertTemp( + const float convertedCh2Temp = convertTemp( vars.slave.ch2.currentTemp, settings.opentherm.unitSystem, settings.system.unitSystem @@ -954,7 +954,7 @@ protected: // Update exhaust temp if (Sensors::getAmountByType(Sensors::Type::OT_EXHAUST_TEMP, true)) { if (this->updateExhaustTemp()) { - float convertedExhaustTemp = convertTemp( + const float convertedExhaustTemp = convertTemp( vars.slave.exhaust.temp, settings.opentherm.unitSystem, settings.system.unitSystem @@ -978,7 +978,7 @@ protected: // Update heat exchanger temp if (Sensors::getAmountByType(Sensors::Type::OT_HEAT_EXCHANGER_TEMP, true)) { if (this->updateHeatExchangerTemp()) { - float convertedHeatExchTemp = convertTemp( + const float convertedHeatExchTemp = convertTemp( vars.slave.heatExchangerTemp, settings.opentherm.unitSystem, settings.system.unitSystem @@ -1002,7 +1002,7 @@ protected: // Update outdoor temp if (Sensors::getAmountByType(Sensors::Type::OT_OUTDOOR_TEMP, true)) { if (this->updateOutdoorTemp()) { - float convertedOutdoorTemp = convertTemp( + const float convertedOutdoorTemp = convertTemp( vars.slave.heating.outdoorTemp, settings.opentherm.unitSystem, settings.system.unitSystem @@ -1026,7 +1026,7 @@ protected: // Update solar storage temp if (Sensors::getAmountByType(Sensors::Type::OT_SOLAR_STORAGE_TEMP, true)) { if (this->updateSolarStorageTemp()) { - float convertedSolarStorageTemp = convertTemp( + const float convertedSolarStorageTemp = convertTemp( vars.slave.solar.storage, settings.opentherm.unitSystem, settings.system.unitSystem @@ -1050,7 +1050,7 @@ protected: // Update solar collector temp if (Sensors::getAmountByType(Sensors::Type::OT_SOLAR_COLLECTOR_TEMP, true)) { if (this->updateSolarCollectorTemp()) { - float convertedSolarCollectorTemp = convertTemp( + const float convertedSolarCollectorTemp = convertTemp( vars.slave.solar.collector, settings.opentherm.unitSystem, settings.system.unitSystem @@ -1096,7 +1096,7 @@ protected: // Update pressure if (Sensors::getAmountByType(Sensors::Type::OT_PRESSURE, true)) { if (this->updatePressure()) { - float convertedPressure = convertPressure( + const float convertedPressure = convertPressure( vars.slave.pressure, settings.opentherm.unitSystem, settings.system.unitSystem @@ -1357,7 +1357,7 @@ protected: // Heating overheat control if (settings.heating.overheatProtection.highTemp > 0 && settings.heating.overheatProtection.lowTemp > 0) { - float highTemp = convertTemp( + const float highTemp = convertTemp( max({ vars.slave.heating.currentTemp, vars.slave.heating.returnTemp, @@ -1394,7 +1394,7 @@ protected: // DHW overheat control if (settings.dhw.overheatProtection.highTemp > 0 && settings.dhw.overheatProtection.lowTemp > 0) { - float highTemp = convertTemp( + const float highTemp = convertTemp( max({ vars.slave.heating.currentTemp, vars.slave.heating.returnTemp, @@ -1488,6 +1488,19 @@ protected: } else { Log.swarningln(FPSTR(L_OT), F("Failed set master config")); } + + /*char buf[100]; + if (this->instance->getStr(OpenThermMessageID::Brand, buf, sizeof(buf) - 1)) { + Log.snoticeln(FPSTR(L_OT), F("Slave brand: %s"), buf); + } + + if (this->instance->getStr(OpenThermMessageID::BrandVersion, buf, sizeof(buf) - 1)) { + Log.snoticeln(FPSTR(L_OT), F("Slave brand version: %s"), buf); + } + + if (this->instance->getStr(OpenThermMessageID::BrandSerialNumber, buf, sizeof(buf) - 1)) { + Log.snoticeln(FPSTR(L_OT), F("Slave brand s/n: %s"), buf); + }*/ } bool isReady() { @@ -1665,7 +1678,7 @@ protected: } - bool setRoomTemp(float temperature) { + bool setRoomTemp(const float temperature) { const unsigned int request = CustomOpenTherm::temperatureToData(temperature); const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( OpenThermMessageType::WRITE_DATA, @@ -1685,7 +1698,7 @@ protected: return CustomOpenTherm::getUInt(response) == request; } - bool setRoomTempCh2(float temperature) { + bool setRoomTempCh2(const float temperature) { const unsigned int request = CustomOpenTherm::temperatureToData(temperature); const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( OpenThermMessageType::WRITE_DATA, @@ -2220,7 +2233,7 @@ protected: return false; } - float value = CustomOpenTherm::getFloat(response); + const float value = CustomOpenTherm::getFloat(response); if (value < 0) { return false; } @@ -2244,7 +2257,7 @@ protected: return false; } - float value = CustomOpenTherm::getFloat(response); + const float value = CustomOpenTherm::getFloat(response); if (value <= 0) { return false; } @@ -2268,7 +2281,7 @@ protected: return false; } - float value = CustomOpenTherm::getFloat(response); + const float value = CustomOpenTherm::getFloat(response); if (value <= 0) { return false; } @@ -2322,7 +2335,7 @@ protected: return false; } - float value = CustomOpenTherm::getFloat(response); + const float value = CustomOpenTherm::getFloat(response); if (value <= 0) { return false; } @@ -2386,7 +2399,7 @@ protected: return false; } - float value = (float) CustomOpenTherm::getInt(response); + const float value = (float) CustomOpenTherm::getInt(response); if (!isValidTemp(value, settings.opentherm.unitSystem, -40, 500)) { return false; } @@ -2410,7 +2423,7 @@ protected: return false; } - float value = (float) CustomOpenTherm::getInt(response); + const float value = (float) CustomOpenTherm::getInt(response); if (value <= 0) { return false; } @@ -2511,7 +2524,7 @@ protected: return false; } - float value = CustomOpenTherm::getFloat(response); + const float value = CustomOpenTherm::getFloat(response); if (value < 0) { return false; } From a9220d9fa19436b30a3212187e43e72d596c04c9 Mon Sep 17 00:00:00 2001 From: Yurii Date: Sat, 13 Dec 2025 06:25:16 +0300 Subject: [PATCH 18/20] refactor: always set temperature to ID 1 --- src/OpenThermTask.h | 100 ++++++++++++++++++++++++++++---------------- src/RegulatorTask.h | 23 +++------- 2 files changed, 71 insertions(+), 52 deletions(-) diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index 131f5ef..0bb7df6 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -171,7 +171,7 @@ protected: vars.master.heating.enabled = this->isReady() && settings.heating.enabled && vars.cascadeControl.input - && (!vars.master.heating.blocking || settings.heating.hysteresis.action != HysteresisAction::DISABLE_HEATING) + && !vars.master.heating.blocking && !vars.master.heating.overheat; // DHW settings @@ -186,7 +186,9 @@ protected: || (settings.opentherm.options.dhwToCh2 && settings.opentherm.options.dhwSupport && settings.dhw.enabled); if (settings.opentherm.options.heatingToCh2) { - vars.master.ch2.targetTemp = vars.master.heating.setpointTemp; + vars.master.ch2.targetTemp = !settings.opentherm.options.nativeOTC + ? vars.master.heating.setpointTemp + : vars.master.heating.targetTemp; } else if (settings.opentherm.options.dhwToCh2) { vars.master.ch2.targetTemp = vars.master.dhw.targetTemp; @@ -1202,9 +1204,12 @@ protected: // Update DHW temp if (vars.master.dhw.enabled) { + // Target dhw temp + const float& targetTemp = vars.master.dhw.targetTemp; + // Converted target dhw temp - float convertedTemp = convertTemp( - vars.master.dhw.targetTemp, + const float convertedTemp = convertTemp( + targetTemp, settings.system.unitSystem, settings.opentherm.unitSystem ); @@ -1216,7 +1221,7 @@ protected: Log.sinfoln( FPSTR(L_OT_DHW), F("Set temp: %.2f (converted: %.2f, response: %.2f)"), - vars.master.dhw.targetTemp, convertedTemp, vars.slave.dhw.targetTemp + targetTemp, convertedTemp, vars.slave.dhw.targetTemp ); } else { @@ -1227,14 +1232,17 @@ protected: // Send indoor temp if AlwaysSendIndoorTemp option is enabled. if (settings.opentherm.options.nativeOTC || settings.opentherm.options.alwaysSendIndoorTemp) { + // Current indoor temp + const float& indoorTemp = vars.master.heating.indoorTemp; + // Converted current indoor temp - float convertedTemp = convertTemp(vars.master.heating.indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem); + const float convertedTemp = convertTemp(indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem); // Set current indoor temp if (this->setRoomTemp(convertedTemp)) { Log.sinfoln( FPSTR(L_OT_HEATING), F("Set current indoor temp: %.2f (converted: %.2f, response: %.2f)"), - vars.master.heating.indoorTemp, convertedTemp, vars.slave.heating.indoorTemp + indoorTemp, convertedTemp, vars.slave.heating.indoorTemp ); } else { @@ -1246,7 +1254,7 @@ protected: if (this->setRoomTempCh2(convertedTemp)) { Log.sinfoln( FPSTR(L_OT_HEATING), F("Set current CH2 indoor temp: %.2f (converted: %.2f, response: %.2f)"), - vars.master.heating.indoorTemp, convertedTemp, vars.slave.ch2.indoorTemp + indoorTemp, convertedTemp, vars.slave.ch2.indoorTemp ); } else { @@ -1257,8 +1265,15 @@ protected: // NativeOTC if (settings.opentherm.options.nativeOTC) { + // Target indoor temp + const float& targetTemp = vars.master.heating.targetTemp; + // Converted target indoor temp - float convertedTemp = convertTemp(vars.master.heating.targetTemp, settings.system.unitSystem, settings.opentherm.unitSystem); + const float convertedTemp = convertTemp( + targetTemp, + settings.system.unitSystem, + settings.opentherm.unitSystem + ); // Set target indoor temp if (this->needSetHeatingTemp(convertedTemp)) { @@ -1267,7 +1282,7 @@ protected: Log.sinfoln( FPSTR(L_OT_HEATING), F("Set target indoor temp: %.2f (converted: %.2f, response: %.2f)"), - vars.master.heating.targetTemp, convertedTemp, vars.slave.heating.targetTemp + targetTemp, convertedTemp, vars.slave.heating.targetTemp ); } else { @@ -1282,7 +1297,7 @@ protected: Log.sinfoln( FPSTR(L_OT_HEATING), F("Set target CH2 indoor temp: %.2f (converted: %.2f, response: %.2f)"), - vars.master.heating.targetTemp, convertedTemp, vars.slave.ch2.targetTemp + targetTemp, convertedTemp, vars.slave.ch2.targetTemp ); } else { @@ -1291,10 +1306,22 @@ protected: } } - // Normal heating control - if (!settings.opentherm.options.nativeOTC && vars.master.heating.enabled) { + // Set heating temp + { + // Target heating temp + float targetTemp = 0.0f; + if (vars.master.heating.enabled) { + targetTemp = !settings.opentherm.options.nativeOTC + ? vars.master.heating.setpointTemp + : vars.master.heating.targetTemp; + } + // Converted target heating temp - float convertedTemp = convertTemp(vars.master.heating.setpointTemp, settings.system.unitSystem, settings.opentherm.unitSystem); + const float convertedTemp = convertTemp( + targetTemp, + settings.system.unitSystem, + settings.opentherm.unitSystem + ); if (this->needSetHeatingTemp(convertedTemp)) { // Set max heating temp @@ -1302,13 +1329,13 @@ protected: if (this->setMaxHeatingTemp(convertedTemp)) { Log.sinfoln( FPSTR(L_OT_HEATING), F("Set max heating temp: %.2f (converted: %.2f)"), - vars.master.heating.setpointTemp, convertedTemp + targetTemp, convertedTemp ); } else { Log.swarningln( FPSTR(L_OT_HEATING), F("Failed set max heating temp: %.2f (converted: %.2f)"), - vars.master.heating.setpointTemp, convertedTemp + targetTemp, convertedTemp ); } } @@ -1319,7 +1346,7 @@ protected: Log.sinfoln( FPSTR(L_OT_HEATING), F("Set target temp: %.2f (converted: %.2f, response: %.2f)"), - vars.master.heating.setpointTemp, convertedTemp, vars.slave.heating.targetTemp + targetTemp, convertedTemp, vars.slave.heating.targetTemp ); } else { @@ -1329,27 +1356,30 @@ protected: } // Set CH2 temp - if (!settings.opentherm.options.nativeOTC && vars.master.ch2.enabled) { - if (settings.opentherm.options.heatingToCh2 || settings.opentherm.options.dhwToCh2) { - // Converted target CH2 temp - float convertedTemp = convertTemp( - vars.master.ch2.targetTemp, - settings.system.unitSystem, - settings.opentherm.unitSystem - ); + if (settings.opentherm.options.heatingToCh2 || settings.opentherm.options.dhwToCh2) { + // Target CH2 heating temp + const float targetTemp = vars.master.ch2.enabled + ? vars.master.ch2.targetTemp + : 0.0f; - if (this->needSetCh2Temp(convertedTemp)) { - if (this->setCh2Temp(convertedTemp)) { - this->ch2SetTempTime = millis(); + // Converted target CH2 temp + const float convertedTemp = convertTemp( + targetTemp, + settings.system.unitSystem, + settings.opentherm.unitSystem + ); - Log.sinfoln( - FPSTR(L_OT_CH2), F("Set temp: %.2f (converted: %.2f, response: %.2f)"), - vars.master.ch2.targetTemp, convertedTemp, vars.slave.ch2.targetTemp - ); + if (this->needSetCh2Temp(convertedTemp)) { + if (this->setCh2Temp(convertedTemp)) { + this->ch2SetTempTime = millis(); - } else { - Log.swarningln(FPSTR(L_OT_CH2), F("Failed set temp")); - } + Log.sinfoln( + FPSTR(L_OT_CH2), F("Set temp: %.2f (converted: %.2f, response: %.2f)"), + targetTemp, convertedTemp, vars.slave.ch2.targetTemp + ); + + } else { + Log.swarningln(FPSTR(L_OT_CH2), F("Failed set temp")); } } } diff --git a/src/RegulatorTask.h b/src/RegulatorTask.h index 30fbb6d..a36137b 100644 --- a/src/RegulatorTask.h +++ b/src/RegulatorTask.h @@ -57,23 +57,12 @@ protected: this->turbo(); this->hysteresis(); - if (vars.master.heating.blocking && settings.heating.hysteresis.action == HysteresisAction::SET_ZERO_TARGET) { - vars.master.heating.targetTemp = 0.0f; - vars.master.heating.setpointTemp = 0.0f; - - // tick if PID enabled - if (settings.pid.enabled) { - this->getHeatingSetpointTemp(); - } - - } else { - vars.master.heating.targetTemp = settings.heating.target; - vars.master.heating.setpointTemp = roundf(constrain( - this->getHeatingSetpointTemp(), - this->getHeatingMinSetpointTemp(), - this->getHeatingMaxSetpointTemp() - ), 0); - } + vars.master.heating.targetTemp = settings.heating.target; + vars.master.heating.setpointTemp = roundf(constrain( + this->getHeatingSetpointTemp(), + this->getHeatingMinSetpointTemp(), + this->getHeatingMaxSetpointTemp() + ), 0); Sensors::setValueByType( Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.setpointTemp, From 3adfabdf40b87658445c0abc87aeb99306afd6a3 Mon Sep 17 00:00:00 2001 From: Yurii Date: Sat, 20 Dec 2025 12:06:06 +0300 Subject: [PATCH 19/20] chore: bump `pioarduino/platform-espressif32` from 3.3.4 to 3.3.5 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 2e7fc7a..36b93a8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -92,7 +92,7 @@ check_flags = ${env.check_flags} ;platform_packages = ; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5 ; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.34/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.35/platform-espressif32.zip platform_packages = ${env.platform_packages} board_build.partitions = esp32_partitions.csv lib_deps = From 56a8574abaefa6e119c34f7c5b5771582c55c6a6 Mon Sep 17 00:00:00 2001 From: Yurii Date: Sat, 20 Dec 2025 12:06:25 +0300 Subject: [PATCH 20/20] chore: bump version to 1.6.0 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 36b93a8..00d9276 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,7 @@ extra_configs = secrets.default.ini core_dir = .pio [env] -version = 1.5.6 +version = 1.6.0 framework = arduino lib_deps = bblanchon/ArduinoJson@^7.4.2