diff --git a/CHANGELOG.md b/CHANGELOG.md
index c10039a93..1dccd7aab 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) solving relay- and lock-switches not switching for the Stretch.
+
+## 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)
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
diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py
index 5d63b3255..c74dcddcb 100644
--- a/plugwise/legacy/smile.py
+++ b/plugwise/legacy/smile.py
@@ -231,21 +231,50 @@ 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"
if self._stretch_v2:
switch.actuator = "actuators"
switch.func_type = "relay"
- switch.func = "state"
+ # Handle switch-lock
+ 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'''
+
+
+
+
+
+ <{switch.actuator}>
+ <{switch.func_type}>
+ {state}
+ {switch.func_type}>
+ {switch.actuator}>
+
+ '''.strip()
+ await self.call_request(APPLIANCES, method="post", data=data)
+ return
+
+ # Handle group of switches
+ data = f"<{switch.func_type}>{state}{switch.func_type}>"
if members is not None:
- return await self._set_groupswitch_member_state(members, state, switch)
-
- data = f"<{switch.func_type}><{switch.func}>{state}{switch.func}>{switch.func_type}>"
- uri = f"{APPLIANCES};id={appl_id}/{switch.func_type}"
+ 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'
@@ -257,16 +286,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 within a group of members.
+ Set the given State of the relevant Switch (relay) 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}{switch.func}>{switch.func_type}>"
-
+ 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:
diff --git a/pyproject.toml b/pyproject.toml
index ae85c0b23..c5c33be97 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.2"
license = {file = "LICENSE"}
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
readme = "README.md"
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)
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",