Skip to content

Commit 5900297

Browse files
authored
Merge pull request #282 from plugwise/mdi_switch
implement (stateless) switch event propagation
2 parents 895142e + 91e75a8 commit 5900297

File tree

5 files changed

+39
-33
lines changed

5 files changed

+39
-33
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## v0.44.7 - 2025-07-08
4+
5+
- PR [282](https://github.com/plugwise/python-plugwise-usb/pull/282): Finalize switch implementation
6+
37
## v0.44.6 - 2025-07-06
48

59
- PR [279](https://github.com/plugwise/python-plugwise-usb/pull/279): Improve registry cache and node load behaviour

plugwise_usb/api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ class RelayState:
205205
timestamp: datetime | None = None
206206

207207

208+
@dataclass(frozen=True)
209+
class SwitchGroup:
210+
"""Status and Group of Switch."""
211+
212+
state: bool | None = None
213+
group: int | None = None
214+
timestamp: datetime | None = None
215+
216+
208217
@dataclass(frozen=True)
209218
class MotionState:
210219
"""Status of motion sensor."""

plugwise_usb/messages/responses.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,11 @@ def switch_state(self) -> bool:
858858
"""Return state of switch (True = On, False = Off)."""
859859
return self._power_state.value != 0
860860

861+
@property
862+
def switch_group(self) -> int:
863+
"""Return group number."""
864+
return self.group.value
865+
861866
def __repr__(self) -> str:
862867
"""Convert request into writable str."""
863868
return f"{super().__repr__()[:-1]}, power_state={self._power_state.value}, group={self.group.value})"

plugwise_usb/nodes/switch.py

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
from asyncio import gather
66
from collections.abc import Awaitable, Callable
7+
from dataclasses import replace
78
from datetime import datetime
89
import logging
9-
from typing import Any, Final
10+
from typing import Any
1011

11-
from ..api import NodeEvent, NodeFeature
12+
from ..api import NodeEvent, NodeFeature, SwitchGroup
1213
from ..connection import StickController
1314
from ..exceptions import MessageError, NodeError
1415
from ..messages.responses import (
@@ -22,9 +23,6 @@
2223

2324
_LOGGER = logging.getLogger(__name__)
2425

25-
CACHE_SWITCH_STATE: Final = "switch_state"
26-
CACHE_SWITCH_TIMESTAMP: Final = "switch_timestamp"
27-
2826

2927
class PlugwiseSwitch(NodeSED):
3028
"""Plugwise Switch node."""
@@ -39,7 +37,7 @@ def __init__(
3937
"""Initialize Scan Device."""
4038
super().__init__(mac, address, controller, loaded_callback)
4139
self._switch_subscription: Callable[[], None] | None = None
42-
self._switch_state: bool | None = None
40+
self._switch = SwitchGroup()
4341

4442
async def load(self) -> bool:
4543
"""Load and activate Switch node features."""
@@ -73,7 +71,7 @@ async def initialize(self) -> bool:
7371
return True
7472

7573
self._switch_subscription = await self._message_subscribe(
76-
self._switch_group,
74+
self._switch_response,
7775
self._mac_in_bytes,
7876
(NODE_SWITCH_GROUP_ID,),
7977
)
@@ -92,50 +90,40 @@ async def unload(self) -> None:
9290
@raise_not_loaded
9391
def switch(self) -> bool:
9492
"""Current state of switch."""
95-
return bool(self._switch_state)
93+
return bool(self._switch.state)
9694

9795
# endregion
9896

99-
async def _switch_group(self, response: PlugwiseResponse) -> bool:
97+
async def _switch_response(self, response: PlugwiseResponse) -> bool:
10098
"""Switch group request from Switch."""
10199
if not isinstance(response, NodeSwitchGroupResponse):
102100
raise MessageError(
103101
f"Invalid response message type ({response.__class__.__name__}) received, expected NodeSwitchGroupResponse"
104102
)
105103
await gather(
106104
self._available_update_state(True, response.timestamp),
107-
self._switch_state_update(response.switch_state, response.timestamp),
105+
self._switch_state_update(
106+
response.switch_state, response.switch_group, response.timestamp
107+
),
108108
)
109109
return True
110110

111111
async def _switch_state_update(
112-
self, switch_state: bool, timestamp: datetime
112+
self, switch_state: bool, switch_group: int, timestamp: datetime
113113
) -> None:
114114
"""Process switch state update."""
115115
_LOGGER.debug(
116-
"_switch_state_update for %s: %s -> %s",
116+
"_switch_state_update for %s: %s",
117117
self.name,
118-
self._switch_state,
119118
switch_state,
120119
)
121-
state_update = False
122-
# Update cache
123-
self._set_cache(CACHE_SWITCH_STATE, str(switch_state))
124-
# Check for a state change
125-
if self._switch_state != switch_state:
126-
self._switch_state = switch_state
127-
state_update = True
128-
129-
self._set_cache(CACHE_SWITCH_TIMESTAMP, timestamp)
130-
if state_update:
131-
await gather(
132-
*[
133-
self.publish_feature_update_to_subscribers(
134-
NodeFeature.SWITCH, self._switch_state
135-
),
136-
self.save_cache(),
137-
]
138-
)
120+
self._switch = replace(
121+
self._switch, state=switch_state, group=switch_group, timestamp=timestamp
122+
)
123+
124+
await self.publish_feature_update_to_subscribers(
125+
NodeFeature.SWITCH, self._switch
126+
)
139127

140128
@raise_not_loaded
141129
async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any]:
@@ -155,7 +143,7 @@ async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any
155143

156144
match feature:
157145
case NodeFeature.SWITCH:
158-
states[NodeFeature.SWITCH] = self._switch_state
146+
states[NodeFeature.SWITCH] = self._switch
159147
case _:
160148
state_result = await super().get_state((feature,))
161149
states[feature] = state_result[feature]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "plugwise_usb"
7-
version = "0.44.6"
7+
version = "0.44.7"
88
license = "MIT"
99
keywords = ["home", "automation", "plugwise", "module", "usb"]
1010
classifiers = [

0 commit comments

Comments
 (0)