Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """DataUpdateCoordinator for the Trafikverket Ferry integration."""
2 
3 from __future__ import annotations
4 
5 from datetime import date, datetime, time, timedelta
6 import logging
7 from typing import TYPE_CHECKING, Any
8 
9 from pytrafikverket import TrafikverketFerry
10 from pytrafikverket.exceptions import InvalidAuthentication, NoFerryFound
11 from pytrafikverket.models import FerryStopModel
12 
13 from homeassistant.const import CONF_API_KEY, CONF_WEEKDAY, WEEKDAYS
14 from homeassistant.core import HomeAssistant
15 from homeassistant.exceptions import ConfigEntryAuthFailed
16 from homeassistant.helpers.aiohttp_client import async_get_clientsession
17 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
18 from homeassistant.util import dt as dt_util
19 
20 from .const import CONF_FROM, CONF_TIME, CONF_TO, DOMAIN
21 
22 if TYPE_CHECKING:
23  from . import TVFerryConfigEntry
24 
25 _LOGGER = logging.getLogger(__name__)
26 TIME_BETWEEN_UPDATES = timedelta(minutes=5)
27 
28 
29 def next_weekday(fromdate: date, weekday: int) -> date:
30  """Return the date of the next time a specific weekday happen."""
31  days_ahead = weekday - fromdate.weekday()
32  if days_ahead <= 0:
33  days_ahead += 7
34  return fromdate + timedelta(days_ahead)
35 
36 
37 def next_departuredate(departure: list[str]) -> date:
38  """Calculate the next departuredate from an array input of short days."""
39  today_date = date.today()
40  today_weekday = date.weekday(today_date)
41  if WEEKDAYS[today_weekday] in departure:
42  return today_date
43  for day in departure:
44  next_departure = WEEKDAYS.index(day)
45  if next_departure > today_weekday:
46  return next_weekday(today_date, next_departure)
47  return next_weekday(today_date, WEEKDAYS.index(departure[0]))
48 
49 
51  """A Trafikverket Data Update Coordinator."""
52 
53  config_entry: TVFerryConfigEntry
54 
55  def __init__(self, hass: HomeAssistant) -> None:
56  """Initialize the Trafikverket coordinator."""
57  super().__init__(
58  hass,
59  _LOGGER,
60  name=DOMAIN,
61  update_interval=TIME_BETWEEN_UPDATES,
62  )
63  self._ferry_api_ferry_api = TrafikverketFerry(
64  async_get_clientsession(hass), self.config_entryconfig_entry.data[CONF_API_KEY]
65  )
66  self._from: str = self.config_entryconfig_entry.data[CONF_FROM]
67  self._to: str = self.config_entryconfig_entry.data[CONF_TO]
68  self._time: time | None = dt_util.parse_time(self.config_entryconfig_entry.data[CONF_TIME])
69  self._weekdays: list[str] = self.config_entryconfig_entry.data[CONF_WEEKDAY]
70 
71  async def _async_update_data(self) -> dict[str, Any]:
72  """Fetch data from Trafikverket."""
73 
74  departure_day = next_departuredate(self._weekdays)
75  current_time = dt_util.now()
76  when = (
77  datetime.combine(
78  departure_day,
79  self._time,
80  dt_util.get_default_time_zone(),
81  )
82  if self._time
83  else dt_util.now()
84  )
85  when = max(when, current_time)
86 
87  try:
88  routedata: list[
89  FerryStopModel
90  ] = await self._ferry_api_ferry_api.async_get_next_ferry_stops(
91  self._from, self._to, when, 3
92  )
93  except NoFerryFound as error:
94  raise UpdateFailed(
95  f"Departure {when} encountered a problem: {error}"
96  ) from error
97  except InvalidAuthentication as error:
98  raise ConfigEntryAuthFailed(error) from error
99 
100  states = {
101  "departure_time": routedata[0].departure_time,
102  "departure_from": routedata[0].from_harbor_name,
103  "departure_to": routedata[0].to_harbor_name,
104  "departure_modified": routedata[0].modified_time,
105  "departure_information": routedata[0].other_information,
106  "departure_time_next": routedata[1].departure_time,
107  "departure_time_next_next": routedata[2].departure_time,
108  }
109  _LOGGER.debug("States: %s", states)
110  return states
date next_weekday(date fromdate, int weekday)
Definition: coordinator.py:29
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)