From bbbb02ca54e99aa0f351612975ca0e01881130cd Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 8 Jun 2026 15:31:07 +0200 Subject: [PATCH 01/89] Change dhw_cm_switch to _dhw_allowed_modes/select_dhw_mode --- plugwise/helper.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 075f5b17d..d8ecadfca 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -482,8 +482,13 @@ def _get_toggle_state( if xml.find("type").text == "heater_central": locator = f"./actuator_functionalities/toggle_functionality[type='{toggle}']/state" if (state := xml.find(locator)) is not None: - data["switches"][name] = state.text == "on" - self._count += 1 + match toggle: + case "cooling_ena_switch": + data["switches"][name] = state.text == "on" + self._count += 1 + case "dhw_cm_switch": + self._dhw_allowed_modes = ["comfort", "off"] + self.select_dhw_mode = "comfort" if state.text == "on" else "off" def _get_plugwise_notifications(self) -> None: """Collect the Plugwise notifications.""" From 34ca03e08e0f74a0744d9ce3961dc06d59244dab Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 9 Jun 2026 08:13:22 +0200 Subject: [PATCH 02/89] Adapt comment to widened scope --- plugwise/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index d8ecadfca..b72a384fb 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -351,7 +351,7 @@ def _get_measurement_data(self, entity_id: str, entity: GwEntityData) -> None: measurements = DEVICE_MEASUREMENTS if self._is_thermostat and entity_id == self.heater_id: measurements = HEATER_CENTRAL_MEASUREMENTS - # Show the allowed dhw_modes (Loria only) + # Show the available dhw_modes if self._dhw_allowed_modes: data["dhw_modes"] = self._dhw_allowed_modes # Counting of this item is done in _appliance_measurements() From de48b75289f66354af45e889441ba8d3b781e977 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 9 Jun 2026 08:14:43 +0200 Subject: [PATCH 03/89] Move toggles collection up --- plugwise/helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index b72a384fb..869a3aef9 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -407,12 +407,12 @@ def _collect_appliance_data( if ( appliance := self._domain_objects.find(f'./appliance[@id="{entity_id}"]') ) is not None: - self._appliance_measurements(appliance, data, measurements) - self._get_lock_state(appliance, data) - for toggle, name in TOGGLES.items(): self._get_toggle_state(appliance, toggle, name, data) + self._appliance_measurements(appliance, data, measurements) + self._get_lock_state(appliance, data) + if appliance.find("type").text in ACTUATOR_CLASSES: self._get_actuator_functionalities(appliance, entity, data) From aadfedb23cf09291a8e504cc8cbe52359f9aef3e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 9 Jun 2026 08:21:29 +0200 Subject: [PATCH 04/89] Improve --- plugwise/helper.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 869a3aef9..c7be3f7fd 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -242,9 +242,8 @@ def _appliance_info_finder(self, appl: Munch, appliance: etree.Element) -> Munch appl := self._appl_heater_central_info(appl, appliance, False) ): # False means non-legacy entity return Munch() - self._dhw_allowed_modes = self._get_appl_actuator_modes( - appliance, "domestic_hot_water_mode_control_functionality" - ) + self._collect_dhw_modes(appliance) + return appl case _ as s if s.endswith("_plug"): # Collect info from plug-types (Plug, Aqara Smart Plug) @@ -265,6 +264,18 @@ def _appliance_info_finder(self, appl: Munch, appliance: etree.Element) -> Munch case _: # pragma: no cover return Munch() + def _collect_dhw_modes(self, appliance: etree.Element) -> None: + """Collect the DHW modes.""" + # Collect the Loria dhw_modes + self._dhw_allowed_modes = self._get_appl_actuator_modes( + appliance, "domestic_hot_water_mode_control_functionality" + ) + # Collect the default dhw modes: comfort and off + if not self._dhw_allowed_modes: + self._get_toggle_state( + appliance, "domestic_hot_water_comfort_mode", "dhw_cm_switch", None + ) + def _appl_gateway_info(self, appl: Munch, appliance: etree.Element) -> Munch: """Helper-function for _appliance_info_finder().""" self._gateway_id = appl.entity_id @@ -407,8 +418,8 @@ def _collect_appliance_data( if ( appliance := self._domain_objects.find(f'./appliance[@id="{entity_id}"]') ) is not None: - for toggle, name in TOGGLES.items(): - self._get_toggle_state(appliance, toggle, name, data) + # Collect the cooling enabled toggle state + self._get_toggle_state(appliance, "cooling_enabled", "cooling_ena_switch", data) self._appliance_measurements(appliance, data, measurements) self._get_lock_state(appliance, data) @@ -483,12 +494,12 @@ def _get_toggle_state( locator = f"./actuator_functionalities/toggle_functionality[type='{toggle}']/state" if (state := xml.find(locator)) is not None: match toggle: - case "cooling_ena_switch": + case "cooling_enabled": data["switches"][name] = state.text == "on" self._count += 1 - case "dhw_cm_switch": + case "domestic_hot_water_comfort_mode": self._dhw_allowed_modes = ["comfort", "off"] - self.select_dhw_mode = "comfort" if state.text == "on" else "off" + # data["select_dhw_mode"] = "comfort" if state.text == "on" else "off" def _get_plugwise_notifications(self) -> None: """Collect the Plugwise notifications.""" From e281ce6a914a6b3c01c375466db62379187f1c23 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 9 Jun 2026 20:06:12 +0200 Subject: [PATCH 05/89] Constants: add 2nd options for select_dhw_mode --- plugwise/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise/constants.py b/plugwise/constants.py index 267f2cbdb..4673b6dd7 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -181,6 +181,7 @@ ), # Available with the Loria and Elga (newer Anna firmware) heatpumps "cooling_state": UOM(NONE), "domestic_hot_water_mode": DATA("select_dhw_mode", NONE), + "domestic_hot_water_comfort_mode": DATA("select_dhw_mode", NONE), "domestic_hot_water_setpoint": UOM(TEMP_CELSIUS), "domestic_hot_water_state": DATA("dhw_state", NONE), "domestic_hot_water_temperature": DATA("dhw_temperature", TEMP_CELSIUS), From 564d902029231f05332e35072459a29b280d14ff Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 9 Jun 2026 20:09:23 +0200 Subject: [PATCH 06/89] Improve 2 --- plugwise/helper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index c7be3f7fd..797277b0d 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -463,6 +463,7 @@ def _appliance_measurements( continue if new_name := getattr(attrs, ATTR_NAME, None): + old_measurement = measurement measurement = new_name match measurement: @@ -471,6 +472,8 @@ def _appliance_measurements( case "select_dhw_mode": if self._dhw_allowed_modes: data["select_dhw_mode"] = appl_p_loc.text + if old_measurement == "domestic_hot_water_comfort_mode": + data["select_dhw_mode"] = "comfort" if appl_p_loc.text == "on" else "off" common_match_cases(measurement, attrs, appl_p_loc, data) @@ -499,7 +502,6 @@ def _get_toggle_state( self._count += 1 case "domestic_hot_water_comfort_mode": self._dhw_allowed_modes = ["comfort", "off"] - # data["select_dhw_mode"] = "comfort" if state.text == "on" else "off" def _get_plugwise_notifications(self) -> None: """Collect the Plugwise notifications.""" From ebd6b8a3a2240b12445cbf28628e975d19e1341b Mon Sep 17 00:00:00 2001 From: autoruff Date: Tue, 9 Jun 2026 18:20:22 +0000 Subject: [PATCH 07/89] fixup: dhw_update Python code fixed using ruff --- plugwise/helper.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 797277b0d..b6f7645c8 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -419,7 +419,9 @@ def _collect_appliance_data( appliance := self._domain_objects.find(f'./appliance[@id="{entity_id}"]') ) is not None: # Collect the cooling enabled toggle state - self._get_toggle_state(appliance, "cooling_enabled", "cooling_ena_switch", data) + self._get_toggle_state( + appliance, "cooling_enabled", "cooling_ena_switch", data + ) self._appliance_measurements(appliance, data, measurements) self._get_lock_state(appliance, data) @@ -473,7 +475,9 @@ def _appliance_measurements( if self._dhw_allowed_modes: data["select_dhw_mode"] = appl_p_loc.text if old_measurement == "domestic_hot_water_comfort_mode": - data["select_dhw_mode"] = "comfort" if appl_p_loc.text == "on" else "off" + data["select_dhw_mode"] = ( + "comfort" if appl_p_loc.text == "on" else "off" + ) common_match_cases(measurement, attrs, appl_p_loc, data) From 64377af41ec4c767785c6ff02db55432d91f1224 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 10 Jun 2026 07:57:02 +0200 Subject: [PATCH 08/89] Update testdata json --- tests/data/adam/adam_bad_thermostat.json | 8 +++++--- tests/data/adam/adam_heatpump_cooling.json | 8 +++++--- tests/data/adam/adam_jip.json | 8 +++++--- tests/data/adam/adam_onoff_cooling_fake_firmware.json | 8 +++++--- tests/data/adam/adam_plus_anna.json | 8 +++++--- tests/data/adam/adam_plus_anna_new.json | 8 +++++--- tests/data/adam/adam_plus_anna_new_regulation_off.json | 8 +++++--- 7 files changed, 35 insertions(+), 21 deletions(-) diff --git a/tests/data/adam/adam_bad_thermostat.json b/tests/data/adam/adam_bad_thermostat.json index 5554c02e1..16978a60c 100644 --- a/tests/data/adam/adam_bad_thermostat.json +++ b/tests/data/adam/adam_bad_thermostat.json @@ -7,6 +7,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "856285a783f24bf4b2573c8bc510eabf", "max_dhw_temperature": { "lower_bound": 0.0, @@ -23,6 +27,7 @@ "model": "Generic heater", "model_id": "1.1", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 49.9, "intended_boiler_temperature": 0.0, @@ -31,9 +36,6 @@ "return_temperature": 33.0, "water_temperature": 20.9 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "WeHeat" }, "3ee6b9486cb04c258a3130fff2b144a4": { diff --git a/tests/data/adam/adam_heatpump_cooling.json b/tests/data/adam/adam_heatpump_cooling.json index 71bfad7e2..6c9f1fabe 100644 --- a/tests/data/adam/adam_heatpump_cooling.json +++ b/tests/data/adam/adam_heatpump_cooling.json @@ -44,6 +44,10 @@ }, "dev_class": "heater_central", "location": "eedadcb297564f1483faa509179aebed", + "dhw_modes": [ + "comfort", + "off" + ], "max_dhw_temperature": { "lower_bound": 40.0, "resolution": 0.01, @@ -59,6 +63,7 @@ "model": "Generic heater/cooler", "model_id": "17.1", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, @@ -68,9 +73,6 @@ "water_pressure": 2.0, "water_temperature": 24.5 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Remeha B.V." }, "1053c8bbf8be43c6921742b146a625f1": { diff --git a/tests/data/adam/adam_jip.json b/tests/data/adam/adam_jip.json index a1136e3c9..3c10a4ca7 100644 --- a/tests/data/adam/adam_jip.json +++ b/tests/data/adam/adam_jip.json @@ -330,6 +330,10 @@ }, "dev_class": "heater_central", "location": "9e4433a9d69f40b3aefd15e74395eaec", + "dhw_modes": [ + "comfort", + "off" + ], "max_dhw_temperature": { "lower_bound": 40.0, "resolution": 0.01, @@ -345,6 +349,7 @@ "model": "Generic heater", "model_id": "10.20", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, @@ -352,9 +357,6 @@ "water_pressure": 1.4, "water_temperature": 37.3 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Remeha B.V." }, "f61f1a2535f54f52ad006a3d18e459ca": { diff --git a/tests/data/adam/adam_onoff_cooling_fake_firmware.json b/tests/data/adam/adam_onoff_cooling_fake_firmware.json index f822e33d8..27d994a0e 100644 --- a/tests/data/adam/adam_onoff_cooling_fake_firmware.json +++ b/tests/data/adam/adam_onoff_cooling_fake_firmware.json @@ -9,6 +9,10 @@ }, "dev_class": "heater_central", "location": "eedadcb297564f1483faa509179aebed", + "dhw_modes": [ + "comfort", + "off" + ], "max_dhw_temperature": { "lower_bound": 40.0, "resolution": 0.01, @@ -23,6 +27,7 @@ }, "model": "Unknown", "name": "OnOff", + "select_dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, @@ -31,9 +36,6 @@ "return_temperature": 24.9, "water_pressure": 2.0, "water_temperature": 24.5 - }, - "switches": { - "dhw_cm_switch": true } }, "7d97fc3117784cfdafe347bcedcbbbcb": { diff --git a/tests/data/adam/adam_plus_anna.json b/tests/data/adam/adam_plus_anna.json index 8533e7468..d86a53176 100644 --- a/tests/data/adam/adam_plus_anna.json +++ b/tests/data/adam/adam_plus_anna.json @@ -35,6 +35,10 @@ }, "dev_class": "heater_central", "location": "07d618f0bb80412687f065b8698ce3e7", + "dhw_modes": [ + "comfort", + "off" + ], "maximum_boiler_temperature": { "lower_bound": 0.0, "resolution": 1.0, @@ -43,12 +47,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "water_temperature": 48.0 - }, - "switches": { - "dhw_cm_switch": false } }, "aa6b0002df0a46e1b1eb94beb61eddfe": { diff --git a/tests/data/adam/adam_plus_anna_new.json b/tests/data/adam/adam_plus_anna_new.json index 0c1057afc..d794d51cd 100644 --- a/tests/data/adam/adam_plus_anna_new.json +++ b/tests/data/adam/adam_plus_anna_new.json @@ -8,6 +8,10 @@ }, "dev_class": "heater_central", "location": "bc93488efab249e5bc54fd7e175a6f91", + "dhw_modes": [ + "comfort", + "off" + ], "maximum_boiler_temperature": { "lower_bound": 25.0, "resolution": 0.01, @@ -16,12 +20,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 22.5, "water_temperature": 43.0 - }, - "switches": { - "dhw_cm_switch": false } }, "10016900610d4c7481df78c89606ef22": { diff --git a/tests/data/adam/adam_plus_anna_new_regulation_off.json b/tests/data/adam/adam_plus_anna_new_regulation_off.json index 4a0576d90..4f3130c05 100644 --- a/tests/data/adam/adam_plus_anna_new_regulation_off.json +++ b/tests/data/adam/adam_plus_anna_new_regulation_off.json @@ -8,6 +8,10 @@ }, "dev_class": "heater_central", "location": "bc93488efab249e5bc54fd7e175a6f91", + "dhw_modes": [ + "comfort", + "off" + ], "maximum_boiler_temperature": { "lower_bound": 25.0, "resolution": 0.01, @@ -16,12 +20,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "water_temperature": 30.0 - }, - "switches": { - "dhw_cm_switch": false } }, "10016900610d4c7481df78c89606ef22": { From 124a633cf01055140c184e7002c03bd6b2c2620c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 10 Jun 2026 08:13:15 +0200 Subject: [PATCH 09/89] Save entity_items updates --- tests/test_adam.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_adam.py b/tests/test_adam.py index b818713f1..98266c7cc 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -47,7 +47,7 @@ async def test_connect_adam_plus_anna_new(self): test_items = await self.device_test(api, "2025-10-12 00:00:01", testdata) assert api.gateway_id == "da224107914542988a88561b4452b0f6" - assert self.entity_items == 231 + assert self.entity_items == 232 assert test_items == self.entity_items assert self.entity_list == [ "da224107914542988a88561b4452b0f6", From eee59b7e0ec506a3689590ad8a52c28c50b26639 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 10 Jun 2026 16:26:41 +0200 Subject: [PATCH 10/89] Fix typing as suggested --- plugwise/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index b6f7645c8..237303fd6 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -491,7 +491,7 @@ def _appliance_measurements( self._count = count_data_items(self._count, data) def _get_toggle_state( - self, xml: etree.Element, toggle: str, name: ToggleNameType, data: GwEntityData + self, xml: etree.Element, toggle: str, name: ToggleNameType, data: GwEntityData | None ) -> None: """Helper-function for _get_measurement_data(). From 052ffc29860dfe1942f851560073512249e26546 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 10 Jun 2026 18:54:31 +0200 Subject: [PATCH 11/89] Remove dhw_cm_switch from SwitchType and SmileSwitch classes --- plugwise/constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index 4673b6dd7..85e901b1c 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -387,7 +387,6 @@ SwitchType = Literal[ "cooling_ena_switch", - "dhw_cm_switch", "lock", "relay", ] @@ -506,7 +505,6 @@ class SmileSwitches(TypedDict, total=False): """Smile Switches class.""" cooling_ena_switch: bool - dhw_cm_switch: bool lock: bool relay: bool From 72dfd8152b7033ea3109e0ea3a86453bca748d26 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 19:24:25 +0200 Subject: [PATCH 12/89] Fix item_count --- fixtures/adam_bad_thermostat/data.json | 8 +++++--- fixtures/adam_plus_anna_new/data.json | 8 +++++--- plugwise/helper.py | 1 - 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/fixtures/adam_bad_thermostat/data.json b/fixtures/adam_bad_thermostat/data.json index 5554c02e1..16978a60c 100644 --- a/fixtures/adam_bad_thermostat/data.json +++ b/fixtures/adam_bad_thermostat/data.json @@ -7,6 +7,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "856285a783f24bf4b2573c8bc510eabf", "max_dhw_temperature": { "lower_bound": 0.0, @@ -23,6 +27,7 @@ "model": "Generic heater", "model_id": "1.1", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 49.9, "intended_boiler_temperature": 0.0, @@ -31,9 +36,6 @@ "return_temperature": 33.0, "water_temperature": 20.9 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "WeHeat" }, "3ee6b9486cb04c258a3130fff2b144a4": { diff --git a/fixtures/adam_plus_anna_new/data.json b/fixtures/adam_plus_anna_new/data.json index 0c1057afc..56c7ffbb0 100644 --- a/fixtures/adam_plus_anna_new/data.json +++ b/fixtures/adam_plus_anna_new/data.json @@ -7,6 +7,10 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "bc93488efab249e5bc54fd7e175a6f91", "maximum_boiler_temperature": { "lower_bound": 25.0, @@ -16,12 +20,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 22.5, "water_temperature": 43.0 - }, - "switches": { - "dhw_cm_switch": false } }, "10016900610d4c7481df78c89606ef22": { diff --git a/plugwise/helper.py b/plugwise/helper.py index 237303fd6..14ba5f8c5 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -503,7 +503,6 @@ def _get_toggle_state( match toggle: case "cooling_enabled": data["switches"][name] = state.text == "on" - self._count += 1 case "domestic_hot_water_comfort_mode": self._dhw_allowed_modes = ["comfort", "off"] From 659d9682b6fc338fda8f5adb465f0d8a93a36062 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 19:27:13 +0200 Subject: [PATCH 13/89] Remove now invalid testcase --- tests/test_adam.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test_adam.py b/tests/test_adam.py index 98266c7cc..55701f17e 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -115,12 +115,6 @@ async def test_connect_adam_plus_anna_new(self): ["2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8"], ) assert switch_change - switch_change = await self.tinker_switch( - api, - "056ee145a816487eaa69243c3280f8bf", - model="dhw_cm_switch", - ) - assert switch_change # Test relay without lock-attribute switch_change = await self.tinker_switch( api, From 431b7dde88c4dba96ea7e8c540441b1b39c05b46 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 19:30:38 +0200 Subject: [PATCH 14/89] Correct test-json --- tests/data/adam/adam_heatpump_cooling.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/adam/adam_heatpump_cooling.json b/tests/data/adam/adam_heatpump_cooling.json index 6c9f1fabe..604192067 100644 --- a/tests/data/adam/adam_heatpump_cooling.json +++ b/tests/data/adam/adam_heatpump_cooling.json @@ -63,7 +63,7 @@ "model": "Generic heater/cooler", "model_id": "17.1", "name": "OpenTherm", - "select_dhw_mode": "off", + "select_dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, From 1dfc1b8cebfeb7ebbd1f1c0b4b5a2e40610e9a60 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 19:32:15 +0200 Subject: [PATCH 15/89] Fix entity_items count --- fixtures/adam_heatpump_cooling/data.json | 8 +++++--- fixtures/adam_plus_anna_new_regulation_off/data.json | 8 +++++--- tests/test_adam.py | 8 ++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/fixtures/adam_heatpump_cooling/data.json b/fixtures/adam_heatpump_cooling/data.json index 4075bf756..d0dc677c0 100644 --- a/fixtures/adam_heatpump_cooling/data.json +++ b/fixtures/adam_heatpump_cooling/data.json @@ -55,6 +55,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "eedadcb297564f1483faa509179aebed", "max_dhw_temperature": { "lower_bound": 40.0, @@ -71,6 +75,7 @@ "model": "Generic heater/cooler", "model_id": "17.1", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, @@ -80,9 +85,6 @@ "water_pressure": 2.0, "water_temperature": 24.5 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Remeha B.V." }, "1053c8bbf8be43c6921742b146a625f1": { diff --git a/fixtures/adam_plus_anna_new_regulation_off/data.json b/fixtures/adam_plus_anna_new_regulation_off/data.json index ca57a68b6..dab5f22f7 100644 --- a/fixtures/adam_plus_anna_new_regulation_off/data.json +++ b/fixtures/adam_plus_anna_new_regulation_off/data.json @@ -7,6 +7,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "bc93488efab249e5bc54fd7e175a6f91", "maximum_boiler_temperature": { "lower_bound": 25.0, @@ -16,12 +20,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "water_temperature": 30.0 - }, - "switches": { - "dhw_cm_switch": false } }, "10016900610d4c7481df78c89606ef22": { diff --git a/tests/test_adam.py b/tests/test_adam.py index 55701f17e..149d717a9 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -346,7 +346,7 @@ async def test_adam_heatpump_cooling(self): server, api, client = await self.connect_wrapper() test_items = await self.device_test(api, "2022-01-02 00:00:01", testdata) - assert self.entity_items == 539 + assert self.entity_items == 540 assert test_items == self.entity_items assert self.cooling_present assert self._cooling_enabled @@ -370,7 +370,7 @@ async def test_connect_adam_onoff_cooling_fake_firmware(self): ) test_items = await self.device_test(api, "2022-01-02 00:00:01", testdata) - assert self.entity_items == 70 + assert self.entity_items == 71 assert test_items == self.entity_items assert self.cooling_present # assert self._cooling_enabled - no cooling_enabled indication present @@ -395,7 +395,7 @@ async def test_connect_adam_plus_anna(self): test_items = await self.device_test(api, "2020-03-22 00:00:01", testdata) assert api.gateway_id == "b128b4bbbd1f47e9bf4d756e8fb5ee94" - assert self.entity_items == 82 + assert self.entity_items == 83 assert test_items == self.entity_items assert "6fb89e35caeb4b1cb275184895202d84" in self.notifications @@ -436,7 +436,7 @@ async def test_adam_plus_jip(self): test_items = await self.device_test(api, "2021-06-20 00:00:01", testdata) assert api.gateway_id == "b5c2386c6f6342669e50fe49dd05b188" - assert self.entity_items == 269 + assert self.entity_items == 270 assert test_items == self.entity_items # Negative test From 7492f2f883d73c945c238af1f54ede7859c58f2c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 19:36:32 +0200 Subject: [PATCH 16/89] Save updated adam fixtures --- fixtures/adam_jip/data.json | 8 +++++--- fixtures/adam_onoff_cooling_fake_firmware/data.json | 8 +++++--- fixtures/adam_plus_anna/data.json | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/fixtures/adam_jip/data.json b/fixtures/adam_jip/data.json index 65d256e10..315eba45b 100644 --- a/fixtures/adam_jip/data.json +++ b/fixtures/adam_jip/data.json @@ -394,6 +394,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "9e4433a9d69f40b3aefd15e74395eaec", "max_dhw_temperature": { "lower_bound": 40.0, @@ -410,6 +414,7 @@ "model": "Generic heater", "model_id": "10.20", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, @@ -417,9 +422,6 @@ "water_pressure": 1.4, "water_temperature": 37.3 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Remeha B.V." }, "f61f1a2535f54f52ad006a3d18e459ca": { diff --git a/fixtures/adam_onoff_cooling_fake_firmware/data.json b/fixtures/adam_onoff_cooling_fake_firmware/data.json index 117954c61..f1902e5a4 100644 --- a/fixtures/adam_onoff_cooling_fake_firmware/data.json +++ b/fixtures/adam_onoff_cooling_fake_firmware/data.json @@ -8,6 +8,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "eedadcb297564f1483faa509179aebed", "max_dhw_temperature": { "lower_bound": 40.0, @@ -23,6 +27,7 @@ }, "model": "Unknown", "name": "OnOff", + "select_dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, @@ -31,9 +36,6 @@ "return_temperature": 24.9, "water_pressure": 2.0, "water_temperature": 24.5 - }, - "switches": { - "dhw_cm_switch": true } }, "7d97fc3117784cfdafe347bcedcbbbcb": { diff --git a/fixtures/adam_plus_anna/data.json b/fixtures/adam_plus_anna/data.json index 57d9011c1..22986758b 100644 --- a/fixtures/adam_plus_anna/data.json +++ b/fixtures/adam_plus_anna/data.json @@ -45,6 +45,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "07d618f0bb80412687f065b8698ce3e7", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -54,12 +58,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "water_temperature": 48.0 - }, - "switches": { - "dhw_cm_switch": false } }, "aa6b0002df0a46e1b1eb94beb61eddfe": { From 0f6df7b1057f19f67339513d1ef5c06155bb3c24 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 20:01:06 +0200 Subject: [PATCH 17/89] Save updated anna test-jsons --- tests/data/anna/anna_elga_2.json | 8 +++++--- tests/data/anna/anna_elga_2_cooling.json | 8 +++++--- tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json | 8 +++++--- tests/data/anna/anna_elga_2_schedule_off.json | 8 +++++--- tests/data/anna/anna_elga_no_cooling.json | 8 +++++--- tests/data/anna/anna_heatpump_cooling.json | 8 +++++--- tests/data/anna/anna_heatpump_cooling_fake_firmware.json | 8 +++++--- tests/data/anna/anna_heatpump_heating.json | 8 +++++--- tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json | 9 ++++++--- tests/data/anna/anna_loria_cooling_active.json | 3 +-- tests/data/anna/anna_loria_driessens.json | 3 +-- tests/data/anna/anna_loria_heating_idle.json | 3 +-- tests/data/anna/anna_p1.json | 5 ++--- tests/data/anna/anna_v4.json | 5 ++--- tests/data/anna/anna_v4_UPDATED_DATA.json | 5 ++--- tests/data/anna/anna_v4_dhw.json | 5 ++--- tests/data/anna/anna_v4_no_tag.json | 5 ++--- 17 files changed, 59 insertions(+), 48 deletions(-) diff --git a/tests/data/anna/anna_elga_2.json b/tests/data/anna/anna_elga_2.json index e417927a2..d2efd669e 100644 --- a/tests/data/anna/anna_elga_2.json +++ b/tests/data/anna/anna_elga_2.json @@ -11,9 +11,14 @@ "secondary_boiler_state": true }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "d34dfe6ab90b410c98068e75de3eb631", "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "domestic_hot_water_setpoint": 60.0, "intended_boiler_temperature": 58.3, @@ -23,9 +28,6 @@ "water_pressure": 0.5, "water_temperature": 42.6 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "ebd90df1ab334565b5895f37590ccff4": { diff --git a/tests/data/anna/anna_elga_2_cooling.json b/tests/data/anna/anna_elga_2_cooling.json index 0c417702b..1910f9e8c 100644 --- a/tests/data/anna/anna_elga_2_cooling.json +++ b/tests/data/anna/anna_elga_2_cooling.json @@ -11,6 +11,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "d34dfe6ab90b410c98068e75de3eb631", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -20,6 +24,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "domestic_hot_water_setpoint": 60.0, "intended_boiler_temperature": 0.0, @@ -29,9 +34,6 @@ "water_pressure": 0.5, "water_temperature": 22.8 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Techneco" }, "ebd90df1ab334565b5895f37590ccff4": { diff --git a/tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json b/tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json index e4dcbd958..446c24759 100644 --- a/tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json +++ b/tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json @@ -11,6 +11,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "d34dfe6ab90b410c98068e75de3eb631", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -20,6 +24,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "domestic_hot_water_setpoint": 60.0, "intended_boiler_temperature": 0.0, @@ -29,9 +34,6 @@ "water_pressure": 0.5, "water_temperature": 22.8 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Techneco" }, "ebd90df1ab334565b5895f37590ccff4": { diff --git a/tests/data/anna/anna_elga_2_schedule_off.json b/tests/data/anna/anna_elga_2_schedule_off.json index 064a483d8..0fa003651 100644 --- a/tests/data/anna/anna_elga_2_schedule_off.json +++ b/tests/data/anna/anna_elga_2_schedule_off.json @@ -11,6 +11,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "d34dfe6ab90b410c98068e75de3eb631", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -20,6 +24,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "domestic_hot_water_setpoint": 60.0, "intended_boiler_temperature": 0.0, @@ -29,9 +34,6 @@ "water_pressure": 0.5, "water_temperature": 22.8 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Techneco" }, "ebd90df1ab334565b5895f37590ccff4": { diff --git a/tests/data/anna/anna_elga_no_cooling.json b/tests/data/anna/anna_elga_no_cooling.json index 23ec151d4..756f47148 100644 --- a/tests/data/anna/anna_elga_no_cooling.json +++ b/tests/data/anna/anna_elga_no_cooling.json @@ -4,6 +4,10 @@ "plugwise_notification": false }, "dev_class": "gateway", + "dhw_modes": [ + "comfort", + "off" + ], "firmware": "4.0.15", "hardware": "AME Smile 2.0 board", "location": "a57efe5f145f498c9be62a9b63626fbf", @@ -42,6 +46,7 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, @@ -51,9 +56,6 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/tests/data/anna/anna_heatpump_cooling.json b/tests/data/anna/anna_heatpump_cooling.json index c722045a2..abdea92dc 100644 --- a/tests/data/anna/anna_heatpump_cooling.json +++ b/tests/data/anna/anna_heatpump_cooling.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -38,6 +42,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 41.5, "domestic_hot_water_setpoint": 60.0, @@ -48,9 +53,6 @@ "water_pressure": 1.61, "water_temperature": 24.7 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/tests/data/anna/anna_heatpump_cooling_fake_firmware.json b/tests/data/anna/anna_heatpump_cooling_fake_firmware.json index 4218240cb..77eaab4e1 100644 --- a/tests/data/anna/anna_heatpump_cooling_fake_firmware.json +++ b/tests/data/anna/anna_heatpump_cooling_fake_firmware.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -38,6 +42,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 41.5, "domestic_hot_water_setpoint": 60.0, @@ -48,9 +53,6 @@ "water_pressure": 1.61, "water_temperature": 24.7 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/tests/data/anna/anna_heatpump_heating.json b/tests/data/anna/anna_heatpump_heating.json index ab6bdf08e..809f9afea 100644 --- a/tests/data/anna/anna_heatpump_heating.json +++ b/tests/data/anna/anna_heatpump_heating.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "max_dhw_temperature": { "lower_bound": 35.0, @@ -44,6 +48,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, @@ -53,9 +58,6 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json b/tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json index a36012587..bb8c9aac8 100644 --- a/tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json +++ b/tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json @@ -1,6 +1,10 @@ { "1cbf783bb11e4a7c8a6843dee3a86927": { "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "model": "Generic heater/cooler", "name": "OpenTherm", @@ -21,6 +25,7 @@ "secondary_boiler_state": false, "flame_state": false }, + "select_dhw_mode": "off", "sensors": { "water_temperature": 29.1, "domestic_hot_water_setpoint": 60.0, @@ -30,9 +35,7 @@ "return_temperature": 25.1, "water_pressure": 1.57, "outdoor_air_temperature": 3.0 - }, - "switches": { - "dhw_cm_switch": false + } } } diff --git a/tests/data/anna/anna_loria_cooling_active.json b/tests/data/anna/anna_loria_cooling_active.json index 8b6c7341e..7d16279cc 100644 --- a/tests/data/anna/anna_loria_cooling_active.json +++ b/tests/data/anna/anna_loria_cooling_active.json @@ -88,8 +88,7 @@ "water_temperature": 25.3 }, "switches": { - "cooling_ena_switch": true, - "dhw_cm_switch": true + "cooling_ena_switch": true }, "vendor": "Atlantic" } diff --git a/tests/data/anna/anna_loria_driessens.json b/tests/data/anna/anna_loria_driessens.json index 2519d1f45..809904ecd 100644 --- a/tests/data/anna/anna_loria_driessens.json +++ b/tests/data/anna/anna_loria_driessens.json @@ -94,8 +94,7 @@ "water_temperature": 23.3 }, "switches": { - "cooling_ena_switch": false, - "dhw_cm_switch": true + "cooling_ena_switch": false }, "vendor": "Atlantic" } diff --git a/tests/data/anna/anna_loria_heating_idle.json b/tests/data/anna/anna_loria_heating_idle.json index d1b640345..c6f83d35a 100644 --- a/tests/data/anna/anna_loria_heating_idle.json +++ b/tests/data/anna/anna_loria_heating_idle.json @@ -88,8 +88,7 @@ "water_temperature": 25.3 }, "switches": { - "cooling_ena_switch": false, - "dhw_cm_switch": true + "cooling_ena_switch": false }, "vendor": "Atlantic" } diff --git a/tests/data/anna/anna_p1.json b/tests/data/anna/anna_p1.json index 2d09ad41f..4afa66135 100644 --- a/tests/data/anna/anna_p1.json +++ b/tests/data/anna/anna_p1.json @@ -39,19 +39,18 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": ["comfort", "off"], "location": "da7be222ab3b420c927f3e49fade0304", "model": "Generic heater", "model_id": "HR24", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, "water_pressure": 6.0, "water_temperature": 35.0 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Intergas" }, "da7be222ab3b420c927f3e49fade0304": { diff --git a/tests/data/anna/anna_v4.json b/tests/data/anna/anna_v4.json index 7e6f138be..6696e2511 100644 --- a/tests/data/anna/anna_v4.json +++ b/tests/data/anna/anna_v4.json @@ -57,6 +57,7 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_modes": ["comfort", "off"], "location": "94c107dc6ac84ed98e9f68c0dd06bf71", "max_dhw_temperature": { "lower_bound": 30.0, @@ -73,6 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -80,9 +82,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Bosch Thermotechniek B.V." } } diff --git a/tests/data/anna/anna_v4_UPDATED_DATA.json b/tests/data/anna/anna_v4_UPDATED_DATA.json index 0d0fde7af..dac96af49 100644 --- a/tests/data/anna/anna_v4_UPDATED_DATA.json +++ b/tests/data/anna/anna_v4_UPDATED_DATA.json @@ -1,5 +1,6 @@ { "cd0e6156b1f04d5f952349ffbe397481": { + "dhw_modes": ["comfort", "off"], "maximum_boiler_temperature": { "setpoint": 69.0, "lower_bound": 0.0, @@ -17,15 +18,13 @@ "heating_state": false, "flame_state": false }, + "select_dhw_mode": "off", "sensors": { "water_temperature": 51.0, "intended_boiler_temperature": 0.0, "modulation_level": 0, "return_temperature": 41.0, "water_pressure": 2.1 - }, - "switches": { - "dhw_cm_switch": true } }, "01b85360fdd243d0aaad4d6ac2a5ba7e": { diff --git a/tests/data/anna/anna_v4_dhw.json b/tests/data/anna/anna_v4_dhw.json index a560de161..504ac4592 100644 --- a/tests/data/anna/anna_v4_dhw.json +++ b/tests/data/anna/anna_v4_dhw.json @@ -57,6 +57,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": ["comfort", "off"], "location": "94c107dc6ac84ed98e9f68c0dd06bf71", "max_dhw_temperature": { "lower_bound": 30.0, @@ -73,6 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -80,9 +82,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Bosch Thermotechniek B.V." } } diff --git a/tests/data/anna/anna_v4_no_tag.json b/tests/data/anna/anna_v4_no_tag.json index 513e7ce20..a9c448f3f 100644 --- a/tests/data/anna/anna_v4_no_tag.json +++ b/tests/data/anna/anna_v4_no_tag.json @@ -57,6 +57,7 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_modes": ["comfort", "off"], "location": "94c107dc6ac84ed98e9f68c0dd06bf71", "max_dhw_temperature": { "lower_bound": 30.0, @@ -73,6 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -80,9 +82,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Bosch Thermotechniek B.V." } } From 4b475f10eeea3d398dbb13bc39c7db470aef2dfa Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 20:02:17 +0200 Subject: [PATCH 18/89] Update entity_items count --- fixtures/anna_v4/data.json | 8 +++++--- tests/test_anna.py | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fixtures/anna_v4/data.json b/fixtures/anna_v4/data.json index a64605171..ea4559532 100644 --- a/fixtures/anna_v4/data.json +++ b/fixtures/anna_v4/data.json @@ -67,6 +67,10 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "94c107dc6ac84ed98e9f68c0dd06bf71", "max_dhw_temperature": { "lower_bound": 30.0, @@ -83,6 +87,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -90,9 +95,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Bosch Thermotechniek B.V." } } diff --git a/tests/test_anna.py b/tests/test_anna.py index 84fbcf27a..00ad59b98 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -29,7 +29,7 @@ async def test_connect_anna_v4(self): await self.device_test(api, "2020-04-05 00:00:01", testdata) assert api.gateway_id == "0466eae8520144c78afb29628384edeb" - assert self.entity_items == 60 + assert self.entity_items == 61 assert not self.notifications assert not self.cooling_present @@ -101,7 +101,7 @@ async def test_connect_anna_v4_dhw(self): ) await self.device_test(api, "2020-04-05 00:00:01", testdata) - assert self.entity_items == 60 + assert self.entity_items == 61 assert not self.notifications result = await self.tinker_thermostat( From ae3f1a3b0d0542d811fe2efdb6c04458532997a1 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 20:04:06 +0200 Subject: [PATCH 19/89] Correct test-json --- tests/data/anna/anna_v4_UPDATED_DATA.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/anna/anna_v4_UPDATED_DATA.json b/tests/data/anna/anna_v4_UPDATED_DATA.json index dac96af49..8e0568a11 100644 --- a/tests/data/anna/anna_v4_UPDATED_DATA.json +++ b/tests/data/anna/anna_v4_UPDATED_DATA.json @@ -18,7 +18,7 @@ "heating_state": false, "flame_state": false }, - "select_dhw_mode": "off", + "select_dhw_mode": "comfort", "sensors": { "water_temperature": 51.0, "intended_boiler_temperature": 0.0, From 6aeb423e7842e67590ec55fd3c380457f758052c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 20:06:10 +0200 Subject: [PATCH 20/89] Update entity_items count --- fixtures/anna_v4_dhw/data.json | 8 +++++--- fixtures/anna_v4_no_tag/data.json | 8 +++++--- tests/test_anna.py | 24 ++++++++++++------------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/fixtures/anna_v4_dhw/data.json b/fixtures/anna_v4_dhw/data.json index 677d0a622..c5d40ae4e 100644 --- a/fixtures/anna_v4_dhw/data.json +++ b/fixtures/anna_v4_dhw/data.json @@ -67,6 +67,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "94c107dc6ac84ed98e9f68c0dd06bf71", "max_dhw_temperature": { "lower_bound": 30.0, @@ -83,6 +87,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -90,9 +95,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Bosch Thermotechniek B.V." } } diff --git a/fixtures/anna_v4_no_tag/data.json b/fixtures/anna_v4_no_tag/data.json index 926b858e2..4f2410b5e 100644 --- a/fixtures/anna_v4_no_tag/data.json +++ b/fixtures/anna_v4_no_tag/data.json @@ -67,6 +67,10 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "94c107dc6ac84ed98e9f68c0dd06bf71", "max_dhw_temperature": { "lower_bound": 30.0, @@ -83,6 +87,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -90,9 +95,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Bosch Thermotechniek B.V." } } diff --git a/tests/test_anna.py b/tests/test_anna.py index 00ad59b98..2aba648ae 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -130,7 +130,7 @@ async def test_connect_anna_v4_no_tag(self): ) await self.device_test(api, "2020-04-05 00:00:01", testdata) - assert self.entity_items == 60 + assert self.entity_items == 61 result = await self.tinker_thermostat( api, @@ -186,7 +186,7 @@ async def test_connect_anna_heatpump_heating(self): await self.device_test(api, "2020-04-12 00:00:01", testdata) assert api.gateway_id == "015ae9ea3f964e668e490fa39da3870b" - assert self.entity_items == 69 + assert self.entity_items == 70 assert not self.notifications assert self.cooling_present assert not self._cooling_enabled @@ -216,7 +216,7 @@ async def test_connect_anna_heatpump_heating(self): await self.device_test( api, "2020-04-13 00:00:01", testdata_updated, initialize=False ) - assert self.entity_items == 66 + assert self.entity_items == 67 await api.close_connection() await self.disconnect(server, client) @@ -241,7 +241,7 @@ async def test_connect_anna_heatpump_cooling(self): ) await self.device_test(api, "2020-04-19 00:00:01", testdata) - assert self.entity_items == 66 + assert self.entity_items == 67 assert self.cooling_present assert not self.notifications @@ -287,7 +287,7 @@ async def test_connect_anna_heatpump_cooling_fake_firmware(self): ) await self.device_test(api, "2020-04-19 00:00:01", testdata) - assert self.entity_items == 66 + assert self.entity_items == 67 assert self.cooling_present assert self._cooling_enabled assert self._cooling_active @@ -313,7 +313,7 @@ async def test_connect_anna_elga_no_cooling(self): await self.device_test(api, "2020-04-12 00:00:01", testdata) assert api.gateway_id == "015ae9ea3f964e668e490fa39da3870b" - assert self.entity_items == 65 + assert self.entity_items == 66 assert not self.notifications assert not self.cooling_present @@ -336,7 +336,7 @@ async def test_connect_anna_elga_2(self): ) await self.device_test(api, "2022-03-13 00:00:01", testdata) - assert self.entity_items == 61 + assert self.entity_items == 62 assert api.gateway_id == "fb49af122f6e4b0f91267e1cf7666d6f" assert self.cooling_present assert not self._cooling_enabled @@ -356,7 +356,7 @@ async def test_connect_anna_elga_2_schedule_off(self): await self.device_test(api, "2022-03-13 00:00:01", testdata) assert not self._cooling_enabled - assert self.entity_items == 65 + assert self.entity_items == 66 result = await self.tinker_thermostat( api, @@ -387,7 +387,7 @@ async def test_connect_anna_elga_2_cooling(self): ) await self.device_test(api, "2022-03-10 00:00:01", testdata) - assert self.entity_items == 65 + assert self.entity_items == 66 assert not self.notifications assert self.cooling_present @@ -440,7 +440,7 @@ async def test_connect_anna_loria_heating_idle(self): ) await self.device_test(api, "2022-05-16 00:00:01", testdata) - assert self.entity_items == 68 + assert self.entity_items == 67 assert self.cooling_present assert not self._cooling_enabled @@ -505,7 +505,7 @@ async def test_connect_anna_loria_cooling_active(self): ) await self.device_test(api, "2022-05-16 00:00:01", testdata) - assert self.entity_items == 68 + assert self.entity_items == 67 assert self.cooling_present assert self._cooling_enabled @@ -528,7 +528,7 @@ async def test_connect_anna_loria_driessens(self): ) await self.device_test(api, "2022-05-16 00:00:01", testdata) - assert self.entity_items == 68 + assert self.entity_items == 67 assert self.cooling_present assert not self._cooling_enabled From 8ae1e252bf4f0125367cd0dcf3d060ea3a52fb4a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 12 Jun 2026 20:07:43 +0200 Subject: [PATCH 21/89] Correct test-json --- tests/data/anna/anna_elga_no_cooling.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/data/anna/anna_elga_no_cooling.json b/tests/data/anna/anna_elga_no_cooling.json index 756f47148..f2f2af764 100644 --- a/tests/data/anna/anna_elga_no_cooling.json +++ b/tests/data/anna/anna_elga_no_cooling.json @@ -4,10 +4,6 @@ "plugwise_notification": false }, "dev_class": "gateway", - "dhw_modes": [ - "comfort", - "off" - ], "firmware": "4.0.15", "hardware": "AME Smile 2.0 board", "location": "a57efe5f145f498c9be62a9b63626fbf", @@ -31,6 +27,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "max_dhw_temperature": { "lower_bound": 35.0, From 9184e3ed0b79a2a822d80039df521d29e1e9465f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 09:12:59 +0200 Subject: [PATCH 22/89] Don't overwrite select_dhw_mode --- plugwise/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 14ba5f8c5..0079eda63 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -472,7 +472,7 @@ def _appliance_measurements( case "elga_status_code": data["elga_status_code"] = int(appl_p_loc.text) case "select_dhw_mode": - if self._dhw_allowed_modes: + if self._dhw_allowed_modes and "select_dhw_mode" not in data: data["select_dhw_mode"] = appl_p_loc.text if old_measurement == "domestic_hot_water_comfort_mode": data["select_dhw_mode"] = ( From 4521707fe0ddae55142addbaf5445790b01a2ca8 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 09:17:51 +0200 Subject: [PATCH 23/89] Update entity_items count --- tests/test_anna.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_anna.py b/tests/test_anna.py index 2aba648ae..66c0973ea 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -551,7 +551,7 @@ async def test_connect_anna_p1(self): ) await self.device_test(api, "2025-11-02 00:00:01", testdata) - assert self.entity_items == 76 + assert self.entity_items == 77 await api.close_connection() await self.disconnect(server, client) From fb1f9454e20986c21c069d8207a87dbd9ce8cb4b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 09:18:54 +0200 Subject: [PATCH 24/89] Clean up --- plugwise/constants.py | 5 ----- plugwise/helper.py | 1 - 2 files changed, 6 deletions(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index 85e901b1c..43816f904 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -406,11 +406,6 @@ "cooling_ena_switch", "dhw_cm_switch", ] -TOGGLES: Final[dict[str, ToggleNameType]] = { - "cooling_enabled": "cooling_ena_switch", - "domestic_hot_water_comfort_mode": "dhw_cm_switch", -} - ZONE_THERMOSTATS: Final[tuple[str, ...]] = ( "thermostat", "thermostatic_radiator_valve", diff --git a/plugwise/helper.py b/plugwise/helper.py index 0079eda63..91d8f4d0a 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -32,7 +32,6 @@ TEMP_CELSIUS, THERMO_MATCHING, THERMOSTAT_CLASSES, - TOGGLES, UOM, ZONE_MEASUREMENTS, ActuatorData, From d7fe43c839bc63a3e14efba18b4bf3b99adbeb3b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 09:21:53 +0200 Subject: [PATCH 25/89] Revert typing-update and adapt --- plugwise/helper.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 91d8f4d0a..8b5a44b6b 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -272,7 +272,7 @@ def _collect_dhw_modes(self, appliance: etree.Element) -> None: # Collect the default dhw modes: comfort and off if not self._dhw_allowed_modes: self._get_toggle_state( - appliance, "domestic_hot_water_comfort_mode", "dhw_cm_switch", None + appliance, "domestic_hot_water_comfort_mode", "dhw_cm_switch", {} ) def _appl_gateway_info(self, appl: Munch, appliance: etree.Element) -> Munch: @@ -490,7 +490,11 @@ def _appliance_measurements( self._count = count_data_items(self._count, data) def _get_toggle_state( - self, xml: etree.Element, toggle: str, name: ToggleNameType, data: GwEntityData | None + self, + xml: etree.Element, + toggle: str, + name: ToggleNameType, + data: GwEntityData, ) -> None: """Helper-function for _get_measurement_data(). From 877d7c53f178e099c0d083e46b98926447fcdb02 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 09:29:19 +0200 Subject: [PATCH 26/89] Revert SmileSwitches removal --- plugwise/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise/constants.py b/plugwise/constants.py index 43816f904..1a7ff4389 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -500,6 +500,7 @@ class SmileSwitches(TypedDict, total=False): """Smile Switches class.""" cooling_ena_switch: bool + dhw_cm_switch: bool lock: bool relay: bool From bb412b0c47343a45f66191fd6b4476327da27a0d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 09:30:33 +0200 Subject: [PATCH 27/89] Save fixture updates --- fixtures/anna_elga_2/data.json | 8 +++++--- fixtures/anna_elga_2_cooling/data.json | 8 +++++--- fixtures/anna_elga_2_schedule_off/data.json | 8 +++++--- fixtures/anna_elga_no_cooling/data.json | 8 +++++--- fixtures/anna_heatpump_cooling/data.json | 8 +++++--- fixtures/anna_heatpump_cooling_fake_firmware/data.json | 8 +++++--- fixtures/anna_heatpump_heating/data.json | 8 +++++--- fixtures/anna_loria_cooling_active/data.json | 3 +-- fixtures/anna_loria_driessens/data.json | 3 +-- fixtures/anna_loria_heating_idle/data.json | 3 +-- fixtures/anna_p1/data.json | 8 +++++--- fixtures/m_adam_cooling/data.json | 8 +++++--- fixtures/m_adam_heating/data.json | 8 +++++--- fixtures/m_adam_heating_off_schedule/data.json | 8 +++++--- fixtures/m_adam_jip/data.json | 8 +++++--- fixtures/m_anna_heatpump_cooling/data.json | 8 +++++--- fixtures/m_anna_heatpump_idle/data.json | 8 +++++--- 17 files changed, 73 insertions(+), 48 deletions(-) diff --git a/fixtures/anna_elga_2/data.json b/fixtures/anna_elga_2/data.json index 0611a5a7c..0d25467b4 100644 --- a/fixtures/anna_elga_2/data.json +++ b/fixtures/anna_elga_2/data.json @@ -11,9 +11,14 @@ "secondary_boiler_state": true }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "d34dfe6ab90b410c98068e75de3eb631", "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "domestic_hot_water_setpoint": 60.0, "intended_boiler_temperature": 58.3, @@ -23,9 +28,6 @@ "water_pressure": 0.5, "water_temperature": 42.6 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "ebd90df1ab334565b5895f37590ccff4": { diff --git a/fixtures/anna_elga_2_cooling/data.json b/fixtures/anna_elga_2_cooling/data.json index e78a5ea40..235d0bce5 100644 --- a/fixtures/anna_elga_2_cooling/data.json +++ b/fixtures/anna_elga_2_cooling/data.json @@ -11,6 +11,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "d34dfe6ab90b410c98068e75de3eb631", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -20,6 +24,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "domestic_hot_water_setpoint": 60.0, "intended_boiler_temperature": 0.0, @@ -29,9 +34,6 @@ "water_pressure": 0.5, "water_temperature": 22.8 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Techneco" }, "ebd90df1ab334565b5895f37590ccff4": { diff --git a/fixtures/anna_elga_2_schedule_off/data.json b/fixtures/anna_elga_2_schedule_off/data.json index 3bc5f122f..1da73a1fa 100644 --- a/fixtures/anna_elga_2_schedule_off/data.json +++ b/fixtures/anna_elga_2_schedule_off/data.json @@ -11,6 +11,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "d34dfe6ab90b410c98068e75de3eb631", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -20,6 +24,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "domestic_hot_water_setpoint": 60.0, "intended_boiler_temperature": 0.0, @@ -29,9 +34,6 @@ "water_pressure": 0.5, "water_temperature": 22.8 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Techneco" }, "ebd90df1ab334565b5895f37590ccff4": { diff --git a/fixtures/anna_elga_no_cooling/data.json b/fixtures/anna_elga_no_cooling/data.json index 9075d01e7..8df4581c4 100644 --- a/fixtures/anna_elga_no_cooling/data.json +++ b/fixtures/anna_elga_no_cooling/data.json @@ -27,6 +27,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "max_dhw_temperature": { "lower_bound": 35.0, @@ -42,6 +46,7 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, @@ -51,9 +56,6 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/fixtures/anna_heatpump_cooling/data.json b/fixtures/anna_heatpump_cooling/data.json index f1bdd86fe..7c03b25d2 100644 --- a/fixtures/anna_heatpump_cooling/data.json +++ b/fixtures/anna_heatpump_cooling/data.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -38,6 +42,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 41.5, "domestic_hot_water_setpoint": 60.0, @@ -48,9 +53,6 @@ "water_pressure": 1.61, "water_temperature": 24.7 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/fixtures/anna_heatpump_cooling_fake_firmware/data.json b/fixtures/anna_heatpump_cooling_fake_firmware/data.json index f08a11bba..e2865ce40 100644 --- a/fixtures/anna_heatpump_cooling_fake_firmware/data.json +++ b/fixtures/anna_heatpump_cooling_fake_firmware/data.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "maximum_boiler_temperature": { "lower_bound": 0.0, @@ -38,6 +42,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 41.5, "domestic_hot_water_setpoint": 60.0, @@ -48,9 +53,6 @@ "water_pressure": 1.61, "water_temperature": 24.7 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/fixtures/anna_heatpump_heating/data.json b/fixtures/anna_heatpump_heating/data.json index 2d90b6f12..00f3c821c 100644 --- a/fixtures/anna_heatpump_heating/data.json +++ b/fixtures/anna_heatpump_heating/data.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "max_dhw_temperature": { "lower_bound": 35.0, @@ -44,6 +48,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, @@ -53,9 +58,6 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/fixtures/anna_loria_cooling_active/data.json b/fixtures/anna_loria_cooling_active/data.json index 66b6e1940..87ace82fe 100644 --- a/fixtures/anna_loria_cooling_active/data.json +++ b/fixtures/anna_loria_cooling_active/data.json @@ -104,8 +104,7 @@ "water_temperature": 25.3 }, "switches": { - "cooling_ena_switch": true, - "dhw_cm_switch": true + "cooling_ena_switch": true }, "vendor": "Atlantic" } diff --git a/fixtures/anna_loria_driessens/data.json b/fixtures/anna_loria_driessens/data.json index 06f266420..0cf0cdc26 100644 --- a/fixtures/anna_loria_driessens/data.json +++ b/fixtures/anna_loria_driessens/data.json @@ -106,8 +106,7 @@ "water_temperature": 23.3 }, "switches": { - "cooling_ena_switch": false, - "dhw_cm_switch": true + "cooling_ena_switch": false }, "vendor": "Atlantic" } diff --git a/fixtures/anna_loria_heating_idle/data.json b/fixtures/anna_loria_heating_idle/data.json index 62e1ca542..961accc91 100644 --- a/fixtures/anna_loria_heating_idle/data.json +++ b/fixtures/anna_loria_heating_idle/data.json @@ -104,8 +104,7 @@ "water_temperature": 25.3 }, "switches": { - "cooling_ena_switch": false, - "dhw_cm_switch": true + "cooling_ena_switch": false }, "vendor": "Atlantic" } diff --git a/fixtures/anna_p1/data.json b/fixtures/anna_p1/data.json index 5918dc8fb..6ee72b54c 100644 --- a/fixtures/anna_p1/data.json +++ b/fixtures/anna_p1/data.json @@ -48,19 +48,21 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "da7be222ab3b420c927f3e49fade0304", "model": "Generic heater", "model_id": "HR24", "name": "OpenTherm", + "select_dhw_mode": "comfort", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, "water_pressure": 6.0, "water_temperature": 35.0 }, - "switches": { - "dhw_cm_switch": true - }, "vendor": "Intergas" }, "53130847be2f436cb946b78dedb9053a": { diff --git a/fixtures/m_adam_cooling/data.json b/fixtures/m_adam_cooling/data.json index d1b3753ea..c5b321660 100644 --- a/fixtures/m_adam_cooling/data.json +++ b/fixtures/m_adam_cooling/data.json @@ -8,6 +8,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "bc93488efab249e5bc54fd7e175a6f91", "maximum_boiler_temperature": { "lower_bound": 25.0, @@ -17,12 +21,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 17.5, "water_temperature": 19.0 - }, - "switches": { - "dhw_cm_switch": false } }, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6": { diff --git a/fixtures/m_adam_heating/data.json b/fixtures/m_adam_heating/data.json index 208b6df84..f29aa84c7 100644 --- a/fixtures/m_adam_heating/data.json +++ b/fixtures/m_adam_heating/data.json @@ -7,6 +7,10 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "bc93488efab249e5bc54fd7e175a6f91", "max_dhw_temperature": { "lower_bound": 40.0, @@ -22,12 +26,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 38.1, "water_temperature": 37.0 - }, - "switches": { - "dhw_cm_switch": false } }, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6": { diff --git a/fixtures/m_adam_heating_off_schedule/data.json b/fixtures/m_adam_heating_off_schedule/data.json index 3aeb08f30..aaabc1495 100644 --- a/fixtures/m_adam_heating_off_schedule/data.json +++ b/fixtures/m_adam_heating_off_schedule/data.json @@ -7,6 +7,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "bc93488efab249e5bc54fd7e175a6f91", "max_dhw_temperature": { "lower_bound": 40.0, @@ -22,12 +26,10 @@ }, "model": "Generic heater", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "water_temperature": 37.0 - }, - "switches": { - "dhw_cm_switch": false } }, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6": { diff --git a/fixtures/m_adam_jip/data.json b/fixtures/m_adam_jip/data.json index a473c7748..573614cb8 100644 --- a/fixtures/m_adam_jip/data.json +++ b/fixtures/m_adam_jip/data.json @@ -393,6 +393,10 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "9e4433a9d69f40b3aefd15e74395eaec", "max_dhw_temperature": { "lower_bound": 40.0, @@ -409,6 +413,7 @@ "model": "Generic heater", "model_id": "10.20", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, @@ -416,9 +421,6 @@ "water_pressure": 1.4, "water_temperature": 37.3 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Remeha B.V." }, "f61f1a2535f54f52ad006a3d18e459ca": { diff --git a/fixtures/m_anna_heatpump_cooling/data.json b/fixtures/m_anna_heatpump_cooling/data.json index e12f137bd..936345082 100644 --- a/fixtures/m_anna_heatpump_cooling/data.json +++ b/fixtures/m_anna_heatpump_cooling/data.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "max_dhw_temperature": { "lower_bound": 35.0, @@ -44,6 +48,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 41.5, "intended_boiler_temperature": 0.0, @@ -53,9 +58,6 @@ "water_pressure": 1.57, "water_temperature": 22.7 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { diff --git a/fixtures/m_anna_heatpump_idle/data.json b/fixtures/m_anna_heatpump_idle/data.json index 5e3c3c6f3..0d01bd40d 100644 --- a/fixtures/m_anna_heatpump_idle/data.json +++ b/fixtures/m_anna_heatpump_idle/data.json @@ -29,6 +29,10 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_modes": [ + "comfort", + "off" + ], "location": "a57efe5f145f498c9be62a9b63626fbf", "max_dhw_temperature": { "lower_bound": 35.0, @@ -44,6 +48,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", + "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 18.0, @@ -53,9 +58,6 @@ "water_pressure": 1.57, "water_temperature": 19.1 }, - "switches": { - "dhw_cm_switch": false - }, "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { From d4d43eea4f5ff644853ae5441f81812d75c670e8 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 09:31:25 +0200 Subject: [PATCH 28/89] Clean up now unused code --- plugwise/smile.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 6acb4233a..a9c18793b 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -43,10 +43,6 @@ def model_to_switch_items(model: str, state: str, switch: Munch) -> tuple[str, M Helper function for set_switch_state(). """ match model: - case "dhw_cm_switch": - switch.device = "toggle" - switch.func_type = "toggle_functionality" - switch.act_type = "domestic_hot_water_comfort_mode" case "cooling_ena_switch": switch.device = "toggle" switch.func_type = "toggle_functionality" From 2a704696eaef6a5074145a8ff28d52aa36e8426e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 10:59:45 +0200 Subject: [PATCH 29/89] Improve old_measurement init, as suggested --- plugwise/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 8b5a44b6b..763e0e6f7 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -463,8 +463,8 @@ def _appliance_measurements( if skip_obsolete_measurements(appliance, measurement): continue + old_measurement = measurement if new_name := getattr(attrs, ATTR_NAME, None): - old_measurement = measurement measurement = new_name match measurement: From 604e8962ed9e8a67ebc1ec415fcc2f28841bbb3f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 11:05:47 +0200 Subject: [PATCH 30/89] Solve complexity --- plugwise/helper.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 763e0e6f7..52092a413 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -471,12 +471,7 @@ def _appliance_measurements( case "elga_status_code": data["elga_status_code"] = int(appl_p_loc.text) case "select_dhw_mode": - if self._dhw_allowed_modes and "select_dhw_mode" not in data: - data["select_dhw_mode"] = appl_p_loc.text - if old_measurement == "domestic_hot_water_comfort_mode": - data["select_dhw_mode"] = ( - "comfort" if appl_p_loc.text == "on" else "off" - ) + self._select_dhw_mode(appl_p_loc.text, data, old_measurement) common_match_cases(measurement, attrs, appl_p_loc, data) @@ -489,6 +484,15 @@ def _appliance_measurements( self._count = count_data_items(self._count, data) + def _select_dhw_mode(self, text: str, data: GwEntityData, measurement: str) -> None: + """Set the selected dhw mode.""" + if self._dhw_allowed_modes and "select_dhw_mode" not in data: + data["select_dhw_mode"] = text + if measurement == "domestic_hot_water_comfort_mode": + data["select_dhw_mode"] = ( + "comfort" if text == "on" else "off" + ) + def _get_toggle_state( self, xml: etree.Element, From 4cebfc1e8267718d751776c0dd6b5039d755d5f5 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 11:11:17 +0200 Subject: [PATCH 31/89] Ruffed --- plugwise/helper.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 52092a413..54a926a51 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -489,9 +489,7 @@ def _select_dhw_mode(self, text: str, data: GwEntityData, measurement: str) -> N if self._dhw_allowed_modes and "select_dhw_mode" not in data: data["select_dhw_mode"] = text if measurement == "domestic_hot_water_comfort_mode": - data["select_dhw_mode"] = ( - "comfort" if text == "on" else "off" - ) + data["select_dhw_mode"] = "comfort" if text == "on" else "off" def _get_toggle_state( self, From 4a813a8a07398afc2d5dc6fa6b9a10cbb0a6e583 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 11:22:36 +0200 Subject: [PATCH 32/89] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6746c458..2ee896c65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v1.12.0 + +- Replace the DHW-comfort-mode switch by a DHW mode selector to match the HA water_heater platform via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) + ## v1.11.4 - Correct Anna P1 detection via PR [#879](https://github.com/plugwise/python-plugwise/pull/879) From bf8d3dc1f61acca468dfc7fa01c73d06dfb1fdaa Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 11:23:04 +0200 Subject: [PATCH 33/89] Set to v1.12.0 release-version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3333fa6f4..997b1731c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.11.4" +version = "1.12.0" license = "MIT" description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" From 3bc032ce89eb51f773d0928c95c179832a42a2c9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 11:25:53 +0200 Subject: [PATCH 34/89] Fix linter issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee896c65..a7e62ff07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v1.12.0 -- Replace the DHW-comfort-mode switch by a DHW mode selector to match the HA water_heater platform via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) +- Replace the DHW-comfort-mode switch by a DHW mode selector to match the HA water_heater platform via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) ## v1.11.4 From 7aa12a1ab5552587af83e57a11e4f13bf4937416 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 11:38:09 +0200 Subject: [PATCH 35/89] Debug --- plugwise/data.py | 4 ++++ scripts/tests_and_coverage.sh | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugwise/data.py b/plugwise/data.py index eb244f0f1..f47212873 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -10,6 +10,7 @@ from plugwise.constants import ( ADAM, ANNA, + LOGGER, MAX_SETPOINT, MIN_SETPOINT, NONE, @@ -78,6 +79,9 @@ def _update_gw_entities(self) -> None: remove_empty_platform_dicts(entity) + if "max_dhw_temperature" in entity: + LOGGER.debug("HOI data: %s", entity) + def _detect_low_batteries(self) -> list[str]: """Helper-function updating the low-battery binary_sensor status from a Battery-is-low message.""" mac_address_list: list[str] = [] diff --git a/scripts/tests_and_coverage.sh b/scripts/tests_and_coverage.sh index 42aca60ec..89998b496 100755 --- a/scripts/tests_and_coverage.sh +++ b/scripts/tests_and_coverage.sh @@ -52,7 +52,8 @@ set +u if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "test_and_coverage" ] ; then # Python tests (rerun with debug if failures) - PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ + # PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || + PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ handle_command_error "python code testing" fi From 7e90f2cc1ba067cbbab6566c3008959ba4c1c388 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 11:45:27 +0200 Subject: [PATCH 36/89] Rename select_dhw_mode to water_heater_mode for applicable systems --- plugwise/data.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugwise/data.py b/plugwise/data.py index f47212873..86c89a7cd 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -80,7 +80,10 @@ def _update_gw_entities(self) -> None: remove_empty_platform_dicts(entity) if "max_dhw_temperature" in entity: - LOGGER.debug("HOI data: %s", entity) + mode = entity.get("select_dhw_mode") + if mode is not None: + entity.pop("select_dhw_mode") + entity["water_heater_mode"] = mode def _detect_low_batteries(self) -> list[str]: """Helper-function updating the low-battery binary_sensor status from a Battery-is-low message.""" From 0092f192fe05e66124a017bfe49d36940a37909a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 12:09:42 +0200 Subject: [PATCH 37/89] Update relevant test-json with water_heater_mode --- tests/data/adam/adam_bad_thermostat.json | 2 +- tests/data/adam/adam_heatpump_cooling.json | 2 +- tests/data/adam/adam_jip.json | 2 +- tests/data/adam/adam_onoff_cooling_fake_firmware.json | 2 +- tests/data/anna/anna_elga_no_cooling.json | 2 +- tests/data/anna/anna_heatpump_heating.json | 2 +- tests/data/anna/anna_loria_cooling_active.json | 2 +- tests/data/anna/anna_loria_driessens.json | 2 +- tests/data/anna/anna_loria_heating_idle.json | 2 +- tests/data/anna/anna_v4.json | 2 +- tests/data/anna/anna_v4_UPDATED_DATA.json | 2 +- tests/data/anna/anna_v4_dhw.json | 2 +- tests/data/anna/anna_v4_no_tag.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/data/adam/adam_bad_thermostat.json b/tests/data/adam/adam_bad_thermostat.json index 16978a60c..b2a3f9f5e 100644 --- a/tests/data/adam/adam_bad_thermostat.json +++ b/tests/data/adam/adam_bad_thermostat.json @@ -27,7 +27,7 @@ "model": "Generic heater", "model_id": "1.1", "name": "OpenTherm", - "select_dhw_mode": "off", + "water_heater_mode": "off", "sensors": { "dhw_temperature": 49.9, "intended_boiler_temperature": 0.0, diff --git a/tests/data/adam/adam_heatpump_cooling.json b/tests/data/adam/adam_heatpump_cooling.json index 604192067..d545ef19a 100644 --- a/tests/data/adam/adam_heatpump_cooling.json +++ b/tests/data/adam/adam_heatpump_cooling.json @@ -63,7 +63,7 @@ "model": "Generic heater/cooler", "model_id": "17.1", "name": "OpenTherm", - "select_dhw_mode": "comfort", + "water_heater_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, diff --git a/tests/data/adam/adam_jip.json b/tests/data/adam/adam_jip.json index 3c10a4ca7..e9dcca0ae 100644 --- a/tests/data/adam/adam_jip.json +++ b/tests/data/adam/adam_jip.json @@ -349,7 +349,7 @@ "model": "Generic heater", "model_id": "10.20", "name": "OpenTherm", - "select_dhw_mode": "off", + "water_heater_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, diff --git a/tests/data/adam/adam_onoff_cooling_fake_firmware.json b/tests/data/adam/adam_onoff_cooling_fake_firmware.json index 27d994a0e..dddcd372d 100644 --- a/tests/data/adam/adam_onoff_cooling_fake_firmware.json +++ b/tests/data/adam/adam_onoff_cooling_fake_firmware.json @@ -27,7 +27,7 @@ }, "model": "Unknown", "name": "OnOff", - "select_dhw_mode": "comfort", + "water_heater_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_elga_no_cooling.json b/tests/data/anna/anna_elga_no_cooling.json index f2f2af764..859ffc3b0 100644 --- a/tests/data/anna/anna_elga_no_cooling.json +++ b/tests/data/anna/anna_elga_no_cooling.json @@ -46,7 +46,7 @@ }, "model": "Generic heater", "name": "OpenTherm", - "select_dhw_mode": "off", + "water_heater_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, diff --git a/tests/data/anna/anna_heatpump_heating.json b/tests/data/anna/anna_heatpump_heating.json index 809f9afea..fb4bbeb50 100644 --- a/tests/data/anna/anna_heatpump_heating.json +++ b/tests/data/anna/anna_heatpump_heating.json @@ -48,7 +48,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", - "select_dhw_mode": "off", + "water_heater_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, diff --git a/tests/data/anna/anna_loria_cooling_active.json b/tests/data/anna/anna_loria_cooling_active.json index 7d16279cc..710f524d8 100644 --- a/tests/data/anna/anna_loria_cooling_active.json +++ b/tests/data/anna/anna_loria_cooling_active.json @@ -78,7 +78,7 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "select_dhw_mode": "auto", + "water_heater_mode": "auto", "sensors": { "dhw_temperature": 52.9, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_loria_driessens.json b/tests/data/anna/anna_loria_driessens.json index 809904ecd..005b0e972 100644 --- a/tests/data/anna/anna_loria_driessens.json +++ b/tests/data/anna/anna_loria_driessens.json @@ -84,7 +84,7 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "select_dhw_mode": "auto", + "water_heater_mode": "auto", "sensors": { "dhw_temperature": 49.5, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_loria_heating_idle.json b/tests/data/anna/anna_loria_heating_idle.json index c6f83d35a..5d9d9f379 100644 --- a/tests/data/anna/anna_loria_heating_idle.json +++ b/tests/data/anna/anna_loria_heating_idle.json @@ -78,7 +78,7 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "select_dhw_mode": "auto", + "water_heater_mode": "auto", "sensors": { "dhw_temperature": 52.9, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_v4.json b/tests/data/anna/anna_v4.json index 6696e2511..50a752c96 100644 --- a/tests/data/anna/anna_v4.json +++ b/tests/data/anna/anna_v4.json @@ -74,7 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "select_dhw_mode": "off", + "water_heater_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, diff --git a/tests/data/anna/anna_v4_UPDATED_DATA.json b/tests/data/anna/anna_v4_UPDATED_DATA.json index 8e0568a11..882b3d481 100644 --- a/tests/data/anna/anna_v4_UPDATED_DATA.json +++ b/tests/data/anna/anna_v4_UPDATED_DATA.json @@ -18,7 +18,7 @@ "heating_state": false, "flame_state": false }, - "select_dhw_mode": "comfort", + "water_heater_mode": "comfort", "sensors": { "water_temperature": 51.0, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_v4_dhw.json b/tests/data/anna/anna_v4_dhw.json index 504ac4592..5024efde7 100644 --- a/tests/data/anna/anna_v4_dhw.json +++ b/tests/data/anna/anna_v4_dhw.json @@ -74,7 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "select_dhw_mode": "off", + "water_heater_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, diff --git a/tests/data/anna/anna_v4_no_tag.json b/tests/data/anna/anna_v4_no_tag.json index a9c448f3f..cb8bceed0 100644 --- a/tests/data/anna/anna_v4_no_tag.json +++ b/tests/data/anna/anna_v4_no_tag.json @@ -74,7 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "select_dhw_mode": "off", + "water_heater_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, From f15c0afc4fcc4917e41eff0db750df8190833e92 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 12:10:32 +0200 Subject: [PATCH 38/89] Save updated fixtures --- fixtures/adam_bad_thermostat/data.json | 4 ++-- fixtures/adam_heatpump_cooling/data.json | 4 ++-- fixtures/adam_jip/data.json | 4 ++-- fixtures/adam_onoff_cooling_fake_firmware/data.json | 4 ++-- fixtures/anna_elga_no_cooling/data.json | 4 ++-- fixtures/anna_heatpump_heating/data.json | 4 ++-- fixtures/anna_loria_cooling_active/data.json | 4 ++-- fixtures/anna_loria_driessens/data.json | 4 ++-- fixtures/anna_loria_heating_idle/data.json | 4 ++-- fixtures/anna_v4/data.json | 4 ++-- fixtures/anna_v4_dhw/data.json | 4 ++-- fixtures/anna_v4_no_tag/data.json | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/fixtures/adam_bad_thermostat/data.json b/fixtures/adam_bad_thermostat/data.json index 16978a60c..81854f325 100644 --- a/fixtures/adam_bad_thermostat/data.json +++ b/fixtures/adam_bad_thermostat/data.json @@ -27,7 +27,6 @@ "model": "Generic heater", "model_id": "1.1", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "dhw_temperature": 49.9, "intended_boiler_temperature": 0.0, @@ -36,7 +35,8 @@ "return_temperature": 33.0, "water_temperature": 20.9 }, - "vendor": "WeHeat" + "vendor": "WeHeat", + "water_heater_mode": "off" }, "3ee6b9486cb04c258a3130fff2b144a4": { "active_preset": "home", diff --git a/fixtures/adam_heatpump_cooling/data.json b/fixtures/adam_heatpump_cooling/data.json index d0dc677c0..b1699c4cd 100644 --- a/fixtures/adam_heatpump_cooling/data.json +++ b/fixtures/adam_heatpump_cooling/data.json @@ -75,7 +75,6 @@ "model": "Generic heater/cooler", "model_id": "17.1", "name": "OpenTherm", - "select_dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, @@ -85,7 +84,8 @@ "water_pressure": 2.0, "water_temperature": 24.5 }, - "vendor": "Remeha B.V." + "vendor": "Remeha B.V.", + "water_heater_mode": "comfort" }, "1053c8bbf8be43c6921742b146a625f1": { "available": true, diff --git a/fixtures/adam_jip/data.json b/fixtures/adam_jip/data.json index 315eba45b..f6b932066 100644 --- a/fixtures/adam_jip/data.json +++ b/fixtures/adam_jip/data.json @@ -414,7 +414,6 @@ "model": "Generic heater", "model_id": "10.20", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, @@ -422,7 +421,8 @@ "water_pressure": 1.4, "water_temperature": 37.3 }, - "vendor": "Remeha B.V." + "vendor": "Remeha B.V.", + "water_heater_mode": "off" }, "f61f1a2535f54f52ad006a3d18e459ca": { "available": true, diff --git a/fixtures/adam_onoff_cooling_fake_firmware/data.json b/fixtures/adam_onoff_cooling_fake_firmware/data.json index f1902e5a4..55f374d3c 100644 --- a/fixtures/adam_onoff_cooling_fake_firmware/data.json +++ b/fixtures/adam_onoff_cooling_fake_firmware/data.json @@ -27,7 +27,6 @@ }, "model": "Unknown", "name": "OnOff", - "select_dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, @@ -36,7 +35,8 @@ "return_temperature": 24.9, "water_pressure": 2.0, "water_temperature": 24.5 - } + }, + "water_heater_mode": "comfort" }, "7d97fc3117784cfdafe347bcedcbbbcb": { "binary_sensors": { diff --git a/fixtures/anna_elga_no_cooling/data.json b/fixtures/anna_elga_no_cooling/data.json index 8df4581c4..9412bf4ba 100644 --- a/fixtures/anna_elga_no_cooling/data.json +++ b/fixtures/anna_elga_no_cooling/data.json @@ -46,7 +46,6 @@ }, "model": "Generic heater", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, @@ -56,7 +55,8 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "vendor": "Techneco" + "vendor": "Techneco", + "water_heater_mode": "off" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", diff --git a/fixtures/anna_heatpump_heating/data.json b/fixtures/anna_heatpump_heating/data.json index 00f3c821c..b0b126a95 100644 --- a/fixtures/anna_heatpump_heating/data.json +++ b/fixtures/anna_heatpump_heating/data.json @@ -48,7 +48,6 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, @@ -58,7 +57,8 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "vendor": "Techneco" + "vendor": "Techneco", + "water_heater_mode": "off" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", diff --git a/fixtures/anna_loria_cooling_active/data.json b/fixtures/anna_loria_cooling_active/data.json index 87ace82fe..8d63f8936 100644 --- a/fixtures/anna_loria_cooling_active/data.json +++ b/fixtures/anna_loria_cooling_active/data.json @@ -94,7 +94,6 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "select_dhw_mode": "auto", "sensors": { "dhw_temperature": 52.9, "intended_boiler_temperature": 0.0, @@ -106,6 +105,7 @@ "switches": { "cooling_ena_switch": true }, - "vendor": "Atlantic" + "vendor": "Atlantic", + "water_heater_mode": "auto" } } diff --git a/fixtures/anna_loria_driessens/data.json b/fixtures/anna_loria_driessens/data.json index 0cf0cdc26..dcd2629fc 100644 --- a/fixtures/anna_loria_driessens/data.json +++ b/fixtures/anna_loria_driessens/data.json @@ -96,7 +96,6 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "select_dhw_mode": "auto", "sensors": { "dhw_temperature": 49.5, "intended_boiler_temperature": 0.0, @@ -108,6 +107,7 @@ "switches": { "cooling_ena_switch": false }, - "vendor": "Atlantic" + "vendor": "Atlantic", + "water_heater_mode": "auto" } } diff --git a/fixtures/anna_loria_heating_idle/data.json b/fixtures/anna_loria_heating_idle/data.json index 961accc91..c157de537 100644 --- a/fixtures/anna_loria_heating_idle/data.json +++ b/fixtures/anna_loria_heating_idle/data.json @@ -94,7 +94,6 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "select_dhw_mode": "auto", "sensors": { "dhw_temperature": 52.9, "intended_boiler_temperature": 0.0, @@ -106,6 +105,7 @@ "switches": { "cooling_ena_switch": false }, - "vendor": "Atlantic" + "vendor": "Atlantic", + "water_heater_mode": "auto" } } diff --git a/fixtures/anna_v4/data.json b/fixtures/anna_v4/data.json index ea4559532..3c40e0d89 100644 --- a/fixtures/anna_v4/data.json +++ b/fixtures/anna_v4/data.json @@ -87,7 +87,6 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -95,6 +94,7 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "vendor": "Bosch Thermotechniek B.V." + "vendor": "Bosch Thermotechniek B.V.", + "water_heater_mode": "off" } } diff --git a/fixtures/anna_v4_dhw/data.json b/fixtures/anna_v4_dhw/data.json index c5d40ae4e..c4c97bf08 100644 --- a/fixtures/anna_v4_dhw/data.json +++ b/fixtures/anna_v4_dhw/data.json @@ -87,7 +87,6 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -95,6 +94,7 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "vendor": "Bosch Thermotechniek B.V." + "vendor": "Bosch Thermotechniek B.V.", + "water_heater_mode": "off" } } diff --git a/fixtures/anna_v4_no_tag/data.json b/fixtures/anna_v4_no_tag/data.json index 4f2410b5e..9ac811ddc 100644 --- a/fixtures/anna_v4_no_tag/data.json +++ b/fixtures/anna_v4_no_tag/data.json @@ -87,7 +87,6 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, @@ -95,6 +94,7 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "vendor": "Bosch Thermotechniek B.V." + "vendor": "Bosch Thermotechniek B.V.", + "water_heater_mode": "off" } } From 61131ae30dca59be514b2ebcc81c6823d6edd20a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 12:11:56 +0200 Subject: [PATCH 39/89] Clean up, add comment --- plugwise/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/data.py b/plugwise/data.py index 86c89a7cd..e9b22c1bb 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -10,7 +10,6 @@ from plugwise.constants import ( ADAM, ANNA, - LOGGER, MAX_SETPOINT, MIN_SETPOINT, NONE, @@ -79,6 +78,7 @@ def _update_gw_entities(self) -> None: remove_empty_platform_dicts(entity) + # Replace select_dhw_mode with water_heater_mode when applicable if "max_dhw_temperature" in entity: mode = entity.get("select_dhw_mode") if mode is not None: From aee15273f04a958012a755b6988775346dc3d6b1 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 12:12:57 +0200 Subject: [PATCH 40/89] Back to normal test-output --- scripts/tests_and_coverage.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/tests_and_coverage.sh b/scripts/tests_and_coverage.sh index 89998b496..42aca60ec 100755 --- a/scripts/tests_and_coverage.sh +++ b/scripts/tests_and_coverage.sh @@ -52,8 +52,7 @@ set +u if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "test_and_coverage" ] ; then # Python tests (rerun with debug if failures) - # PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || - PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ + PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ handle_command_error "python code testing" fi From b69913122a4b90ba7f44cf218930893a7b011251 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 12:16:38 +0200 Subject: [PATCH 41/89] Update constants --- plugwise/constants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index 1a7ff4389..27e6d98d0 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -556,8 +556,9 @@ class GwEntityData(TypedDict, total=False): # Device availability available: bool | None - # Loria + # DHW mode related select_dhw_mode: str + water_heater_mode: str dhw_modes: list[str] # Gateway From ad62171031b64e3c3e6eda70e8c919b7d2700e6a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 12:17:43 +0200 Subject: [PATCH 42/89] Save updated manual fixtures --- fixtures/m_adam_jip/data.json | 4 ++-- fixtures/m_anna_heatpump_cooling/data.json | 4 ++-- fixtures/m_anna_heatpump_idle/data.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fixtures/m_adam_jip/data.json b/fixtures/m_adam_jip/data.json index 573614cb8..021649f96 100644 --- a/fixtures/m_adam_jip/data.json +++ b/fixtures/m_adam_jip/data.json @@ -413,7 +413,6 @@ "model": "Generic heater", "model_id": "10.20", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, @@ -421,7 +420,8 @@ "water_pressure": 1.4, "water_temperature": 37.3 }, - "vendor": "Remeha B.V." + "vendor": "Remeha B.V.", + "water_heater_mode": "off" }, "f61f1a2535f54f52ad006a3d18e459ca": { "available": true, diff --git a/fixtures/m_anna_heatpump_cooling/data.json b/fixtures/m_anna_heatpump_cooling/data.json index 936345082..eab63824d 100644 --- a/fixtures/m_anna_heatpump_cooling/data.json +++ b/fixtures/m_anna_heatpump_cooling/data.json @@ -48,7 +48,6 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "dhw_temperature": 41.5, "intended_boiler_temperature": 0.0, @@ -58,7 +57,8 @@ "water_pressure": 1.57, "water_temperature": 22.7 }, - "vendor": "Techneco" + "vendor": "Techneco", + "water_heater_mode": "off" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", diff --git a/fixtures/m_anna_heatpump_idle/data.json b/fixtures/m_anna_heatpump_idle/data.json index 0d01bd40d..7a21aa396 100644 --- a/fixtures/m_anna_heatpump_idle/data.json +++ b/fixtures/m_anna_heatpump_idle/data.json @@ -48,7 +48,6 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", - "select_dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 18.0, @@ -58,7 +57,8 @@ "water_pressure": 1.57, "water_temperature": 19.1 }, - "vendor": "Techneco" + "vendor": "Techneco", + "water_heater_mode": "off" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", From ce1030b4aa6d5463bb0aaf2a58afe99636edff13 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 13 Jun 2026 12:19:25 +0200 Subject: [PATCH 43/89] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7e62ff07..b5fa67870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v1.12.0 -- Replace the DHW-comfort-mode switch by a DHW mode selector to match the HA water_heater platform via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) +- Replace the DHW-comfort-mode switch by a DHW mode selector to match the HA select or water_heater platforms, via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) ## v1.11.4 From cc3a265ff82ad22a914f670cd8e2783cc2de3753 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 6 Jun 2026 11:35:30 +0200 Subject: [PATCH 44/89] Implement improvement suggestion --- plugwise/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index efe23dfd4..6767a3db4 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -210,10 +210,11 @@ async def _collect_smile_data( elec_point_meters = result.findall( "./location/logs/point_log/electricity_point_meter" ) - for meter in elec_point_meters: - if meter.get("id") and model == "smile_thermo": - self.smile.anna_p1 = True - break + if model == "smile_thermo": + for meter in elec_point_meters: + if meter.get("id"): + self.smile.anna_p1 = True + break else: model = await self._smile_detect_legacy(result, dsmrmain, model) From f36c42b06c749e2b94671a697e4a170de308ac03 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 14 Jun 2026 08:52:57 +0200 Subject: [PATCH 45/89] Change `water_heater_mode` to `dhw_mode` --- plugwise/constants.py | 4 ++-- plugwise/data.py | 2 +- tests/data/adam/adam_bad_thermostat.json | 2 +- tests/data/adam/adam_heatpump_cooling.json | 2 +- tests/data/adam/adam_jip.json | 2 +- tests/data/adam/adam_onoff_cooling_fake_firmware.json | 2 +- tests/data/anna/anna_elga_no_cooling.json | 2 +- tests/data/anna/anna_heatpump_heating.json | 2 +- tests/data/anna/anna_loria_cooling_active.json | 2 +- tests/data/anna/anna_loria_driessens.json | 2 +- tests/data/anna/anna_loria_heating_idle.json | 2 +- tests/data/anna/anna_v4.json | 2 +- tests/data/anna/anna_v4_UPDATED_DATA.json | 2 +- tests/data/anna/anna_v4_dhw.json | 2 +- tests/data/anna/anna_v4_no_tag.json | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index 27e6d98d0..55e4b8e91 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -557,9 +557,9 @@ class GwEntityData(TypedDict, total=False): available: bool | None # DHW mode related - select_dhw_mode: str - water_heater_mode: str + dhw_mode: str dhw_modes: list[str] + select_dhw_mode: str # Gateway gateway_modes: list[str] diff --git a/plugwise/data.py b/plugwise/data.py index e9b22c1bb..566f9e714 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -83,7 +83,7 @@ def _update_gw_entities(self) -> None: mode = entity.get("select_dhw_mode") if mode is not None: entity.pop("select_dhw_mode") - entity["water_heater_mode"] = mode + entity["dhw_mode"] = mode def _detect_low_batteries(self) -> list[str]: """Helper-function updating the low-battery binary_sensor status from a Battery-is-low message.""" diff --git a/tests/data/adam/adam_bad_thermostat.json b/tests/data/adam/adam_bad_thermostat.json index b2a3f9f5e..71b54b9a0 100644 --- a/tests/data/adam/adam_bad_thermostat.json +++ b/tests/data/adam/adam_bad_thermostat.json @@ -27,7 +27,7 @@ "model": "Generic heater", "model_id": "1.1", "name": "OpenTherm", - "water_heater_mode": "off", + "dhw_mode": "off", "sensors": { "dhw_temperature": 49.9, "intended_boiler_temperature": 0.0, diff --git a/tests/data/adam/adam_heatpump_cooling.json b/tests/data/adam/adam_heatpump_cooling.json index d545ef19a..e2401f79a 100644 --- a/tests/data/adam/adam_heatpump_cooling.json +++ b/tests/data/adam/adam_heatpump_cooling.json @@ -63,7 +63,7 @@ "model": "Generic heater/cooler", "model_id": "17.1", "name": "OpenTherm", - "water_heater_mode": "comfort", + "dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, diff --git a/tests/data/adam/adam_jip.json b/tests/data/adam/adam_jip.json index e9dcca0ae..7ae98246a 100644 --- a/tests/data/adam/adam_jip.json +++ b/tests/data/adam/adam_jip.json @@ -349,7 +349,7 @@ "model": "Generic heater", "model_id": "10.20", "name": "OpenTherm", - "water_heater_mode": "off", + "dhw_mode": "off", "sensors": { "intended_boiler_temperature": 0.0, "modulation_level": 0.0, diff --git a/tests/data/adam/adam_onoff_cooling_fake_firmware.json b/tests/data/adam/adam_onoff_cooling_fake_firmware.json index dddcd372d..c31a7710c 100644 --- a/tests/data/adam/adam_onoff_cooling_fake_firmware.json +++ b/tests/data/adam/adam_onoff_cooling_fake_firmware.json @@ -27,7 +27,7 @@ }, "model": "Unknown", "name": "OnOff", - "water_heater_mode": "comfort", + "dhw_mode": "comfort", "sensors": { "dhw_temperature": 63.5, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_elga_no_cooling.json b/tests/data/anna/anna_elga_no_cooling.json index 859ffc3b0..4bb9b040c 100644 --- a/tests/data/anna/anna_elga_no_cooling.json +++ b/tests/data/anna/anna_elga_no_cooling.json @@ -46,7 +46,7 @@ }, "model": "Generic heater", "name": "OpenTherm", - "water_heater_mode": "off", + "dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, diff --git a/tests/data/anna/anna_heatpump_heating.json b/tests/data/anna/anna_heatpump_heating.json index fb4bbeb50..d38345a3a 100644 --- a/tests/data/anna/anna_heatpump_heating.json +++ b/tests/data/anna/anna_heatpump_heating.json @@ -48,7 +48,7 @@ }, "model": "Generic heater/cooler", "name": "OpenTherm", - "water_heater_mode": "off", + "dhw_mode": "off", "sensors": { "dhw_temperature": 46.3, "intended_boiler_temperature": 35.0, diff --git a/tests/data/anna/anna_loria_cooling_active.json b/tests/data/anna/anna_loria_cooling_active.json index 710f524d8..1dcb2e851 100644 --- a/tests/data/anna/anna_loria_cooling_active.json +++ b/tests/data/anna/anna_loria_cooling_active.json @@ -78,7 +78,7 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "water_heater_mode": "auto", + "dhw_mode": "auto", "sensors": { "dhw_temperature": 52.9, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_loria_driessens.json b/tests/data/anna/anna_loria_driessens.json index 005b0e972..3d665c289 100644 --- a/tests/data/anna/anna_loria_driessens.json +++ b/tests/data/anna/anna_loria_driessens.json @@ -84,7 +84,7 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "water_heater_mode": "auto", + "dhw_mode": "auto", "sensors": { "dhw_temperature": 49.5, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_loria_heating_idle.json b/tests/data/anna/anna_loria_heating_idle.json index 5d9d9f379..628515d57 100644 --- a/tests/data/anna/anna_loria_heating_idle.json +++ b/tests/data/anna/anna_loria_heating_idle.json @@ -78,7 +78,7 @@ "model": "Generic heater/cooler", "model_id": "173", "name": "OpenTherm", - "water_heater_mode": "auto", + "dhw_mode": "auto", "sensors": { "dhw_temperature": 52.9, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_v4.json b/tests/data/anna/anna_v4.json index 50a752c96..09f295bad 100644 --- a/tests/data/anna/anna_v4.json +++ b/tests/data/anna/anna_v4.json @@ -74,7 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "water_heater_mode": "off", + "dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, diff --git a/tests/data/anna/anna_v4_UPDATED_DATA.json b/tests/data/anna/anna_v4_UPDATED_DATA.json index 882b3d481..e36a6e5b1 100644 --- a/tests/data/anna/anna_v4_UPDATED_DATA.json +++ b/tests/data/anna/anna_v4_UPDATED_DATA.json @@ -18,7 +18,7 @@ "heating_state": false, "flame_state": false }, - "water_heater_mode": "comfort", + "dhw_mode": "comfort", "sensors": { "water_temperature": 51.0, "intended_boiler_temperature": 0.0, diff --git a/tests/data/anna/anna_v4_dhw.json b/tests/data/anna/anna_v4_dhw.json index 5024efde7..dc8ba3858 100644 --- a/tests/data/anna/anna_v4_dhw.json +++ b/tests/data/anna/anna_v4_dhw.json @@ -74,7 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "water_heater_mode": "off", + "dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, diff --git a/tests/data/anna/anna_v4_no_tag.json b/tests/data/anna/anna_v4_no_tag.json index cb8bceed0..d9a500cfc 100644 --- a/tests/data/anna/anna_v4_no_tag.json +++ b/tests/data/anna/anna_v4_no_tag.json @@ -74,7 +74,7 @@ "model": "Generic heater", "model_id": "2.32", "name": "OpenTherm", - "water_heater_mode": "off", + "dhw_mode": "off", "sensors": { "intended_boiler_temperature": 39.9, "modulation_level": 0.0, From 8bde080826c2e1d414afbf7be6effa05f8d6ce5b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 14 Jun 2026 08:57:36 +0200 Subject: [PATCH 46/89] Save updated fixtures --- fixtures/adam_bad_thermostat/data.json | 4 ++-- fixtures/adam_heatpump_cooling/data.json | 4 ++-- fixtures/adam_jip/data.json | 4 ++-- fixtures/adam_onoff_cooling_fake_firmware/data.json | 4 ++-- fixtures/anna_elga_no_cooling/data.json | 4 ++-- fixtures/anna_heatpump_heating/data.json | 4 ++-- fixtures/anna_loria_cooling_active/data.json | 4 ++-- fixtures/anna_loria_driessens/data.json | 4 ++-- fixtures/anna_loria_heating_idle/data.json | 4 ++-- fixtures/anna_v4/data.json | 4 ++-- fixtures/anna_v4_dhw/data.json | 4 ++-- fixtures/anna_v4_no_tag/data.json | 4 ++-- fixtures/m_adam_jip/data.json | 4 ++-- fixtures/m_anna_heatpump_cooling/data.json | 4 ++-- fixtures/m_anna_heatpump_idle/data.json | 4 ++-- 15 files changed, 30 insertions(+), 30 deletions(-) diff --git a/fixtures/adam_bad_thermostat/data.json b/fixtures/adam_bad_thermostat/data.json index 81854f325..14d7d4c4d 100644 --- a/fixtures/adam_bad_thermostat/data.json +++ b/fixtures/adam_bad_thermostat/data.json @@ -7,6 +7,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -35,8 +36,7 @@ "return_temperature": 33.0, "water_temperature": 20.9 }, - "vendor": "WeHeat", - "water_heater_mode": "off" + "vendor": "WeHeat" }, "3ee6b9486cb04c258a3130fff2b144a4": { "active_preset": "home", diff --git a/fixtures/adam_heatpump_cooling/data.json b/fixtures/adam_heatpump_cooling/data.json index b1699c4cd..0102ce080 100644 --- a/fixtures/adam_heatpump_cooling/data.json +++ b/fixtures/adam_heatpump_cooling/data.json @@ -55,6 +55,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "comfort", "dhw_modes": [ "comfort", "off" @@ -84,8 +85,7 @@ "water_pressure": 2.0, "water_temperature": 24.5 }, - "vendor": "Remeha B.V.", - "water_heater_mode": "comfort" + "vendor": "Remeha B.V." }, "1053c8bbf8be43c6921742b146a625f1": { "available": true, diff --git a/fixtures/adam_jip/data.json b/fixtures/adam_jip/data.json index f6b932066..bbf89908e 100644 --- a/fixtures/adam_jip/data.json +++ b/fixtures/adam_jip/data.json @@ -394,6 +394,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -421,8 +422,7 @@ "water_pressure": 1.4, "water_temperature": 37.3 }, - "vendor": "Remeha B.V.", - "water_heater_mode": "off" + "vendor": "Remeha B.V." }, "f61f1a2535f54f52ad006a3d18e459ca": { "available": true, diff --git a/fixtures/adam_onoff_cooling_fake_firmware/data.json b/fixtures/adam_onoff_cooling_fake_firmware/data.json index 55f374d3c..c35ef647f 100644 --- a/fixtures/adam_onoff_cooling_fake_firmware/data.json +++ b/fixtures/adam_onoff_cooling_fake_firmware/data.json @@ -8,6 +8,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "comfort", "dhw_modes": [ "comfort", "off" @@ -35,8 +36,7 @@ "return_temperature": 24.9, "water_pressure": 2.0, "water_temperature": 24.5 - }, - "water_heater_mode": "comfort" + } }, "7d97fc3117784cfdafe347bcedcbbbcb": { "binary_sensors": { diff --git a/fixtures/anna_elga_no_cooling/data.json b/fixtures/anna_elga_no_cooling/data.json index 9412bf4ba..4a19bb86e 100644 --- a/fixtures/anna_elga_no_cooling/data.json +++ b/fixtures/anna_elga_no_cooling/data.json @@ -27,6 +27,7 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -55,8 +56,7 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "vendor": "Techneco", - "water_heater_mode": "off" + "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", diff --git a/fixtures/anna_heatpump_heating/data.json b/fixtures/anna_heatpump_heating/data.json index b0b126a95..54a045273 100644 --- a/fixtures/anna_heatpump_heating/data.json +++ b/fixtures/anna_heatpump_heating/data.json @@ -29,6 +29,7 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -57,8 +58,7 @@ "water_pressure": 1.57, "water_temperature": 29.1 }, - "vendor": "Techneco", - "water_heater_mode": "off" + "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", diff --git a/fixtures/anna_loria_cooling_active/data.json b/fixtures/anna_loria_cooling_active/data.json index 8d63f8936..f46113d09 100644 --- a/fixtures/anna_loria_cooling_active/data.json +++ b/fixtures/anna_loria_cooling_active/data.json @@ -71,6 +71,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "auto", "dhw_modes": [ "off", "auto", @@ -105,7 +106,6 @@ "switches": { "cooling_ena_switch": true }, - "vendor": "Atlantic", - "water_heater_mode": "auto" + "vendor": "Atlantic" } } diff --git a/fixtures/anna_loria_driessens/data.json b/fixtures/anna_loria_driessens/data.json index dcd2629fc..31d518a8f 100644 --- a/fixtures/anna_loria_driessens/data.json +++ b/fixtures/anna_loria_driessens/data.json @@ -73,6 +73,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "auto", "dhw_modes": [ "comfort", "eco", @@ -107,7 +108,6 @@ "switches": { "cooling_ena_switch": false }, - "vendor": "Atlantic", - "water_heater_mode": "auto" + "vendor": "Atlantic" } } diff --git a/fixtures/anna_loria_heating_idle/data.json b/fixtures/anna_loria_heating_idle/data.json index c157de537..c13abbbcf 100644 --- a/fixtures/anna_loria_heating_idle/data.json +++ b/fixtures/anna_loria_heating_idle/data.json @@ -71,6 +71,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "auto", "dhw_modes": [ "off", "auto", @@ -105,7 +106,6 @@ "switches": { "cooling_ena_switch": false }, - "vendor": "Atlantic", - "water_heater_mode": "auto" + "vendor": "Atlantic" } } diff --git a/fixtures/anna_v4/data.json b/fixtures/anna_v4/data.json index 3c40e0d89..563560b49 100644 --- a/fixtures/anna_v4/data.json +++ b/fixtures/anna_v4/data.json @@ -67,6 +67,7 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -94,7 +95,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "vendor": "Bosch Thermotechniek B.V.", - "water_heater_mode": "off" + "vendor": "Bosch Thermotechniek B.V." } } diff --git a/fixtures/anna_v4_dhw/data.json b/fixtures/anna_v4_dhw/data.json index c4c97bf08..9c1f13360 100644 --- a/fixtures/anna_v4_dhw/data.json +++ b/fixtures/anna_v4_dhw/data.json @@ -67,6 +67,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -94,7 +95,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "vendor": "Bosch Thermotechniek B.V.", - "water_heater_mode": "off" + "vendor": "Bosch Thermotechniek B.V." } } diff --git a/fixtures/anna_v4_no_tag/data.json b/fixtures/anna_v4_no_tag/data.json index 9ac811ddc..8fe2f6606 100644 --- a/fixtures/anna_v4_no_tag/data.json +++ b/fixtures/anna_v4_no_tag/data.json @@ -67,6 +67,7 @@ "heating_state": true }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -94,7 +95,6 @@ "water_pressure": 2.2, "water_temperature": 45.0 }, - "vendor": "Bosch Thermotechniek B.V.", - "water_heater_mode": "off" + "vendor": "Bosch Thermotechniek B.V." } } diff --git a/fixtures/m_adam_jip/data.json b/fixtures/m_adam_jip/data.json index 021649f96..ff33d77c2 100644 --- a/fixtures/m_adam_jip/data.json +++ b/fixtures/m_adam_jip/data.json @@ -393,6 +393,7 @@ "heating_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -420,8 +421,7 @@ "water_pressure": 1.4, "water_temperature": 37.3 }, - "vendor": "Remeha B.V.", - "water_heater_mode": "off" + "vendor": "Remeha B.V." }, "f61f1a2535f54f52ad006a3d18e459ca": { "available": true, diff --git a/fixtures/m_anna_heatpump_cooling/data.json b/fixtures/m_anna_heatpump_cooling/data.json index eab63824d..fb4085270 100644 --- a/fixtures/m_anna_heatpump_cooling/data.json +++ b/fixtures/m_anna_heatpump_cooling/data.json @@ -29,6 +29,7 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -57,8 +58,7 @@ "water_pressure": 1.57, "water_temperature": 22.7 }, - "vendor": "Techneco", - "water_heater_mode": "off" + "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", diff --git a/fixtures/m_anna_heatpump_idle/data.json b/fixtures/m_anna_heatpump_idle/data.json index 7a21aa396..e50349146 100644 --- a/fixtures/m_anna_heatpump_idle/data.json +++ b/fixtures/m_anna_heatpump_idle/data.json @@ -29,6 +29,7 @@ "secondary_boiler_state": false }, "dev_class": "heater_central", + "dhw_mode": "off", "dhw_modes": [ "comfort", "off" @@ -57,8 +58,7 @@ "water_pressure": 1.57, "water_temperature": 19.1 }, - "vendor": "Techneco", - "water_heater_mode": "off" + "vendor": "Techneco" }, "3cb70739631c4d17a86b8b12e8a5161b": { "active_preset": "home", From 867f777138ad5766c1df679226e6112d4957cf70 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 14 Jun 2026 09:34:15 +0200 Subject: [PATCH 47/89] Fix comment as suggested --- plugwise/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/data.py b/plugwise/data.py index 566f9e714..0448eb859 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -78,7 +78,7 @@ def _update_gw_entities(self) -> None: remove_empty_platform_dicts(entity) - # Replace select_dhw_mode with water_heater_mode when applicable + # Replace select_dhw_mode with dhw_mode when applicable if "max_dhw_temperature" in entity: mode = entity.get("select_dhw_mode") if mode is not None: From 352c357073b7df0658ca8c5b0ac41290dae3b1f2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 14 Jun 2026 13:24:18 +0200 Subject: [PATCH 48/89] Handle changed select and water_heater setters --- plugwise/smile.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index a9c18793b..19ee58def 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -43,6 +43,10 @@ def model_to_switch_items(model: str, state: str, switch: Munch) -> tuple[str, M Helper function for set_switch_state(). """ match model: + case "select_dhw_mode": + switch.device = "toggle" + switch.func_type = "toggle_functionality" + switch.act_type = "domestic_hot_water_comfort_mode" case "cooling_ena_switch": switch.device = "toggle" switch.func_type = "toggle_functionality" @@ -234,7 +238,8 @@ async def set_select( """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" match key: case "select_dhw_mode": - await self.set_dhw_mode(option) + state = STATE_ON if option == "comfort" else STATE_OFF + await self.set_switch_state(loc_id, None, key, state) case "select_gateway_mode": await self.set_gateway_mode(option) case "select_regulation_mode": @@ -245,18 +250,27 @@ async def set_select( case "select_zone_profile": await self.set_zone_profile(loc_id, option) - async def set_dhw_mode(self, mode: str) -> None: - """Set the domestic hot water heating regulation mode.""" + async def set_dhw_mode( + self, key: str, loc_id: str, length: int, mode: str + ) -> None: + """Set the domestic hot water mode.""" if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") - data = ( - "" - f"{mode}" - "" - ) - uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control" - await self.call_request(uri, method="put", data=data) + match length: + # Devices with the domestic_hot_water_comfort switch + case 2: + state = STATE_ON if mode == "comfort" else STATE_OFF + await self.set_switch_state(loc_id, None, key, state) + # Loria with extended dhw modes + case _: + data = ( + "" + f"{mode}" + "" + ) + uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control" + await self.call_request(uri, method="put", data=data) async def set_gateway_mode(self, mode: str) -> None: """Set the gateway mode.""" From d0646301d5a3ca8e4798b1ecf5b32eeed0de41ae Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 08:19:46 +0200 Subject: [PATCH 49/89] Update tinker_dhw_mode() --- tests/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_init.py b/tests/test_init.py index 1f8f3c803..53277f1e6 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -859,7 +859,7 @@ async def tinker_dhw_mode(api, unhappy=False): mode = mode[1:] _LOGGER.info("%s", f"- Adjusting dhw mode to {mode}{warning}") try: - await api.set_select("select_dhw_mode", "dummy", mode) + await api.set_dhw_mode("select_dhw_mode", 4, mode) _LOGGER.info(" + tinker_dhw_mode worked as intended") tinker_dhw_mode_passed = True except pw_exceptions.PlugwiseError: From 1f586a300b6b4be72510694c95e408c6dbf4590b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 18:57:12 +0200 Subject: [PATCH 50/89] Adapt set_dhw_mode() functions --- plugwise/__init__.py | 4 ++-- plugwise/smile.py | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 6767a3db4..b0611977c 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -461,10 +461,10 @@ async def set_regulation_mode(self, mode: str) -> None: f"Failed to set regulation mode: {str(exc)}" ) from exc # pragma no cover - async def set_dhw_mode(self, mode: str) -> None: + async def set_dhw_mode(self, location: str, length: int, mode: str) -> None: """Set the domestic hot water heating regulation mode.""" try: # pragma no cover - await self._smile_api.set_dhw_mode(mode) # pragma: no cover + await self._smile_api.set_dhw_mode(location, length, mode) # pragma: no cover except ConnectionFailedError as exc: # pragma no cover raise ConnectionFailedError( f"Failed to set dhw mode: {str(exc)}" diff --git a/plugwise/smile.py b/plugwise/smile.py index 19ee58def..7fb47b84d 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -250,9 +250,7 @@ async def set_select( case "select_zone_profile": await self.set_zone_profile(loc_id, option) - async def set_dhw_mode( - self, key: str, loc_id: str, length: int, mode: str - ) -> None: + async def set_dhw_mode(self, loc_id: str, length: int, mode: str) -> None: """Set the domestic hot water mode.""" if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") @@ -261,7 +259,7 @@ async def set_dhw_mode( # Devices with the domestic_hot_water_comfort switch case 2: state = STATE_ON if mode == "comfort" else STATE_OFF - await self.set_switch_state(loc_id, None, key, state) + await self.set_switch_state(loc_id, None, "select_dhw_mode", state) # Loria with extended dhw modes case _: data = ( From d1cda3f778e95cb36e26be1df4ad0f1ca25276d7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:01:50 +0200 Subject: [PATCH 51/89] Update tinker_dhw_mode() functions in test --- tests/test_anna.py | 6 ++++-- tests/test_init.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_anna.py b/tests/test_anna.py index 66c0973ea..f20b16d52 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -474,7 +474,7 @@ async def test_connect_anna_loria_heating_idle(self): "ERROR raised setting block cooling: %s", exc.value ) # pragma: no cover - tinkered = await self.tinker_dhw_mode(api) + tinkered = await self.tinker_dhw_mode(api, "bfb5ee0a88e14e5f97bfa725a760cc49") assert not tinkered await api.close_connection() @@ -483,7 +483,9 @@ async def test_connect_anna_loria_heating_idle(self): server, api, client = await self.connect_wrapper(raise_timeout=True) await self.device_test(api, "2022-05-16 00:00:01", testdata, skip_testing=True) - tinkered = await self.tinker_dhw_mode(api, unhappy=True) + tinkered = await self.tinker_dhw_mode( + api, "bfb5ee0a88e14e5f97bfa725a760cc49", unhappy=True + ) assert tinkered await api.close_connection() diff --git a/tests/test_init.py b/tests/test_init.py index 53277f1e6..bce9af28a 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -849,7 +849,7 @@ async def tinker_legacy_thermostat( return result_1 and result_2 and result_3 @staticmethod - async def tinker_dhw_mode(api, unhappy=False): + async def tinker_dhw_mode(api, appliance, unhappy=False): """Toggle dhw to test functionality.""" tinker_dhw_mode_passed = False for mode in ["auto", "boost", BOGUS]: @@ -859,7 +859,7 @@ async def tinker_dhw_mode(api, unhappy=False): mode = mode[1:] _LOGGER.info("%s", f"- Adjusting dhw mode to {mode}{warning}") try: - await api.set_dhw_mode("select_dhw_mode", 4, mode) + await api.set_dhw_mode(appliance, 4, mode) _LOGGER.info(" + tinker_dhw_mode worked as intended") tinker_dhw_mode_passed = True except pw_exceptions.PlugwiseError: From 37aa2003832e1fe60cc23f09f15595bbc618e619 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:08:17 +0200 Subject: [PATCH 52/89] Add dhw_mode test for comfort_mode toggle --- tests/test_adam.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_adam.py b/tests/test_adam.py index 149d717a9..6ba0eab35 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -455,5 +455,9 @@ async def test_adam_plus_jip(self): good_schedules=[None], ) assert result + + tinkered = await self.tinker_dhw_mode(api, "e4684553153b44afbef2200885f379dc") + assert not tinkered + await api.close_connection() await self.disconnect(server, client) From 812510f29859a2f64d4515bab8edc3742328b23d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:14:21 +0200 Subject: [PATCH 53/89] Ruffed --- plugwise/__init__.py | 4 +++- plugwise/smile.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index b0611977c..78c678b87 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -464,7 +464,9 @@ async def set_regulation_mode(self, mode: str) -> None: async def set_dhw_mode(self, location: str, length: int, mode: str) -> None: """Set the domestic hot water heating regulation mode.""" try: # pragma no cover - await self._smile_api.set_dhw_mode(location, length, mode) # pragma: no cover + await self._smile_api.set_dhw_mode( + location, length, mode + ) # pragma: no cover except ConnectionFailedError as exc: # pragma no cover raise ConnectionFailedError( f"Failed to set dhw mode: {str(exc)}" diff --git a/plugwise/smile.py b/plugwise/smile.py index 7fb47b84d..f64f6df13 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -267,7 +267,9 @@ async def set_dhw_mode(self, loc_id: str, length: int, mode: str) -> None: f"{mode}" "" ) - uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control" + uri = ( + f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control" + ) await self.call_request(uri, method="put", data=data) async def set_gateway_mode(self, mode: str) -> None: From 01f017c45f208b45df6fd3e03d528b8a7636ebc0 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:20:09 +0200 Subject: [PATCH 54/89] Wide dhw_mode options in tinker-test --- tests/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_init.py b/tests/test_init.py index bce9af28a..8600f9cd4 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -852,7 +852,7 @@ async def tinker_legacy_thermostat( async def tinker_dhw_mode(api, appliance, unhappy=False): """Toggle dhw to test functionality.""" tinker_dhw_mode_passed = False - for mode in ["auto", "boost", BOGUS]: + for mode in ["comfort", "auto", BOGUS]: warning = "" if mode[0] == "!": warning = " TD Negative test" From 4a5c5e7c94ccdff1c79899ed7d45c4e2f2e3cafd Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:22:42 +0200 Subject: [PATCH 55/89] Update legacy set_dhw_mode() placeholder --- plugwise/legacy/smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 89e582256..8847e8558 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -131,7 +131,7 @@ async def delete_notification(self) -> None: async def reboot_gateway(self) -> None: """Set-function placeholder for legacy devices.""" - async def set_dhw_mode(self, mode: str) -> None: + async def set_dhw_mode(self, loc_id: str, length: int, mode: str) -> None: """Set-function placeholder for legacy devices.""" async def set_gateway_mode(self, mode: str) -> None: From 0ea621d5b7da974993f8c423bfae632aabdc59bb Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:25:08 +0200 Subject: [PATCH 56/89] Add select_dhw_mode testcase --- tests/test_adam.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_adam.py b/tests/test_adam.py index 6ba0eab35..0fe5b06a0 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -138,6 +138,9 @@ async def test_connect_adam_plus_anna_new(self): "854f8a9b0e7e425db97f1f110e1ce4b3", ) + tinkered = await self.tinker_dhw_mode(api, "056ee145a816487eaa69243c3280f8bf") + assert not tinkered + tinkered = await self.tinker_gateway_mode(api) assert not tinkered From a219f5c3dcb73392c0ccd2da8992c5bc4a5fa416 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:28:29 +0200 Subject: [PATCH 57/89] Tinker_dhw_mode(): pass lenght as argument --- tests/test_init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_init.py b/tests/test_init.py index 8600f9cd4..d0b5fab6b 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -849,7 +849,7 @@ async def tinker_legacy_thermostat( return result_1 and result_2 and result_3 @staticmethod - async def tinker_dhw_mode(api, appliance, unhappy=False): + async def tinker_dhw_mode(api, appliance, length, unhappy=False): """Toggle dhw to test functionality.""" tinker_dhw_mode_passed = False for mode in ["comfort", "auto", BOGUS]: @@ -859,7 +859,7 @@ async def tinker_dhw_mode(api, appliance, unhappy=False): mode = mode[1:] _LOGGER.info("%s", f"- Adjusting dhw mode to {mode}{warning}") try: - await api.set_dhw_mode(appliance, 4, mode) + await api.set_dhw_mode(appliance, lenght, mode) _LOGGER.info(" + tinker_dhw_mode worked as intended") tinker_dhw_mode_passed = True except pw_exceptions.PlugwiseError: From a4552dc5b7c49545d6d424b95e66516d3f3d2162 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:29:49 +0200 Subject: [PATCH 58/89] Adapt use in test cases --- tests/test_adam.py | 4 ++-- tests/test_anna.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_adam.py b/tests/test_adam.py index 0fe5b06a0..9f6f466f6 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -138,7 +138,7 @@ async def test_connect_adam_plus_anna_new(self): "854f8a9b0e7e425db97f1f110e1ce4b3", ) - tinkered = await self.tinker_dhw_mode(api, "056ee145a816487eaa69243c3280f8bf") + tinkered = await self.tinker_dhw_mode(api, "056ee145a816487eaa69243c3280f8bf", 2) assert not tinkered tinkered = await self.tinker_gateway_mode(api) @@ -459,7 +459,7 @@ async def test_adam_plus_jip(self): ) assert result - tinkered = await self.tinker_dhw_mode(api, "e4684553153b44afbef2200885f379dc") + tinkered = await self.tinker_dhw_mode(api, "e4684553153b44afbef2200885f379dc", 2) assert not tinkered await api.close_connection() diff --git a/tests/test_anna.py b/tests/test_anna.py index f20b16d52..037a183ac 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -474,7 +474,7 @@ async def test_connect_anna_loria_heating_idle(self): "ERROR raised setting block cooling: %s", exc.value ) # pragma: no cover - tinkered = await self.tinker_dhw_mode(api, "bfb5ee0a88e14e5f97bfa725a760cc49") + tinkered = await self.tinker_dhw_mode(api, "bfb5ee0a88e14e5f97bfa725a760cc49", 4) assert not tinkered await api.close_connection() @@ -484,7 +484,7 @@ async def test_connect_anna_loria_heating_idle(self): await self.device_test(api, "2022-05-16 00:00:01", testdata, skip_testing=True) tinkered = await self.tinker_dhw_mode( - api, "bfb5ee0a88e14e5f97bfa725a760cc49", unhappy=True + api, "bfb5ee0a88e14e5f97bfa725a760cc49", 4, unhappy=True ) assert tinkered From ecc4acec8464a6c96c4a5fdf4862e6db8cac5d33 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 15 Jun 2026 19:30:52 +0200 Subject: [PATCH 59/89] Fix typo --- tests/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_init.py b/tests/test_init.py index d0b5fab6b..15ce5d9de 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -859,7 +859,7 @@ async def tinker_dhw_mode(api, appliance, length, unhappy=False): mode = mode[1:] _LOGGER.info("%s", f"- Adjusting dhw mode to {mode}{warning}") try: - await api.set_dhw_mode(appliance, lenght, mode) + await api.set_dhw_mode(appliance, length, mode) _LOGGER.info(" + tinker_dhw_mode worked as intended") tinker_dhw_mode_passed = True except pw_exceptions.PlugwiseError: From 2178fc47a5f75363b182359a60ebc3d7d132f79f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 08:19:26 +0200 Subject: [PATCH 60/89] Use try except to manage a missing switch --- plugwise/smile.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index f64f6df13..d408124d8 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -419,7 +419,11 @@ async def set_switch_state( Return the requested state when successful, the current state otherwise. """ model_type = cast(SwitchType, model) - current_state = self.gw_entities[appl_id]["switches"][model_type] + try: + current_state = self.gw_entities[appl_id]["switches"][model_type] + except KeyError: + current_state = None + requested_state = state == STATE_ON switch = Munch() switch.actuator = "actuator_functionalities" From f6580c52de665ee54cd5c0df3eafee613cf8eae0 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 08:24:52 +0200 Subject: [PATCH 61/89] Update typing --- plugwise/__init__.py | 2 +- plugwise/smile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 78c678b87..d80d66d27 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -423,7 +423,7 @@ async def set_temperature_offset(self, dev_id: str, offset: float) -> None: async def set_switch_state( self, appl_id: str, members: list[str] | None, model: str, state: str - ) -> bool: + ) -> bool | None: """Set the given State of the relevant Switch. Return the result: diff --git a/plugwise/smile.py b/plugwise/smile.py index d408124d8..18dc7d979 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -410,7 +410,7 @@ def determine_contexts(self, loc_id: str, state: str, sched_id: str) -> str: async def set_switch_state( self, appl_id: str, members: list[str] | None, model: str, state: str - ) -> bool: + ) -> bool | None: """Set the given state of the relevant Switch. For individual switches, sets the state directly. From 5ea53054896af93ba0c1695e2ab853b44ce7aad4 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 08:31:00 +0200 Subject: [PATCH 62/89] Remove double code --- plugwise/smile.py | 1 - tests/test_adam.py | 8 ++++++-- tests/test_anna.py | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 18dc7d979..4c13b0488 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -238,7 +238,6 @@ async def set_select( """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" match key: case "select_dhw_mode": - state = STATE_ON if option == "comfort" else STATE_OFF await self.set_switch_state(loc_id, None, key, state) case "select_gateway_mode": await self.set_gateway_mode(option) diff --git a/tests/test_adam.py b/tests/test_adam.py index 9f6f466f6..1276bbd63 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -138,7 +138,9 @@ async def test_connect_adam_plus_anna_new(self): "854f8a9b0e7e425db97f1f110e1ce4b3", ) - tinkered = await self.tinker_dhw_mode(api, "056ee145a816487eaa69243c3280f8bf", 2) + tinkered = await self.tinker_dhw_mode( + api, "056ee145a816487eaa69243c3280f8bf", 2 + ) assert not tinkered tinkered = await self.tinker_gateway_mode(api) @@ -459,7 +461,9 @@ async def test_adam_plus_jip(self): ) assert result - tinkered = await self.tinker_dhw_mode(api, "e4684553153b44afbef2200885f379dc", 2) + tinkered = await self.tinker_dhw_mode( + api, "e4684553153b44afbef2200885f379dc", 2 + ) assert not tinkered await api.close_connection() diff --git a/tests/test_anna.py b/tests/test_anna.py index 037a183ac..08eb73e89 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -474,7 +474,9 @@ async def test_connect_anna_loria_heating_idle(self): "ERROR raised setting block cooling: %s", exc.value ) # pragma: no cover - tinkered = await self.tinker_dhw_mode(api, "bfb5ee0a88e14e5f97bfa725a760cc49", 4) + tinkered = await self.tinker_dhw_mode( + api, "bfb5ee0a88e14e5f97bfa725a760cc49", 4 + ) assert not tinkered await api.close_connection() From 4ec51b97ddceaf6fe34c967644870123d0050c47 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 08:35:49 +0200 Subject: [PATCH 63/89] Add comment for required functionality --- plugwise/smile.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugwise/smile.py b/plugwise/smile.py index 4c13b0488..9aa48b959 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -16,6 +16,7 @@ DOMAIN_OBJECTS, GATEWAY_REBOOT, LOCATIONS, + LOGGER, MAX_SETPOINT, MIN_SETPOINT, NONE, @@ -257,8 +258,12 @@ async def set_dhw_mode(self, loc_id: str, length: int, mode: str) -> None: match length: # Devices with the domestic_hot_water_comfort switch case 2: + # two options here: + # - with max_dhw_temperature dict: call set_select() + # - without: call set_switch_state() state = STATE_ON if mode == "comfort" else STATE_OFF await self.set_switch_state(loc_id, None, "select_dhw_mode", state) + # Loria with extended dhw modes case _: data = ( From f5e5af8ccaaaa1ebfa37a7126920e4f0b1fca4a0 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 17:40:47 +0200 Subject: [PATCH 64/89] Adapt for both select_dhw_mode and dhw_mode options --- plugwise/smile.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 9aa48b959..19ac83123 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -44,7 +44,7 @@ def model_to_switch_items(model: str, state: str, switch: Munch) -> tuple[str, M Helper function for set_switch_state(). """ match model: - case "select_dhw_mode": + case "select_dhw_mode" | "dhw_mode": switch.device = "toggle" switch.func_type = "toggle_functionality" switch.act_type = "domestic_hot_water_comfort_mode" @@ -238,7 +238,7 @@ async def set_select( ) -> None: """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" match key: - case "select_dhw_mode": + case "select_dhw_mode" | "dhw_mode": await self.set_switch_state(loc_id, None, key, state) case "select_gateway_mode": await self.set_gateway_mode(option) @@ -250,7 +250,9 @@ async def set_select( case "select_zone_profile": await self.set_zone_profile(loc_id, option) - async def set_dhw_mode(self, loc_id: str, length: int, mode: str) -> None: + async def set_dhw_mode( + self, key: str, loc_id: str, length: int, mode: str + ) -> None: """Set the domestic hot water mode.""" if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") @@ -258,12 +260,8 @@ async def set_dhw_mode(self, loc_id: str, length: int, mode: str) -> None: match length: # Devices with the domestic_hot_water_comfort switch case 2: - # two options here: - # - with max_dhw_temperature dict: call set_select() - # - without: call set_switch_state() state = STATE_ON if mode == "comfort" else STATE_OFF - await self.set_switch_state(loc_id, None, "select_dhw_mode", state) - + await self.set_select(key, loc_id, None, state) # Loria with extended dhw modes case _: data = ( From f30442c420a03fbac198da7ce55ee57491b7cc2a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 17:42:37 +0200 Subject: [PATCH 65/89] Adapt tinker_dhw_mode --- tests/test_init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_init.py b/tests/test_init.py index 15ce5d9de..dad9cfb53 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -849,7 +849,7 @@ async def tinker_legacy_thermostat( return result_1 and result_2 and result_3 @staticmethod - async def tinker_dhw_mode(api, appliance, length, unhappy=False): + async def tinker_dhw_mode(api, appliance, key, length, unhappy=False): """Toggle dhw to test functionality.""" tinker_dhw_mode_passed = False for mode in ["comfort", "auto", BOGUS]: @@ -859,7 +859,7 @@ async def tinker_dhw_mode(api, appliance, length, unhappy=False): mode = mode[1:] _LOGGER.info("%s", f"- Adjusting dhw mode to {mode}{warning}") try: - await api.set_dhw_mode(appliance, length, mode) + await api.set_dhw_mode(key, appliance, length, mode) _LOGGER.info(" + tinker_dhw_mode worked as intended") tinker_dhw_mode_passed = True except pw_exceptions.PlugwiseError: From dd49a03f56eedce5352dfc2779a9e100d11a086d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 17:44:36 +0200 Subject: [PATCH 66/89] Update related test cases --- tests/test_adam.py | 4 ++-- tests/test_anna.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_adam.py b/tests/test_adam.py index 1276bbd63..1ef525961 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -139,7 +139,7 @@ async def test_connect_adam_plus_anna_new(self): ) tinkered = await self.tinker_dhw_mode( - api, "056ee145a816487eaa69243c3280f8bf", 2 + api, "056ee145a816487eaa69243c3280f8bf", "select_dhw_mode", 2 ) assert not tinkered @@ -462,7 +462,7 @@ async def test_adam_plus_jip(self): assert result tinkered = await self.tinker_dhw_mode( - api, "e4684553153b44afbef2200885f379dc", 2 + api, "e4684553153b44afbef2200885f379dc", "dhw_mode", 2 ) assert not tinkered diff --git a/tests/test_anna.py b/tests/test_anna.py index 08eb73e89..d6334d7e7 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -475,7 +475,7 @@ async def test_connect_anna_loria_heating_idle(self): ) # pragma: no cover tinkered = await self.tinker_dhw_mode( - api, "bfb5ee0a88e14e5f97bfa725a760cc49", 4 + api, "bfb5ee0a88e14e5f97bfa725a760cc49", "dhw_mode", 4 ) assert not tinkered @@ -486,7 +486,7 @@ async def test_connect_anna_loria_heating_idle(self): await self.device_test(api, "2022-05-16 00:00:01", testdata, skip_testing=True) tinkered = await self.tinker_dhw_mode( - api, "bfb5ee0a88e14e5f97bfa725a760cc49", 4, unhappy=True + api, "bfb5ee0a88e14e5f97bfa725a760cc49", "dhw_mode", 4, unhappy=True ) assert tinkered From f984ac9883644143371262a287d9447a7eaafb11 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 17:47:20 +0200 Subject: [PATCH 67/89] Line up all set_dhw_mode() functions --- plugwise/__init__.py | 4 ++-- plugwise/legacy/smile.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index d80d66d27..0c38fbaac 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -461,11 +461,11 @@ async def set_regulation_mode(self, mode: str) -> None: f"Failed to set regulation mode: {str(exc)}" ) from exc # pragma no cover - async def set_dhw_mode(self, location: str, length: int, mode: str) -> None: + async def set_dhw_mode(self, key: str, location: str, length: int, mode: str) -> None: """Set the domestic hot water heating regulation mode.""" try: # pragma no cover await self._smile_api.set_dhw_mode( - location, length, mode + key, location, length, mode ) # pragma: no cover except ConnectionFailedError as exc: # pragma no cover raise ConnectionFailedError( diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 8847e8558..e75bd5b2c 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -131,7 +131,7 @@ async def delete_notification(self) -> None: async def reboot_gateway(self) -> None: """Set-function placeholder for legacy devices.""" - async def set_dhw_mode(self, loc_id: str, length: int, mode: str) -> None: + async def set_dhw_mode(self, key: str, location: str, length: int, mode: str) -> None: """Set-function placeholder for legacy devices.""" async def set_gateway_mode(self, mode: str) -> None: From ef833bd8e5cf321dd1acbac0a404282f28923aa7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 17:47:56 +0200 Subject: [PATCH 68/89] Clean up --- plugwise/__init__.py | 4 +++- plugwise/legacy/smile.py | 4 +++- plugwise/smile.py | 5 +---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 0c38fbaac..fe8a9470e 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -461,7 +461,9 @@ async def set_regulation_mode(self, mode: str) -> None: f"Failed to set regulation mode: {str(exc)}" ) from exc # pragma no cover - async def set_dhw_mode(self, key: str, location: str, length: int, mode: str) -> None: + async def set_dhw_mode( + self, key: str, location: str, length: int, mode: str + ) -> None: """Set the domestic hot water heating regulation mode.""" try: # pragma no cover await self._smile_api.set_dhw_mode( diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index e75bd5b2c..622d7ca38 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -131,7 +131,9 @@ async def delete_notification(self) -> None: async def reboot_gateway(self) -> None: """Set-function placeholder for legacy devices.""" - async def set_dhw_mode(self, key: str, location: str, length: int, mode: str) -> None: + async def set_dhw_mode( + self, key: str, location: str, length: int, mode: str + ) -> None: """Set-function placeholder for legacy devices.""" async def set_gateway_mode(self, mode: str) -> None: diff --git a/plugwise/smile.py b/plugwise/smile.py index 19ac83123..82de8d5b2 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -16,7 +16,6 @@ DOMAIN_OBJECTS, GATEWAY_REBOOT, LOCATIONS, - LOGGER, MAX_SETPOINT, MIN_SETPOINT, NONE, @@ -250,9 +249,7 @@ async def set_select( case "select_zone_profile": await self.set_zone_profile(loc_id, option) - async def set_dhw_mode( - self, key: str, loc_id: str, length: int, mode: str - ) -> None: + async def set_dhw_mode(self, key: str, loc_id: str, length: int, mode: str) -> None: """Set the domestic hot water mode.""" if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") From e8f331b72e21175caf9595f9bb7c48baaaf147fe Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 17:51:26 +0200 Subject: [PATCH 69/89] Typing fixes --- plugwise/smile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 82de8d5b2..3b9adee8d 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -238,7 +238,8 @@ async def set_select( """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" match key: case "select_dhw_mode" | "dhw_mode": - await self.set_switch_state(loc_id, None, key, state) + if state is not None: + await self.set_switch_state(loc_id, None, key, state) case "select_gateway_mode": await self.set_gateway_mode(option) case "select_regulation_mode": @@ -258,7 +259,7 @@ async def set_dhw_mode(self, key: str, loc_id: str, length: int, mode: str) -> N # Devices with the domestic_hot_water_comfort switch case 2: state = STATE_ON if mode == "comfort" else STATE_OFF - await self.set_select(key, loc_id, None, state) + await self.set_select(key, loc_id, "dummy", state) # Loria with extended dhw modes case _: data = ( From e46c74e156347ff4bcf12213eea6f854dfed9792 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 19:46:47 +0200 Subject: [PATCH 70/89] Fix length in test as suggested --- tests/test_anna.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_anna.py b/tests/test_anna.py index d6334d7e7..464d2e277 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -475,7 +475,7 @@ async def test_connect_anna_loria_heating_idle(self): ) # pragma: no cover tinkered = await self.tinker_dhw_mode( - api, "bfb5ee0a88e14e5f97bfa725a760cc49", "dhw_mode", 4 + api, "bfb5ee0a88e14e5f97bfa725a760cc49", "dhw_mode", 5 ) assert not tinkered From 197457ef90b30165383635e8d0cac1ddcb16b167 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 20:02:29 +0200 Subject: [PATCH 71/89] Test dhw_mode off to as suggested --- tests/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_init.py b/tests/test_init.py index dad9cfb53..7e6394df5 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -852,7 +852,7 @@ async def tinker_legacy_thermostat( async def tinker_dhw_mode(api, appliance, key, length, unhappy=False): """Toggle dhw to test functionality.""" tinker_dhw_mode_passed = False - for mode in ["comfort", "auto", BOGUS]: + for mode in ["off", "comfort", "auto", BOGUS]: warning = "" if mode[0] == "!": warning = " TD Negative test" From fb343b6e5927357cf2d9b884c0cbce544fc3ee18 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 20:22:12 +0200 Subject: [PATCH 72/89] Improve --- plugwise/smile.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 3b9adee8d..5205adb06 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -238,8 +238,9 @@ async def set_select( """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" match key: case "select_dhw_mode" | "dhw_mode": - if state is not None: - await self.set_switch_state(loc_id, None, key, state) + state = STATE_ON if option == "comfort" else STATE_OFF + # loc_id = appliance_id + await self.set_switch_state(loc_id, None, key, state) case "select_gateway_mode": await self.set_gateway_mode(option) case "select_regulation_mode": @@ -250,7 +251,7 @@ async def set_select( case "select_zone_profile": await self.set_zone_profile(loc_id, option) - async def set_dhw_mode(self, key: str, loc_id: str, length: int, mode: str) -> None: + async def set_dhw_mode(self, key: str, appl_id: str, length: int, mode: str) -> None: """Set the domestic hot water mode.""" if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") @@ -258,8 +259,7 @@ async def set_dhw_mode(self, key: str, loc_id: str, length: int, mode: str) -> N match length: # Devices with the domestic_hot_water_comfort switch case 2: - state = STATE_ON if mode == "comfort" else STATE_OFF - await self.set_select(key, loc_id, "dummy", state) + await self.set_select(key, appl_id, mode, None) # Loria with extended dhw modes case _: data = ( From 20579fba75a36e722bd3dc43669d9c84ecf635de Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 20:24:36 +0200 Subject: [PATCH 73/89] Ruffed --- plugwise/smile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 5205adb06..37da99d20 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -251,7 +251,9 @@ async def set_select( case "select_zone_profile": await self.set_zone_profile(loc_id, option) - async def set_dhw_mode(self, key: str, appl_id: str, length: int, mode: str) -> None: + async def set_dhw_mode( + self, key: str, appl_id: str, length: int, mode: str + ) -> None: """Set the domestic hot water mode.""" if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") From 050e6d7c826278a47b56b37f3930d446c3f43b01 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 20:29:02 +0200 Subject: [PATCH 74/89] Debug --- plugwise/smile.py | 16 ++++++++++++++++ scripts/tests_and_coverage.sh | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 37da99d20..0d4318e66 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -16,6 +16,7 @@ DOMAIN_OBJECTS, GATEWAY_REBOOT, LOCATIONS, + LOGGER, MAX_SETPOINT, MIN_SETPOINT, NONE, @@ -236,6 +237,9 @@ async def set_select( self, key: str, loc_id: str, option: str, state: str | None ) -> None: """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" + LOGGER.debug( + "HOI set_select called with: %s, %s, %s, %s", key, loc_id, option, state + ) match key: case "select_dhw_mode" | "dhw_mode": state = STATE_ON if option == "comfort" else STATE_OFF @@ -258,6 +262,9 @@ async def set_dhw_mode( if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") + LOGGER.debug( + "HOI set_dhw_mode called with: %s, %s, %s, %s", key, appl_id, length, mode + ) match length: # Devices with the domestic_hot_water_comfort switch case 2: @@ -420,6 +427,13 @@ async def set_switch_state( For switch-locks, sets the lock state using a different data format. Return the requested state when successful, the current state otherwise. """ + LOGGER.debug( + "HOI set_switch_state called with: %s, %s, %s, %s", + appl_id, + members, + model, + state, + ) model_type = cast(SwitchType, model) try: current_state = self.gw_entities[appl_id]["switches"][model_type] @@ -449,11 +463,13 @@ async def set_switch_state( for item in found: # multiple types of e.g. toggle_functionality present if (sw_type := item.find("type")) is not None: + LOGGER.debug("HOI switch_type: %s", sw_type.text) if sw_type.text == switch.act_type: switch_id = item.get("id") break else: # actuators with a single item like relay_functionality switch_id = item.get("id") + LOGGER.debug("HOI switch_id: %s", switch_id) uri = f"{APPLIANCES};id={appl_id}/{switch.device};id={switch_id}" if model == "relay": diff --git a/scripts/tests_and_coverage.sh b/scripts/tests_and_coverage.sh index 42aca60ec..89998b496 100755 --- a/scripts/tests_and_coverage.sh +++ b/scripts/tests_and_coverage.sh @@ -52,7 +52,8 @@ set +u if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "test_and_coverage" ] ; then # Python tests (rerun with debug if failures) - PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ + # PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || + PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ handle_command_error "python code testing" fi From c0dd270db429e74efb4e9fc50ff9669acba0eb40 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 20:42:13 +0200 Subject: [PATCH 75/89] Init switch_id --- plugwise/smile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise/smile.py b/plugwise/smile.py index 0d4318e66..5485f00b0 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -460,6 +460,7 @@ async def set_switch_state( locator = f'appliance[@id="{appl_id}"]/{switch.actuator}/{switch.func_type}' found = self._domain_objects.findall(locator) + switch_id: str | None = None for item in found: # multiple types of e.g. toggle_functionality present if (sw_type := item.find("type")) is not None: From 07e5beec256330e939c445e399cd8048f324a905 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 16 Jun 2026 20:46:52 +0200 Subject: [PATCH 76/89] Debug 2 --- plugwise/smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 5485f00b0..6dc01316c 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -470,7 +470,7 @@ async def set_switch_state( break else: # actuators with a single item like relay_functionality switch_id = item.get("id") - LOGGER.debug("HOI switch_id: %s", switch_id) + LOGGER.debug("HOI switch_id: %s", switch_id) uri = f"{APPLIANCES};id={appl_id}/{switch.device};id={switch_id}" if model == "relay": From 12f638c7a199d66d19d5260465a23a0c6198df18 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Wed, 17 Jun 2026 20:24:30 +0200 Subject: [PATCH 77/89] Update set_switch_state() URI call for toggle, relay and lock --- plugwise/smile.py | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 6dc01316c..33a0baead 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -46,11 +46,11 @@ def model_to_switch_items(model: str, state: str, switch: Munch) -> tuple[str, M match model: case "select_dhw_mode" | "dhw_mode": switch.device = "toggle" - switch.func_type = "toggle_functionality" + switch.func_type = "toggle" switch.act_type = "domestic_hot_water_comfort_mode" case "cooling_ena_switch": switch.device = "toggle" - switch.func_type = "toggle_functionality" + switch.func_type = "toggle" switch.act_type = "cooling_enabled" case "lock": switch.func = "lock" @@ -452,27 +452,16 @@ async def set_switch_state( f"<{switch.func}>{state}" f"" ) + extra = "" + if switch.device == "toggle": + extra = f";type={switch.act_type}" if members is not None: return await self._set_groupswitch_member_state( appl_id, data, members, state, switch ) - locator = f'appliance[@id="{appl_id}"]/{switch.actuator}/{switch.func_type}' - found = self._domain_objects.findall(locator) - switch_id: str | None = None - for item in found: - # multiple types of e.g. toggle_functionality present - if (sw_type := item.find("type")) is not None: - LOGGER.debug("HOI switch_type: %s", sw_type.text) - if sw_type.text == switch.act_type: - switch_id = item.get("id") - break - else: # actuators with a single item like relay_functionality - switch_id = item.get("id") - LOGGER.debug("HOI switch_id: %s", switch_id) - - uri = f"{APPLIANCES};id={appl_id}/{switch.device};id={switch_id}" + uri = f"{APPLIANCES};id={appl_id}/{switch.device}{extra}" if model == "relay": lock_blocked = self.gw_entities[appl_id]["switches"].get("lock") if lock_blocked or lock_blocked is None: From ccd331dc3c3e04afeedd92a30b41024c4e2cd2f5 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 18 Jun 2026 08:13:34 +0200 Subject: [PATCH 78/89] Also adapt _set_groupswitch_member_state() --- plugwise/smile.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 33a0baead..44a1de7a1 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -484,9 +484,7 @@ async def _set_groupswitch_member_state( requested_state = state == STATE_ON switched = 0 for member in members: - locator = f'appliance[@id="{member}"]/{switch.actuator}/{switch.func_type}' - switch_id = self._domain_objects.find(locator).get("id") - uri = f"{APPLIANCES};id={member}/{switch.device};id={switch_id}" + uri = f"{APPLIANCES};id={member}/{switch.device}" lock_blocked = self.gw_entities[member]["switches"].get("lock") # Assume Plugs under Plugwise control are not part of a group if lock_blocked is not None and not lock_blocked: From 5d391fb9948561f3648c6cf28d1df4fff5aab8a3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 18 Jun 2026 18:28:51 +0200 Subject: [PATCH 79/89] Use the appropriate method --- plugwise/smile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 44a1de7a1..4dbd505f9 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -54,6 +54,7 @@ def model_to_switch_items(model: str, state: str, switch: Munch) -> tuple[str, M switch.act_type = "cooling_enabled" case "lock": switch.func = "lock" + switch.method = "post" state = "true" if state == STATE_ON else "false" return state, switch @@ -446,6 +447,7 @@ async def set_switch_state( switch.device = "relay" switch.func_type = "relay_functionality" switch.func = "state" + switch.method = "put" state, switch = model_to_switch_items(model, state, switch) data = ( f"<{switch.func_type}>" @@ -469,7 +471,7 @@ async def set_switch_state( # lock is present. That means the relay can't be controlled by the user. return current_state - await self.call_request(uri, method="put", data=data) + await self.call_request(uri, method=switch.method, data=data) return requested_state async def _set_groupswitch_member_state( From 853569e96f0c216c56eb3bea639c0c783344a248 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 18 Jun 2026 19:29:18 +0200 Subject: [PATCH 80/89] Allow POST for core/appliances --- tests/test_init.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/test_init.py b/tests/test_init.py index 7e6394df5..db3790005 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -115,22 +115,23 @@ def setup_app( # Introducte timeout with 2 seconds, test by setting response to 10ms # Don't actually wait 2 seconds as this will prolongue testing if not raise_timeout: + app.router.add_route("POST", CORE_APPLIANCES_TAIL, self.smile_http_accept) + app.router.add_route("PUT", CORE_APPLIANCES_TAIL, self.smile_http_accept) app.router.add_route("POST", CORE_GATEWAYS_TAIL, self.smile_http_accept) - app.router.add_route("PUT", CORE_LOCATIONS_TAIL, self.smile_http_accept) app.router.add_route("POST", CORE_LOCATIONS_TAIL, self.smile_http_accept) + app.router.add_route("PUT", CORE_LOCATIONS_TAIL, self.smile_http_accept) app.router.add_route( "DELETE", CORE_NOTIFICATIONS_TAIL, self.smile_http_accept ) app.router.add_route("PUT", CORE_RULES_TAIL, self.smile_http_accept) - app.router.add_route("PUT", CORE_APPLIANCES_TAIL, self.smile_http_accept) else: + app.router.add_route("POST", CORE_APPLIANCES_TAIL, self.smile_timeout) + app.router.add_route("PUT", CORE_APPLIANCES_TAIL, self.smile_timeout) app.router.add_route("POST", CORE_GATEWAYS_TAIL, self.smile_timeout) - app.router.add_route("PUT", CORE_LOCATIONS_TAIL, self.smile_timeout) app.router.add_route("POST", CORE_LOCATIONS_TAIL, self.smile_timeout) - app.router.add_route("PUT", CORE_RULES_TAIL, self.smile_timeout) - app.router.add_route("PUT", CORE_APPLIANCES_TAIL, self.smile_timeout) + app.router.add_route("PUT", CORE_LOCATIONS_TAIL, self.smile_timeout) app.router.add_route("DELETE", CORE_NOTIFICATIONS_TAIL, self.smile_timeout) - + app.router.add_route("PUT", CORE_RULES_TAIL, self.smile_timeout) return app def setup_legacy_app( From f33536932326c939f30d476a4f5ee7a632083e63 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 19 Jun 2026 12:34:54 +0200 Subject: [PATCH 81/89] Rework to single if with walrus --- plugwise/data.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugwise/data.py b/plugwise/data.py index 0448eb859..3dfc293d0 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -79,11 +79,12 @@ def _update_gw_entities(self) -> None: remove_empty_platform_dicts(entity) # Replace select_dhw_mode with dhw_mode when applicable - if "max_dhw_temperature" in entity: - mode = entity.get("select_dhw_mode") - if mode is not None: - entity.pop("select_dhw_mode") - entity["dhw_mode"] = mode + if ( + "max_dhw_temperature" in entity + and (mode := entity.get("select_dhw_mode")) is not None + ): + entity.pop("select_dhw_mode") + entity["dhw_mode"] = mode def _detect_low_batteries(self) -> list[str]: """Helper-function updating the low-battery binary_sensor status from a Battery-is-low message.""" From 621df52019df5dc559a4a05eb720f78027e82422 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 19 Jun 2026 12:50:51 +0200 Subject: [PATCH 82/89] Improve comments --- plugwise/helper.py | 4 ++-- plugwise/smile.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 54a926a51..bd7a35ca3 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -265,11 +265,11 @@ def _appliance_info_finder(self, appl: Munch, appliance: etree.Element) -> Munch def _collect_dhw_modes(self, appliance: etree.Element) -> None: """Collect the DHW modes.""" - # Collect the Loria dhw_modes + # Collect the Loria dhw modes self._dhw_allowed_modes = self._get_appl_actuator_modes( appliance, "domestic_hot_water_mode_control_functionality" ) - # Collect the default dhw modes: comfort and off + # Determine the dhw modes from the domestic_hot_water_comfort_mode toggle if not self._dhw_allowed_modes: self._get_toggle_state( appliance, "domestic_hot_water_comfort_mode", "dhw_cm_switch", {} diff --git a/plugwise/smile.py b/plugwise/smile.py index 4dbd505f9..461537cc8 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -244,14 +244,15 @@ async def set_select( match key: case "select_dhw_mode" | "dhw_mode": state = STATE_ON if option == "comfort" else STATE_OFF - # loc_id = appliance_id + # Appliance id is passed as loc_id await self.set_switch_state(loc_id, None, key, state) case "select_gateway_mode": await self.set_gateway_mode(option) case "select_regulation_mode": await self.set_regulation_mode(option) case "select_schedule": - # schedule name corresponds to select option + # The schedule name corresponds to the select option + # Location id is passed as loc_id await self.set_schedule_state(loc_id, state, option) case "select_zone_profile": await self.set_zone_profile(loc_id, option) From 94792bfc4e061c1655165e6a93e191dc27ad0110 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 19 Jun 2026 17:14:07 +0200 Subject: [PATCH 83/89] Optimize --- plugwise/smile.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 461537cc8..91749454a 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -45,12 +45,10 @@ def model_to_switch_items(model: str, state: str, switch: Munch) -> tuple[str, M """ match model: case "select_dhw_mode" | "dhw_mode": - switch.device = "toggle" - switch.func_type = "toggle" + switch.device = switch.func_type = "toggle" switch.act_type = "domestic_hot_water_comfort_mode" case "cooling_ena_switch": - switch.device = "toggle" - switch.func_type = "toggle" + switch.device = switch.func_type = "toggle" switch.act_type = "cooling_enabled" case "lock": switch.func = "lock" From d08492ec7d22d3b9510dd05014f0ce7f535cbed5 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 19 Jun 2026 17:16:18 +0200 Subject: [PATCH 84/89] Remove debug-logger lines --- plugwise/smile.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 91749454a..9cb65c5d4 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -16,7 +16,6 @@ DOMAIN_OBJECTS, GATEWAY_REBOOT, LOCATIONS, - LOGGER, MAX_SETPOINT, MIN_SETPOINT, NONE, @@ -236,9 +235,6 @@ async def set_select( self, key: str, loc_id: str, option: str, state: str | None ) -> None: """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" - LOGGER.debug( - "HOI set_select called with: %s, %s, %s, %s", key, loc_id, option, state - ) match key: case "select_dhw_mode" | "dhw_mode": state = STATE_ON if option == "comfort" else STATE_OFF @@ -262,9 +258,6 @@ async def set_dhw_mode( if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") - LOGGER.debug( - "HOI set_dhw_mode called with: %s, %s, %s, %s", key, appl_id, length, mode - ) match length: # Devices with the domestic_hot_water_comfort switch case 2: @@ -427,13 +420,6 @@ async def set_switch_state( For switch-locks, sets the lock state using a different data format. Return the requested state when successful, the current state otherwise. """ - LOGGER.debug( - "HOI set_switch_state called with: %s, %s, %s, %s", - appl_id, - members, - model, - state, - ) model_type = cast(SwitchType, model) try: current_state = self.gw_entities[appl_id]["switches"][model_type] From 4515b6a7bc5b3431f967bb9ef92bffdc54105b35 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 19 Jun 2026 17:35:46 +0200 Subject: [PATCH 85/89] Improve comments, docstring --- plugwise/smile.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 9cb65c5d4..9624389ac 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -232,37 +232,41 @@ async def set_preset(self, loc_id: str, preset: str) -> None: await self.call_request(uri, method="put", data=data) async def set_select( - self, key: str, loc_id: str, option: str, state: str | None + self, key: str, appl_or_loc_id: str, option: str, state: str | None ) -> None: """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" match key: case "select_dhw_mode" | "dhw_mode": state = STATE_ON if option == "comfort" else STATE_OFF - # Appliance id is passed as loc_id - await self.set_switch_state(loc_id, None, key, state) + # Appliance id is passed + await self.set_switch_state(appl_or_loc_id, None, key, state) case "select_gateway_mode": await self.set_gateway_mode(option) case "select_regulation_mode": await self.set_regulation_mode(option) case "select_schedule": # The schedule name corresponds to the select option - # Location id is passed as loc_id - await self.set_schedule_state(loc_id, state, option) + # Location id is passed + await self.set_schedule_state(appl_or_loc_id, state, option) case "select_zone_profile": - await self.set_zone_profile(loc_id, option) + # Location id is passed + await self.set_zone_profile(appl_or_loc_id, option) async def set_dhw_mode( self, key: str, appl_id: str, length: int, mode: str ) -> None: - """Set the domestic hot water mode.""" + """Set the domestic hot water mode. + + Two options are known: + - 2 modes, comfort and off, representing the dhw comfort mode on and off switch states, + - and the 5 modes available on the Loria. + """ if mode not in self._dhw_allowed_modes: raise PlugwiseError("Plugwise: invalid dhw mode.") match length: - # Devices with the domestic_hot_water_comfort switch case 2: await self.set_select(key, appl_id, mode, None) - # Loria with extended dhw modes case _: data = ( "" From 235e0341dcebef917d0d5a9e9b3204ef6c016a9d Mon Sep 17 00:00:00 2001 From: autoruff Date: Fri, 19 Jun 2026 15:37:24 +0000 Subject: [PATCH 86/89] fixup: dhw_update Python code fixed using ruff --- plugwise/smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 9624389ac..40fdbc627 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -256,7 +256,7 @@ async def set_dhw_mode( self, key: str, appl_id: str, length: int, mode: str ) -> None: """Set the domestic hot water mode. - + Two options are known: - 2 modes, comfort and off, representing the dhw comfort mode on and off switch states, - and the 5 modes available on the Loria. From 667e26a105ea948a0e155e2c07d7bf92f64fbecc Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 19 Jun 2026 17:39:53 +0200 Subject: [PATCH 87/89] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5fa67870..83dfd45b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v1.12.0 -- Replace the DHW-comfort-mode switch by a DHW mode selector to match the HA select or water_heater platforms, via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) +- Replace the DHW-comfort-mode switch by a DHW mode selector to match the new HA select or water_heater platform updates, via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) ## v1.11.4 From b17a1fc62d26bf9a01c46059c890ec58dce32396 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 20 Jun 2026 09:29:56 +0200 Subject: [PATCH 88/89] Back to normal test-output --- scripts/tests_and_coverage.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/tests_and_coverage.sh b/scripts/tests_and_coverage.sh index 89998b496..42aca60ec 100755 --- a/scripts/tests_and_coverage.sh +++ b/scripts/tests_and_coverage.sh @@ -52,8 +52,7 @@ set +u if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "test_and_coverage" ] ; then # Python tests (rerun with debug if failures) - # PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || - PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ + PYTHONPATH=$(pwd) pytest -qx tests/ --cov='.' --no-cov-on-fail --cov-report term-missing || PYTHONPATH=$(pwd) pytest -xrpP --log-level debug tests/ handle_command_error "python code testing" fi From eefb304869eb5e0aecf8f4edf2bc894eb523dbfb Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 20 Jun 2026 09:50:20 +0200 Subject: [PATCH 89/89] Adapt legacy set_switch_state() output typing --- plugwise/legacy/smile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 622d7ca38..419f2fde9 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -223,7 +223,7 @@ async def set_schedule_state( async def set_switch_state( self, appl_id: str, members: list[str] | None, model: str, state: str - ) -> bool: + ) -> bool | None: """Set the given state of the relevant switch. For individual switches, sets the state directly. @@ -231,7 +231,7 @@ async def set_switch_state( For switch-locks, sets the lock state using a different data format. Return the requested state when successful, the current state otherwise. """ - current_state = self.gw_entities[appl_id]["switches"]["relay"] + current_state = self.gw_entities[appl_id]["switches"].get("relay") requested_state = state == STATE_ON switch = Munch() switch.actuator = "actuator_functionalities"