From 0965538cd65e91adf10dc9b8ebc2e0cee51cc9ef Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Tue, 18 Nov 2025 11:09:23 +0100 Subject: [PATCH 01/13] feat: add alfea excellia PAC and ECS --- custom_components/cozytouch/capability.py | 14 +++++++-- custom_components/cozytouch/const.py | 2 ++ custom_components/cozytouch/icons.json | 13 +++++++- custom_components/cozytouch/model.py | 36 +++++++++++++++++++++-- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/custom_components/cozytouch/capability.py b/custom_components/cozytouch/capability.py index 16c1f4e..95fa78a 100644 --- a/custom_components/cozytouch/capability.py +++ b/custom_components/cozytouch/capability.py @@ -50,7 +50,7 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s elif modelInfos["type"] == CozytouchDeviceType.HEAT_PUMP: if capabilityId in (1, 7): capability["name"] = "heat_pump_z1" - capability["targetCapabilityId"] = 17 + #capability["targetCapabilityId"] = 17 if modelInfos.get("currentTemperatureAvailableZ1", True): capability["currentValueCapabilityId"] = 117 else: @@ -65,6 +65,9 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s # capability["lowestValueCapabilityId"] = 172 # capability["highestValueCapabilityId"] = 171 + capability["progOverrideCapabilityId"] = 157 + capability["progOverrideTotalTimeCapabilityId"] = 158 + capability["progOverrideTimeCapabilityId"] = 159 capability.pop("lowestValueCapabilityId") capability.pop("highestValueCapabilityId") capability["icon"] = "mdi:heat-pump" @@ -361,7 +364,7 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["category"] = "diag" capability["icon"] = "mdi:radio-tower" - elif capabilityId == 172: + elif capabilityId in (17, 172): capability["name"] = "away_mode_temperature" capability["type"] = "temperature_adjustment_number" capability["category"] = "sensor" @@ -740,6 +743,13 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["lowest_value"] = 5 capability["highest_value"] = 60 capability["step"] = 5 + + elif capabilityId == 105636: + capability["name"] = "heating_mode" + capability["type"] = "select" + capability["category"] = "sensor" + capability["icon"] = "mdi:water-boiler" + capability["modelList"] = "HeatingModes" elif capabilityId == 105906: capability["name"] = "Target 105906" diff --git a/custom_components/cozytouch/const.py b/custom_components/cozytouch/const.py index 14bb08f..f2814e3 100644 --- a/custom_components/cozytouch/const.py +++ b/custom_components/cozytouch/const.py @@ -30,4 +30,6 @@ class CozytouchCapabilityVariableType(IntEnum): HEATING_MODE_OFF = "off" HEATING_MODE_MANUAL = "manual" HEATING_MODE_ECO_PLUS = "eco_plus" +HEATING_MODE_ECO = "eco" HEATING_MODE_PROG = "prog" +HEATING_MODE_COMFORT = "comfort" diff --git a/custom_components/cozytouch/icons.json b/custom_components/cozytouch/icons.json index 0b35b82..b11d840 100644 --- a/custom_components/cozytouch/icons.json +++ b/custom_components/cozytouch/icons.json @@ -52,7 +52,18 @@ } } } + }, + "heat_pump_z1": { + "state_attributes": { + "preset_mode": { + "state": { + "basic": "mdi:thermometer", + "prog": "mdi:clock-outline", + "override": "mdi:timer-sand-complete" + } + } + } } } } -} +} \ No newline at end of file diff --git a/custom_components/cozytouch/model.py b/custom_components/cozytouch/model.py index aa9df2c..fee9c60 100644 --- a/custom_components/cozytouch/model.py +++ b/custom_components/cozytouch/model.py @@ -37,6 +37,8 @@ SWING_MODE_MIDDLE_DOWN, SWING_MODE_MIDDLE_UP, SWING_MODE_UP, + HEATING_MODE_ECO, + HEATING_MODE_COMFORT, ) @@ -185,7 +187,24 @@ def get_model_infos(modelId: int, zoneName: str | None = None): 0: HVACMode.OFF, } - elif modelId >= 557 and modelId <= 561: + # Alfea Excellia PAC + elif modelId == 557: + name = "PAC " + if zoneName is not None: + modelInfos["name"] = name + "(" + zoneName + ")" + else: + modelInfos["name"] = name + "(#" + str(modelId - 556) + ")" + + modelInfos["type"] = CozytouchDeviceType.HEAT_PUMP + modelInfos["currentTemperatureAvailableZ1"] = True + + modelInfos["HVACModes"] = { + 0: HVACMode.OFF, + 1: HVACMode.AUTO, + 4: HVACMode.HEAT, + } + + elif modelId >= 558 and modelId <= 561: name = "Air Conditioner " if zoneName is not None: modelInfos["name"] = name + "(" + zoneName + ")" @@ -238,7 +257,20 @@ def get_model_infos(modelId: int, zoneName: str | None = None): 0: HVACMode.OFF, } - elif modelId in (1369, 1376): + elif modelId == 1376: + modelInfos["name"] = "ECS Alfea Excellia" + modelInfos["type"] = CozytouchDeviceType.WATER_HEATER + modelInfos["HVACModes"] = { + 0: HVACMode.OFF, + 1: HVACMode.HEAT, + } + + modelInfos["HeatingModes"] = { + 0: HEATING_MODE_ECO, + 1: HEATING_MODE_COMFORT + } + + elif modelId in (1369, 1375): modelInfos["name"] = "Calypso Split" modelInfos["type"] = CozytouchDeviceType.WATER_HEATER modelInfos["HVACModes"] = { From 71d8ade571028d93f6b8a2a64e58b7ad7a0ea094 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 19 Nov 2025 16:07:01 +0100 Subject: [PATCH 02/13] feat: add Alfea Excellia modelId --- custom_components/cozytouch/model.py | 32 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/custom_components/cozytouch/model.py b/custom_components/cozytouch/model.py index fee9c60..178a5fa 100644 --- a/custom_components/cozytouch/model.py +++ b/custom_components/cozytouch/model.py @@ -187,16 +187,15 @@ def get_model_infos(modelId: int, zoneName: str | None = None): 0: HVACMode.OFF, } - # Alfea Excellia PAC elif modelId == 557: - name = "PAC " + name = "Thermostat Navilink Connect 228" if zoneName is not None: - modelInfos["name"] = name + "(" + zoneName + ")" - else: - modelInfos["name"] = name + "(#" + str(modelId - 556) + ")" + modelInfos["name"] = name + " (" + zoneName + ")" - modelInfos["type"] = CozytouchDeviceType.HEAT_PUMP + modelInfos["type"] = CozytouchDeviceType.THERMOSTAT modelInfos["currentTemperatureAvailableZ1"] = True + modelInfos["currentTemperatureAvailableZ2"] = False + modelInfos["overrideModeAvailable"] = True modelInfos["HVACModes"] = { 0: HVACMode.OFF, @@ -322,6 +321,16 @@ def get_model_infos(modelId: int, zoneName: str | None = None): 4: HVACMode.HEAT, } + elif modelId == 1391: + name = "Alfea Excellia Generator" + if zoneName is not None: + modelInfos["name"] = name + " (" + zoneName + ")" + + modelInfos["type"] = CozytouchDeviceType.UNKNOWN + modelInfos["HVACModes"] = { + 0: HVACMode.OFF, + } + elif modelId == 1444: modelInfos["name"] = "Naema 3 Micro 25" modelInfos["type"] = CozytouchDeviceType.GAZ_BOILER @@ -453,6 +462,17 @@ def get_model_infos(modelId: int, zoneName: str | None = None): 4: HEATING_MODE_PROG, } + elif modelId == 1693: + name = "Alfea Excellia User Interface" + if zoneName is not None: + modelInfos["name"] = name + " (" + zoneName + ")" + + modelInfos["type"] = CozytouchDeviceType.UNKNOWN + modelInfos["HVACModes"] = { + 0: HVACMode.OFF, + } + modelInfos["HVACModesCapabilityId"] = {} + else: modelInfos["name"] = "Unknown product (" + str(modelId) + ")" modelInfos["type"] = CozytouchDeviceType.UNKNOWN From becd46cee07003a45bbc10c013c48881fe52ab43 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 19 Nov 2025 16:08:11 +0100 Subject: [PATCH 03/13] feat: rollback changes on heatpump and some known capabilities --- custom_components/cozytouch/capability.py | 32 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/custom_components/cozytouch/capability.py b/custom_components/cozytouch/capability.py index 95fa78a..55f8c9d 100644 --- a/custom_components/cozytouch/capability.py +++ b/custom_components/cozytouch/capability.py @@ -15,6 +15,7 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s if ( capabilityId in (1, 2, 7, 8) and capabilityId in modelInfos["HVACModesCapabilityId"] + and float(capabilityValue) < 3000 ): # Default Ids capability["targetCapabilityId"] = 40 @@ -50,7 +51,7 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s elif modelInfos["type"] == CozytouchDeviceType.HEAT_PUMP: if capabilityId in (1, 7): capability["name"] = "heat_pump_z1" - #capability["targetCapabilityId"] = 17 + capability["targetCapabilityId"] = 17 if modelInfos.get("currentTemperatureAvailableZ1", True): capability["currentValueCapabilityId"] = 117 else: @@ -65,12 +66,17 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s # capability["lowestValueCapabilityId"] = 172 # capability["highestValueCapabilityId"] = 171 - capability["progOverrideCapabilityId"] = 157 - capability["progOverrideTotalTimeCapabilityId"] = 158 - capability["progOverrideTimeCapabilityId"] = 159 capability.pop("lowestValueCapabilityId") capability.pop("highestValueCapabilityId") capability["icon"] = "mdi:heat-pump" + elif modelInfos["type"] == CozytouchDeviceType.THERMOSTAT: + capability["name"] = "heat" + capability["progOverrideCapabilityId"] = 157 + capability["progOverrideTotalTimeCapabilityId"] = 158 + capability["progOverrideTimeCapabilityId"] = 159 + capability["lowestCoolValueCapabilityId"] = 162 + capability["highestCoolValueCapabilityId"] = 163 + capability["icon"] = "mdi:thermostat" else: capability["name"] = "heat" @@ -177,6 +183,12 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["displayed_unit_of_measurement"] = UnitOfEnergy.KILO_WATT_HOUR capability["category"] = "sensor" + elif capabilityId == 60: + capability["name"] = "total_power_consumption" + capability["type"] = "energy" + capability["displayed_unit_of_measurement"] = UnitOfEnergy.KILO_WATT_HOUR + capability["category"] = "sensor" + elif capabilityId == 86: capability["name"] = "domestic_hot_water" capability["type"] = "switch" @@ -348,6 +360,18 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["highest_value"] = 28 capability["step"] = 0.5 + elif capabilityId == 162: + capability["name"] = "temperature_min" + capability["type"] = "temperature" + capability["category"] = "sensor" + capability["icon"] = "mdi:thermometer-chevron-down" + + elif capabilityId == 163: + capability["name"] = "temperature_max" + capability["type"] = "temperature" + capability["category"] = "sensor" + capability["icon"] = "mdi:thermometer-chevron-up" + elif capabilityId == 165: capability["name"] = "boost_mode" capability["type"] = "switch" From 5f6955d3120017a28ae98f5386856a02c759a9c1 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 19 Nov 2025 16:36:17 +0100 Subject: [PATCH 04/13] fix: fail --- custom_components/cozytouch/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/cozytouch/model.py b/custom_components/cozytouch/model.py index 178a5fa..10c4900 100644 --- a/custom_components/cozytouch/model.py +++ b/custom_components/cozytouch/model.py @@ -269,7 +269,7 @@ def get_model_infos(modelId: int, zoneName: str | None = None): 1: HEATING_MODE_COMFORT } - elif modelId in (1369, 1375): + elif modelId == 1369: modelInfos["name"] = "Calypso Split" modelInfos["type"] = CozytouchDeviceType.WATER_HEATER modelInfos["HVACModes"] = { From 50acc4ae09b0ec5896e6dbfb49a335bc04e2556f Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 19 Nov 2025 22:52:51 +0100 Subject: [PATCH 05/13] feat: clean useless entities for alfea excellia --- custom_components/cozytouch/capability.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/custom_components/cozytouch/capability.py b/custom_components/cozytouch/capability.py index 55f8c9d..bb19b47 100644 --- a/custom_components/cozytouch/capability.py +++ b/custom_components/cozytouch/capability.py @@ -156,21 +156,21 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["lowestValueCapabilityId"] = 160 capability["highestValueCapabilityId"] = 161 - elif capabilityId == 44: + elif capabilityId == 44 and modelId not in (1693,): capability["name"] = "ch_power_consumption" capability["type"] = "energy" capability["displayed_unit_of_measurement"] = UnitOfEnergy.KILO_WATT_HOUR capability["category"] = "sensor" capability["icon"] = "mdi:radiator" - elif capabilityId == 45: + elif capabilityId == 45 and modelId not in (1693,): capability["name"] = "dhw_power_consumption" capability["type"] = "energy" capability["displayed_unit_of_measurement"] = UnitOfEnergy.KILO_WATT_HOUR capability["category"] = "sensor" capability["icon"] = "mdi:faucet" - elif capabilityId == 46: + elif capabilityId == 46 and modelId not in (1693,): capability["name"] = "total_power_consumption" capability["type"] = "energy" capability["displayed_unit_of_measurement"] = UnitOfEnergy.KILO_WATT_HOUR @@ -195,13 +195,20 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["category"] = "sensor" capability["icon"] = "mdi:faucet" - elif capabilityId == 87: + elif capabilityId == 87 and modelId not in (1376,): capability["name"] = "heating_mode" capability["type"] = "select" capability["category"] = "sensor" capability["icon"] = "mdi:water-boiler" capability["modelList"] = "HeatingModes" + elif capabilityId == 87 and modelId in (1376,): + capability["name"] = "heating_mode" + capability["type"] = "string" + capability["category"] = "sensor" + capability["icon"] = "mdi:water-boiler" + capability["modelList"] = "HeatingModes" + elif capabilityId == 88: capability["name"] = "model_name" capability["type"] = "string" @@ -497,7 +504,7 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["category"] = "diag" capability["icon"] = "mdi:wifi" - elif capabilityId in (222, 226): + elif capabilityId in (222, 226) and modelId not in (1376,): capability["name"] = "away_mode" capability["name_0"] = "away_mode_start" capability["name_1"] = "away_mode_stop" From a33ec70c5ae98581e5969b836869a5824a16cb84 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 26 Nov 2025 10:54:08 +0100 Subject: [PATCH 06/13] feat: modelId 557 add effective temperature --- custom_components/cozytouch/capability.py | 18 +++++++++---- custom_components/cozytouch/strings.json | 25 ++++++++++--------- .../cozytouch/translations/en.json | 25 ++++++++++--------- .../cozytouch/translations/fr.json | 25 ++++++++++--------- 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/custom_components/cozytouch/capability.py b/custom_components/cozytouch/capability.py index bb19b47..8cc3afd 100644 --- a/custom_components/cozytouch/capability.py +++ b/custom_components/cozytouch/capability.py @@ -80,6 +80,10 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s else: capability["name"] = "heat" + # Add effective target temperature for model 557 + if modelId == 557: + capability["effectiveTargetTemperatureId"] = 17 + capability["type"] = "climate" capability["category"] = "sensor" @@ -396,14 +400,18 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["icon"] = "mdi:radio-tower" elif capabilityId in (17, 172): - capability["name"] = "away_mode_temperature" - capability["type"] = "temperature_adjustment_number" capability["category"] = "sensor" - capability["lowestValueCapabilityId"] = 160 - capability["highestValueCapabilityId"] = 161 + if modelId in (557,): + capability["name"] = "effective_target_temperature" + capability["type"] = "temperature" + else: + capability["name"] = "away_mode_temperature" + capability["type"] = "temperature_adjustment_number" + capability["lowestValueCapabilityId"] = 160 + capability["highestValueCapabilityId"] = 161 elif capabilityId == 177: - if modelInfos["type"] == CozytouchDeviceType.GAZ_BOILER: + if modelInfos["type"] == CozytouchDeviceType.GAZ_BOILER or modelId in (557,): return {} capability["name"] = "target_cool_temperature" diff --git a/custom_components/cozytouch/strings.json b/custom_components/cozytouch/strings.json index be2eca3..b753dc2 100644 --- a/custom_components/cozytouch/strings.json +++ b/custom_components/cozytouch/strings.json @@ -139,17 +139,17 @@ "away_mode_stop": { "name": "Away Mode Stop" } }, "number": { - "away_mode_temperature": { "name": "Away Mode Temperature" }, - "boost_timeout_max": { "name": "Boost Timeout Max." }, - "override_total_time": { "name": "Override Total Time" }, - "override_total_time_z1": { "name": "Override Total Time Z1" }, - "override_total_time_z2": { "name": "Override Total Time Z2" }, - "target_cool_temperature": { "name": "Target Cool Temperature" }, - "target_temperature": { "name": "Target Temperature" }, - "target_temperature_dhw": { "name": "Target Temperature DHW" }, - "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, - "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, - "temperature_adjustment_max": { "name": "Maximum Temperature" } + "away_mode_temperature": { "name": "Away Mode Temperature" }, + "boost_timeout_max": { "name": "Boost Timeout Max." }, + "override_total_time": { "name": "Override Total Time" }, + "override_total_time_z1": { "name": "Override Total Time Z1" }, + "override_total_time_z2": { "name": "Override Total Time Z2" }, + "target_cool_temperature": { "name": "Target Cool Temperature" }, + "target_temperature": { "name": "Target Temperature" }, + "target_temperature_dhw": { "name": "Target Temperature DHW" }, + "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, + "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, + "temperature_adjustment_max": { "name": "Maximum Temperature" } }, "select": { "heating_mode": @@ -266,7 +266,8 @@ "wifi_signal": { "name": "Wifi Signal" }, "wifi_ssid": { "name": "Wifi SSID" }, "zone_1": { "name": "Zone 1" }, - "zone_2": { "name": "Zone 2" } + "zone_2": { "name": "Zone 2" }, + "effective_target_temperature": { "name": "Effective Target Temperature" } }, "switch": { "away_mode": { "name": "Away Mode" }, diff --git a/custom_components/cozytouch/translations/en.json b/custom_components/cozytouch/translations/en.json index be2eca3..b753dc2 100644 --- a/custom_components/cozytouch/translations/en.json +++ b/custom_components/cozytouch/translations/en.json @@ -139,17 +139,17 @@ "away_mode_stop": { "name": "Away Mode Stop" } }, "number": { - "away_mode_temperature": { "name": "Away Mode Temperature" }, - "boost_timeout_max": { "name": "Boost Timeout Max." }, - "override_total_time": { "name": "Override Total Time" }, - "override_total_time_z1": { "name": "Override Total Time Z1" }, - "override_total_time_z2": { "name": "Override Total Time Z2" }, - "target_cool_temperature": { "name": "Target Cool Temperature" }, - "target_temperature": { "name": "Target Temperature" }, - "target_temperature_dhw": { "name": "Target Temperature DHW" }, - "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, - "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, - "temperature_adjustment_max": { "name": "Maximum Temperature" } + "away_mode_temperature": { "name": "Away Mode Temperature" }, + "boost_timeout_max": { "name": "Boost Timeout Max." }, + "override_total_time": { "name": "Override Total Time" }, + "override_total_time_z1": { "name": "Override Total Time Z1" }, + "override_total_time_z2": { "name": "Override Total Time Z2" }, + "target_cool_temperature": { "name": "Target Cool Temperature" }, + "target_temperature": { "name": "Target Temperature" }, + "target_temperature_dhw": { "name": "Target Temperature DHW" }, + "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, + "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, + "temperature_adjustment_max": { "name": "Maximum Temperature" } }, "select": { "heating_mode": @@ -266,7 +266,8 @@ "wifi_signal": { "name": "Wifi Signal" }, "wifi_ssid": { "name": "Wifi SSID" }, "zone_1": { "name": "Zone 1" }, - "zone_2": { "name": "Zone 2" } + "zone_2": { "name": "Zone 2" }, + "effective_target_temperature": { "name": "Effective Target Temperature" } }, "switch": { "away_mode": { "name": "Away Mode" }, diff --git a/custom_components/cozytouch/translations/fr.json b/custom_components/cozytouch/translations/fr.json index bfa1e76..d795dcd 100644 --- a/custom_components/cozytouch/translations/fr.json +++ b/custom_components/cozytouch/translations/fr.json @@ -139,17 +139,17 @@ "away_mode_stop": { "name": "Absence Fin" } }, "number": { - "away_mode_temperature": { "name": "Température Absence" }, - "boost_timeout_max": { "name": "Temporisation Max. Boost" }, - "override_total_time": { "name": "Durée Délégation" }, - "override_total_time_z1": { "name": "Durée Délégation Z1" }, - "override_total_time_z2": { "name": "Durée Délégation Z2" }, - "target_cool_temperature": { "name": "Température Consigne Cool" }, - "target_temperature": { "name": "Température Consigne" }, - "target_temperature_dhw": { "name": "Température Consigne DHW" }, - "target_temperature_eco_z1": { "name": "Température Consigne Eco Z1" }, - "target_temperature_eco_z2": { "name": "Température Consigne Eco Z2" }, - "temperature_adjustment_max": { "name": "Température Maximale" } + "away_mode_temperature": { "name": "Température Absence" }, + "boost_timeout_max": { "name": "Temporisation Max. Boost" }, + "override_total_time": { "name": "Durée Délégation" }, + "override_total_time_z1": { "name": "Durée Délégation Z1" }, + "override_total_time_z2": { "name": "Durée Délégation Z2" }, + "target_cool_temperature": { "name": "Température Consigne Cool" }, + "target_temperature": { "name": "Température Consigne" }, + "target_temperature_dhw": { "name": "Température Consigne DHW" }, + "target_temperature_eco_z1": { "name": "Température Consigne Eco Z1" }, + "target_temperature_eco_z2": { "name": "Température Consigne Eco Z2" }, + "temperature_adjustment_max": { "name": "Température Maximale" } }, "select": { "heating_mode": @@ -266,7 +266,8 @@ "wifi_signal": { "name": "Signal Wifi" }, "wifi_ssid": { "name": "Wifi SSID" }, "zone_1": { "name": "Zone 1" }, - "zone_2": { "name": "Zone 2" } + "zone_2": { "name": "Zone 2" }, + "effective_target_temperature": { "name": "Température Cible Effective" } }, "switch": { "away_mode": { "name": "Absence" }, From 644a07b9746e9d308fa2f15bc28e2d000d17fed7 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 26 Nov 2025 10:54:42 +0100 Subject: [PATCH 07/13] feat: add hvac_action --- custom_components/cozytouch/climate.py | 42 +++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/custom_components/cozytouch/climate.py b/custom_components/cozytouch/climate.py index 8143a4d..a5aa9af 100644 --- a/custom_components/cozytouch/climate.py +++ b/custom_components/cozytouch/climate.py @@ -8,6 +8,7 @@ ClimateEntity, ClimateEntityFeature, HVACMode, + HVACAction, ) from homeassistant.components.climate.const import ( PRESET_ACTIVITY, @@ -363,7 +364,46 @@ def current_temperature(self): def target_temperature(self): """Return target temperature.""" return self._native_value - + + @property + def extra_state_attributes(self): + """Return the computed target temperature.""" + if "effectiveTargetTemperatureId" in self._capability: + effective_temp = self.coordinator.get_capability_value( + self._capability["effectiveTargetTemperatureId"] + ) + if effective_temp is not None: + return { + "effective_target_temperature": float(effective_temp), + } + return None + + @property + def hvac_action(self): + """Return the current HVAC action.""" + if self._attr_hvac_mode == HVACMode.OFF: + return HVACAction.OFF + + # Get effective target temperature + effective_temp = None + if "effectiveTargetTemperatureId" in self._capability: + temp_value = self.coordinator.get_capability_value( + self._capability["effectiveTargetTemperatureId"] + ) + if temp_value is not None: + effective_temp = float(temp_value) + + # If no effective temp, fall back to target temperature + if effective_temp is None: + effective_temp = self._native_value + + # Determine action based on current vs target temperature + if self._current_value is not None and effective_temp is not None: + if self._current_value < effective_temp - 0.2: + return HVACAction.HEATING + + return HVACAction.IDLE + async def async_set_temperature(self, **kwargs): """Set new target temperature.""" temperature = kwargs.get("temperature") From bc136d3f90225c31bfb0420056deb91ddf02b821 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 26 Nov 2025 10:55:35 +0100 Subject: [PATCH 08/13] fix: schedule temperature as float --- custom_components/cozytouch/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/cozytouch/sensor.py b/custom_components/cozytouch/sensor.py index dbcfd1a..66b6f15 100644 --- a/custom_components/cozytouch/sensor.py +++ b/custom_components/cozytouch/sensor.py @@ -723,7 +723,7 @@ def get_value(self) -> str: if strValue != "": strValue += " / " strValue += "%02d:%02d " % (hours, minutes) - strValue += " %d°C" % (prog[1]) + strValue += " %g°C" % (prog[1]) return strValue From fb63f3683dd3853c3baa550e6b1c25733fdd7ed4 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Tue, 18 Nov 2025 11:09:23 +0100 Subject: [PATCH 09/13] feat: add alfea excellia PAC and ECS --- custom_components/cozytouch/capability.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/custom_components/cozytouch/capability.py b/custom_components/cozytouch/capability.py index 8cc3afd..5e988ec 100644 --- a/custom_components/cozytouch/capability.py +++ b/custom_components/cozytouch/capability.py @@ -400,6 +400,8 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["icon"] = "mdi:radio-tower" elif capabilityId in (17, 172): + capability["name"] = "away_mode_temperature" + capability["type"] = "temperature_adjustment_number" capability["category"] = "sensor" if modelId in (557,): capability["name"] = "effective_target_temperature" From 058c5950b70dbf2b7d2343b26c9719d924a7dfec Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Wed, 26 Nov 2025 11:07:02 +0100 Subject: [PATCH 10/13] fix: revert useless spacing in json --- custom_components/cozytouch/strings.json | 22 +++++++++---------- .../cozytouch/translations/en.json | 22 +++++++++---------- .../cozytouch/translations/fr.json | 22 +++++++++---------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/custom_components/cozytouch/strings.json b/custom_components/cozytouch/strings.json index b753dc2..c2e2efb 100644 --- a/custom_components/cozytouch/strings.json +++ b/custom_components/cozytouch/strings.json @@ -139,17 +139,17 @@ "away_mode_stop": { "name": "Away Mode Stop" } }, "number": { - "away_mode_temperature": { "name": "Away Mode Temperature" }, - "boost_timeout_max": { "name": "Boost Timeout Max." }, - "override_total_time": { "name": "Override Total Time" }, - "override_total_time_z1": { "name": "Override Total Time Z1" }, - "override_total_time_z2": { "name": "Override Total Time Z2" }, - "target_cool_temperature": { "name": "Target Cool Temperature" }, - "target_temperature": { "name": "Target Temperature" }, - "target_temperature_dhw": { "name": "Target Temperature DHW" }, - "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, - "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, - "temperature_adjustment_max": { "name": "Maximum Temperature" } + "away_mode_temperature": { "name": "Away Mode Temperature" }, + "boost_timeout_max": { "name": "Boost Timeout Max." }, + "override_total_time": { "name": "Override Total Time" }, + "override_total_time_z1": { "name": "Override Total Time Z1" }, + "override_total_time_z2": { "name": "Override Total Time Z2" }, + "target_cool_temperature": { "name": "Target Cool Temperature" }, + "target_temperature": { "name": "Target Temperature" }, + "target_temperature_dhw": { "name": "Target Temperature DHW" }, + "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, + "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, + "temperature_adjustment_max": { "name": "Maximum Temperature" } }, "select": { "heating_mode": diff --git a/custom_components/cozytouch/translations/en.json b/custom_components/cozytouch/translations/en.json index b753dc2..c2e2efb 100644 --- a/custom_components/cozytouch/translations/en.json +++ b/custom_components/cozytouch/translations/en.json @@ -139,17 +139,17 @@ "away_mode_stop": { "name": "Away Mode Stop" } }, "number": { - "away_mode_temperature": { "name": "Away Mode Temperature" }, - "boost_timeout_max": { "name": "Boost Timeout Max." }, - "override_total_time": { "name": "Override Total Time" }, - "override_total_time_z1": { "name": "Override Total Time Z1" }, - "override_total_time_z2": { "name": "Override Total Time Z2" }, - "target_cool_temperature": { "name": "Target Cool Temperature" }, - "target_temperature": { "name": "Target Temperature" }, - "target_temperature_dhw": { "name": "Target Temperature DHW" }, - "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, - "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, - "temperature_adjustment_max": { "name": "Maximum Temperature" } + "away_mode_temperature": { "name": "Away Mode Temperature" }, + "boost_timeout_max": { "name": "Boost Timeout Max." }, + "override_total_time": { "name": "Override Total Time" }, + "override_total_time_z1": { "name": "Override Total Time Z1" }, + "override_total_time_z2": { "name": "Override Total Time Z2" }, + "target_cool_temperature": { "name": "Target Cool Temperature" }, + "target_temperature": { "name": "Target Temperature" }, + "target_temperature_dhw": { "name": "Target Temperature DHW" }, + "target_temperature_eco_z1": { "name": "Target Temperature Eco Z1" }, + "target_temperature_eco_z2": { "name": "Target Temperature Eco Z2" }, + "temperature_adjustment_max": { "name": "Maximum Temperature" } }, "select": { "heating_mode": diff --git a/custom_components/cozytouch/translations/fr.json b/custom_components/cozytouch/translations/fr.json index d795dcd..075d192 100644 --- a/custom_components/cozytouch/translations/fr.json +++ b/custom_components/cozytouch/translations/fr.json @@ -139,17 +139,17 @@ "away_mode_stop": { "name": "Absence Fin" } }, "number": { - "away_mode_temperature": { "name": "Température Absence" }, - "boost_timeout_max": { "name": "Temporisation Max. Boost" }, - "override_total_time": { "name": "Durée Délégation" }, - "override_total_time_z1": { "name": "Durée Délégation Z1" }, - "override_total_time_z2": { "name": "Durée Délégation Z2" }, - "target_cool_temperature": { "name": "Température Consigne Cool" }, - "target_temperature": { "name": "Température Consigne" }, - "target_temperature_dhw": { "name": "Température Consigne DHW" }, - "target_temperature_eco_z1": { "name": "Température Consigne Eco Z1" }, - "target_temperature_eco_z2": { "name": "Température Consigne Eco Z2" }, - "temperature_adjustment_max": { "name": "Température Maximale" } + "away_mode_temperature": { "name": "Température Absence" }, + "boost_timeout_max": { "name": "Temporisation Max. Boost" }, + "override_total_time": { "name": "Durée Délégation" }, + "override_total_time_z1": { "name": "Durée Délégation Z1" }, + "override_total_time_z2": { "name": "Durée Délégation Z2" }, + "target_cool_temperature": { "name": "Température Consigne Cool" }, + "target_temperature": { "name": "Température Consigne" }, + "target_temperature_dhw": { "name": "Température Consigne DHW" }, + "target_temperature_eco_z1": { "name": "Température Consigne Eco Z1" }, + "target_temperature_eco_z2": { "name": "Température Consigne Eco Z2" }, + "temperature_adjustment_max": { "name": "Température Maximale" } }, "select": { "heating_mode": From 772dfb8f20c60afc178d86d70f77e8caf1b69050 Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Thu, 27 Nov 2025 16:38:36 +0100 Subject: [PATCH 11/13] refactor: rename effective temp as temperature_setpoint --- custom_components/cozytouch/capability.py | 4 +-- custom_components/cozytouch/climate.py | 28 +++++++++---------- custom_components/cozytouch/strings.json | 3 +- .../cozytouch/translations/en.json | 3 +- .../cozytouch/translations/fr.json | 5 ++-- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/custom_components/cozytouch/capability.py b/custom_components/cozytouch/capability.py index 5e988ec..94263dd 100644 --- a/custom_components/cozytouch/capability.py +++ b/custom_components/cozytouch/capability.py @@ -82,7 +82,7 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s # Add effective target temperature for model 557 if modelId == 557: - capability["effectiveTargetTemperatureId"] = 17 + capability["setpointTemperatureId"] = 17 capability["type"] = "climate" capability["category"] = "sensor" @@ -404,7 +404,7 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["type"] = "temperature_adjustment_number" capability["category"] = "sensor" if modelId in (557,): - capability["name"] = "effective_target_temperature" + capability["name"] = "temperature_setpoint" capability["type"] = "temperature" else: capability["name"] = "away_mode_temperature" diff --git a/custom_components/cozytouch/climate.py b/custom_components/cozytouch/climate.py index a5aa9af..f714fcf 100644 --- a/custom_components/cozytouch/climate.py +++ b/custom_components/cozytouch/climate.py @@ -368,13 +368,13 @@ def target_temperature(self): @property def extra_state_attributes(self): """Return the computed target temperature.""" - if "effectiveTargetTemperatureId" in self._capability: + if "setpointTemperatureId" in self._capability: effective_temp = self.coordinator.get_capability_value( - self._capability["effectiveTargetTemperatureId"] + self._capability["setpointTemperatureId"] ) if effective_temp is not None: return { - "effective_target_temperature": float(effective_temp), + "temperature_setpoint": float(effective_temp), } return None @@ -385,21 +385,21 @@ def hvac_action(self): return HVACAction.OFF # Get effective target temperature - effective_temp = None - if "effectiveTargetTemperatureId" in self._capability: - temp_value = self.coordinator.get_capability_value( - self._capability["effectiveTargetTemperatureId"] + setpoint_temp = None + if "setpointTemperatureId" in self._capability: + setpoint_temp_value = self.coordinator.get_capability_value( + self._capability["setpointTemperatureId"] ) - if temp_value is not None: - effective_temp = float(temp_value) + if setpoint_temp_value is not None: + setpoint_temp = float(setpoint_temp_value) - # If no effective temp, fall back to target temperature - if effective_temp is None: - effective_temp = self._native_value + # If no setpoint temp, fall back to target temperature + if setpoint_temp is None: + setpoint_temp = self._native_value # Determine action based on current vs target temperature - if self._current_value is not None and effective_temp is not None: - if self._current_value < effective_temp - 0.2: + if self._current_value is not None and setpoint_temp is not None: + if self._current_value < setpoint_temp: return HVACAction.HEATING return HVACAction.IDLE diff --git a/custom_components/cozytouch/strings.json b/custom_components/cozytouch/strings.json index c2e2efb..be2eca3 100644 --- a/custom_components/cozytouch/strings.json +++ b/custom_components/cozytouch/strings.json @@ -266,8 +266,7 @@ "wifi_signal": { "name": "Wifi Signal" }, "wifi_ssid": { "name": "Wifi SSID" }, "zone_1": { "name": "Zone 1" }, - "zone_2": { "name": "Zone 2" }, - "effective_target_temperature": { "name": "Effective Target Temperature" } + "zone_2": { "name": "Zone 2" } }, "switch": { "away_mode": { "name": "Away Mode" }, diff --git a/custom_components/cozytouch/translations/en.json b/custom_components/cozytouch/translations/en.json index c2e2efb..be2eca3 100644 --- a/custom_components/cozytouch/translations/en.json +++ b/custom_components/cozytouch/translations/en.json @@ -266,8 +266,7 @@ "wifi_signal": { "name": "Wifi Signal" }, "wifi_ssid": { "name": "Wifi SSID" }, "zone_1": { "name": "Zone 1" }, - "zone_2": { "name": "Zone 2" }, - "effective_target_temperature": { "name": "Effective Target Temperature" } + "zone_2": { "name": "Zone 2" } }, "switch": { "away_mode": { "name": "Away Mode" }, diff --git a/custom_components/cozytouch/translations/fr.json b/custom_components/cozytouch/translations/fr.json index 075d192..74109cc 100644 --- a/custom_components/cozytouch/translations/fr.json +++ b/custom_components/cozytouch/translations/fr.json @@ -187,7 +187,7 @@ "heat_pump_z2": { "name": "PAC Z2" }, "hot_water_available": { "name": "Hot Water Available" }, "interface_fw": { "name": "Interface FW" }, - "temperature_setpoint": { "name": "Température Consigne" }, + "temperature_setpoint": { "name": "Température Cible" }, "model_name": { "name": "Modèle" }, "number_of_hours_burner": { "name": "Nb heures brûleur" }, "number_of_hours_ch_pump": { "name": "Nb heures pompe CH" }, @@ -266,8 +266,7 @@ "wifi_signal": { "name": "Signal Wifi" }, "wifi_ssid": { "name": "Wifi SSID" }, "zone_1": { "name": "Zone 1" }, - "zone_2": { "name": "Zone 2" }, - "effective_target_temperature": { "name": "Température Cible Effective" } + "zone_2": { "name": "Zone 2" } }, "switch": { "away_mode": { "name": "Absence" }, From 9075c5add07177152b03f72f0684f29f49cb531c Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Fri, 28 Nov 2025 11:56:39 +0100 Subject: [PATCH 12/13] docs: :memo: add Alfea Excellia Duo to tested devices in README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9ef95a..140592a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ This has been tested using on : - `Atlantic Naema 2 Duo 25` gas boiler using a `Ǹavilink Radio-Connect 128` thermostat - `Atlantic Naia 2 Micro 25` gas boiler using a `Ǹavilink Radio-Connect 128` thermostat - `Atlantic Loria Duo 6006 R32` heat pump using a `Navilink Radio-Connect 128` thermostat + - `Atlantic Alfea Excellia Duo` heat pump using a `Navilink Radio-Connect 228` thermostat - `Takao M3` air conditionning - `Kelud 1750W` towel rack - `Sauter Asama Connecté II Ventilo 1750W` towel rack @@ -25,7 +26,7 @@ More informations about HACS [here](https://hacs.xyz/). #### Manually -Clone this repository and copy `custom_components/cozytouch` to your Home Assistant config durectory (ex : `config/custom_components/cozytouch`) +Clone this repository and copy `custom_components/cozytouch` to your Home Assistant config directory (ex : `config/custom_components/cozytouch`) Restart Home Assistant. From 90715117d18c7e167b52a61a6dadecf077b52d2b Mon Sep 17 00:00:00 2001 From: Etienne Foussat Date: Mon, 2 Mar 2026 15:54:45 +0100 Subject: [PATCH 13/13] fix: override delay & HVACAction Heating action --- custom_components/cozytouch/capability.py | 8 ++- custom_components/cozytouch/climate.py | 62 ++++++++++++++++++- custom_components/cozytouch/icons.json | 8 +-- custom_components/cozytouch/model.py | 2 +- .../cozytouch/translations/en.json | 1 + .../cozytouch/translations/fr.json | 1 + 6 files changed, 71 insertions(+), 11 deletions(-) diff --git a/custom_components/cozytouch/capability.py b/custom_components/cozytouch/capability.py index 94263dd..a09ff7d 100644 --- a/custom_components/cozytouch/capability.py +++ b/custom_components/cozytouch/capability.py @@ -328,9 +328,11 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s capability["category"] = "diag" capability["icon"] = "mdi:home-floor-2" - # elif capabilityId == 157: - # # Prog override flag - # return {} + elif capabilityId == 157: + capability["name"] = "override" + capability["type"] = "switch" + capability["category"] = "sensor" + capability["icon"] = "mdi:gesture-tap" elif capabilityId == 158: if modelInfos["type"] == CozytouchDeviceType.TOWEL_RACK: diff --git a/custom_components/cozytouch/climate.py b/custom_components/cozytouch/climate.py index f714fcf..1c6107f 100644 --- a/custom_components/cozytouch/climate.py +++ b/custom_components/cozytouch/climate.py @@ -2,6 +2,7 @@ from __future__ import annotations +import asyncio import logging from homeassistant.components.climate import ( @@ -34,6 +35,9 @@ PRESET_PROG = "prog" PRESET_OVERRIDE = "override" +PRESET_TRANSITION_TIMEOUT = 15.0 +PRESET_TRANSITION_POLL_INTERVAL = 0.5 + # config flow setup async def async_setup_entry( @@ -98,6 +102,7 @@ def __init__( self._native_value = 0 self._current_value = None + self._current_boiler_temperature_value = None self._attr_native_step = 0.5 self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_min_temp = 0 @@ -182,6 +187,29 @@ def _configure_presets(self): if PRESET_NONE not in self._attr_preset_modes : self._attr_preset_mode = PRESET_BASIC + async def _wait_for_capability_value( + self, + capability_id, + expected_value, + *, + timeout: float = PRESET_TRANSITION_TIMEOUT, + interval: float = PRESET_TRANSITION_POLL_INTERVAL, + ) -> bool: + """Wait until a capability reaches the expected value.""" + expected = str(expected_value) + deadline = asyncio.get_running_loop().time() + timeout + + while True: + current_value = self.coordinator.get_capability_value(capability_id) + if current_value is not None and str(current_value) == expected: + return True + + if asyncio.get_running_loop().time() >= deadline: + return False + + await asyncio.sleep(interval) + await self.coordinator.async_request_refresh() + @callback def _handle_coordinator_update(self) -> None: """Update the values from the hub.""" @@ -225,6 +253,13 @@ def _handle_coordinator_update(self) -> None: self.coordinator.get_capability_value(currentValueId) ) + # Current boiler water temperature value + currentBoilerTemperatureValueId = 109 + if currentBoilerTemperatureValueId: + self._current_boiler_temperature_value = float( + self.coordinator.get_capability_value(currentBoilerTemperatureValueId) + ) + # Lowest adjustment value if ( self._attr_hvac_mode in ( @@ -398,8 +433,8 @@ def hvac_action(self): setpoint_temp = self._native_value # Determine action based on current vs target temperature - if self._current_value is not None and setpoint_temp is not None: - if self._current_value < setpoint_temp: + if self._current_boiler_temperature_value is not None and setpoint_temp is not None: + if self._current_boiler_temperature_value < setpoint_temp: return HVACAction.HEATING return HVACAction.IDLE @@ -529,9 +564,20 @@ async def async_set_preset_mode(self, preset_mode): if preset_mode == PRESET_BASIC: await self.coordinator.set_capability_value(progCapabilityId, "0") - elif preset_mode == PRESET_PROG: + elif preset_mode in (PRESET_PROG, PRESET_OVERRIDE): await self.coordinator.set_capability_value(progCapabilityId, "1") + if preset_mode == PRESET_OVERRIDE: + await self.coordinator.async_request_refresh() + prog_ok = await self._wait_for_capability_value( + progCapabilityId, + "1", + ) + if not prog_ok: + _LOGGER.warning( + "Timeout while waiting for prog mode before applying override" + ) + if progOverrideCapabilityId: progOverrideTimeCapabilityId = self._capability.get( "progOverrideTimeCapabilityId", None @@ -556,6 +602,16 @@ async def async_set_preset_mode(self, preset_mode): progOverrideCapabilityId, "1" ) + await self.coordinator.async_request_refresh() + override_ok = await self._wait_for_capability_value( + progOverrideCapabilityId, + "1", + ) + if not override_ok: + _LOGGER.warning( + "Timeout while waiting for override mode to be applied" + ) + else: if progOverrideTimeCapabilityId: await self.coordinator.set_capability_value( diff --git a/custom_components/cozytouch/icons.json b/custom_components/cozytouch/icons.json index b11d840..630555b 100644 --- a/custom_components/cozytouch/icons.json +++ b/custom_components/cozytouch/icons.json @@ -46,9 +46,9 @@ "state_attributes": { "preset_mode": { "state": { - "basic": "mdi:thermostat", - "prog": "mdi:thermostat-auto", - "override": "mdi:thermostat-cog" + "basic": "mdi:thermometer", + "prog": "mdi:clock-outline", + "override": "mdi:timer-sand-complete" } } } @@ -66,4 +66,4 @@ } } } -} \ No newline at end of file +} diff --git a/custom_components/cozytouch/model.py b/custom_components/cozytouch/model.py index 10c4900..178a5fa 100644 --- a/custom_components/cozytouch/model.py +++ b/custom_components/cozytouch/model.py @@ -269,7 +269,7 @@ def get_model_infos(modelId: int, zoneName: str | None = None): 1: HEATING_MODE_COMFORT } - elif modelId == 1369: + elif modelId in (1369, 1375): modelInfos["name"] = "Calypso Split" modelInfos["type"] = CozytouchDeviceType.WATER_HEATER modelInfos["HVACModes"] = { diff --git a/custom_components/cozytouch/translations/en.json b/custom_components/cozytouch/translations/en.json index be2eca3..efabe70 100644 --- a/custom_components/cozytouch/translations/en.json +++ b/custom_components/cozytouch/translations/en.json @@ -240,6 +240,7 @@ "prog_heat_saturday": { "name": "Saturday Heat prog" }, "prog_heat_sunday": { "name": "Sunday Heat prog" }, "prog_mode": { "name": "Prog Mode" }, + "override": { "name": "Override" }, "quiet_mode": { "name": "Quiet Mode" }, "radio_signal": { "name": "Radio Signal" }, "resistance": { "name": "Resistance" }, diff --git a/custom_components/cozytouch/translations/fr.json b/custom_components/cozytouch/translations/fr.json index 74109cc..2ae521c 100644 --- a/custom_components/cozytouch/translations/fr.json +++ b/custom_components/cozytouch/translations/fr.json @@ -240,6 +240,7 @@ "prog_heat_saturday": { "name": "Samedi Prog Heat" }, "prog_heat_sunday": { "name": "Dimanche Prog Heat" }, "prog_mode": { "name": "Mode Prog" }, + "override": { "name": "Délégation" }, "quiet_mode": { "name": "Mode Silence" }, "radio_signal": { "name": "Signal Radio" }, "resistance": { "name": "Résistance" },