From 8a72b2ceb2580315d463ae0f6dc043aa92c4c51c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 14 Feb 2025 19:32:27 +0100 Subject: [PATCH 01/21] Adapt URI based on user-input --- 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 5d63b3255..ea0ab5491 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -234,10 +234,10 @@ async def set_switch_state( """Set the given State of the relevant Switch.""" switch = Munch() switch.actuator = "actuator_functionalities" - switch.func_type = "relay_functionality" if self._stretch_v2: switch.actuator = "actuators" - switch.func_type = "relay" + # From #833 it seems this is not correct: switch.func_type = "relay_functionality" + switch.func_type = "relay" switch.func = "state" if members is not None: From 78589b85738537b2766d6c40213195969c876d18 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 14 Feb 2025 19:40:05 +0100 Subject: [PATCH 02/21] Update CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c10039a93..7b17ebd26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog -## v.1.7.1 +## v1.7.2 + +- Bugfix for Plugwise-beta Issue [#833](https://github.com/plugwise/plugwise-beta/issues/833) + +## v1.7.1 - Avoid None-init for smile_version [#699](https://github.com/plugwise/python-plugwise/pull/699) - Replace string.split() by string.partition() [#702](https://github.com/plugwise/python-plugwise/pull/702) From f45e8f1b03340e8a4fb1ec73a1b07d7fadf39f0e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 14 Feb 2025 19:41:18 +0100 Subject: [PATCH 03/21] Bump to v1.7.2a0 test-version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ae85c0b23..4a1138160 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.7.1" +version = "1.7.2a0" license = {file = "LICENSE"} description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" From daa5fe90144337ba8a19342cbeae1a9ceb43d12d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 14 Feb 2025 19:49:04 +0100 Subject: [PATCH 04/21] Alternative solution --- plugwise/legacy/smile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index ea0ab5491..9bec46587 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -234,17 +234,17 @@ async def set_switch_state( """Set the given State of the relevant Switch.""" switch = Munch() switch.actuator = "actuator_functionalities" + switch.func_type = "relay_functionality" if self._stretch_v2: switch.actuator = "actuators" - # From #833 it seems this is not correct: switch.func_type = "relay_functionality" - switch.func_type = "relay" + switch.func_type = "relay" switch.func = "state" if members is not None: return await self._set_groupswitch_member_state(members, state, switch) data = f"<{switch.func_type}><{switch.func}>{state}" - uri = f"{APPLIANCES};id={appl_id}/{switch.func_type}" + uri = f"{APPLIANCES};id={appl_id}/relay" if model == "relay": locator = ( From fea476edef597cb62f8f34969f19990d1fbb4950 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 08:29:46 +0100 Subject: [PATCH 05/21] Fix _set_groupswitch_member_state() function as well --- 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 9bec46587..234d8f997 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -264,8 +264,8 @@ async def _set_groupswitch_member_state( Set the given State of the relevant Switch within a group of members. """ for member in members: - uri = f"{APPLIANCES};id={member}/{switch.func_type}" data = f"<{switch.func_type}><{switch.func}>{state}" + uri = f"{APPLIANCES};id={member}/relay" await self.call_request(uri, method="put", data=data) From d7f650ac4981e07058c3c8bd2a207804639e6c19 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 08:59:04 +0100 Subject: [PATCH 06/21] Simplify, add support for lock-switching --- plugwise/legacy/smile.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 234d8f997..28b5dc15c 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -238,13 +238,15 @@ async def set_switch_state( if self._stretch_v2: switch.actuator = "actuators" switch.func_type = "relay" - switch.func = "state" + + if model == "lock": + state = "false" if state == "off" else "true" if members is not None: return await self._set_groupswitch_member_state(members, state, switch) - data = f"<{switch.func_type}><{switch.func}>{state}" - uri = f"{APPLIANCES};id={appl_id}/relay" + data = f"<{switch.func_type}>{state}" + uri = f"{APPLIANCES};id={appl_id}/{model}" if model == "relay": locator = ( @@ -261,10 +263,10 @@ async def _set_groupswitch_member_state( ) -> None: """Helper-function for set_switch_state(). - Set the given State of the relevant Switch within a group of members. + Set the given State of the relevant Switch (relay) within a group of members. """ for member in members: - data = f"<{switch.func_type}><{switch.func}>{state}" + data = f"<{switch.func_type}>{state}" uri = f"{APPLIANCES};id={member}/relay" await self.call_request(uri, method="put", data=data) From 45782f95246f687f652599efba0b4d8b2c7b17bb Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 09:00:50 +0100 Subject: [PATCH 07/21] Add test for lock --- tests/test_legacy_stretch.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_legacy_stretch.py b/tests/test_legacy_stretch.py index f5056dee6..021cc385b 100644 --- a/tests/test_legacy_stretch.py +++ b/tests/test_legacy_stretch.py @@ -74,6 +74,10 @@ async def test_connect_stretch_v23(self): smile, "2587a7fcdd7e482dab03fda256076b4b" ) assert switch_change + switch_change = await self.tinker_switch( + smile, "2587a7fcdd7e482dab03fda256076b4b", model="lock" + ) + assert switch_change switch_change = await self.tinker_switch( smile, "f7b145c8492f4dd7a4de760456fdef3e", From e4b5dc4d49dab6a93149bb76b0757baab0f0de96 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 09:14:07 +0100 Subject: [PATCH 08/21] Fix typing --- plugwise/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 407fcd8b0..d2160663f 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -296,7 +296,7 @@ async def _smile_detect_legacy( ): system = await self._request(SYSTEM) self.smile_version = parse(system.find("./gateway/firmware").text) - return_model = system.find("./gateway/product").text + return_model = str(system.find("./gateway/product").text) self.smile_hostname = system.find("./gateway/hostname").text # If wlan0 contains data it's active, so eth0 should be checked last for network in ("wlan0", "eth0"): @@ -307,7 +307,7 @@ async def _smile_detect_legacy( elif dsmrmain is not None: status = await self._request(STATUS) self.smile_version = parse(status.find("./system/version").text) - return_model = status.find("./system/product").text + return_model = str(status.find("./system/product").text) self.smile_hostname = status.find("./network/hostname").text self.smile_mac_address = status.find("./network/mac_address").text else: # pragma: no cover From 5323f55c2c6fa357d3aaf472c6ceb7cea3541705 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 09:16:34 +0100 Subject: [PATCH 09/21] Bump to a1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4a1138160..b9bafe39d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.7.2a0" +version = "1.7.2a1" license = {file = "LICENSE"} description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" From 07856387c1ac646b88e89bf46b6373d99717fc83 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 10:22:18 +0100 Subject: [PATCH 10/21] Implement legacy lock-toggle --- plugwise/legacy/smile.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 28b5dc15c..f7561217d 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -241,12 +241,22 @@ async def set_switch_state( if model == "lock": state = "false" if state == "off" else "true" + appliance = self._appliances.find(f'appliance[@id="{appl_id}"]') + appl_name = appliance.find("name").text + appl_type = appliance.find("type").text + data = ( + f'' + f"" + "true" + ) + await self.call_request(APPLIANCES, method="post", data=data) + return if members is not None: return await self._set_groupswitch_member_state(members, state, switch) data = f"<{switch.func_type}>{state}" - uri = f"{APPLIANCES};id={appl_id}/{model}" + uri = f"{APPLIANCES};id={appl_id}/relay" if model == "relay": locator = ( From c7fbe70c1efca655233f5b84ed30f706de5ddadf Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 11:53:29 +0100 Subject: [PATCH 11/21] Legacy-testing: add support for POST --- tests/test_init.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_init.py b/tests/test_init.py index 028e33c11..b585125ab 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -167,8 +167,10 @@ async def setup_legacy_app( "PUT", CORE_APPLIANCES_TAIL, self.smile_http_accept ) else: + app.router.add_route("POST", CORE_APPLIANCES_TAIL, self.smile_http_ok) app.router.add_route("PUT", CORE_APPLIANCES_TAIL, self.smile_http_ok) else: + app.router.add_route("POST", CORE_APPLIANCES_TAIL, self.smile_timeout) app.router.add_route("PUT", 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) From f102fb45c93f6c11dfc636b9dc2ba40f6b4f46a9 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 11:58:00 +0100 Subject: [PATCH 12/21] Bump to a2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b9bafe39d..8081cfb3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.7.2a1" +version = "1.7.2a2" license = {file = "LICENSE"} description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" From 086e6346578fa0027f406e2519072a181327ecb8 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 12:46:04 +0100 Subject: [PATCH 13/21] Improve --- plugwise/legacy/smile.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index f7561217d..9cc23c868 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -246,17 +246,19 @@ async def set_switch_state( appl_type = appliance.find("type").text data = ( f'' - f"" - "true" + f"" + f"<{switch.actuator}><{switch.func_type}>{state}" + "" ) await self.call_request(APPLIANCES, method="post", data=data) return - if members is not None: - return await self._set_groupswitch_member_state(members, state, switch) - data = f"<{switch.func_type}>{state}" uri = f"{APPLIANCES};id={appl_id}/relay" + if members is not None: + return await self._set_groupswitch_member_state( + data, members, state, switch + ) if model == "relay": locator = ( @@ -269,16 +271,14 @@ async def set_switch_state( await self.call_request(uri, method="put", data=data) async def _set_groupswitch_member_state( - self, members: list[str], state: str, switch: Munch + self, data: str, members: list[str], state: str, switch: Munch ) -> None: """Helper-function for set_switch_state(). Set the given State of the relevant Switch (relay) within a group of members. """ for member in members: - data = f"<{switch.func_type}>{state}" uri = f"{APPLIANCES};id={member}/relay" - await self.call_request(uri, method="put", data=data) async def set_temperature(self, _: str, items: dict[str, float]) -> None: From bb2acba238bc60a0cbd031e11fd7df657075b088 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 12:52:49 +0100 Subject: [PATCH 14/21] Bump to a3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8081cfb3f..1382e4043 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.7.2a2" +version = "1.7.2a3" license = {file = "LICENSE"} description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" From a57e010f8a50f71e436b8db49579a80d80f77d83 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 13:41:36 +0100 Subject: [PATCH 15/21] Improve further --- plugwise/legacy/smile.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 9cc23c868..a0c241e08 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -231,7 +231,12 @@ async def set_schedule_state( async def set_switch_state( self, appl_id: str, members: list[str] | None, model: str, state: str ) -> None: - """Set the given State of the relevant Switch.""" + """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. + """ switch = Munch() switch.actuator = "actuator_functionalities" switch.func_type = "relay_functionality" @@ -239,6 +244,7 @@ async def set_switch_state( switch.actuator = "actuators" switch.func_type = "relay" + # Handle switch-lock if model == "lock": state = "false" if state == "off" else "true" appliance = self._appliances.find(f'appliance[@id="{appl_id}"]') @@ -253,13 +259,15 @@ async def set_switch_state( await self.call_request(APPLIANCES, method="post", data=data) return + # Handle group of switches data = f"<{switch.func_type}>{state}" - uri = f"{APPLIANCES};id={appl_id}/relay" if members is not None: return await self._set_groupswitch_member_state( data, members, state, switch ) + # Handle individual relay switches + uri = f"{APPLIANCES};id={appl_id}/relay" if model == "relay": locator = ( f'appliance[@id="{appl_id}"]/{switch.actuator}/{switch.func_type}/lock' From 73ad65f29032bbdec24afaebd62091d267604ae6 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 14:17:13 +0100 Subject: [PATCH 16/21] Fix typo --- 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 a0c241e08..5a680b7a2 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -251,7 +251,7 @@ async def set_switch_state( appl_name = appliance.find("name").text appl_type = appliance.find("type").text data = ( - f'' + f'' f"" f"<{switch.actuator}><{switch.func_type}>{state}" "" From 33d881f7a0ff10d5b9d8aa2315ed10fec516f180 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 14:18:29 +0100 Subject: [PATCH 17/21] Bump to a4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1382e4043..e47d21bc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.7.2a3" +version = "1.7.2a4" license = {file = "LICENSE"} description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" From 0d4fb02c193ddbe098ba905c0c6650f76676b880 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 15:02:27 +0100 Subject: [PATCH 18/21] Bump to v1.7.2 release-version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e47d21bc0..c5c33be97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.7.2a4" +version = "1.7.2" license = {file = "LICENSE"} description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" From 465363d3ab51fa42c9eed5e85a3d4116271095ee Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 15:10:11 +0100 Subject: [PATCH 19/21] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b17ebd26..1dccd7aab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v1.7.2 -- Bugfix for Plugwise-beta Issue [#833](https://github.com/plugwise/plugwise-beta/issues/833) +- Bugfix for Plugwise-beta issue [833](https://github.com/plugwise/plugwise-beta/issues/833) solving relay- and lock-switches not switching for the Stretch. ## v1.7.1 From 84f79c57923122cf88450ad459f8432caffd3b61 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 15:11:56 +0100 Subject: [PATCH 20/21] Implement CoderabbitAI suggestion --- plugwise/legacy/smile.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 5a680b7a2..d159a2e22 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -250,12 +250,25 @@ async def set_switch_state( appliance = self._appliances.find(f'appliance[@id="{appl_id}"]') appl_name = appliance.find("name").text appl_type = appliance.find("type").text - data = ( - f'' - f"" - f"<{switch.actuator}><{switch.func_type}>{state}" - "" - ) +- data = ( +- f'' +- f"" +- f"<{switch.actuator}><{switch.func_type}>{state}" +- "" +- ) ++ data = f''' ++ ++ ++ ++ ++ ++ <{switch.actuator}> ++ <{switch.func_type}> ++ {state} ++ ++ ++ ++ '''.strip() await self.call_request(APPLIANCES, method="post", data=data) return From 6a30c51762c3e780e23d6567b655543b7f4e2e24 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 15 Feb 2025 15:14:49 +0100 Subject: [PATCH 21/21] Fix --- plugwise/legacy/smile.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index d159a2e22..c74dcddcb 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -250,25 +250,19 @@ async def set_switch_state( appliance = self._appliances.find(f'appliance[@id="{appl_id}"]') appl_name = appliance.find("name").text appl_type = appliance.find("type").text -- data = ( -- f'' -- f"" -- f"<{switch.actuator}><{switch.func_type}>{state}" -- "" -- ) -+ data = f''' -+ -+ -+ -+ -+ -+ <{switch.actuator}> -+ <{switch.func_type}> -+ {state} -+ -+ -+ -+ '''.strip() + data = f''' + + + + + + <{switch.actuator}> + <{switch.func_type}> + {state} + + + + '''.strip() await self.call_request(APPLIANCES, method="post", data=data) return