11import asyncio
22import binascii
33import enum
4+ import functools
45import logging
56
67from zigpy .types import LVList
1112LOGGER = logging .getLogger (__name__ )
1213
1314AT_COMMAND_TIMEOUT = 2
15+ REMOTE_AT_COMMAND_TIMEOUT = 30
1416
1517
1618class ModemStatus (t .uint8_t , t .UndefinedEnum ):
@@ -29,22 +31,22 @@ class ModemStatus(t.uint8_t, t.UndefinedEnum):
2931
3032# https://www.digi.com/resources/documentation/digidocs/PDFs/90000976.pdf
3133COMMANDS = {
32- 'at' : (0x08 , (t .uint8_t , t .ATCommand , t .Bytes ), 0x88 ),
33- 'queued_at' : (0x09 , (t .uint8_t , t .ATCommand , t .Bytes ), 0x88 ),
34- 'remote_at' : (0x17 , (), None ),
34+ 'at' : (0x08 , (t .FrameId , t .ATCommand , t .Bytes ), 0x88 ),
35+ 'queued_at' : (0x09 , (t .FrameId , t .ATCommand , t .Bytes ), 0x88 ),
36+ 'remote_at' : (0x17 , (t . FrameId , t . EUI64 , t . NWK , t . uint8_t , t . ATCommand , t . Bytes ), 0x97 ),
3537 'tx' : (0x10 , (), None ),
36- 'tx_explicit' : (0x11 , (t .uint8_t , t .EUI64 , t .NWK , t .uint8_t , t .uint8_t , t .uint16_t , t .uint16_t , t .uint8_t , t .uint8_t , t .Bytes ), None ),
37- 'create_source_route' : (0x21 , (t .uint8_t , t .EUI64 , t .NWK , t .uint8_t , LVList (t .NWK )), None ),
38+ 'tx_explicit' : (0x11 , (t .FrameId , t .EUI64 , t .NWK , t .uint8_t , t .uint8_t , t .uint16_t , t .uint16_t , t .uint8_t , t .uint8_t , t .Bytes ), None ),
39+ 'create_source_route' : (0x21 , (t .FrameId , t .EUI64 , t .NWK , t .uint8_t , LVList (t .NWK )), None ),
3840 'register_joining_device' : (0x24 , (), None ),
3941
40- 'at_response' : (0x88 , (t .uint8_t , t .ATCommand , t .uint8_t , t .Bytes ), None ),
42+ 'at_response' : (0x88 , (t .FrameId , t .ATCommand , t .uint8_t , t .Bytes ), None ),
4143 'modem_status' : (0x8A , (ModemStatus , ), None ),
42- 'tx_status' : (0x8B , (t .uint8_t , t .NWK , t .uint8_t , t .uint8_t , t .uint8_t ), None ),
44+ 'tx_status' : (0x8B , (t .FrameId , t .NWK , t .uint8_t , t .uint8_t , t .uint8_t ), None ),
4345 'route_information' : (0x8D , (), None ),
4446 'rx' : (0x90 , (), None ),
4547 'explicit_rx_indicator' : (0x91 , (t .EUI64 , t .NWK , t .uint8_t , t .uint8_t , t .uint16_t , t .uint16_t , t .uint8_t , t .Bytes ), None ),
4648 'rx_io_data_long_addr' : (0x92 , (), None ),
47- 'remote_at_response' : (0x97 , (), None ),
49+ 'remote_at_response' : (0x97 , (t . FrameId , t . EUI64 , t . NWK , t . ATCommand , t . uint8_t , t . Bytes ), None ),
4850 'extended_status' : (0x98 , (), None ),
4951 'route_record_indicator' : (0xA1 , (t .EUI64 , t .NWK , t .uint8_t , LVList (t .NWK )), None ),
5052 'many_to_one_rri' : (0xA3 , (t .EUI64 , t .NWK , t .uint8_t ), None ),
@@ -183,6 +185,7 @@ class ATCommandResult(enum.IntEnum):
183185 ERROR = 1
184186 INVALID_COMMAND = 2
185187 INVALID_PARAMETER = 3
188+ TX_FAILURE = 4
186189
187190
188191class XBee :
@@ -233,27 +236,32 @@ def _seq_command(self, name, *args):
233236 LOGGER .debug ("Sequenced command: %s %s" , name , args )
234237 return self ._command (name , self ._seq , * args )
235238
236- def _queued_at (self , name , * args ):
237- LOGGER .debug ("Queue AT command: %s %s" , name , args )
239+ async def _remote_at_command (self , ieee , nwk , options , name , * args ):
240+ LOGGER .debug ("Remote AT command: %s %s" , name , args )
238241 data = t .serialize (args , (AT_COMMANDS [name ], ))
239- return self ._command (
240- 'queued_at' ,
241- self ._seq ,
242- name .encode ('ascii' ),
243- data ,
244- )
245-
246- async def _at_command (self , name , * args ):
247- LOGGER .debug ("AT command: %s %s" , name , args )
242+ try :
243+ return await asyncio .wait_for (
244+ self ._command ('remote_at' , self ._seq , ieee , nwk , options ,
245+ name .encode ('ascii' ), data ,),
246+ timeout = REMOTE_AT_COMMAND_TIMEOUT )
247+ except asyncio .TimeoutError :
248+ LOGGER .warning ("No response to %s command" , name )
249+ raise
250+
251+ async def _at_partial (self , cmd_type , name , * args ):
252+ LOGGER .debug ("%s command: %s %s" , cmd_type , name , args )
248253 data = t .serialize (args , (AT_COMMANDS [name ], ))
249254 try :
250255 return await asyncio .wait_for (
251- self ._command ('at' , self ._seq , name .encode ('ascii' ), data , ),
256+ self ._command (cmd_type , self ._seq , name .encode ('ascii' ), data ),
252257 timeout = AT_COMMAND_TIMEOUT )
253258 except asyncio .TimeoutError :
254- LOGGER .warning ("No response to %s command" , name )
259+ LOGGER .warning ("%s: No response to %s command" , cmd_type , name )
255260 raise
256261
262+ _at_command = functools .partialmethod (_at_partial , 'at' )
263+ _queued_at = functools .partialmethod (_at_partial , 'queued_at' )
264+
257265 def _api_frame (self , name , * args ):
258266 c = COMMANDS [name ]
259267 return (bytes ([c [0 ]]) + t .serialize (args , c [1 ])), c [2 ]
@@ -288,6 +296,11 @@ def _handle_at_response(self, data):
288296 response , remains = response_type .deserialize (data [3 ])
289297 fut .set_result (response )
290298
299+ def _handle_remote_at_response (self , data ):
300+ """Remote AT command response."""
301+ LOGGER .debug ("Remote AT command response: %s" , data )
302+ return self ._handle_at_response ((data [0 ], data [3 ], data [4 ], data [5 ]))
303+
291304 def _handle_many_to_one_rri (self , data ):
292305 LOGGER .debug ("_handle_many_to_one_rri: %s" , data )
293306
0 commit comments