Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Define a Ridwell coordinator."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from datetime import timedelta
7 from typing import cast
8 
9 from aioridwell.client import async_get_client
10 from aioridwell.errors import InvalidCredentialsError, RidwellError
11 from aioridwell.model import RidwellAccount, RidwellPickupEvent
12 
13 from homeassistant.config_entries import ConfigEntry
14 from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
15 from homeassistant.core import HomeAssistant
16 from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
17 from homeassistant.helpers import aiohttp_client
18 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
19 
20 from .const import LOGGER
21 
22 UPDATE_INTERVAL = timedelta(hours=1)
23 
24 
26  DataUpdateCoordinator[dict[str, list[RidwellPickupEvent]]]
27 ):
28  """Class to manage fetching data from single endpoint."""
29 
30  config_entry: ConfigEntry
31 
32  def __init__(self, hass: HomeAssistant, *, name: str) -> None:
33  """Initialize."""
34  # These will be filled in by async_initialize; we give them these defaults to
35  # avoid arduous typing checks down the line:
36  self.accountsaccounts: dict[str, RidwellAccount] = {}
37  self.dashboard_urldashboard_url = ""
38  self.user_iduser_id = ""
39 
40  super().__init__(hass, LOGGER, name=name, update_interval=UPDATE_INTERVAL)
41 
42  async def _async_update_data(self) -> dict[str, list[RidwellPickupEvent]]:
43  """Fetch the latest data from the source."""
44  data = {}
45 
46  async def async_get_pickups(account: RidwellAccount) -> None:
47  """Get the latest pickups for an account."""
48  data[account.account_id] = await account.async_get_pickup_events()
49 
50  tasks = [async_get_pickups(account) for account in self.accountsaccounts.values()]
51  results = await asyncio.gather(*tasks, return_exceptions=True)
52  for result in results:
53  if isinstance(result, InvalidCredentialsError):
54  raise ConfigEntryAuthFailed("Invalid username/password") from result
55  if isinstance(result, RidwellError):
56  raise UpdateFailed(result) from result
57 
58  return data
59 
60  async def async_initialize(self) -> None:
61  """Initialize the coordinator."""
62  session = aiohttp_client.async_get_clientsession(self.hasshass)
63 
64  try:
65  client = await async_get_client(
66  self.config_entryconfig_entry.data[CONF_USERNAME],
67  self.config_entryconfig_entry.data[CONF_PASSWORD],
68  session=session,
69  )
70  except InvalidCredentialsError as err:
71  raise ConfigEntryAuthFailed("Invalid username/password") from err
72  except RidwellError as err:
73  raise ConfigEntryNotReady(err) from err
74 
75  self.accountsaccounts = await client.async_get_accounts()
76  await self.async_config_entry_first_refreshasync_config_entry_first_refresh()
77 
78  self.dashboard_urldashboard_url = client.get_dashboard_url()
79  self.user_iduser_id = cast(str, client.user_id)