1-
21import asyncio
32import logging
43from typing import Optional , List
54
65_LOGGER = logging .getLogger (__name__ )
76
7+
88class DiscoveredDevice (object ):
99 mac : str
1010 type : int
@@ -15,13 +15,15 @@ class DiscoveredDevice(object):
1515
1616 @staticmethod
1717 def create_from_announce_msg (raw_addr , announce_msg ):
18+ _LOGGER .debug ("received announce msg '%s' from %s " , announce_msg , raw_addr )
1819 if len (announce_msg ) != 8 :
19- raise RuntimeError ("unexpected announcement, %s " % announce_msg )
20+ raise RuntimeError ("unexpected announcement, '%s' " % announce_msg )
2021
21- device = DiscoveredDevice (host = raw_addr [0 ],
22- mac = announce_msg [0 :6 ].hex (":" ))
22+ device = DiscoveredDevice (host = raw_addr [0 ], mac = announce_msg [0 :6 ].hex (":" ))
2323 device .type = announce_msg [6 ]
2424 status = announce_msg [7 ]
25+
26+ # parse status field
2527 device .is_child = status & 1 != 0
2628 device .mystrom_registered = status & 2 != 0
2729 device .mystrom_online = status & 4 != 0
@@ -33,48 +35,67 @@ def __init__(self, host, mac):
3335 self .mac = mac
3436
3537
38+ class DeviceRegistry (object ):
39+ def __init__ (self ):
40+ self .devices_by_mac = {}
41+
42+ def register (self , device ):
43+ self .devices_by_mac [device .mac ] = device
44+
45+ def devices (self ):
46+ return list (self .devices_by_mac .values ())
47+
48+
3649class DiscoveryProtocol (asyncio .DatagramProtocol ):
37- def __init__ (self , registry ):
50+ def __init__ (self , registry : DeviceRegistry ):
3851 super ().__init__ ()
3952 self .registry = registry
4053
4154 def connection_made (self , transport ):
55+ _LOGGER .debug ("starting up udp listener" )
4256 self .transport = transport
4357
4458 def datagram_received (self , data , addr ):
4559 device = DiscoveredDevice .create_from_announce_msg (addr , data )
4660 self .registry .register (device )
4761
4862 def connection_lost (self , exc : Optional [Exception ]) -> None :
63+ _LOGGER .debug ("shutting down udp listener" )
4964 super ().connection_lost (exc )
5065
5166
52- class DeviceRegistry (object ):
53- def __init__ (self ):
54- self .devices_by_mac = {}
55-
56- def register (self , device ):
57- self .devices_by_mac [device .mac ] = device
58-
59- def devices (self ):
60- return list (self .devices_by_mac .values ())
67+ async def discover_dingz_devices (timeout : int = 7 ) -> List [DiscoveredDevice ]:
68+ """
69+ Try to discover all local dingz instances. All dingz instances
70+ report their presence every ~5 seconds in a UDP broadcast to port 7979.
6171
62-
63- async def discover_dingz_devices (timeout = 7 ) -> List [DiscoveredDevice ]:
72+ :param timeout: timeout in seconds for discover.
73+ :return: list of discovered devices
74+ """
6475 registry = DeviceRegistry ()
6576 loop = asyncio .get_event_loop ()
66- (transport , protocol ) = await loop .create_datagram_endpoint (lambda : DiscoveryProtocol (registry ), local_addr = ("0.0.0.0" , 7979 ))
67-
77+ (transport , protocol ) = await loop .create_datagram_endpoint (
78+ lambda : DiscoveryProtocol (registry ), local_addr = ("0.0.0.0" , 7979 )
79+ )
80+ # server runs in the background, meanwhile wait until timeout expires
6881 await asyncio .sleep (timeout )
82+
83+ # shutdown server
6984 transport .close ()
85+
7086 devices = registry .devices ()
87+ for device in devices :
88+ _LOGGER .debug (
89+ "discovered dingz %s (%s) with mac %s" , device .host , device .type , device .mac
90+ )
7191 return devices
7292
7393
7494if __name__ == "__main__" :
95+ logging .basicConfig (level = logging .DEBUG )
7596 loop = asyncio .get_event_loop ()
7697 devices = asyncio .run (discover_dingz_devices ())
7798
7899 print ("found %s devices" % len (devices ))
79100 for device in devices :
80- print (f' { device .mac } { device .host } ' )
101+ print (f" { device .mac } { device .host } " )
0 commit comments