Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """DataUpdateCoordinator for the OpenSky integration."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 
7 from python_opensky import OpenSky, OpenSkyError, StateVector
8 
9 from homeassistant.config_entries import ConfigEntry
10 from homeassistant.const import (
11  ATTR_LATITUDE,
12  ATTR_LONGITUDE,
13  CONF_LATITUDE,
14  CONF_LONGITUDE,
15  CONF_RADIUS,
16 )
17 from homeassistant.core import HomeAssistant
18 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
19 
20 from .const import (
21  ATTR_ALTITUDE,
22  ATTR_CALLSIGN,
23  ATTR_ICAO24,
24  ATTR_SENSOR,
25  CONF_ALTITUDE,
26  DEFAULT_ALTITUDE,
27  DOMAIN,
28  EVENT_OPENSKY_ENTRY,
29  EVENT_OPENSKY_EXIT,
30  LOGGER,
31 )
32 
33 
35  """An OpenSky Data Update Coordinator."""
36 
37  config_entry: ConfigEntry
38 
39  def __init__(self, hass: HomeAssistant, opensky: OpenSky) -> None:
40  """Initialize the OpenSky data coordinator."""
41  super().__init__(
42  hass,
43  LOGGER,
44  name=DOMAIN,
45  update_interval={
46  True: timedelta(seconds=90),
47  False: timedelta(minutes=15),
48  }.get(opensky.is_authenticated),
49  )
50  self._opensky_opensky = opensky
51  self._previously_tracked_previously_tracked: set[str] | None = None
52  self._bounding_box_bounding_box = OpenSky.get_bounding_box(
53  self.config_entryconfig_entry.data[CONF_LATITUDE],
54  self.config_entryconfig_entry.data[CONF_LONGITUDE],
55  self.config_entryconfig_entry.options[CONF_RADIUS],
56  )
57  self._altitude_altitude = self.config_entryconfig_entry.options.get(CONF_ALTITUDE, DEFAULT_ALTITUDE)
58 
59  async def _async_update_data(self) -> int:
60  try:
61  response = await self._opensky_opensky.get_states(bounding_box=self._bounding_box_bounding_box)
62  except OpenSkyError as exc:
63  raise UpdateFailed from exc
64  currently_tracked = set()
65  flight_metadata: dict[str, StateVector] = {}
66  for flight in response.states:
67  if not flight.callsign:
68  continue
69  callsign = flight.callsign.strip()
70  if callsign:
71  flight_metadata[callsign] = flight
72  else:
73  continue
74  if (
75  flight.longitude is None
76  or flight.latitude is None
77  or flight.on_ground
78  or flight.barometric_altitude is None
79  ):
80  continue
81  altitude = flight.barometric_altitude
82  if altitude > self._altitude_altitude and self._altitude_altitude != 0:
83  continue
84  currently_tracked.add(callsign)
85  if self._previously_tracked_previously_tracked is not None:
86  entries = currently_tracked - self._previously_tracked_previously_tracked
87  exits = self._previously_tracked_previously_tracked - currently_tracked
88  self._handle_boundary_handle_boundary(entries, EVENT_OPENSKY_ENTRY, flight_metadata)
89  self._handle_boundary_handle_boundary(exits, EVENT_OPENSKY_EXIT, flight_metadata)
90  self._previously_tracked_previously_tracked = currently_tracked
91 
92  return len(currently_tracked)
93 
95  self, flights: set[str], event: str, metadata: dict[str, StateVector]
96  ) -> None:
97  """Handle flights crossing region boundary."""
98  for flight in flights:
99  if flight in metadata:
100  altitude = metadata[flight].barometric_altitude
101  longitude = metadata[flight].longitude
102  latitude = metadata[flight].latitude
103  icao24 = metadata[flight].icao24
104  else:
105  # Assume Flight has landed if missing.
106  altitude = 0
107  longitude = None
108  latitude = None
109  icao24 = None
110 
111  data = {
112  ATTR_CALLSIGN: flight,
113  ATTR_ALTITUDE: altitude,
114  ATTR_SENSOR: self.config_entryconfig_entry.title,
115  ATTR_LONGITUDE: longitude,
116  ATTR_LATITUDE: latitude,
117  ATTR_ICAO24: icao24,
118  }
119  self.hasshass.bus.fire(event, data)
None _handle_boundary(self, set[str] flights, str event, dict[str, StateVector] metadata)
Definition: coordinator.py:96
None __init__(self, HomeAssistant hass, OpenSky opensky)
Definition: coordinator.py:39
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88