11import asyncio
22import binascii
3- import datetime
3+ from datetime import datetime , timezone
44import enum
55import functools
66import logging
77from typing import Any , Dict
88
9- import serial
9+ from zigpy . datastructures import PriorityLock
1010import zigpy .exceptions
1111import zigpy .types
1212
13- import zigpy_zigate .config
1413import zigpy_zigate .uart
1514
1615from . import types as t
@@ -227,8 +226,7 @@ def __init__(self, device_config: Dict[str, Any]):
227226 self ._uart = None
228227 self ._awaiting = {}
229228 self ._status_awaiting = {}
230- self ._lock = asyncio .Lock ()
231- self ._conn_lost_task = None
229+ self ._lock = PriorityLock ()
232230
233231 self .network_state = None
234232
@@ -245,59 +243,14 @@ async def connect(self):
245243
246244 def connection_lost (self , exc : Exception ) -> None :
247245 """Lost serial connection."""
248- LOGGER .warning (
249- "Serial '%s' connection lost unexpectedly: %s" ,
250- self ._config [zigpy_zigate .config .CONF_DEVICE_PATH ],
251- exc ,
252- )
253- self ._uart = None
254- if self ._conn_lost_task and not self ._conn_lost_task .done ():
255- self ._conn_lost_task .cancel ()
256- self ._conn_lost_task = asyncio .ensure_future (self ._connection_lost ())
257-
258- async def _connection_lost (self ) -> None :
259- """Reconnect serial port."""
260- try :
261- await self ._reconnect_till_done ()
262- except asyncio .CancelledError :
263- LOGGER .debug ("Cancelling reconnection attempt" )
264-
265- async def _reconnect_till_done (self ) -> None :
266- attempt = 1
267- while True :
268- try :
269- await asyncio .wait_for (self .reconnect (), timeout = 10 )
270- break
271- except (asyncio .TimeoutError , OSError ) as exc :
272- wait = 2 ** min (attempt , 5 )
273- attempt += 1
274- LOGGER .debug (
275- "Couldn't re-open '%s' serial port, retrying in %ss: %s" ,
276- self ._config [zigpy_zigate .config .CONF_DEVICE_PATH ],
277- wait ,
278- str (exc ),
279- )
280- await asyncio .sleep (wait )
281-
282- LOGGER .debug (
283- "Reconnected '%s' serial port after %s attempts" ,
284- self ._config [zigpy_zigate .config .CONF_DEVICE_PATH ],
285- attempt ,
286- )
246+ if self ._app is not None :
247+ self ._app .connection_lost (exc )
287248
288249 def close (self ):
289250 if self ._uart :
290251 self ._uart .close ()
291252 self ._uart = None
292253
293- def reconnect (self ):
294- """Reconnect using saved parameters."""
295- LOGGER .debug (
296- "Reconnecting '%s' serial port" ,
297- self ._config [zigpy_zigate .config .CONF_DEVICE_PATH ],
298- )
299- return self .connect ()
300-
301254 def set_application (self , app ):
302255 self ._app = app
303256
@@ -351,6 +304,14 @@ async def wait_for_response(self, wait_response):
351304 self ._awaiting [wait_response ].cancel ()
352305 del self ._awaiting [wait_response ]
353306
307+ def _get_command_priority (self , cmd ):
308+ return {
309+ # Watchdog command is prioritized
310+ CommandId .SET_TIMESERVER : 9999 ,
311+ # APS command is deprioritized
312+ CommandId .SEND_RAW_APS_DATA_PACKET : - 1 ,
313+ }.get (cmd , 0 )
314+
354315 async def command (
355316 self ,
356317 cmd ,
@@ -359,7 +320,7 @@ async def command(
359320 wait_status = True ,
360321 timeout = COMMAND_TIMEOUT ,
361322 ):
362- async with self ._lock :
323+ async with self ._lock ( priority = self . _get_command_priority ( cmd )) :
363324 tries = 3
364325
365326 tasks = []
@@ -454,13 +415,13 @@ async def erase_persistent_data(self):
454415 CommandId .RESET , wait_response = ResponseId .NODE_FACTORY_NEW_RESTART
455416 )
456417
457- async def set_time (self , dt = None ):
458- """set internal time
459- if timestamp is None, now is used
460- """
461- dt = dt or datetime . datetime . now ()
462- timestamp = int (( dt - datetime . datetime ( 2000 , 1 , 1 )). total_seconds ())
463- data = t .serialize ([timestamp ], COMMANDS [CommandId .SET_TIMESERVER ])
418+ async def set_time (self ):
419+ """set internal time"""
420+ timestamp = (
421+ datetime . now ( timezone . utc ) - datetime ( 2000 , 1 , 1 , tzinfo = timezone . utc )
422+ ). total_seconds ()
423+
424+ data = t .serialize ([int ( timestamp ) ], COMMANDS [CommandId .SET_TIMESERVER ])
464425 await self .command (CommandId .SET_TIMESERVER , data )
465426
466427 async def get_time_server (self ):
@@ -585,30 +546,3 @@ async def get_network_key(self):
585546 raise CommandNotSupportedError ()
586547
587548 return rsp [0 ]
588-
589- @classmethod
590- async def probe (cls , device_config : Dict [str , Any ]) -> bool :
591- """Probe port for the device presence."""
592- api = cls (zigpy_zigate .config .SCHEMA_DEVICE (device_config ))
593- try :
594- await asyncio .wait_for (api ._probe (), timeout = PROBE_TIMEOUT )
595- return True
596- except (
597- asyncio .TimeoutError ,
598- serial .SerialException ,
599- zigpy .exceptions .ZigbeeException ,
600- ) as exc :
601- LOGGER .debug (
602- "Unsuccessful radio probe of '%s' port" ,
603- device_config [zigpy_zigate .config .CONF_DEVICE_PATH ],
604- exc_info = exc ,
605- )
606- finally :
607- api .close ()
608-
609- return False
610-
611- async def _probe (self ) -> None :
612- """Open port and try sending a command"""
613- await self .connect ()
614- await self .set_raw_mode ()
0 commit comments