Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Support for Vodafone Station."""
2 
3 from dataclasses import dataclass
4 from datetime import datetime, timedelta
5 from typing import Any
6 
7 from aiovodafone import VodafoneStationDevice, VodafoneStationSercommApi, exceptions
8 
9 from homeassistant.components.device_tracker import DEFAULT_CONSIDER_HOME
10 from homeassistant.core import HomeAssistant
11 from homeassistant.exceptions import ConfigEntryAuthFailed
12 from homeassistant.helpers.device_registry import DeviceInfo
13 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
14 from homeassistant.util import dt as dt_util
15 
16 from .const import _LOGGER, DOMAIN
17 
18 CONSIDER_HOME_SECONDS = DEFAULT_CONSIDER_HOME.total_seconds()
19 
20 
21 @dataclass(slots=True)
23  """Representation of a device connected to the Vodafone Station."""
24 
25  device: VodafoneStationDevice
26  update_time: datetime | None
27  home: bool
28 
29 
30 @dataclass(slots=True)
32  """Update coordinator data type."""
33 
34  devices: dict[str, VodafoneStationDeviceInfo]
35  sensors: dict[str, Any]
36 
37 
38 class VodafoneStationRouter(DataUpdateCoordinator[UpdateCoordinatorDataType]):
39  """Queries router running Vodafone Station firmware."""
40 
41  def __init__(
42  self,
43  hass: HomeAssistant,
44  host: str,
45  username: str,
46  password: str,
47  config_entry_unique_id: str | None,
48  ) -> None:
49  """Initialize the scanner."""
50 
51  self._host_host = host
52  self.apiapi = VodafoneStationSercommApi(host, username, password)
53 
54  # Last resort as no MAC or S/N can be retrieved via API
55  self._id_id = config_entry_unique_id
56 
57  super().__init__(
58  hass=hass,
59  logger=_LOGGER,
60  name=f"{DOMAIN}-{host}-coordinator",
61  update_interval=timedelta(seconds=30),
62  )
63 
65  self, device: VodafoneStationDevice, utc_point_in_time: datetime
66  ) -> tuple[datetime | None, bool]:
67  """Return update time and consider home.
68 
69  If the device is connected, return the current time and True.
70 
71  If the device is not connected, return the last update time and
72  whether the device was considered home at that time.
73 
74  If the device is not connected and there is no last update time,
75  return None and False.
76  """
77  if device.connected:
78  return utc_point_in_time, True
79 
80  if (
81  (data := self.datadata)
82  and (stored_device := data.devices.get(device.mac))
83  and (update_time := stored_device.update_time)
84  ):
85  return (
86  update_time,
87  (
88  (utc_point_in_time - update_time).total_seconds()
89  < CONSIDER_HOME_SECONDS
90  ),
91  )
92 
93  return None, False
94 
95  async def _async_update_data(self) -> UpdateCoordinatorDataType:
96  """Update router data."""
97  _LOGGER.debug("Polling Vodafone Station host: %s", self._host_host)
98  try:
99  try:
100  await self.apiapi.login()
101  raw_data_devices = await self.apiapi.get_devices_data()
102  data_sensors = await self.apiapi.get_sensor_data()
103  await self.apiapi.logout()
104  except exceptions.CannotAuthenticate as err:
105  raise ConfigEntryAuthFailed from err
106  except (
107  exceptions.CannotConnect,
108  exceptions.AlreadyLogged,
109  exceptions.GenericLoginError,
110  ) as err:
111  raise UpdateFailed(f"Error fetching data: {err!r}") from err
112  except (ConfigEntryAuthFailed, UpdateFailed):
113  await self.apiapi.close()
114  raise
115 
116  utc_point_in_time = dt_util.utcnow()
117  data_devices = {
118  dev_info.mac: VodafoneStationDeviceInfo(
119  dev_info,
120  *self._calculate_update_time_and_consider_home_calculate_update_time_and_consider_home(
121  dev_info, utc_point_in_time
122  ),
123  )
124  for dev_info in (raw_data_devices).values()
125  }
126  return UpdateCoordinatorDataType(data_devices, data_sensors)
127 
128  @property
129  def signal_device_new(self) -> str:
130  """Event specific per Vodafone Station entry to signal new device."""
131  return f"{DOMAIN}-device-new-{self._id}"
132 
133  @property
134  def serial_number(self) -> str:
135  """Device serial number."""
136  return self.datadata.sensors["sys_serial_number"]
137 
138  @property
139  def device_info(self) -> DeviceInfo:
140  """Set device info."""
141  sensors_data = self.datadata.sensors
142  return DeviceInfo(
143  configuration_url=self.apiapi.base_url,
144  identifiers={(DOMAIN, self.serial_numberserial_number)},
145  name=f"Vodafone Station ({self.serial_number})",
146  manufacturer="Vodafone",
147  model=sensors_data.get("sys_model_name"),
148  hw_version=sensors_data["sys_hardware_version"],
149  sw_version=sensors_data["sys_firmware_version"],
150  )
tuple[datetime|None, bool] _calculate_update_time_and_consider_home(self, VodafoneStationDevice device, datetime utc_point_in_time)
Definition: coordinator.py:66
None __init__(self, HomeAssistant hass, str host, str username, str password, str|None config_entry_unique_id)
Definition: coordinator.py:48