Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Coordinator for La Marzocco API."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable, Coroutine
6 from datetime import timedelta
7 import logging
8 from time import time
9 from typing import Any
10 
11 from pylamarzocco.client_bluetooth import LaMarzoccoBluetoothClient
12 from pylamarzocco.client_cloud import LaMarzoccoCloudClient
13 from pylamarzocco.client_local import LaMarzoccoLocalClient
14 from pylamarzocco.exceptions import AuthFail, RequestNotSuccessful
15 from pylamarzocco.lm_machine import LaMarzoccoMachine
16 from websockets.protocol import State
17 
18 from homeassistant.config_entries import ConfigEntry
19 from homeassistant.const import CONF_MODEL, CONF_NAME, EVENT_HOMEASSISTANT_STOP
20 from homeassistant.core import HomeAssistant
21 from homeassistant.exceptions import ConfigEntryAuthFailed
22 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
23 
24 from .const import DOMAIN
25 
26 SCAN_INTERVAL = timedelta(seconds=30)
27 FIRMWARE_UPDATE_INTERVAL = 3600
28 STATISTICS_UPDATE_INTERVAL = 300
29 
30 _LOGGER = logging.getLogger(__name__)
31 
32 type LaMarzoccoConfigEntry = ConfigEntry[LaMarzoccoUpdateCoordinator]
33 
34 
36  """Class to handle fetching data from the La Marzocco API centrally."""
37 
38  config_entry: LaMarzoccoConfigEntry
39 
40  def __init__(
41  self,
42  hass: HomeAssistant,
43  entry: LaMarzoccoConfigEntry,
44  cloud_client: LaMarzoccoCloudClient,
45  local_client: LaMarzoccoLocalClient | None,
46  bluetooth_client: LaMarzoccoBluetoothClient | None,
47  ) -> None:
48  """Initialize coordinator."""
49  super().__init__(
50  hass,
51  _LOGGER,
52  config_entry=entry,
53  name=DOMAIN,
54  update_interval=SCAN_INTERVAL,
55  )
56  self.local_connection_configuredlocal_connection_configured = local_client is not None
57 
58  assert self.config_entryconfig_entry.unique_id
59  self.devicedevice = LaMarzoccoMachine(
60  model=self.config_entryconfig_entry.data[CONF_MODEL],
61  serial_number=self.config_entryconfig_entry.unique_id,
62  name=self.config_entryconfig_entry.data[CONF_NAME],
63  cloud_client=cloud_client,
64  local_client=local_client,
65  bluetooth_client=bluetooth_client,
66  )
67 
68  self._last_firmware_data_update_last_firmware_data_update: float | None = None
69  self._last_statistics_data_update_last_statistics_data_update: float | None = None
70  self._local_client_local_client = local_client
71 
72  async def _async_setup(self) -> None:
73  """Set up the coordinator."""
74  if self._local_client_local_client is not None:
75  _LOGGER.debug("Init WebSocket in background task")
76 
77  self.config_entryconfig_entry.async_create_background_task(
78  hass=self.hasshass,
79  target=self.devicedevice.websocket_connect(
80  notify_callback=lambda: self.async_set_updated_dataasync_set_updated_data(None)
81  ),
82  name="lm_websocket_task",
83  )
84 
85  async def websocket_close(_: Any | None = None) -> None:
86  if (
87  self._local_client_local_client is not None
88  and self._local_client_local_client.websocket is not None
89  and self._local_client_local_client.websocket.state is State.OPEN
90  ):
91  self._local_client_local_client.terminating = True
92  await self._local_client_local_client.websocket.close()
93 
94  self.config_entryconfig_entry.async_on_unload(
95  self.hasshass.bus.async_listen_once(
96  EVENT_HOMEASSISTANT_STOP, websocket_close
97  )
98  )
99  self.config_entryconfig_entry.async_on_unload(websocket_close)
100 
101  async def _async_update_data(self) -> None:
102  """Fetch data from API endpoint."""
103  await self._async_handle_request(self.devicedevice.get_config)
104 
105  if (
106  self._last_firmware_data_update_last_firmware_data_update is None
107  or (self._last_firmware_data_update_last_firmware_data_update + FIRMWARE_UPDATE_INTERVAL) < time()
108  ):
109  await self._async_handle_request(self.devicedevice.get_firmware)
110  self._last_firmware_data_update_last_firmware_data_update = time()
111 
112  if (
113  self._last_statistics_data_update_last_statistics_data_update is None
114  or (self._last_statistics_data_update_last_statistics_data_update + STATISTICS_UPDATE_INTERVAL) < time()
115  ):
116  await self._async_handle_request(self.devicedevice.get_statistics)
117  self._last_statistics_data_update_last_statistics_data_update = time()
118 
119  _LOGGER.debug("Current status: %s", str(self.devicedevice.config))
120 
121  async def _async_handle_request[**_P](
122  self,
123  func: Callable[_P, Coroutine[None, None, None]],
124  *args: _P.args,
125  **kwargs: _P.kwargs,
126  ) -> None:
127  try:
128  await func(*args, **kwargs)
129  except AuthFail as ex:
130  _LOGGER.debug("Authentication failed", exc_info=True)
131  raise ConfigEntryAuthFailed(
132  translation_domain=DOMAIN, translation_key="authentication_failed"
133  ) from ex
134  except RequestNotSuccessful as ex:
135  _LOGGER.debug(ex, exc_info=True)
136  raise UpdateFailed(
137  translation_domain=DOMAIN, translation_key="api_error"
138  ) from ex
None __init__(self, HomeAssistant hass, LaMarzoccoConfigEntry entry, LaMarzoccoCloudClient cloud_client, LaMarzoccoLocalClient|None local_client, LaMarzoccoBluetoothClient|None bluetooth_client)
Definition: coordinator.py:47
bool time(HomeAssistant hass, dt_time|str|None before=None, dt_time|str|None after=None, str|Container[str]|None weekday=None)
Definition: condition.py:802