Skip to content

Commit 6882b65

Browse files
committed
Update Switch
1 parent 0588937 commit 6882b65

File tree

1 file changed

+103
-27
lines changed

1 file changed

+103
-27
lines changed

plugwise_usb/nodes/switch.py

Lines changed: 103 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
from __future__ import annotations
44

5-
from collections.abc import Callable
5+
from asyncio import gather
6+
from collections.abc import Awaitable, Callable
7+
from datetime import datetime
68
import logging
9+
from typing import Any, Final
710

811
from ..api import NodeEvent, NodeFeature
9-
from ..exceptions import MessageError
12+
from ..connection import StickController
13+
from ..exceptions import MessageError, NodeError
1014
from ..messages.responses import (
1115
NODE_SWITCH_GROUP_ID,
1216
NodeSwitchGroupResponse,
@@ -18,29 +22,43 @@
1822

1923
_LOGGER = logging.getLogger(__name__)
2024

25+
CACHE_SWITCH_STATE: Final = "switch_state"
26+
CACHE_SWITCH_TIMESTAMP: Final = "switch_timestamp"
27+
2128

2229
class PlugwiseSwitch(NodeSED):
2330
"""Plugwise Switch node."""
2431

25-
_switch_subscription: Callable[[], None] | None = None
26-
_switch_state: bool | None = None
32+
def __init__(
33+
self,
34+
mac: str,
35+
address: int,
36+
controller: StickController,
37+
loaded_callback: Callable[[NodeEvent, str], Awaitable[None]],
38+
):
39+
"""Initialize Scan Device."""
40+
super().__init__(mac, address, controller, loaded_callback)
41+
self._switch_subscription: Callable[[], None] | None = None
42+
self._switch_state: bool | None = None
43+
self._switch: bool | None = None
2744

2845
async def load(self) -> bool:
2946
"""Load and activate Switch node features."""
3047
if self._loaded:
3148
return True
32-
self._node_info.is_battery_powered = True
3349
if self._cache_enabled:
3450
_LOGGER.debug("Load Switch node %s from cache", self._node_info.mac)
35-
if await self._load_from_cache():
36-
self._loaded = True
37-
self._setup_protocol(
38-
SWITCH_FIRMWARE_SUPPORT,
39-
(NodeFeature.INFO, NodeFeature.SWITCH),
40-
)
41-
if await self.initialize():
42-
await self._loaded_callback(NodeEvent.LOADED, self.mac)
43-
return True
51+
await self._load_from_cache()
52+
else:
53+
self._load_defaults()
54+
self._loaded = True
55+
self._setup_protocol(
56+
SWITCH_FIRMWARE_SUPPORT,
57+
(NodeFeature.BATTERY, NodeFeature.INFO, NodeFeature.PING, NodeFeature.SWITCH),
58+
)
59+
if await self.initialize():
60+
await self._loaded_callback(NodeEvent.LOADED, self.mac)
61+
return True
4462
_LOGGER.debug("Load of Switch node %s failed", self._node_info.mac)
4563
return False
4664

@@ -49,7 +67,7 @@ async def initialize(self) -> bool:
4967
"""Initialize Switch node."""
5068
if self._initialized:
5169
return True
52-
self._switch_subscription = self._message_subscribe(
70+
self._switch_subscription = await self._message_subscribe(
5371
self._switch_group,
5472
self._mac_in_bytes,
5573
(NODE_SWITCH_GROUP_ID,),
@@ -58,29 +76,87 @@ async def initialize(self) -> bool:
5876

5977
async def unload(self) -> None:
6078
"""Unload node."""
61-
self._loaded = False
6279
if self._switch_subscription is not None:
6380
self._switch_subscription()
6481
await super().unload()
6582

83+
# region Properties
84+
85+
@property
86+
@raise_not_loaded
87+
def switch(self) -> bool:
88+
"""Current state of switch."""
89+
return bool(self._switch_state)
90+
91+
#endregion
92+
6693
async def _switch_group(self, response: PlugwiseResponse) -> bool:
6794
"""Switch group request from Switch."""
6895
if not isinstance(response, NodeSwitchGroupResponse):
6996
raise MessageError(
7097
f"Invalid response message type ({response.__class__.__name__}) received, expected NodeSwitchGroupResponse"
7198
)
99+
await gather(
100+
self._available_update_state(True, response.timestamp),
101+
self._switch_state_update(response.switch_state, response.timestamp)
102+
)
103+
return True
104+
105+
async def _switch_state_update(
106+
self, switch_state: bool, timestamp: datetime
107+
) -> None:
108+
"""Process motion state update."""
109+
_LOGGER.debug(
110+
"_switch_state_update for %s: %s -> %s",
111+
self.name,
112+
self._switch_state,
113+
switch_state,
114+
)
115+
state_update = False
72116
# Switch on
73-
if response.switch_state:
117+
if switch_state:
118+
self._set_cache(CACHE_SWITCH_STATE, "True")
74119
if self._switch_state is None or not self._switch:
75120
self._switch_state = True
76-
await self.publish_feature_update_to_subscribers(
77-
NodeFeature.SWITCH, True
78-
)
79-
return True
80-
# Switch off
81-
if self._switch is None or self._switch:
82-
self._switch = False
83-
await self.publish_feature_update_to_subscribers(
84-
NodeFeature.SWITCH, False
121+
state_update = True
122+
else:
123+
# Switch off
124+
self._set_cache(CACHE_SWITCH_STATE, "False")
125+
if self._switch is None or self._switch:
126+
self._switch_state = False
127+
state_update = True
128+
self._set_cache(CACHE_SWITCH_TIMESTAMP, timestamp)
129+
if state_update:
130+
self._switch = switch_state
131+
await gather(
132+
*[
133+
self.publish_feature_update_to_subscribers(
134+
NodeFeature.SWITCH, self._switch_state
135+
),
136+
self.save_cache(),
137+
]
85138
)
86-
return True
139+
140+
@raise_not_loaded
141+
async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any]:
142+
"""Update latest state for given feature."""
143+
states: dict[NodeFeature, Any] = {}
144+
for feature in features:
145+
_LOGGER.debug(
146+
"Updating node %s - feature '%s'",
147+
self._node_info.mac,
148+
feature,
149+
)
150+
if feature not in self._features:
151+
raise NodeError(
152+
f"Update of feature '{feature.name}' is "
153+
+ f"not supported for {self.mac}"
154+
)
155+
if feature == NodeFeature.SWITCH:
156+
states[NodeFeature.SWITCH] = self._switch_state
157+
else:
158+
state_result = await super().get_state((feature,))
159+
states[feature] = state_result[feature]
160+
if NodeFeature.AVAILABLE not in states:
161+
states[NodeFeature.AVAILABLE] = self.available_state
162+
return states

0 commit comments

Comments
 (0)