Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Code to manage fetching LIVISI data API."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 from typing import Any
7 
8 from aiohttp import ClientConnectorError
9 from aiolivisi import AioLivisi, LivisiEvent, Websocket
10 from aiolivisi.errors import TokenExpiredException
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import CONF_HOST, CONF_PASSWORD
14 from homeassistant.core import HomeAssistant
15 from homeassistant.helpers.dispatcher import async_dispatcher_send
16 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
17 
18 from .const import (
19  AVATAR,
20  AVATAR_PORT,
21  CLASSIC_PORT,
22  DEVICE_POLLING_DELAY,
23  LIVISI_REACHABILITY_CHANGE,
24  LIVISI_STATE_CHANGE,
25  LOGGER,
26 )
27 
28 
30  """Class to manage fetching LIVISI data API."""
31 
32  config_entry: ConfigEntry
33 
34  def __init__(
35  self, hass: HomeAssistant, config_entry: ConfigEntry, aiolivisi: AioLivisi
36  ) -> None:
37  """Initialize my coordinator."""
38  super().__init__(
39  hass,
40  LOGGER,
41  name="Livisi devices",
42  update_interval=timedelta(seconds=DEVICE_POLLING_DELAY),
43  )
44  self.config_entryconfig_entryconfig_entry = config_entry
45  self.hasshasshass = hass
46  self.aiolivisiaiolivisi = aiolivisi
47  self.websocketwebsocket = Websocket(aiolivisi)
48  self.devices: set[str] = set()
49  self.rooms: dict[str, Any] = {}
50  self.serial_numberserial_number: str = ""
51  self.controller_typecontroller_type: str = ""
52  self.is_avataris_avatar: bool = False
53  self.portport: int = 0
54 
55  async def _async_update_data(self) -> list[dict[str, Any]]:
56  """Get device configuration from LIVISI."""
57  try:
58  return await self.async_get_devicesasync_get_devices()
59  except TokenExpiredException:
60  await self.aiolivisiaiolivisi.async_set_token(self.aiolivisiaiolivisi.livisi_connection_data)
61  return await self.async_get_devicesasync_get_devices()
62  except ClientConnectorError as exc:
63  raise UpdateFailed("Failed to get livisi devices from controller") from exc
64 
65  def _async_dispatcher_send(self, event: str, source: str, data: Any) -> None:
66  if data is not None:
67  async_dispatcher_send(self.hasshasshass, f"{event}_{source}", data)
68 
69  async def async_setup(self) -> None:
70  """Set up the Livisi Smart Home Controller."""
71  if not self.aiolivisiaiolivisi.livisi_connection_data:
72  livisi_connection_data = {
73  "ip_address": self.config_entryconfig_entryconfig_entry.data[CONF_HOST],
74  "password": self.config_entryconfig_entryconfig_entry.data[CONF_PASSWORD],
75  }
76 
77  await self.aiolivisiaiolivisi.async_set_token(
78  livisi_connection_data=livisi_connection_data
79  )
80  controller_data = await self.aiolivisiaiolivisi.async_get_controller()
81  if (controller_type := controller_data["controllerType"]) == AVATAR:
82  self.portport = AVATAR_PORT
83  self.is_avataris_avatar = True
84  else:
85  self.portport = CLASSIC_PORT
86  self.is_avataris_avatar = False
87  self.controller_typecontroller_type = controller_type
88  self.serial_numberserial_number = controller_data["serialNumber"]
89 
90  async def async_get_devices(self) -> list[dict[str, Any]]:
91  """Set the discovered devices list."""
92  return await self.aiolivisiaiolivisi.async_get_devices()
93 
94  async def async_get_device_state(self, capability: str, key: str) -> Any | None:
95  """Get state from livisi devices."""
96  response: dict[str, Any] = await self.aiolivisiaiolivisi.async_get_device_state(
97  capability[1:]
98  )
99  if response is None:
100  return None
101  return response.get(key, {}).get("value")
102 
103  async def async_set_all_rooms(self) -> None:
104  """Set the room list."""
105  response: list[dict[str, Any]] = await self.aiolivisiaiolivisi.async_get_all_rooms()
106 
107  for available_room in response:
108  available_room_config: dict[str, Any] = available_room["config"]
109  self.rooms[available_room["id"]] = available_room_config["name"]
110 
111  def on_data(self, event_data: LivisiEvent) -> None:
112  """Define a handler to fire when the data is received."""
113  self._async_dispatcher_send_async_dispatcher_send(
114  LIVISI_STATE_CHANGE, event_data.source, event_data.onState
115  )
116  self._async_dispatcher_send_async_dispatcher_send(
117  LIVISI_STATE_CHANGE, event_data.source, event_data.vrccData
118  )
119  self._async_dispatcher_send_async_dispatcher_send(
120  LIVISI_REACHABILITY_CHANGE, event_data.source, event_data.isReachable
121  )
122  self._async_dispatcher_send_async_dispatcher_send(
123  LIVISI_STATE_CHANGE, event_data.source, event_data.isOpen
124  )
125 
126  async def on_close(self) -> None:
127  """Define a handler to fire when the websocket is closed."""
128  for device_id in self.devices:
129  self._async_dispatcher_send_async_dispatcher_send(LIVISI_REACHABILITY_CHANGE, device_id, False)
130 
131  await self.websocketwebsocket.connect(self.on_dataon_data, self.on_closeon_close, self.portport)
132 
133  async def ws_connect(self) -> None:
134  """Connect the websocket."""
135  await self.websocketwebsocket.connect(self.on_dataon_data, self.on_closeon_close, self.portport)
None _async_dispatcher_send(self, str event, str source, Any data)
Definition: coordinator.py:65
None __init__(self, HomeAssistant hass, ConfigEntry config_entry, AioLivisi aiolivisi)
Definition: coordinator.py:36
Any|None async_get_device_state(self, str capability, str key)
Definition: coordinator.py:94
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
Controller async_get_controller(HomeAssistant hass)
Definition: utils.py:47
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:193