From 3fe93d926c5fa645e36d5a5fb854a17576f47e29 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 16:33:36 +0100 Subject: [PATCH 01/12] Remove switch and report groups Use the HA Group helper to create a group of switches --- plugwise/common.py | 21 +++++---------------- plugwise/constants.py | 2 -- plugwise/data.py | 3 +-- plugwise/legacy/data.py | 3 --- plugwise/legacy/smile.py | 34 ---------------------------------- plugwise/smile.py | 34 +--------------------------------- 6 files changed, 7 insertions(+), 90 deletions(-) diff --git a/plugwise/common.py b/plugwise/common.py index 9722975e6..a0db912b8 100644 --- a/plugwise/common.py +++ b/plugwise/common.py @@ -9,7 +9,6 @@ from plugwise.constants import ( ANNA, - GROUP_TYPES, NONE, PRIORITY_DEVICE_CLASSES, SPECIAL_PLUG_TYPES, @@ -175,23 +174,10 @@ def _reorder_devices(self) -> None: break self.gw_entities = {**reordered, **self.gw_entities} - def _entity_switching_group(self, entity: GwEntityData, data: GwEntityData) -> None: - """Helper-function for _get_device_zone_data(). - - Determine switching group device data. - """ - if entity["dev_class"] in SWITCH_GROUP_TYPES: - counter = 0 - for member in entity["members"]: - if self.gw_entities[member]["switches"].get("relay"): - counter += 1 - data["switches"]["relay"] = counter != 0 - self._count += 1 - def _get_groups(self) -> dict[str, GwEntityData]: """Helper-function for smile.py: get_all_gateway_entities(). - Collect switching-, pumping- or report-group info. + Collect pumping-groups info. """ groups: dict[str, GwEntityData] = {} # P1 and Anna don't have groups @@ -203,13 +189,16 @@ def _get_groups(self) -> dict[str, GwEntityData]: group_id = group.attrib["id"] group_name = group.find("name").text group_type = group.find("type").text + if group_type != "pumping": + continue + group_appliances = group.findall("appliances/appliance") for item in group_appliances: # Check if members are not orphaned - stretch if item.attrib["id"] in self.gw_entities: members.append(item.attrib["id"]) - if group_type in GROUP_TYPES and members: + if members: groups[group_id] = { "dev_class": group_type, "model": "Group", diff --git a/plugwise/constants.py b/plugwise/constants.py index 1ddd37c5f..a17c9b404 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -304,8 +304,6 @@ ] BINARY_SENSORS: Final[tuple[str, ...]] = get_args(BinarySensorType) -GROUP_TYPES: Final[tuple[str, ...]] = ("pumping", "report", "switching") - SensorType = Literal[ "battery", "cooling_activation_outdoor_temperature", diff --git a/plugwise/data.py b/plugwise/data.py index 3ab9f53f4..b9dc6c579 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -194,14 +194,13 @@ def _get_entity_data(self, entity_id: str) -> GwEntityData: self._check_availability( entity, "smartmeter", data, "P1 does not seem to be connected" ) + # OpenTherm entity if entity["name"] != "OnOff": self._check_availability( entity, "heater_central", data, "no OpenTherm communication" ) - # Switching groups data - self._entity_switching_group(entity, data) # Adam data if self.check_name(ADAM): self._get_adam_data(entity, data) diff --git a/plugwise/legacy/data.py b/plugwise/legacy/data.py index c5cde29a7..7a3800648 100644 --- a/plugwise/legacy/data.py +++ b/plugwise/legacy/data.py @@ -40,9 +40,6 @@ def _get_entity_data(self, entity_id: str) -> GwEntityData: entity = self.gw_entities[entity_id] data = self._get_measurement_data(entity_id) - # Switching groups data - self._entity_switching_group(entity, data) - # Skip obtaining data when not a thermostat if entity["dev_class"] != "thermostat": return data diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 54a21c15d..dad597365 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -78,13 +78,9 @@ def get_all_gateway_entities(self) -> None: """Collect the Plugwise gateway entities and their data and states from the received raw XML-data. First, collect all the connected entities and their initial data. - Collect and add switching- and/or pump-group entities. Finally, collect the data and states for each entity. """ self._all_appliances() - if group_data := self._get_groups(): - self.gw_entities.update(group_data) - self._all_entity_data() async def async_update(self) -> dict[str, GwEntityData]: @@ -225,7 +221,6 @@ async def set_switch_state( """Set the given state of the relevant switch. For individual switches, sets the state directly. - For group switches, sets the state for each member in the group separately. For switch-locks, sets the lock state using a different data format. Return the requested state when succesful, the current state otherwise. """ @@ -261,13 +256,6 @@ async def set_switch_state( await self.call_request(APPLIANCES, method="post", data=data) return requested_state - # Handle group of switches - data = f"<{switch.func_type}>{state}" - if members is not None: - return await self._set_groupswitch_member_state( - appl_id, data, members, state, switch - ) - # Handle individual relay switches uri = f"{APPLIANCES};id={appl_id}/relay" if model == "relay" and self.gw_entities[appl_id]["switches"]["lock"]: @@ -277,28 +265,6 @@ async def set_switch_state( await self.call_request(uri, method="put", data=data) return requested_state - async def _set_groupswitch_member_state( - self, appl_id: str, data: str, members: list[str], state: str, switch: Munch - ) -> bool: - """Helper-function for set_switch_state(). - - Set the requested state of the relevant switch within a group of switches. - Return the current group-state when none of the switches has changed its state, the requested state otherwise. - """ - current_state = self.gw_entities[appl_id]["switches"]["relay"] - requested_state = state == STATE_ON - switched = 0 - for member in members: - if not self.gw_entities[member]["switches"]["lock"]: - uri = f"{APPLIANCES};id={member}/relay" - await self.call_request(uri, method="put", data=data) - switched += 1 - - if switched > 0: - return requested_state - - return current_state # pragma: no cover - async def set_temperature(self, _: str, items: dict[str, float]) -> None: """Set the given Temperature on the relevant Thermostat.""" setpoint: float | None = None diff --git a/plugwise/smile.py b/plugwise/smile.py index 22010af5c..01b0d8ee2 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -104,7 +104,7 @@ def get_all_gateway_entities(self) -> None: First, collect all the connected entities and their initial data. If a thermostat-gateway, collect a list of thermostats with offset-capability. - Collect and add switching- and/or pump-group entities. + Collect and add pumping-group entities. Finally, collect the data and states for each entity. """ self._all_appliances() @@ -399,7 +399,6 @@ async def set_switch_state( """Set the given state of the relevant Switch. For individual switches, sets the state directly. - For group switches, sets the state for each member in the group separately. For switch-locks, sets the lock state using a different data format. Return the requested state when succesful, the current state otherwise. """ @@ -418,11 +417,6 @@ async def set_switch_state( f"" ) - 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) for item in found: @@ -445,32 +439,6 @@ async def set_switch_state( await self.call_request(uri, method="put", data=data) return requested_state - async def _set_groupswitch_member_state( - self, appl_id: str, data: str, members: list[str], state: str, switch: Munch - ) -> bool: - """Helper-function for set_switch_state(). - - Set the requested state of the relevant switch within a group of switches. - Return the current group-state when none of the switches has changed its state, the requested state otherwise. - """ - current_state = self.gw_entities[appl_id]["switches"]["relay"] - 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).attrib["id"] - uri = f"{APPLIANCES};id={member}/{switch.device};id={switch_id}" - 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: - await self.call_request(uri, method="put", data=data) - switched += 1 - - if switched > 0: - return requested_state - - return current_state - async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None: """Set the given Temperature on the relevant Thermostat.""" setpoint: float | None = None From 6ef2eae02431565ae902ad5462f137b081df70cc Mon Sep 17 00:00:00 2001 From: autoruff Date: Thu, 27 Nov 2025 15:39:52 +0000 Subject: [PATCH 02/12] fixup: remove-sw-group Python code fixed using ruff --- plugwise/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/common.py b/plugwise/common.py index a0db912b8..d37d6774c 100644 --- a/plugwise/common.py +++ b/plugwise/common.py @@ -191,7 +191,7 @@ def _get_groups(self) -> dict[str, GwEntityData]: group_type = group.find("type").text if group_type != "pumping": continue - + group_appliances = group.findall("appliances/appliance") for item in group_appliances: # Check if members are not orphaned - stretch From f0a4d6ecafb1fe8da57c1ddf547b77a2ecb5e139 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 17:56:12 +0100 Subject: [PATCH 03/12] Adapt test-json, test-cases/-asserts --- fixtures/adam_plus_anna_new/data.json | 66 ++++++++++++------- .../adam/adam_multiple_devices_per_zone.json | 17 ----- tests/data/adam/adam_plus_anna_new.json | 17 ----- .../adam/adam_plus_anna_new_UPDATED_DATA.json | 9 --- .../adam_plus_anna_new_regulation_off.json | 13 ---- tests/data/stretch/stretch_v23.json | 10 --- tests/data/stretch/stretch_v31.json | 29 -------- .../stretch/stretch_v31_UPDATED_DATA.json | 9 --- tests/test_adam.py | 18 +---- tests/test_legacy_stretch.py | 10 +-- 10 files changed, 45 insertions(+), 153 deletions(-) diff --git a/fixtures/adam_plus_anna_new/data.json b/fixtures/adam_plus_anna_new/data.json index afa38abd8..424e5df4b 100644 --- a/fixtures/adam_plus_anna_new/data.json +++ b/fixtures/adam_plus_anna_new/data.json @@ -202,7 +202,11 @@ }, "dev_class": "gateway", "firmware": "3.9.0", - "gateway_modes": ["away", "full", "vacation"], + "gateway_modes": [ + "away", + "full", + "vacation" + ], "hardware": "AME Smile 2.0 board", "location": "bc93488efab249e5bc54fd7e175a6f91", "mac_address": "D40FB201CBA0", @@ -210,7 +214,12 @@ "model_id": "smile_open_therm", "name": "Adam", "notifications": {}, - "regulation_modes": ["bleeding_cold", "heating", "off", "bleeding_hot"], + "regulation_modes": [ + "bleeding_cold", + "heating", + "off", + "bleeding_hot" + ], "select_gateway_mode": "full", "select_regulation_mode": "heating", "sensors": { @@ -266,23 +275,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "000D6F000C86CBA0" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "2568cc4b9c1e401495d4741a5f89bee1", - "29542b2b6a6a4169acecc15c72a599b8" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 16.5, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f2bf9048bef64cc5b6d5110154e33c81": { "active_preset": "home", "available_schedules": [ @@ -297,7 +289,13 @@ "dev_class": "climate", "model": "ThermoZone", "name": "Living room", - "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], + "preset_modes": [ + "vacation", + "no_frost", + "asleep", + "home", + "away" + ], "select_schedule": "Weekschema", "select_zone_profile": "active", "sensors": { @@ -320,7 +318,11 @@ "secondary": [] }, "vendor": "Plugwise", - "zone_profiles": ["active", "off", "passive"] + "zone_profiles": [ + "active", + "off", + "passive" + ] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "vacation", @@ -336,7 +338,13 @@ "dev_class": "climate", "model": "ThermoZone", "name": "Bathroom", - "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], + "preset_modes": [ + "vacation", + "no_frost", + "asleep", + "home", + "away" + ], "select_schedule": "off", "select_zone_profile": "passive", "sensors": { @@ -351,10 +359,18 @@ "upper_bound": 99.9 }, "thermostats": { - "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], - "secondary": ["1772a4ea304041adb83f357b751341ff"] + "primary": [ + "e2f4322d57924fa090fbbc48b3a140dc" + ], + "secondary": [ + "1772a4ea304041adb83f357b751341ff" + ] }, "vendor": "Plugwise", - "zone_profiles": ["active", "off", "passive"] + "zone_profiles": [ + "active", + "off", + "passive" + ] } } diff --git a/tests/data/adam/adam_multiple_devices_per_zone.json b/tests/data/adam/adam_multiple_devices_per_zone.json index 364d71e59..56329565a 100644 --- a/tests/data/adam/adam_multiple_devices_per_zone.json +++ b/tests/data/adam/adam_multiple_devices_per_zone.json @@ -554,23 +554,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A11" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "02cf28bfec924855854c544690a609ef", - "4a810418d5394b3f82727340b91ba740" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 14.8, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f1fee6043d3642a9b0a65297455f008e": { "available": true, "binary_sensors": { diff --git a/tests/data/adam/adam_plus_anna_new.json b/tests/data/adam/adam_plus_anna_new.json index afa38abd8..15f7e42ab 100644 --- a/tests/data/adam/adam_plus_anna_new.json +++ b/tests/data/adam/adam_plus_anna_new.json @@ -266,23 +266,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "000D6F000C86CBA0" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "2568cc4b9c1e401495d4741a5f89bee1", - "29542b2b6a6a4169acecc15c72a599b8" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 16.5, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f2bf9048bef64cc5b6d5110154e33c81": { "active_preset": "home", "available_schedules": [ diff --git a/tests/data/adam/adam_plus_anna_new_UPDATED_DATA.json b/tests/data/adam/adam_plus_anna_new_UPDATED_DATA.json index 65bbf87d9..1b92eb631 100644 --- a/tests/data/adam/adam_plus_anna_new_UPDATED_DATA.json +++ b/tests/data/adam/adam_plus_anna_new_UPDATED_DATA.json @@ -32,14 +32,5 @@ "binary_sensors": { "plugwise_notification": true } - }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "members": [ - "2568cc4b9c1e401495d4741a5f89bee1", - "29542b2b6a6a4169acecc15c72a599b8" - ], - "switches": { - "relay": false - } } } 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 10cd1add3..4485173e4 100644 --- a/tests/data/adam/adam_plus_anna_new_regulation_off.json +++ b/tests/data/adam/adam_plus_anna_new_regulation_off.json @@ -214,19 +214,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "000D6F000C869B61" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "2568cc4b9c1e401495d4741a5f89bee1", - "29542b2b6a6a4169acecc15c72a599b8" - ], - "model": "Group", - "name": "Test", - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f2bf9048bef64cc5b6d5110154e33c81": { "active_preset": "home", "available_schedules": [ diff --git a/tests/data/stretch/stretch_v23.json b/tests/data/stretch/stretch_v23.json index 5ca04fcd8..2da4cebf7 100644 --- a/tests/data/stretch/stretch_v23.json +++ b/tests/data/stretch/stretch_v23.json @@ -302,16 +302,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A11" }, - "f7b145c8492f4dd7a4de760456fdef3e": { - "dev_class": "switching", - "members": ["407aa1c1099d463c9137a3a9eda787fd"], - "model": "Group", - "name": "Test", - "switches": { - "relay": false - }, - "vendor": "Plugwise" - }, "fd1b74f59e234a9dae4e23b2b5cf07ed": { "dev_class": "dryer", "firmware": "2011-06-27T10:52:18+02:00", diff --git a/tests/data/stretch/stretch_v31.json b/tests/data/stretch/stretch_v31.json index 9927b5c13..e46b53f7e 100644 --- a/tests/data/stretch/stretch_v31.json +++ b/tests/data/stretch/stretch_v31.json @@ -85,35 +85,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A04" }, - "d03738edfcc947f7b8f4573571d90d2d": { - "dev_class": "switching", - "members": [ - "059e4d03c7a34d278add5c7a4a781d19", - "cfe95cf3de1948c0b8955125bf754614" - ], - "model": "Group", - "name": "Schakel", - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, - "d950b314e9d8499f968e6db8d82ef78c": { - "dev_class": "report", - "members": [ - "059e4d03c7a34d278add5c7a4a781d19", - "5871317346d045bc9f6b987ef25ee638", - "aac7b735042c4832ac9ff33aae4f453b", - "cfe95cf3de1948c0b8955125bf754614", - "e1c884e7dede431dadee09506ec4f859" - ], - "model": "Group", - "name": "Stroomvreters", - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "e1c884e7dede431dadee09506ec4f859": { "dev_class": "refrigerator", "firmware": "2011-06-27T10:47:37+02:00", diff --git a/tests/data/stretch/stretch_v31_UPDATED_DATA.json b/tests/data/stretch/stretch_v31_UPDATED_DATA.json index 44a1049ef..8381af99c 100644 --- a/tests/data/stretch/stretch_v31_UPDATED_DATA.json +++ b/tests/data/stretch/stretch_v31_UPDATED_DATA.json @@ -31,14 +31,5 @@ "relay": false, "lock": false } - }, - "d03738edfcc947f7b8f4573571d90d2d": { - "members": [ - "059e4d03c7a34d278add5c7a4a781d19", - "cfe95cf3de1948c0b8955125bf754614" - ], - "switches": { - "relay": false - } } } diff --git a/tests/test_adam.py b/tests/test_adam.py index 8484d56d7..73c9940a0 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -34,7 +34,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 == 230 + assert self.entity_items == 222 assert test_items == self.entity_items assert self.entity_list == [ "da224107914542988a88561b4452b0f6", @@ -50,7 +50,6 @@ async def test_connect_adam_plus_anna_new(self): "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6", "da575e9e09b947e281fb6e3ebce3b174", "c9293d1d68ee48fc8843c6f0dee2b6be", - "e8ef2a01ed3b4139a53bf749204fe6b4", "f2bf9048bef64cc5b6d5110154e33c81", "f871b8c4d63549319221e294e4f88074", ] @@ -96,12 +95,6 @@ async def test_connect_adam_plus_anna_new(self): ) assert result_1 and result_2 - switch_change = await self.tinker_switch( - api, - "e8ef2a01ed3b4139a53bf749204fe6b4", - ["2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8"], - ) - assert switch_change switch_change = await self.tinker_switch( api, "056ee145a816487eaa69243c3280f8bf", @@ -302,7 +295,7 @@ async def test_connect_adam_multiple_devices_per_zone(self): ) test_items = await self.device_test(api, "2022-05-16 00:00:01", testdata) - assert self.entity_items == 394 + assert self.entity_items == 386 assert test_items == self.entity_items assert "af82e4ccf9c548528166d38e560662a4" in self.notifications @@ -319,13 +312,6 @@ async def test_connect_adam_multiple_devices_per_zone(self): api, "675416a629f343c495449970e2ca37b5" ) assert not switch_change - # Test a blocked group-change, both relays are locked. - group_change = await self.tinker_switch( - api, - "e8ef2a01ed3b4139a53bf749204fe6b4", - ["02cf28bfec924855854c544690a609ef", "4a810418d5394b3f82727340b91ba740"], - ) - assert not group_change await api.close_connection() await self.disconnect(server, client) diff --git a/tests/test_legacy_stretch.py b/tests/test_legacy_stretch.py index 764453162..e8b2c97ec 100644 --- a/tests/test_legacy_stretch.py +++ b/tests/test_legacy_stretch.py @@ -29,7 +29,7 @@ async def test_connect_stretch_v31(self): await self.device_test(api, "2022-05-16 00:00:01", testdata) assert api.gateway_id == "0000aaaa0000aaaa0000aaaa0000aa00" - assert self.entity_items == 85 + assert self.entity_items == 73 switch_change = await self.tinker_switch( api, @@ -68,7 +68,7 @@ async def test_connect_stretch_v23(self): ) await self.device_test(api, "2022-05-16 00:00:01", testdata) - assert self.entity_items == 244 + assert self.entity_items == 238 switch_change = await self.tinker_switch( api, "2587a7fcdd7e482dab03fda256076b4b" @@ -78,12 +78,6 @@ async def test_connect_stretch_v23(self): api, "2587a7fcdd7e482dab03fda256076b4b", model="lock" ) assert switch_change - switch_change = await self.tinker_switch( - api, - "f7b145c8492f4dd7a4de760456fdef3e", - ["407aa1c1099d463c9137a3a9eda787fd"], - ) - assert switch_change await api.close_connection() await self.disconnect(server, client) From 9f2cefdf33effb2a1e08c7d3529de7cd468c1834 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 18:20:31 +0100 Subject: [PATCH 04/12] Revert remove of data-line --- plugwise/legacy/smile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index dad597365..840d79b24 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -257,6 +257,7 @@ async def set_switch_state( return requested_state # Handle individual relay switches + data = f"<{switch.func_type}>{state}" uri = f"{APPLIANCES};id={appl_id}/relay" if model == "relay" and self.gw_entities[appl_id]["switches"]["lock"]: # Don't bother switching a relay when the corresponding lock-state is true From 6b9b7858631600a8c0eeab178bf18146685e35d3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 18:21:34 +0100 Subject: [PATCH 05/12] Clean up --- plugwise/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugwise/common.py b/plugwise/common.py index d37d6774c..e463ddc93 100644 --- a/plugwise/common.py +++ b/plugwise/common.py @@ -12,7 +12,6 @@ NONE, PRIORITY_DEVICE_CLASSES, SPECIAL_PLUG_TYPES, - SWITCH_GROUP_TYPES, ApplianceType, GwEntityData, ModuleData, From bfaed141febca5781fa77354fd512e6681b6aefb Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 18:49:33 +0100 Subject: [PATCH 06/12] Save updated fixtures --- .../adam_multiple_devices_per_zone/data.json | 17 ------- fixtures/adam_plus_anna_new/data.json | 49 +++---------------- .../data.json | 17 ------- fixtures/m_adam_cooling/data.json | 17 ------- fixtures/m_adam_heating/data.json | 17 ------- .../data.json | 17 ------- fixtures/stretch_v23/data.json | 10 ---- fixtures/stretch_v31/data.json | 29 ----------- 8 files changed, 8 insertions(+), 165 deletions(-) diff --git a/fixtures/adam_multiple_devices_per_zone/data.json b/fixtures/adam_multiple_devices_per_zone/data.json index 364d71e59..56329565a 100644 --- a/fixtures/adam_multiple_devices_per_zone/data.json +++ b/fixtures/adam_multiple_devices_per_zone/data.json @@ -554,23 +554,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A11" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "02cf28bfec924855854c544690a609ef", - "4a810418d5394b3f82727340b91ba740" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 14.8, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f1fee6043d3642a9b0a65297455f008e": { "available": true, "binary_sensors": { diff --git a/fixtures/adam_plus_anna_new/data.json b/fixtures/adam_plus_anna_new/data.json index 424e5df4b..15f7e42ab 100644 --- a/fixtures/adam_plus_anna_new/data.json +++ b/fixtures/adam_plus_anna_new/data.json @@ -202,11 +202,7 @@ }, "dev_class": "gateway", "firmware": "3.9.0", - "gateway_modes": [ - "away", - "full", - "vacation" - ], + "gateway_modes": ["away", "full", "vacation"], "hardware": "AME Smile 2.0 board", "location": "bc93488efab249e5bc54fd7e175a6f91", "mac_address": "D40FB201CBA0", @@ -214,12 +210,7 @@ "model_id": "smile_open_therm", "name": "Adam", "notifications": {}, - "regulation_modes": [ - "bleeding_cold", - "heating", - "off", - "bleeding_hot" - ], + "regulation_modes": ["bleeding_cold", "heating", "off", "bleeding_hot"], "select_gateway_mode": "full", "select_regulation_mode": "heating", "sensors": { @@ -289,13 +280,7 @@ "dev_class": "climate", "model": "ThermoZone", "name": "Living room", - "preset_modes": [ - "vacation", - "no_frost", - "asleep", - "home", - "away" - ], + "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "Weekschema", "select_zone_profile": "active", "sensors": { @@ -318,11 +303,7 @@ "secondary": [] }, "vendor": "Plugwise", - "zone_profiles": [ - "active", - "off", - "passive" - ] + "zone_profiles": ["active", "off", "passive"] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "vacation", @@ -338,13 +319,7 @@ "dev_class": "climate", "model": "ThermoZone", "name": "Bathroom", - "preset_modes": [ - "vacation", - "no_frost", - "asleep", - "home", - "away" - ], + "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "off", "select_zone_profile": "passive", "sensors": { @@ -359,18 +334,10 @@ "upper_bound": 99.9 }, "thermostats": { - "primary": [ - "e2f4322d57924fa090fbbc48b3a140dc" - ], - "secondary": [ - "1772a4ea304041adb83f357b751341ff" - ] + "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], + "secondary": ["1772a4ea304041adb83f357b751341ff"] }, "vendor": "Plugwise", - "zone_profiles": [ - "active", - "off", - "passive" - ] + "zone_profiles": ["active", "off", "passive"] } } diff --git a/fixtures/adam_plus_anna_new_regulation_off/data.json b/fixtures/adam_plus_anna_new_regulation_off/data.json index 0a859e23a..929ddb86e 100644 --- a/fixtures/adam_plus_anna_new_regulation_off/data.json +++ b/fixtures/adam_plus_anna_new_regulation_off/data.json @@ -219,23 +219,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "000D6F000C869B61" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "2568cc4b9c1e401495d4741a5f89bee1", - "29542b2b6a6a4169acecc15c72a599b8" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 14.8, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f2bf9048bef64cc5b6d5110154e33c81": { "active_preset": "home", "available_schedules": [ diff --git a/fixtures/m_adam_cooling/data.json b/fixtures/m_adam_cooling/data.json index 9b95475eb..68e4007e8 100644 --- a/fixtures/m_adam_cooling/data.json +++ b/fixtures/m_adam_cooling/data.json @@ -182,23 +182,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "000D6F000C86CBA0" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "2568cc4b9c1e401495d4741a5f89bee1", - "29542b2b6a6a4169acecc15c72a599b8" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 16.5, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f2bf9048bef64cc5b6d5110154e33c81": { "active_preset": "home", "available_schedules": [ diff --git a/fixtures/m_adam_heating/data.json b/fixtures/m_adam_heating/data.json index 47a8fdeb2..e0436f8b0 100644 --- a/fixtures/m_adam_heating/data.json +++ b/fixtures/m_adam_heating/data.json @@ -181,23 +181,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "000D6F000C86CBA0" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "2568cc4b9c1e401495d4741a5f89bee1", - "29542b2b6a6a4169acecc15c72a599b8" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 16.5, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f2bf9048bef64cc5b6d5110154e33c81": { "active_preset": "home", "available_schedules": [ diff --git a/fixtures/m_adam_multiple_devices_per_zone/data.json b/fixtures/m_adam_multiple_devices_per_zone/data.json index 126031795..104ee68ae 100644 --- a/fixtures/m_adam_multiple_devices_per_zone/data.json +++ b/fixtures/m_adam_multiple_devices_per_zone/data.json @@ -547,23 +547,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A11" }, - "e8ef2a01ed3b4139a53bf749204fe6b4": { - "dev_class": "switching", - "members": [ - "02cf28bfec924855854c544690a609ef", - "4a810418d5394b3f82727340b91ba740" - ], - "model": "Group", - "name": "Test", - "sensors": { - "electricity_consumed": 14.8, - "electricity_produced": 0.0 - }, - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "f1fee6043d3642a9b0a65297455f008e": { "available": true, "binary_sensors": { diff --git a/fixtures/stretch_v23/data.json b/fixtures/stretch_v23/data.json index 5ca04fcd8..2da4cebf7 100644 --- a/fixtures/stretch_v23/data.json +++ b/fixtures/stretch_v23/data.json @@ -302,16 +302,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A11" }, - "f7b145c8492f4dd7a4de760456fdef3e": { - "dev_class": "switching", - "members": ["407aa1c1099d463c9137a3a9eda787fd"], - "model": "Group", - "name": "Test", - "switches": { - "relay": false - }, - "vendor": "Plugwise" - }, "fd1b74f59e234a9dae4e23b2b5cf07ed": { "dev_class": "dryer", "firmware": "2011-06-27T10:52:18+02:00", diff --git a/fixtures/stretch_v31/data.json b/fixtures/stretch_v31/data.json index 9927b5c13..e46b53f7e 100644 --- a/fixtures/stretch_v31/data.json +++ b/fixtures/stretch_v31/data.json @@ -85,35 +85,6 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A04" }, - "d03738edfcc947f7b8f4573571d90d2d": { - "dev_class": "switching", - "members": [ - "059e4d03c7a34d278add5c7a4a781d19", - "cfe95cf3de1948c0b8955125bf754614" - ], - "model": "Group", - "name": "Schakel", - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, - "d950b314e9d8499f968e6db8d82ef78c": { - "dev_class": "report", - "members": [ - "059e4d03c7a34d278add5c7a4a781d19", - "5871317346d045bc9f6b987ef25ee638", - "aac7b735042c4832ac9ff33aae4f453b", - "cfe95cf3de1948c0b8955125bf754614", - "e1c884e7dede431dadee09506ec4f859" - ], - "model": "Group", - "name": "Stroomvreters", - "switches": { - "relay": true - }, - "vendor": "Plugwise" - }, "e1c884e7dede431dadee09506ec4f859": { "dev_class": "refrigerator", "firmware": "2011-06-27T10:47:37+02:00", From 610d5e58f8cd662063978e72e02c78eab5a1bb42 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 18:51:14 +0100 Subject: [PATCH 07/12] More clean up --- plugwise/__init__.py | 4 ++-- plugwise/legacy/smile.py | 2 +- plugwise/smile.py | 2 +- tests/test_init.py | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index bf4f3b04b..d1d520e40 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -409,7 +409,7 @@ async def set_temperature_offset(self, dev_id: str, offset: float) -> None: ) from exc # pragma no cover async def set_switch_state( - self, appl_id: str, members: list[str] | None, model: str, state: str + self, appl_id: str, model: str, state: str ) -> bool: """Set the given State of the relevant Switch. @@ -423,7 +423,7 @@ async def set_switch_state( try: return await self._smile_api.set_switch_state( - appl_id, members, model, state + appl_id, model, state ) except ConnectionFailedError as exc: raise ConnectionFailedError( diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 840d79b24..437207e95 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -216,7 +216,7 @@ async def set_schedule_state( await self.call_request(uri, method="put", data=data) async def set_switch_state( - self, appl_id: str, members: list[str] | None, model: str, state: str + self, appl_id: str, model: str, state: str ) -> bool: """Set the given state of the relevant switch. diff --git a/plugwise/smile.py b/plugwise/smile.py index 01b0d8ee2..e80447e13 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -394,7 +394,7 @@ def determine_contexts(self, loc_id: str, state: str, sched_id: str) -> str: return str(etree.tostring(contexts, encoding="unicode").rstrip()) async def set_switch_state( - self, appl_id: str, members: list[str] | None, model: str, state: str + self, appl_id: str, model: str, state: str ) -> bool: """Set the given state of the relevant Switch. diff --git a/tests/test_init.py b/tests/test_init.py index 1f8f3c803..48c7f39af 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -622,7 +622,7 @@ async def tinker_reboot(self, api, unhappy=False): @pytest.mark.asyncio async def tinker_switch( - self, api, dev_id=None, members=None, model="relay", unhappy=False + self, api, dev_id=None, model="relay", unhappy=False ): """Turn a Switch on and off to test functionality.""" _LOGGER.info("Asserting modifying settings for switch devices:") @@ -632,7 +632,7 @@ async def tinker_switch( for new_state in ["off", "on", "off"]: _LOGGER.info("- Switching %s", new_state) try: - result = await api.set_switch_state(dev_id, members, model, new_state) + result = await api.set_switch_state(dev_id, model, new_state) if result == convert[new_state]: tinker_switch_passed = True _LOGGER.info(" + tinker_switch worked as intended") @@ -653,14 +653,14 @@ async def tinker_switch( @pytest.mark.asyncio async def tinker_switch_bad_input( - self, api, dev_id=None, members=None, model="relay", unhappy=False + self, api, dev_id=None, model="relay", unhappy=False ): """Enter a wrong state as input to toggle a Switch.""" _LOGGER.info("Test entering bad input set_switch_state:") _LOGGER.info("- Devices (%s):", dev_id) new_state = "false" try: - await api.set_switch_state(dev_id, members, model, new_state) + await api.set_switch_state(dev_id, model, new_state) except pw_exceptions.PlugwiseError: _LOGGER.info(" + failed input-check as expected") return True # test is pass! From 957c9f2b7c02589dd20ec54e3518e07e343e0053 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 18:53:32 +0100 Subject: [PATCH 08/12] Ruffed --- plugwise/__init__.py | 8 ++------ plugwise/legacy/smile.py | 4 +--- plugwise/smile.py | 4 +--- tests/test_init.py | 4 +--- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index d1d520e40..b99c58888 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -408,9 +408,7 @@ async def set_temperature_offset(self, dev_id: str, offset: float) -> None: f"Failed to set temperature offset: {str(exc)}" ) from exc # pragma no cover - async def set_switch_state( - self, appl_id: str, model: str, state: str - ) -> bool: + async def set_switch_state(self, appl_id: str, model: str, state: str) -> bool: """Set the given State of the relevant Switch. Return the result: @@ -422,9 +420,7 @@ async def set_switch_state( raise PlugwiseError("Invalid state supplied to set_switch_state") try: - return await self._smile_api.set_switch_state( - appl_id, model, state - ) + return await self._smile_api.set_switch_state(appl_id, model, state) except ConnectionFailedError as exc: raise ConnectionFailedError( f"Failed to set switch state: {str(exc)}" diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 437207e95..e63c8989e 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -215,9 +215,7 @@ async def set_schedule_state( uri = f"{RULES};id={schedule_rule_id}" await self.call_request(uri, method="put", data=data) - async def set_switch_state( - self, appl_id: str, model: str, state: str - ) -> bool: + async def set_switch_state(self, appl_id: str, model: str, state: str) -> bool: """Set the given state of the relevant switch. For individual switches, sets the state directly. diff --git a/plugwise/smile.py b/plugwise/smile.py index e80447e13..6d4b3a718 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -393,9 +393,7 @@ def determine_contexts(self, loc_id: str, state: str, sched_id: str) -> str: return str(etree.tostring(contexts, encoding="unicode").rstrip()) - async def set_switch_state( - self, appl_id: str, model: str, state: str - ) -> bool: + async def set_switch_state(self, appl_id: str, model: str, state: str) -> bool: """Set the given state of the relevant Switch. For individual switches, sets the state directly. diff --git a/tests/test_init.py b/tests/test_init.py index 48c7f39af..0b08dbb2a 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -621,9 +621,7 @@ async def tinker_reboot(self, api, unhappy=False): return False @pytest.mark.asyncio - async def tinker_switch( - self, api, dev_id=None, model="relay", unhappy=False - ): + async def tinker_switch(self, api, dev_id=None, model="relay", unhappy=False): """Turn a Switch on and off to test functionality.""" _LOGGER.info("Asserting modifying settings for switch devices:") _LOGGER.info("- Devices (%s):", dev_id) From 1cfd793a963207974a74ccb96b5421dcfd90d94a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 19:04:31 +0100 Subject: [PATCH 09/12] Improve --- plugwise/common.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugwise/common.py b/plugwise/common.py index e463ddc93..534b5e2ec 100644 --- a/plugwise/common.py +++ b/plugwise/common.py @@ -176,7 +176,7 @@ def _reorder_devices(self) -> None: def _get_groups(self) -> dict[str, GwEntityData]: """Helper-function for smile.py: get_all_gateway_entities(). - Collect pumping-groups info. + Collect info for pumping-group(s). """ groups: dict[str, GwEntityData] = {} # P1 and Anna don't have groups @@ -184,14 +184,14 @@ def _get_groups(self) -> dict[str, GwEntityData]: return groups for group in self._domain_objects.findall("./group"): - members: list[str] = [] - group_id = group.attrib["id"] - group_name = group.find("name").text group_type = group.find("type").text if group_type != "pumping": continue group_appliances = group.findall("appliances/appliance") + group_id = group.attrib["id"] + group_name = group.find("name").text + members: list[str] = [] for item in group_appliances: # Check if members are not orphaned - stretch if item.attrib["id"] in self.gw_entities: From 69cf6e68ee6cea75a8c6ded8801217f2956646be Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 19:05:49 +0100 Subject: [PATCH 10/12] Final clean up --- plugwise/constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index a17c9b404..f02bdccd6 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -382,8 +382,6 @@ ] SWITCHES: Final[tuple[str, ...]] = get_args(SwitchType) -SWITCH_GROUP_TYPES: Final[tuple[str, ...]] = ("report", "switching") - THERMOSTAT_CLASSES: Final[tuple[str, ...]] = ( "thermostat", "thermostatic_radiator_valve", From 6fdc495db98483a01e4fae09f35e23efa81d019f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 19:10:49 +0100 Subject: [PATCH 11/12] Improve comments --- plugwise/data.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugwise/data.py b/plugwise/data.py index b9dc6c579..8117543d4 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -190,12 +190,11 @@ def _get_entity_data(self, entity_id: str) -> GwEntityData: data = self._get_measurement_data(entity_id) # Check availability of wired-connected entities - # Smartmeter + # Smartmeter entity: self._check_availability( entity, "smartmeter", data, "P1 does not seem to be connected" ) - - # OpenTherm entity + # OpenTherm entity: if entity["name"] != "OnOff": self._check_availability( entity, "heater_central", data, "no OpenTherm communication" From 9e9474cc27fb018155af0a79b45c773c9bb383a7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Thu, 27 Nov 2025 19:12:50 +0100 Subject: [PATCH 12/12] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f97c0f90..785fd947a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Ongoing + +- Remove switching groups + ## v1.11.0 - Extend feature: support pumping group, add group sensors