Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for OASA Telematics from telematics.oasa.gr."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 from operator import itemgetter
8 
9 import oasatelematics
10 import voluptuous as vol
11 
13  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
14  SensorDeviceClass,
15  SensorEntity,
16 )
17 from homeassistant.const import CONF_NAME
18 from homeassistant.core import HomeAssistant
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
22 from homeassistant.util import dt as dt_util
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 ATTR_STOP_ID = "stop_id"
27 ATTR_STOP_NAME = "stop_name"
28 ATTR_ROUTE_ID = "route_id"
29 ATTR_ROUTE_NAME = "route_name"
30 ATTR_NEXT_ARRIVAL = "next_arrival"
31 ATTR_SECOND_NEXT_ARRIVAL = "second_next_arrival"
32 ATTR_NEXT_DEPARTURE = "next_departure"
33 
34 CONF_STOP_ID = "stop_id"
35 CONF_ROUTE_ID = "route_id"
36 
37 DEFAULT_NAME = "OASA Telematics"
38 
39 
40 SCAN_INTERVAL = timedelta(seconds=60)
41 
42 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
43  {
44  vol.Required(CONF_STOP_ID): cv.string,
45  vol.Required(CONF_ROUTE_ID): cv.string,
46  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
47  }
48 )
49 
50 
52  hass: HomeAssistant,
53  config: ConfigType,
54  add_entities: AddEntitiesCallback,
55  discovery_info: DiscoveryInfoType | None = None,
56 ) -> None:
57  """Set up the OASA Telematics sensor."""
58  name = config[CONF_NAME]
59  stop_id = config[CONF_STOP_ID]
60  route_id = config.get(CONF_ROUTE_ID)
61 
62  data = OASATelematicsData(stop_id, route_id)
63 
64  add_entities([OASATelematicsSensor(data, stop_id, route_id, name)], True)
65 
66 
68  """Implementation of the OASA Telematics sensor."""
69 
70  _attr_attribution = "Data retrieved from telematics.oasa.gr"
71  _attr_icon = "mdi:bus"
72 
73  def __init__(self, data, stop_id, route_id, name):
74  """Initialize the sensor."""
75  self.datadata = data
76  self._name_name = name
77  self._stop_id_stop_id = stop_id
78  self._route_id_route_id = route_id
79  self._name_data_name_data = self._times_times = self._state_state = None
80 
81  @property
82  def name(self):
83  """Return the name of the sensor."""
84  return self._name_name
85 
86  @property
87  def device_class(self):
88  """Return the class of this sensor."""
89  return SensorDeviceClass.TIMESTAMP
90 
91  @property
92  def native_value(self):
93  """Return the state of the sensor."""
94  return self._state_state
95 
96  @property
98  """Return the state attributes."""
99  params = {}
100  if self._times_times is not None:
101  next_arrival_data = self._times_times[0]
102  if ATTR_NEXT_ARRIVAL in next_arrival_data:
103  next_arrival = next_arrival_data[ATTR_NEXT_ARRIVAL]
104  params.update({ATTR_NEXT_ARRIVAL: next_arrival.isoformat()})
105  if len(self._times_times) > 1:
106  second_next_arrival_time = self._times_times[1][ATTR_NEXT_ARRIVAL]
107  if second_next_arrival_time is not None:
108  second_arrival = second_next_arrival_time
109  params.update(
110  {ATTR_SECOND_NEXT_ARRIVAL: second_arrival.isoformat()}
111  )
112  params.update(
113  {
114  ATTR_ROUTE_ID: self._times_times[0][ATTR_ROUTE_ID],
115  ATTR_STOP_ID: self._stop_id_stop_id,
116  }
117  )
118  params.update(
119  {
120  ATTR_ROUTE_NAME: self._name_data_name_data[ATTR_ROUTE_NAME],
121  ATTR_STOP_NAME: self._name_data_name_data[ATTR_STOP_NAME],
122  }
123  )
124  return {k: v for k, v in params.items() if v}
125 
126  def update(self) -> None:
127  """Get the latest data from OASA API and update the states."""
128  self.datadata.update()
129  self._times_times = self.datadata.info
130  self._name_data_name_data = self.datadata.name_data
131  next_arrival_data = self._times_times[0]
132  if ATTR_NEXT_ARRIVAL in next_arrival_data:
133  self._state_state = next_arrival_data[ATTR_NEXT_ARRIVAL]
134 
135 
137  """The class for handling data retrieval."""
138 
139  def __init__(self, stop_id, route_id):
140  """Initialize the data object."""
141  self.stop_idstop_id = stop_id
142  self.route_idroute_id = route_id
143  self.infoinfo = self.empty_resultempty_result()
144  self.oasa_apioasa_api = oasatelematics
145  self.name_dataname_data = {
146  ATTR_ROUTE_NAME: self.get_route_nameget_route_name(),
147  ATTR_STOP_NAME: self.get_stop_nameget_stop_name(),
148  }
149 
150  def empty_result(self):
151  """Object returned when no arrivals are found."""
152  return [{ATTR_ROUTE_ID: self.route_idroute_id}]
153 
154  def get_route_name(self):
155  """Get the route name from the API."""
156  try:
157  route = self.oasa_apioasa_api.getRouteName(self.route_idroute_id)
158  if route:
159  return route[0].get("route_departure_eng")
160  except TypeError:
161  _LOGGER.error("Cannot get route name from OASA API")
162  return None
163 
164  def get_stop_name(self):
165  """Get the stop name from the API."""
166  try:
167  name_data = self.oasa_apioasa_api.getStopNameAndXY(self.stop_idstop_id)
168  if name_data:
169  return name_data[0].get("stop_descr_matrix_eng")
170  except TypeError:
171  _LOGGER.error("Cannot get stop name from OASA API")
172  return None
173 
174  def update(self):
175  """Get the latest arrival data from telematics.oasa.gr API."""
176  self.infoinfo = []
177 
178  results = self.oasa_apioasa_api.getStopArrivals(self.stop_idstop_id)
179 
180  if not results:
181  self.infoinfo = self.empty_resultempty_result()
182  return
183 
184  # Parse results
185  results = [r for r in results if r.get("route_code") in self.route_idroute_id]
186  current_time = dt_util.utcnow()
187 
188  for result in results:
189  if (btime2 := result.get("btime2")) is not None:
190  arrival_min = int(btime2)
191  timestamp = current_time + timedelta(minutes=arrival_min)
192  arrival_data = {
193  ATTR_NEXT_ARRIVAL: timestamp,
194  ATTR_ROUTE_ID: self.route_idroute_id,
195  }
196  self.infoinfo.append(arrival_data)
197 
198  if not self.infoinfo:
199  _LOGGER.debug("No arrivals with given parameters")
200  self.infoinfo = self.empty_resultempty_result()
201  return
202 
203  # Sort the data by time
204  sort = sorted(self.infoinfo, key=itemgetter(ATTR_NEXT_ARRIVAL))
205  self.infoinfo = sort
def __init__(self, data, stop_id, route_id, name)
Definition: sensor.py:73
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:56