18
18
19
19
import asyncio
20
20
import json
21
+ import logging
21
22
from dataclasses import asdict , dataclass
22
23
from enum import Enum
23
24
from typing import Dict , List , Optional
24
25
25
26
from zeroconf import IPVersion , ServiceStateChange , Zeroconf
26
27
from zeroconf .asyncio import AsyncServiceBrowser , AsyncServiceInfo , AsyncZeroconfServiceTypes
27
28
29
+ logger = logging .getLogger (__name__ )
30
+
28
31
29
32
@dataclass
30
33
class MdnsServiceInfo :
@@ -79,7 +82,7 @@ class MdnsDiscovery:
79
82
80
83
DISCOVERY_TIMEOUT_SEC = 15
81
84
82
- def __init__ (self ):
85
+ def __init__ (self , verbose_logging : bool = False ):
83
86
"""
84
87
Initializes the MdnsDiscovery instance.
85
88
@@ -99,9 +102,15 @@ def __init__(self):
99
102
# A list of service types
100
103
self ._service_types = []
101
104
105
+ # Filtering to apply for received data items
106
+ self ._name_filter = None
107
+
102
108
# An asyncio Event to signal when a service has been discovered
103
109
self ._event = asyncio .Event ()
104
110
111
+ # Verbose logging
112
+ self ._verbose_logging = verbose_logging
113
+
105
114
# Public methods
106
115
async def get_commissioner_service (self , log_output : bool = False ,
107
116
discovery_timeout_sec : float = DISCOVERY_TIMEOUT_SEC
@@ -116,6 +125,7 @@ async def get_commissioner_service(self, log_output: bool = False,
116
125
Returns:
117
126
Optional[MdnsServiceInfo]: An instance of MdnsServiceInfo or None if timeout reached.
118
127
"""
128
+ self ._name_filter = None
119
129
return await self ._get_service (MdnsServiceType .COMMISSIONER , log_output , discovery_timeout_sec )
120
130
121
131
async def get_commissionable_service (self , log_output : bool = False ,
@@ -131,10 +141,12 @@ async def get_commissionable_service(self, log_output: bool = False,
131
141
Returns:
132
142
Optional[MdnsServiceInfo]: An instance of MdnsServiceInfo or None if timeout reached.
133
143
"""
144
+ self ._name_filter = None
134
145
return await self ._get_service (MdnsServiceType .COMMISSIONABLE , log_output , discovery_timeout_sec )
135
146
136
- async def get_operational_service (self , service_name : str = None ,
137
- service_type : str = None ,
147
+ async def get_operational_service (self ,
148
+ node_id : Optional [int ] = None ,
149
+ compressed_fabric_id : Optional [int ] = None ,
138
150
discovery_timeout_sec : float = DISCOVERY_TIMEOUT_SEC ,
139
151
log_output : bool = False
140
152
) -> Optional [MdnsServiceInfo ]:
@@ -144,35 +156,16 @@ async def get_operational_service(self, service_name: str = None,
144
156
Args:
145
157
log_output (bool): Logs the discovered services to the console. Defaults to False.
146
158
discovery_timeout_sec (float): Defaults to 15 seconds.
147
- service_name (str): The unique name of the mDNS service. Defaults to None.
148
- service_type (str): The service type of the service. Defaults to None.
159
+ node_id: the node id to create the service name from
160
+ compressed_fabric_id: the fabric id to create the service name from
149
161
150
162
Returns:
151
163
Optional[MdnsServiceInfo]: An instance of MdnsServiceInfo or None if timeout reached.
152
164
"""
153
165
# Validation to ensure both or none of the parameters are provided
154
- if (service_name is None ) != (service_type is None ):
155
- raise ValueError ("Both service_name and service_type must be provided together or not at all." )
156
-
157
- mdns_service_info = None
158
-
159
- if service_name is None and service_type is None :
160
- mdns_service_info = await self ._get_service (MdnsServiceType .OPERATIONAL , log_output , discovery_timeout_sec )
161
- else :
162
- print (f"Looking for MDNS service type '{ service_type } ', service name '{ service_name } '" )
163
166
164
- # Get service info
165
- service_info = AsyncServiceInfo (service_type , service_name )
166
- is_discovered = await service_info .async_request (self ._zc , 3000 )
167
- if is_discovered :
168
- mdns_service_info = self ._to_mdns_service_info_class (service_info )
169
- self ._discovered_services = {}
170
- self ._discovered_services [service_type ] = [mdns_service_info ]
171
-
172
- if log_output :
173
- self ._log_output ()
174
-
175
- return mdns_service_info
167
+ self ._name_filter = f'{ compressed_fabric_id :016x} -{ node_id :016x} .{ MdnsServiceType .OPERATIONAL .value } ' .upper ()
168
+ return await self ._get_service (MdnsServiceType .OPERATIONAL , log_output , discovery_timeout_sec )
176
169
177
170
async def get_border_router_service (self , log_output : bool = False ,
178
171
discovery_timeout_sec : float = DISCOVERY_TIMEOUT_SEC
@@ -237,7 +230,7 @@ async def _discover(self,
237
230
if all_services :
238
231
self ._service_types = list (await AsyncZeroconfServiceTypes .async_find ())
239
232
240
- print (f"Browsing for MDNS service(s) of type: { self ._service_types } " )
233
+ logger . info (f"Browsing for MDNS service(s) of type: { self ._service_types } " )
241
234
242
235
aiobrowser = AsyncServiceBrowser (zeroconf = self ._zc ,
243
236
type_ = self ._service_types ,
@@ -247,7 +240,7 @@ async def _discover(self,
247
240
try :
248
241
await asyncio .wait_for (self ._event .wait (), timeout = discovery_timeout_sec )
249
242
except asyncio .TimeoutError :
250
- print ( f "MDNS service discovery timed out after { discovery_timeout_sec } seconds." )
243
+ logger . error ( "MDNS service discovery timed out after %d seconds." , discovery_timeout_sec )
251
244
finally :
252
245
await aiobrowser .async_cancel ()
253
246
@@ -276,13 +269,25 @@ def _on_service_state_change(
276
269
Returns:
277
270
None: This method does not return any value.
278
271
"""
279
- if state_change .value == ServiceStateChange .Added .value :
280
- self ._event .set ()
281
- asyncio .ensure_future (self ._query_service_info (
282
- zeroconf ,
283
- service_type ,
284
- name )
285
- )
272
+ if self ._verbose_logging :
273
+ logger .info ("Service state change: %s on %s/%s" , state_change , name , service_type )
274
+
275
+ if state_change .value == ServiceStateChange .Removed .value :
276
+ return
277
+
278
+ if self ._name_filter is not None and name .upper () != self ._name_filter :
279
+ if self ._verbose_logging :
280
+ logger .info (" Name does NOT match %s" , self ._name_filter )
281
+ return
282
+
283
+ if self ._verbose_logging :
284
+ logger .info ("Received service data. Unlocking service information" )
285
+
286
+ asyncio .ensure_future (self ._query_service_info (
287
+ zeroconf ,
288
+ service_type ,
289
+ name )
290
+ )
286
291
287
292
async def _query_service_info (self , zeroconf : Zeroconf , service_type : str , service_name : str ) -> None :
288
293
"""
@@ -304,12 +309,19 @@ async def _query_service_info(self, zeroconf: Zeroconf, service_type: str, servi
304
309
service_info .async_clear_cache ()
305
310
306
311
if is_service_discovered :
312
+ if self ._verbose_logging :
313
+ logger .warning ("Service discovered for %s/%s." , service_name , service_type )
314
+
307
315
mdns_service_info = self ._to_mdns_service_info_class (service_info )
308
316
309
317
if service_type not in self ._discovered_services :
310
318
self ._discovered_services [service_type ] = [mdns_service_info ]
311
319
else :
312
320
self ._discovered_services [service_type ].append (mdns_service_info )
321
+ elif self ._verbose_logging :
322
+ logger .warning ("Service information not found." )
323
+
324
+ self ._event .set ()
313
325
314
326
def _to_mdns_service_info_class (self , service_info : AsyncServiceInfo ) -> MdnsServiceInfo :
315
327
"""
@@ -355,21 +367,24 @@ async def _get_service(self, service_type: MdnsServiceType,
355
367
any. Returns None if no service of the specified type is discovered within
356
368
the timeout period.
357
369
"""
358
- mdns_service_info = None
359
370
self ._service_types = [service_type .value ]
360
371
await self ._discover (discovery_timeout_sec , log_output )
361
- if service_type .value in self ._discovered_services :
362
- mdns_service_info = self ._discovered_services [service_type .value ][0 ]
363
372
364
- return mdns_service_info
373
+ if self ._verbose_logging :
374
+ logger .info ("Getting service from discovered services: %s" , self ._discovered_services )
375
+
376
+ if service_type .value in self ._discovered_services :
377
+ return self ._discovered_services [service_type .value ][0 ]
378
+ else :
379
+ return None
365
380
366
381
def _log_output (self ) -> str :
367
382
"""
368
- Converts the discovered services to a JSON string and prints it.
383
+ Converts the discovered services to a JSON string and log it.
369
384
370
385
The method is intended to be used for debugging or informational purposes, providing a clear and
371
386
comprehensive view of all services discovered during the mDNS service discovery process.
372
387
"""
373
388
converted_services = {key : [asdict (item ) for item in value ] for key , value in self ._discovered_services .items ()}
374
389
json_str = json .dumps (converted_services , indent = 4 )
375
- print ( json_str )
390
+ logger . info ( "Discovery data: \n %s" , json_str )
0 commit comments