Home Assistant Unofficial Reference 2024.12.1
websocket.py
Go to the documentation of this file.
1 """Websocket handler for UniFi Network integration."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from datetime import datetime, timedelta
7 
8 import aiohttp
9 import aiounifi
10 
11 from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
12 from homeassistant.helpers.dispatcher import async_dispatcher_send
13 from homeassistant.helpers.event import async_track_time_interval
14 
15 from ..const import LOGGER
16 
17 RETRY_TIMER = 15
18 CHECK_WEBSOCKET_INTERVAL = timedelta(minutes=1)
19 
20 
22  """Manages a single UniFi Network instance."""
23 
24  def __init__(
25  self, hass: HomeAssistant, api: aiounifi.Controller, signal: str
26  ) -> None:
27  """Initialize the system."""
28  self.hasshass = hass
29  self.apiapi = api
30  self.signalsignal = signal
31 
32  self.ws_taskws_task: asyncio.Task | None = None
33  self._cancel_websocket_check_cancel_websocket_check: CALLBACK_TYPE | None = None
34 
35  self.availableavailable = True
36 
37  @callback
38  def start(self) -> None:
39  """Start websocket handler."""
40  self._cancel_websocket_check_cancel_websocket_check = async_track_time_interval(
41  self.hasshass, self._async_watch_websocket_async_watch_websocket, CHECK_WEBSOCKET_INTERVAL
42  )
43  self.start_websocketstart_websocket()
44 
45  @callback
46  def stop(self) -> None:
47  """Stop websocket handler."""
48  if self._cancel_websocket_check_cancel_websocket_check:
49  self._cancel_websocket_check_cancel_websocket_check()
50  self._cancel_websocket_check_cancel_websocket_check = None
51 
52  if self.ws_taskws_task is not None:
53  self.ws_taskws_task.cancel()
54 
55  async def stop_and_wait(self) -> None:
56  """Stop websocket handler and await tasks."""
57  if self._cancel_websocket_check_cancel_websocket_check:
58  self._cancel_websocket_check_cancel_websocket_check()
59  self._cancel_websocket_check_cancel_websocket_check = None
60 
61  if self.ws_taskws_task is not None:
62  self.stopstop()
63 
64  _, pending = await asyncio.wait([self.ws_taskws_task], timeout=10)
65 
66  if pending:
67  LOGGER.warning(
68  "Unloading UniFi Network (%s). Task %s did not complete in time",
69  self.apiapi.connectivity.config.host,
70  self.ws_taskws_task,
71  )
72 
73  @callback
74  def start_websocket(self) -> None:
75  """Start up connection to websocket."""
76 
77  async def _websocket_runner() -> None:
78  """Start websocket."""
79  try:
80  await self.apiapi.start_websocket()
81  except (aiohttp.ClientConnectorError, aiohttp.WSServerHandshakeError):
82  LOGGER.error("Websocket setup failed")
83  except aiounifi.WebsocketError:
84  LOGGER.error("Websocket disconnected")
85 
86  self.availableavailable = False
87  async_dispatcher_send(self.hasshass, self.signalsignal)
88  self.hasshass.loop.call_later(RETRY_TIMER, self.reconnectreconnect, True)
89 
90  if not self.availableavailable:
91  self.availableavailable = True
92  async_dispatcher_send(self.hasshass, self.signalsignal)
93 
94  self.ws_taskws_task = self.hasshass.loop.create_task(_websocket_runner())
95 
96  @callback
97  def reconnect(self, log: bool = False) -> None:
98  """Prepare to reconnect UniFi session."""
99 
100  async def _reconnect() -> None:
101  """Try to reconnect UniFi Network session."""
102  try:
103  async with asyncio.timeout(5):
104  await self.apiapi.login()
105 
106  except (
107  TimeoutError,
108  aiounifi.BadGateway,
109  aiounifi.ServiceUnavailable,
110  aiounifi.AiounifiException,
111  ) as exc:
112  LOGGER.debug("Schedule reconnect to UniFi Network '%s'", exc)
113  self.hasshass.loop.call_later(RETRY_TIMER, self.reconnectreconnect)
114 
115  else:
116  self.start_websocketstart_websocket()
117 
118  if log:
119  LOGGER.info("Will try to reconnect to UniFi Network")
120 
121  self.hasshass.loop.create_task(_reconnect())
122 
123  @callback
124  def _async_watch_websocket(self, now: datetime) -> None:
125  """Watch timestamp for last received websocket message."""
126  LOGGER.debug(
127  "Last received websocket timestamp: %s",
128  self.apiapi.connectivity.ws_message_received,
129  )
None __init__(self, HomeAssistant hass, aiounifi.Controller api, str signal)
Definition: websocket.py:26
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:193
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
Definition: event.py:1679