8
8
9
9
import serial
10
10
from zigpy .config import CONF_DEVICE_PATH , SCHEMA_DEVICE
11
+ from zigpy .datastructures import PriorityLock
11
12
from zigpy .exceptions import APIException , DeliveryError
12
13
import zigpy .types as t
13
14
@@ -289,7 +290,7 @@ def __init__(self, device_config: Dict[str, Any]) -> None:
289
290
self ._cmd_mode_future : Optional [asyncio .Future ] = None
290
291
self ._reset : asyncio .Event = asyncio .Event ()
291
292
self ._running : asyncio .Event = asyncio .Event ()
292
- self ._send_lock = asyncio . Lock ()
293
+ self ._send_lock = PriorityLock ()
293
294
294
295
@property
295
296
def reset_event (self ):
@@ -334,33 +335,43 @@ def close(self):
334
335
self ._uart .close ()
335
336
self ._uart = None
336
337
337
- def _command (self , name , * args , mask_frame_id = False ):
338
+ def _get_command_priority (self , name : str , * args ) -> int :
339
+ return {
340
+ "tx_explicit" : - 1 ,
341
+ "remote_at" : - 1 ,
342
+ }.get (name , 0 )
343
+
344
+ async def _command (self , name , * args , mask_frame_id = False ):
338
345
"""Send API frame to the device."""
339
- LOGGER .debug ("Command %s %s" , name , args )
340
346
if self ._uart is None :
341
347
raise APIException ("API is not running" )
342
- frame_id = 0 if mask_frame_id else self ._seq
343
- data , needs_response = self ._api_frame (name , frame_id , * args )
344
- self ._uart .send (data )
345
- future = None
346
- if needs_response and frame_id :
348
+
349
+ async with self ._send_lock (priority = self ._get_command_priority (name )):
350
+ LOGGER .debug ("Command %s %s" , name , args )
351
+ frame_id = 0 if mask_frame_id else self ._seq
352
+ data , needs_response = self ._api_frame (name , frame_id , * args )
353
+ self ._uart .send (data )
354
+
355
+ if not needs_response or not frame_id :
356
+ return
357
+
347
358
future = asyncio .Future ()
348
359
self ._awaiting [frame_id ] = (future ,)
349
- self ._seq = (self ._seq % 255 ) + 1
350
- return future
360
+ self ._seq = (self ._seq % 255 ) + 1
361
+
362
+ return await future
351
363
352
364
async def _remote_at_command (self , ieee , nwk , options , name , * args ):
353
365
"""Execute AT command on a different XBee module in the network."""
354
366
LOGGER .debug ("Remote AT command: %s %s" , name , args )
355
367
data = t .serialize (args , (AT_COMMANDS [name ],))
356
368
try :
357
- async with self ._send_lock :
358
- return await asyncio .wait_for (
359
- self ._command (
360
- "remote_at" , ieee , nwk , options , name .encode ("ascii" ), data
361
- ),
362
- timeout = REMOTE_AT_COMMAND_TIMEOUT ,
363
- )
369
+ return await asyncio .wait_for (
370
+ self ._command (
371
+ "remote_at" , ieee , nwk , options , name .encode ("ascii" ), data
372
+ ),
373
+ timeout = REMOTE_AT_COMMAND_TIMEOUT ,
374
+ )
364
375
except asyncio .TimeoutError :
365
376
LOGGER .warning ("No response to %s command" , name )
366
377
raise
@@ -369,11 +380,10 @@ async def _at_partial(self, cmd_type, name, *args):
369
380
LOGGER .debug ("%s command: %s %s" , cmd_type , name , args )
370
381
data = t .serialize (args , (AT_COMMANDS [name ],))
371
382
try :
372
- async with self ._send_lock :
373
- return await asyncio .wait_for (
374
- self ._command (cmd_type , name .encode ("ascii" ), data ),
375
- timeout = AT_COMMAND_TIMEOUT ,
376
- )
383
+ return await asyncio .wait_for (
384
+ self ._command (cmd_type , name .encode ("ascii" ), data ),
385
+ timeout = AT_COMMAND_TIMEOUT ,
386
+ )
377
387
except asyncio .TimeoutError :
378
388
LOGGER .warning ("%s: No response to %s command" , cmd_type , name )
379
389
raise
0 commit comments