Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Data update coordinator for the Radarr integration."""
2 
3 from __future__ import annotations
4 
5 from abc import ABC, abstractmethod
6 import asyncio
7 from dataclasses import dataclass
8 from datetime import date, datetime, timedelta
9 from typing import TYPE_CHECKING, Generic, TypeVar, cast
10 
11 from aiopyarr import (
12  Health,
13  RadarrCalendarItem,
14  RadarrMovie,
15  RootFolder,
16  SystemStatus,
17  exceptions,
18 )
19 from aiopyarr.models.host_configuration import PyArrHostConfiguration
20 from aiopyarr.radarr_client import RadarrClient
21 
22 from homeassistant.components.calendar import CalendarEvent
23 from homeassistant.core import HomeAssistant
24 from homeassistant.exceptions import ConfigEntryAuthFailed
25 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
26 
27 from .const import DEFAULT_MAX_RECORDS, DOMAIN, LOGGER
28 
29 if TYPE_CHECKING:
30  from . import RadarrConfigEntry
31 
32 T = TypeVar("T", bound=SystemStatus | list[RootFolder] | list[Health] | int | None)
33 
34 
35 @dataclass
37  """Mixin for Radarr calendar event."""
38 
39  release_type: str
40 
41 
42 @dataclass
44  """A class to describe a Radarr calendar event."""
45 
46 
47 class RadarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T], ABC):
48  """Data update coordinator for the Radarr integration."""
49 
50  config_entry: RadarrConfigEntry
51  _update_interval = timedelta(seconds=30)
52 
53  def __init__(
54  self,
55  hass: HomeAssistant,
56  host_configuration: PyArrHostConfiguration,
57  api_client: RadarrClient,
58  ) -> None:
59  """Initialize the coordinator."""
60  super().__init__(
61  hass=hass,
62  logger=LOGGER,
63  name=DOMAIN,
64  update_interval=self._update_interval_update_interval_update_interval,
65  )
66  self.api_clientapi_client = api_client
67  self.host_configurationhost_configuration = host_configuration
68 
69  async def _async_update_data(self) -> T:
70  """Get the latest data from Radarr."""
71  try:
72  return await self._fetch_data_fetch_data()
73 
74  except exceptions.ArrConnectionException as ex:
75  raise UpdateFailed(ex) from ex
76  except exceptions.ArrAuthenticationException as ex:
78  "API Key is no longer valid. Please reauthenticate"
79  ) from ex
80 
81  @abstractmethod
82  async def _fetch_data(self) -> T:
83  """Fetch the actual data."""
84  raise NotImplementedError
85 
86 
88  """Status update coordinator for Radarr."""
89 
90  async def _fetch_data(self) -> SystemStatus:
91  """Fetch the data."""
92  return await self.api_clientapi_client.async_get_system_status()
93 
94 
96  """Disk space update coordinator for Radarr."""
97 
98  async def _fetch_data(self) -> list[RootFolder]:
99  """Fetch the data."""
100  root_folders = await self.api_clientapi_client.async_get_root_folders()
101  if isinstance(root_folders, RootFolder):
102  return [root_folders]
103  return root_folders
104 
105 
107  """Health update coordinator."""
108 
109  async def _fetch_data(self) -> list[Health]:
110  """Fetch the health data."""
111  health = await self.api_clientapi_client.async_get_failed_health_checks()
112  if isinstance(health, Health):
113  return [health]
114  return health
115 
116 
118  """Movies update coordinator."""
119 
120  async def _fetch_data(self) -> int:
121  """Fetch the movies data."""
122  return len(cast(list[RadarrMovie], await self.api_clientapi_client.async_get_movies()))
123 
124 
126  """Queue update coordinator."""
127 
128  async def _fetch_data(self) -> int:
129  """Fetch the movies in queue."""
130  return (
131  await self.api_clientapi_client.async_get_queue(page_size=DEFAULT_MAX_RECORDS)
132  ).totalRecords
133 
134 
136  """Calendar update coordinator."""
137 
138  _update_interval = timedelta(hours=1)
139 
140  def __init__(
141  self,
142  hass: HomeAssistant,
143  host_configuration: PyArrHostConfiguration,
144  api_client: RadarrClient,
145  ) -> None:
146  """Initialize."""
147  super().__init__(hass, host_configuration, api_client)
148  self.eventevent: RadarrEvent | None = None
149  self._events_events: list[RadarrEvent] = []
150 
151  async def _fetch_data(self) -> None:
152  """Fetch the calendar."""
153  self.eventevent = None
154  _date = datetime.today()
155  while self.eventevent is None:
156  await self.async_get_eventsasync_get_events(_date, _date + timedelta(days=1))
157  for event in self._events_events:
158  if event.start >= _date.date():
159  self.eventevent = event
160  break
161  # Prevent infinite loop in case there is nothing recent in the calendar
162  if (_date - datetime.today()).days > 45:
163  break
164  _date = _date + timedelta(days=1)
165 
166  async def async_get_events(
167  self, start_date: datetime, end_date: datetime
168  ) -> list[RadarrEvent]:
169  """Get cached events and request missing dates."""
170  # remove older events to prevent memory leak
171  self._events_events = [
172  e
173  for e in self._events_events
174  if e.start >= datetime.now().date() - timedelta(days=30)
175  ]
176  _days = (end_date - start_date).days
177  await asyncio.gather(
178  *(
179  self._async_get_events_async_get_events(d)
180  for d in ((start_date + timedelta(days=x)).date() for x in range(_days))
181  if d not in (event.start for event in self._events_events)
182  )
183  )
184  return self._events_events
185 
186  async def _async_get_events(self, _date: date) -> None:
187  """Return events from specified date."""
188  self._events_events.extend(
190  for evt in await self.api_clientapi_client.async_get_calendar(
191  start_date=_date, end_date=_date + timedelta(days=1)
192  )
193  if evt.title not in (e.summary for e in self._events_events)
194  )
195 
196 
197 def _get_calendar_event(event: RadarrCalendarItem) -> RadarrEvent:
198  """Return a RadarrEvent from an API event."""
199  _date, _type = event.releaseDateType()
200  return RadarrEvent(
201  summary=event.title,
202  start=_date - timedelta(days=1),
203  end=_date,
204  description=event.overview.replace(":", ";"),
205  release_type=_type,
206  )
None __init__(self, HomeAssistant hass, PyArrHostConfiguration host_configuration, RadarrClient api_client)
Definition: coordinator.py:145
list[RadarrEvent] async_get_events(self, datetime start_date, datetime end_date)
Definition: coordinator.py:168
None __init__(self, HomeAssistant hass, PyArrHostConfiguration host_configuration, RadarrClient api_client)
Definition: coordinator.py:58
RadarrEvent _get_calendar_event(RadarrCalendarItem event)
Definition: coordinator.py:197