55
66from datetime import datetime , timezone
77from enum import Enum
8+ from typing import Any , Callable , Coroutine
89
910from .legacy_discovery import LegacyDiscovery
1011from .plug_api import PlugApi
12+ from .xlatemsg import Event
13+
14+ _AsyncCallback = Callable [[Event ], Coroutine [None , None , None ]]
1115
1216EXPIRY_CHECK_INTERVAL_S = 30
1317EXPIRY_TIMEOUT_S = 5 * 60
@@ -46,9 +50,17 @@ class _PowersensorDevicesBase:
4650
4751 **device_found**
4852 A device has been discovered or re-discovered.
53+
4954 Note that due to device hardware limitations, role information is NOT
5055 reliably available at this time, and therefore not included in this
51- message.
56+ message. There are situations where role information will never become
57+ available, and therefore the API can make no promises otherwise. If a
58+ user requires role information, they must manage that themselves and
59+ also provide a mechanism for handling the situation of a device not
60+ being able to provide role information in the first place. If a device
61+ does supply a role at any point, it should be considered authoritative
62+ and override any user provided value.
63+
5264 ``{ event: "device_found", device_type: "plug"|"sensor", mac: "..." }``
5365
5466 **device_lost**
@@ -96,7 +108,7 @@ def __init__(
96108 library emits debug/warning/error messages via this logger. When
97109 None (default) the library is completely silent.
98110 """
99- self ._event_cb = None
111+ self ._event_cb : _AsyncCallback | None = None
100112 self ._devices : dict [str , '_PowersensorDevicesBase._Device' ] = {}
101113 self ._plug_apis : dict [str , PlugApi ] = {}
102114 self ._timer : '_PowersensorDevicesBase._Timer | None' = None
@@ -107,7 +119,7 @@ def __init__(
107119 # Internal logging helper
108120 # ------------------------------------------------------------------
109121
110- def _maybe_log (self , level : _LogLevel , msg : str , * args ) -> None :
122+ def _maybe_log (self , level : _LogLevel , msg : str , * args : Any ) -> None :
111123 """Emit a log message if a logger was provided at construction."""
112124 if self ._logger is None :
113125 return
@@ -191,19 +203,20 @@ async def _plug_lost(self, mac: str) -> None:
191203 # Internal event routing
192204 # ------------------------------------------------------------------
193205
194- async def _emit_if_subscribed (self , ev : str , mac : str , obj : dict ) -> None :
206+ async def _emit_if_subscribed (self , ev : str , mac : str , obj : Event ) -> None :
195207 if self ._event_cb is None :
196208 return
197209 device = self ._devices .get (mac )
198210 if device is not None and device .subscribed :
199211 obj ['event' ] = ev
200212 await self ._event_cb (obj )
201213
202- async def _reemit (self , ev : str , obj : dict [ str , str ] ) -> None :
203- mac : str | None = obj .get ('mac' )
214+ async def _reemit (self , ev : str , obj : Event ) -> None :
215+ mac = obj .get ('mac' )
204216 if mac is None :
205217 self ._maybe_log (_LogLevel .WARNING , "Received event '%s' with no MAC address — ignoring" , ev )
206218 return
219+ mac = str (mac )
207220 device = self ._devices .get (mac )
208221 if device is not None :
209222 device .mark_active ()
@@ -264,7 +277,7 @@ def has_expired(self) -> bool:
264277 return delta .total_seconds () > EXPIRY_TIMEOUT_S
265278
266279 class _Timer :
267- def __init__ (self , interval_s : float , callback ) -> None :
280+ def __init__ (self , interval_s : float , callback : Callable [[], Coroutine [ Any , Any , None ]]) :
268281 self ._terminate = False
269282 self ._interval = interval_s
270283 self ._callback = callback
@@ -300,7 +313,7 @@ def __init__(
300313 super ().__init__ (relay_now_relaying_for = relay_now_relaying_for , logger = logger )
301314 self ._discovery = LegacyDiscovery (bcast_addr )
302315
303- async def start (self , async_event_cb ) -> int :
316+ async def start (self , async_event_cb : _AsyncCallback ) -> int :
304317 """Register the async event callback and scan the local network.
305318
306319 The callback has the form::
@@ -329,7 +342,7 @@ async def rescan(self) -> None:
329342 """Perform a fresh scan to discover added or moved devices."""
330343 await self ._on_scanned (await self ._discovery .scan ())
331344
332- async def _on_scanned (self , found : list ) -> None :
345+ async def _on_scanned (self , found : list [ dict [ str , str ]] ) -> None :
333346 for device in found :
334347 mac = device ['id' ]
335348 ip = device ['ip' ]
0 commit comments