11import asyncio
22import json
3+ import sys
34
45from datetime import datetime , timezone
6+ from pathlib import Path
7+ project_root = str (Path (__file__ ).parents [1 ])
8+ if project_root not in sys .path :
9+ sys .path .append (project_root )
510
6- from .listener import PowersensorListener
7- from .xlatemsg import translate_raw_message
11+ from powersensor_local .legacy_discovery import LegacyDiscovery
12+ from powersensor_local .plug_api import PlugApi
13+ from powersensor_local .xlatemsg import translate_raw_message
814
915EXPIRY_CHECK_INTERVAL_S = 30
1016EXPIRY_TIMEOUT_S = 5 * 60
@@ -17,9 +23,10 @@ class PowersensorDevices:
1723 def __init__ (self , bcast_addr = '<broadcast>' ):
1824 """Creates a fresh instance, without scanning for devices."""
1925 self ._event_cb = None
20- self ._ps = PowersensorListener (bcast_addr )
26+ self ._discovery = LegacyDiscovery (bcast_addr )
2127 self ._devices = dict ()
2228 self ._timer = None
29+ self ._plug_apis = dict ()
2330
2431 async def start (self , async_event_cb ):
2532 """Registers the async event callback function and starts the scan
@@ -46,10 +53,6 @@ async def yourcallback(event: dict)
4653 mac: "...",
4754 }
4855
49- An optional field named "via" is present for sensor devices, and
50- shows the MAC address of the gateway the sensor is communicating
51- via.
52-
5356 device_lost:
5457 A device appears to no longer be present on the network.
5558
@@ -66,20 +69,21 @@ async def yourcallback(event: dict)
6669 a plug via long-range radio.
6770 """
6871 self ._event_cb = async_event_cb
69- await self ._on_scanned (await self ._ps .scan ())
72+ await self ._on_scanned (await self ._discovery .scan ())
7073 self ._timer = self ._Timer (EXPIRY_CHECK_INTERVAL_S , self ._on_timer )
71- return len (self ._ips )
74+ return len (self ._plug_apis )
7275
7376 async def rescan (self ):
7477 """Performs a fresh scan of the network to discover added devices,
7578 or devices which have changed their IP address for some reason."""
76- await self ._on_scanned (await self ._ps .scan ())
79+ await self ._on_scanned (await self ._discovery .scan ())
7780
7881 async def stop (self ):
7982 """Stops the event streaming and disconnects from the devices.
8083 To restart the event streaming, call start() again."""
81- await self ._ps .unsubscribe ()
82- await self ._ps .stop ()
84+ for plug in self ._plug_apis .values ():
85+ await plug .disconnect ()
86+ self ._plug_apis = dict ()
8387 self ._event_cb = None
8488 if self ._timer :
8589 self ._timer .terminate ()
@@ -97,78 +101,78 @@ def unsubscribe(self, mac):
97101 if device :
98102 device .subscribed = False
99103
100- async def _on_scanned (self , ips ):
101- self ._ips = ips
102- if self ._event_cb :
103- ev = {
104- 'event' : 'scan_complete' ,
105- 'gateway_count' : len (ips ),
106- }
107- await self ._event_cb (ev )
108-
109- await self ._ps .subscribe (self ._on_msg )
110-
111- async def _on_msg (self , obj ):
112- mac = obj .get ('mac' )
113- if mac and not self ._devices .get (mac ):
114- typ = obj .get ('device' )
115- via = obj .get ('via' )
116- await self ._add_device (mac , typ , via )
117-
118- device = self ._devices [mac ]
119- device .mark_active ()
104+ async def _emit_if_subscribed (self , ev , obj ):
105+ if self ._event_cb is None :
106+ return
107+ device = self ._devices .get (obj .get ('mac' ))
108+ if device is not None and device .subscribed :
109+ obj ['event' ] = ev
110+ await self ._event_cb (obj )
120111
121- if self ._event_cb and device .subscribed :
122- relayer = obj .get ('via' ) or mac
123- evs = self ._mk_events (obj , relayer )
124- if len (evs ) > 0 :
125- for ev in evs :
126- await self ._event_cb (ev )
112+ async def _reemit (self , ev , obj ):
113+ mac = obj ['mac' ]
114+ device = self ._devices .get (mac )
115+ if device is not None :
116+ device .mark_active ()
117+
118+ if ev == 'now_relaying_for' :
119+ await self ._add_device (mac , 'sensor' )
120+ else :
121+ await self ._emit_if_subscribed (ev , obj )
122+
123+ async def _on_scanned (self , found ):
124+ for device in found :
125+ mac = device ['id' ]
126+ ip = device ['ip' ]
127+ if not mac in self ._devices :
128+ await self ._add_device (mac , 'plug' )
129+ api = PlugApi (mac , ip )
130+ self ._plug_apis [mac ] = api
131+ api .subscribe ('average_flow' , self ._reemit )
132+ api .subscribe ('average_power' , self ._reemit )
133+ api .subscribe ('average_power_components' , self ._reemit )
134+ api .subscribe ('battery_level' , self ._reemit )
135+ api .subscribe ('exception' , self ._reemit )
136+ api .subscribe ('now_relaying_for' , self ._reemit )
137+ api .subscribe ('radio_signal_quality' , self ._reemit )
138+ api .subscribe ('summation_energy' , self ._reemit )
139+ api .subscribe ('summation_volume' , self ._reemit )
140+ api .connect ()
141+
142+ await self ._event_cb ({
143+ 'event' : 'scan_complete' ,
144+ 'gateway_count' : len (found ),
145+ })
127146
128147 async def _on_timer (self ):
129148 devices = list (self ._devices .values ())
130149 for device in devices :
131150 if device .has_expired ():
132151 await self ._remove_device (device .mac )
133152
134- async def _add_device (self , mac , typ , via ):
135- self ._devices [mac ] = self ._Device (mac , typ , via )
136- if self ._event_cb :
137- ev = {
138- 'event' : 'device_found' ,
139- 'device_type' : typ ,
140- 'mac' : mac ,
141- }
142- if via :
143- ev ['via' ] = via
144- await self ._event_cb (ev )
153+ async def _add_device (self , mac , typ ):
154+ if mac in self ._devices :
155+ return
156+ self ._devices [mac ] = self ._Device (mac )
157+ await self ._event_cb ({
158+ 'event' : 'device_found' ,
159+ 'mac' : mac ,
160+ 'device_type:' : typ ,
161+ })
145162
146163 async def _remove_device (self , mac ):
147- if self ._devices . get ( mac ) :
164+ if mac in self ._devices :
148165 self ._devices .pop (mac )
149- if self ._event_cb :
150- ev = {
151- 'event' : 'device_lost' ,
152- 'mac' : mac
153- }
154- await self ._event_cb (ev )
155-
156- def _mk_events (self , obj , relayer ):
157- evs = []
158- kvs = translate_raw_message (obj , relayer )
159- for key , ev in kvs .items ():
160- ev ['event' ] = key
161- evs .append (ev )
162-
163- return evs
166+ await self ._event_cb ({
167+ 'event' : 'device_lost' ,
168+ 'mac' : mac ,
169+ })
164170
165171 ### Supporting classes ###
166172
167173 class _Device :
168- def __init__ (self , mac , typ , via ):
174+ def __init__ (self , mac ):
169175 self .mac = mac
170- self .type = typ
171- self .via = via
172176 self .subscribed = False
173177 self ._last_active = datetime .now (timezone .utc )
174178
0 commit comments