1 """Support for departure information for Rhein-Main public transport."""
3 from __future__
import annotations
6 from datetime
import timedelta
9 from RMVtransport
import RMVtransport
10 from RMVtransport.rmvtransport
import (
11 RMVtransportApiConnectionError,
12 RMVtransportDataError,
14 import voluptuous
as vol
17 PLATFORM_SCHEMA
as SENSOR_PLATFORM_SCHEMA,
28 _LOGGER = logging.getLogger(__name__)
30 CONF_NEXT_DEPARTURE =
"next_departure"
32 CONF_STATION =
"station"
33 CONF_DESTINATIONS =
"destinations"
34 CONF_DIRECTION =
"direction"
36 CONF_PRODUCTS =
"products"
37 CONF_TIME_OFFSET =
"time_offset"
38 CONF_MAX_JOURNEYS =
"max_journeys"
40 DEFAULT_NAME =
"RMV Journey"
42 VALID_PRODUCTS = [
"U-Bahn",
"Tram",
"Bus",
"S",
"RB",
"RE",
"EC",
"IC",
"ICE"]
45 "U-Bahn":
"mdi:subway",
54 "SEV":
"mdi:checkbox-blank-circle-outline",
57 ATTRIBUTION =
"Data provided by opendata.rmv.de"
61 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
63 vol.Required(CONF_NEXT_DEPARTURE): [
65 vol.Required(CONF_STATION): cv.string,
66 vol.Optional(CONF_DESTINATIONS, default=[]): vol.All(
67 cv.ensure_list, [cv.string]
69 vol.Optional(CONF_DIRECTION): cv.string,
70 vol.Optional(CONF_LINES, default=[]): vol.All(
71 cv.ensure_list, [cv.positive_int, cv.string]
73 vol.Optional(CONF_PRODUCTS, default=VALID_PRODUCTS): vol.All(
74 cv.ensure_list, [vol.In(VALID_PRODUCTS)]
76 vol.Optional(CONF_TIME_OFFSET, default=0): cv.positive_int,
77 vol.Optional(CONF_MAX_JOURNEYS, default=5): cv.positive_int,
78 vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
81 vol.Optional(CONF_TIMEOUT, default=10): cv.positive_int,
89 async_add_entities: AddEntitiesCallback,
90 discovery_info: DiscoveryInfoType |
None =
None,
92 """Set up the RMV departure sensor."""
93 timeout = config.get(CONF_TIMEOUT)
97 next_departure[CONF_STATION],
98 next_departure.get(CONF_DESTINATIONS),
99 next_departure.get(CONF_DIRECTION),
100 next_departure.get(CONF_LINES),
101 next_departure.get(CONF_PRODUCTS),
102 next_departure.get(CONF_TIME_OFFSET),
103 next_departure.get(CONF_MAX_JOURNEYS),
104 next_departure.get(CONF_NAME),
107 for next_departure
in config[CONF_NEXT_DEPARTURE]
110 tasks = [asyncio.create_task(sensor.async_update())
for sensor
in sensors]
112 await asyncio.wait(tasks)
114 if not any(sensor.data
for sensor
in sensors):
115 raise PlatformNotReady
121 """Implementation of an RMV departure sensor."""
123 _attr_attribution = ATTRIBUTION
137 """Initialize the sensor."""
155 """Return the name of the sensor."""
156 return self.
_name_name
160 """Return True if entity is available."""
161 return self.
_state_state
is not None
165 """Return the next departure time."""
170 """Return the state attributes."""
173 "next_departures": self.
datadata.departures[1:],
174 "direction": self.
datadata.departures[0].
get(
"direction"),
175 "line": self.
datadata.departures[0].
get(
"line"),
176 "minutes": self.
datadata.departures[0].
get(
"minutes"),
177 "departure_time": self.
datadata.departures[0].
get(
"departure_time"),
178 "product": self.
datadata.departures[0].
get(
"product"),
185 """Icon to use in the frontend, if any."""
186 return self.
_icon_icon
190 """Return the unit this state is expressed in."""
191 return UnitOfTime.MINUTES
194 """Get the latest data and update the state."""
197 if self.
_name_name == DEFAULT_NAME:
202 if not self.
datadata.departures:
204 self.
_icon_icon = ICONS[
None]
207 self.
_state_state = self.
datadata.departures[0].
get(
"minutes")
208 self.
_icon_icon = ICONS[self.
datadata.departures[0].
get(
"product")]
212 """Pull data from the opendata.rmv.de web page."""
225 """Initialize the sensor."""
234 self.
rmvrmv = RMVtransport(timeout)
238 @Throttle(SCAN_INTERVAL)
240 """Update the connection data."""
242 _data = await self.
rmvrmv.get_departures(
249 except (RMVtransportApiConnectionError, RMVtransportDataError):
251 _LOGGER.warning(
"Could not retrieve data from rmv.de")
254 self.
stationstation = _data.get(
"station")
259 for journey
in _data[
"journeys"]:
265 if dest
in journey[
"stops"]:
267 if dest
in _deps_not_found:
268 _deps_not_found.remove(dest)
269 _nextdep[
"destination"] = dest
276 and journey[
"number"]
not in self.
_lines_lines
281 for attr
in (
"direction",
"departure_time",
"product",
"minutes"):
282 _nextdep[attr] = journey.get(attr,
"")
284 _nextdep[
"line"] = journey.get(
"number",
"")
285 _deps.append(_nextdep)
292 _LOGGER.warning(
"Destination(s) %s not found",
", ".join(_deps_not_found))
def __init__(self, station_id, destinations, direction, lines, products, time_offset, max_journeys, timeout)
def extra_state_attributes(self)
def native_unit_of_measurement(self)
def __init__(self, station, destinations, direction, lines, products, time_offset, max_journeys, name, timeout)
web.Response get(self, web.Request request, str config_key)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)