22
33from __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
68import logging
9+ from typing import Any , Final
710
811from ..api import NodeEvent , NodeFeature
9- from ..exceptions import MessageError
12+ from ..connection import StickController
13+ from ..exceptions import MessageError , NodeError
1014from ..messages .responses import (
1115 NODE_SWITCH_GROUP_ID ,
1216 NodeSwitchGroupResponse ,
1822
1923_LOGGER = logging .getLogger (__name__ )
2024
25+ CACHE_SWITCH_STATE : Final = "switch_state"
26+ CACHE_SWITCH_TIMESTAMP : Final = "switch_timestamp"
27+
2128
2229class 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