1 """Get ride details and liveboard details for NMBS (Belgian railway)."""
3 from __future__
import annotations
7 from pyrail
import iRail
8 import voluptuous
as vol
11 PLATFORM_SCHEMA
as SENSOR_PLATFORM_SCHEMA,
27 _LOGGER = logging.getLogger(__name__)
33 DEFAULT_ICON =
"mdi:train"
34 DEFAULT_ICON_ALERT =
"mdi:alert-octagon"
36 CONF_STATION_FROM =
"station_from"
37 CONF_STATION_TO =
"station_to"
38 CONF_STATION_LIVE =
"station_live"
39 CONF_EXCLUDE_VIAS =
"exclude_vias"
41 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
43 vol.Required(CONF_STATION_FROM): cv.string,
44 vol.Required(CONF_STATION_TO): cv.string,
45 vol.Optional(CONF_STATION_LIVE): cv.string,
46 vol.Optional(CONF_EXCLUDE_VIAS, default=
False): cv.boolean,
47 vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
48 vol.Optional(CONF_SHOW_ON_MAP, default=
False): cv.boolean,
54 """Calculate the time between now and a train's departure time."""
55 if departure_time
is None:
58 delta = dt_util.utc_from_timestamp(
int(departure_time)) - dt_util.now()
59 return round(delta.total_seconds() / 60)
63 """Get the delay in minutes from a delay in seconds."""
64 return round(
int(delay) / 60)
68 """Calculate the total travel time in minutes."""
69 duration = dt_util.utc_from_timestamp(
71 ) - dt_util.utc_from_timestamp(
int(departure_time))
72 duration_time =
int(round(duration.total_seconds() / 60))
79 add_entities: AddEntitiesCallback,
80 discovery_info: DiscoveryInfoType |
None =
None,
82 """Set up the NMBS sensor with iRail API."""
86 name = config[CONF_NAME]
87 show_on_map = config[CONF_SHOW_ON_MAP]
88 station_from = config[CONF_STATION_FROM]
89 station_to = config[CONF_STATION_TO]
90 station_live = config.get(CONF_STATION_LIVE)
91 excl_vias = config[CONF_EXCLUDE_VIAS]
93 sensors: list[SensorEntity] = [
94 NMBSSensor(api_client, name, show_on_map, station_from, station_to, excl_vias)
97 if station_live
is not None:
99 NMBSLiveBoard(api_client, station_live, station_from, station_to)
106 """Get the next train from a station's liveboard."""
108 _attr_attribution =
"https://api.irail.be/"
110 def __init__(self, api_client, live_station, station_from, station_to):
111 """Initialize the sensor for getting liveboard data."""
121 """Return the sensor default name."""
122 return f
"NMBS Live ({self._station})"
126 """Return a unique ID."""
127 unique_id = f
"{self._station}_{self._station_from}_{self._station_to}"
129 return f
"nmbs_live_{unique_id}"
133 """Return the default icon or an alert icon if delays."""
135 return DEFAULT_ICON_ALERT
141 """Return sensor state."""
146 """Return the sensor attributes if data is available."""
154 "departure": f
"In {departure} minutes",
155 "departure_minutes": departure,
156 "extra_train":
int(self.
_attrs_attrs[
"isExtra"]) > 0,
157 "vehicle_id": self.
_attrs_attrs[
"vehicle"],
158 "monitored_station": self.
_station_station,
162 attrs[
"delay"] = f
"{delay} minutes"
163 attrs[
"delay_minutes"] = delay
168 """Set the state equal to the next departure."""
171 if liveboard == API_FAILURE:
172 _LOGGER.warning(
"API failed in NMBSLiveBoard")
175 if not (departures := liveboard.get(
"departures")):
176 _LOGGER.warning(
"API returned invalid departures: %r", liveboard)
179 _LOGGER.debug(
"API returned departures: %r", departures)
180 if departures[
"number"] ==
"0":
183 next_departure = departures[
"departure"][0]
185 self.
_attrs_attrs = next_departure
187 f
"Track {next_departure['platform']} - {next_departure['station']}"
192 """Get the total travel time for a given connection."""
194 _attr_attribution =
"https://api.irail.be/"
195 _attr_native_unit_of_measurement = UnitOfTime.MINUTES
198 self, api_client, name, show_on_map, station_from, station_to, excl_vias
200 """Initialize the NMBS connection sensor."""
213 """Return the name of the sensor."""
214 return self.
_name_name
218 """Return the sensor default icon or an alert icon if any delay."""
222 return "mdi:alert-octagon"
228 """Return sensor attributes if data is available."""
234 canceled =
int(self.
_attrs_attrs[
"departure"][
"canceled"])
238 "direction": self.
_attrs_attrs[
"departure"][
"direction"][
"name"],
239 "platform_arriving": self.
_attrs_attrs[
"arrival"][
"platform"],
240 "platform_departing": self.
_attrs_attrs[
"departure"][
"platform"],
241 "vehicle_id": self.
_attrs_attrs[
"departure"][
"vehicle"],
245 attrs[
"departure"] = f
"In {departure} minutes"
246 attrs[
"departure_minutes"] = departure
247 attrs[
"canceled"] =
False
249 attrs[
"departure"] =
None
250 attrs[
"departure_minutes"] =
None
251 attrs[
"canceled"] =
True
258 via = self.
_attrs_attrs[
"vias"][
"via"][0]
260 attrs[
"via"] = via[
"station"]
261 attrs[
"via_arrival_platform"] = via[
"arrival"][
"platform"]
262 attrs[
"via_transfer_platform"] = via[
"departure"][
"platform"]
268 attrs[
"delay"] = f
"{delay} minutes"
269 attrs[
"delay_minutes"] = delay
275 """Return the state of the device."""
280 """Get the lat, long coordinates for station."""
284 latitude =
float(self.
_attrs_attrs[
"departure"][
"stationinfo"][
"locationY"])
285 longitude =
float(self.
_attrs_attrs[
"departure"][
"stationinfo"][
"locationX"])
286 return [latitude, longitude]
290 """Return whether the connection goes through another station."""
294 return "vias" in self.
_attrs_attrs
and int(self.
_attrs_attrs[
"vias"][
"number"]) > 0
297 """Set the state to the duration of a connection."""
298 connections = self.
_api_client_api_client.get_connections(
302 if connections == API_FAILURE:
303 _LOGGER.warning(
"API failed in NMBSSensor")
306 if not (connection := connections.get(
"connection")):
307 _LOGGER.warning(
"API returned invalid connection: %r", connections)
310 _LOGGER.debug(
"API returned connection: %r", connection)
311 if int(connection[0][
"departure"][
"left"]) > 0:
312 next_connection = connection[1]
314 next_connection = connection[0]
316 self.
_attrs_attrs = next_connection
320 "Skipping update of NMBSSensor because this connection is a via"
325 next_connection[
"departure"][
"time"],
326 next_connection[
"arrival"][
"time"],
327 next_connection[
"departure"][
"delay"],
330 self.
_state_state = duration
def extra_state_attributes(self)
def __init__(self, api_client, live_station, station_from, station_to)
def station_coordinates(self)
def __init__(self, api_client, name, show_on_map, station_from, station_to, excl_vias)
def extra_state_attributes(self)
def is_via_connection(self)
def add_entities(account, async_add_entities, tracked)
def get_delay_in_minutes(delay=0)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
def get_time_until(departure_time=None)
def get_ride_duration(departure_time, arrival_time, delay=0)