Home Assistant Unofficial Reference 2024.12.1
binary_sensor.py
Go to the documentation of this file.
1 """Binary sensor platform for hvv_departures."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from datetime import timedelta
7 import logging
8 from typing import Any
9 
10 from aiohttp import ClientConnectorError
11 from pygti.exceptions import InvalidAuth
12 
14  BinarySensorDeviceClass,
15  BinarySensorEntity,
16 )
17 from homeassistant.config_entries import ConfigEntry
18 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
22  CoordinatorEntity,
23  DataUpdateCoordinator,
24  UpdateFailed,
25 )
26 
27 from .const import ATTRIBUTION, CONF_STATION, DOMAIN, MANUFACTURER
28 
29 _LOGGER = logging.getLogger(__name__)
30 
31 
33  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
34 ) -> None:
35  """Set up the binary_sensor platform."""
36  hub = hass.data[DOMAIN][entry.entry_id]
37  station_name = entry.data[CONF_STATION]["name"]
38  station = entry.data[CONF_STATION]
39 
40  def get_elevator_entities_from_station_information(
41  station_name, station_information
42  ):
43  """Convert station information into a list of elevators."""
44  elevators = {}
45 
46  if station_information is None:
47  return {}
48 
49  for partial_station in station_information.get("partialStations", []):
50  for elevator in partial_station.get("elevators", []):
51  state = elevator.get("state") != "READY"
52  available = elevator.get("state") != "UNKNOWN"
53  label = elevator.get("label")
54  description = elevator.get("description")
55 
56  if label is not None:
57  name = f"Elevator {label}"
58  else:
59  name = "Unknown elevator"
60 
61  if description is not None:
62  name += f" ({description})"
63 
64  lines = elevator.get("lines")
65 
66  idx = f"{station_name}-{label}-{lines}"
67 
68  elevators[idx] = {
69  "state": state,
70  "name": name,
71  "available": available,
72  "attributes": {
73  "cabin_width": elevator.get("cabinWidth"),
74  "cabin_length": elevator.get("cabinLength"),
75  "door_width": elevator.get("doorWidth"),
76  "elevator_type": elevator.get("elevatorType"),
77  "button_type": elevator.get("buttonType"),
78  "cause": elevator.get("cause"),
79  "lines": lines,
80  },
81  }
82  return elevators
83 
84  async def async_update_data():
85  """Fetch data from API endpoint.
86 
87  This is the place to pre-process the data to lookup tables
88  so entities can quickly look up their data.
89  """
90 
91  payload = {"station": {"id": station["id"], "type": station["type"]}}
92 
93  try:
94  async with asyncio.timeout(10):
95  return get_elevator_entities_from_station_information(
96  station_name, await hub.gti.stationInformation(payload)
97  )
98  except InvalidAuth as err:
99  raise UpdateFailed(f"Authentication failed: {err}") from err
100  except ClientConnectorError as err:
101  raise UpdateFailed(f"Network not available: {err}") from err
102  except Exception as err:
103  raise UpdateFailed(f"Error occurred while fetching data: {err}") from err
104 
105  coordinator = DataUpdateCoordinator(
106  hass,
107  _LOGGER,
108  # Name of the data. For logging purposes.
109  name="hvv_departures.binary_sensor",
110  update_method=async_update_data,
111  # Polling interval. Will only be polled if there are subscribers.
112  update_interval=timedelta(hours=1),
113  )
114 
115  # Fetch initial data so we have data when entities subscribe
116  await coordinator.async_refresh()
117 
119  HvvDepartureBinarySensor(coordinator, idx, entry)
120  for (idx, ent) in coordinator.data.items()
121  )
122 
123 
125  """HVVDepartureBinarySensor class."""
126 
127  _attr_attribution = ATTRIBUTION
128  _attr_has_entity_name = True
129  _attr_device_class = BinarySensorDeviceClass.PROBLEM
130 
131  def __init__(self, coordinator, idx, config_entry):
132  """Initialize."""
133  super().__init__(coordinator)
134  self.coordinatorcoordinator = coordinator
135  self.idxidx = idx
136 
137  self._attr_name_attr_name = coordinator.data[idx]["name"]
138  self._attr_unique_id_attr_unique_id = idx
139  self._attr_device_info_attr_device_info = DeviceInfo(
140  entry_type=DeviceEntryType.SERVICE,
141  identifiers={
142  (
143  DOMAIN,
144  config_entry.entry_id,
145  config_entry.data[CONF_STATION]["id"],
146  config_entry.data[CONF_STATION]["type"],
147  )
148  },
149  manufacturer=MANUFACTURER,
150  name=f"Departures at {config_entry.data[CONF_STATION]['name']}",
151  )
152 
153  @property
154  def is_on(self):
155  """Return entity state."""
156  return self.coordinatorcoordinator.data[self.idxidx]["state"]
157 
158  @property
159  def available(self) -> bool:
160  """Return if entity is available."""
161  return (
162  self.coordinatorcoordinator.last_update_success
163  and self.coordinatorcoordinator.data[self.idxidx]["available"]
164  )
165 
166  @property
167  def extra_state_attributes(self) -> dict[str, Any] | None:
168  """Return the state attributes."""
169  if not (
170  self.coordinatorcoordinator.last_update_success
171  and self.coordinatorcoordinator.data[self.idxidx]["available"]
172  ):
173  return None
174  return {
175  k: v
176  for k, v in self.coordinatorcoordinator.data[self.idxidx]["attributes"].items()
177  if v is not None
178  }
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)