1 """Support for Dublin RTPI information from data.dublinked.ie.
3 For more info on the API see :
4 https://data.gov.ie/dataset/real-time-passenger-information-rtpi-for-dublin-bus-bus-eireann-luas-and-irish-rail/resource/4b9f2c4f-6bf5-4958-a43a-f12dab04cf61
7 from __future__
import annotations
9 from contextlib
import suppress
10 from datetime
import datetime, timedelta
11 from http
import HTTPStatus
12 from typing
import Any
15 import voluptuous
as vol
18 PLATFORM_SCHEMA
as SENSOR_PLATFORM_SCHEMA,
28 _RESOURCE =
"https://data.dublinked.ie/cgi-bin/rtpi/realtimebusinformation"
30 ATTR_STOP_ID =
"Stop ID"
32 ATTR_DUE_IN =
"Due in"
33 ATTR_DUE_AT =
"Due at"
34 ATTR_NEXT_UP =
"Later Bus"
36 CONF_STOP_ID =
"stopid"
39 DEFAULT_NAME =
"Next Bus"
43 TIME_STR_FORMAT =
"%H:%M"
45 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
47 vol.Required(CONF_STOP_ID): cv.string,
48 vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
49 vol.Optional(CONF_ROUTE, default=
""): cv.string,
55 """Get the time in minutes from a timestamp.
57 The timestamp should be in the format day/month/year hour/minute/second
59 diff = datetime.strptime(timestamp,
"%d/%m/%Y %H:%M:%S") - dt_util.now().replace(
63 return str(
int(diff.total_seconds() / 60))
69 add_entities: AddEntitiesCallback,
70 discovery_info: DiscoveryInfoType |
None =
None,
72 """Set up the Dublin public transport sensor."""
73 name = config[CONF_NAME]
74 stop = config[CONF_STOP_ID]
75 route = config[CONF_ROUTE]
82 """Implementation of an Dublin public transport sensor."""
84 _attr_attribution =
"Data provided by data.dublinked.ie"
85 _attr_icon =
"mdi:bus"
88 """Initialize the sensor."""
97 """Return the name of the sensor."""
98 return self.
_name_name
102 """Return the state of the sensor."""
107 """Return the state attributes."""
108 if self.
_times_times
is not None:
110 if len(self.
_times_times) > 1:
111 next_up = f
"{self._times[1][ATTR_ROUTE]} in "
112 next_up += self.
_times_times[1][ATTR_DUE_IN]
115 ATTR_DUE_IN: self.
_times_times[0][ATTR_DUE_IN],
116 ATTR_DUE_AT: self.
_times_times[0][ATTR_DUE_AT],
117 ATTR_STOP_ID: self.
_stop_stop,
118 ATTR_ROUTE: self.
_times_times[0][ATTR_ROUTE],
119 ATTR_NEXT_UP: next_up,
125 """Return the unit this state is expressed in."""
126 return UnitOfTime.MINUTES
129 """Get the latest data from opendata.ch and update the states."""
132 with suppress(TypeError):
137 """The Class for handling the data retrieval."""
140 """Initialize the data object."""
143 self.
infoinfo = [{ATTR_DUE_AT:
"n/a", ATTR_ROUTE: self.
routeroute, ATTR_DUE_IN:
"n/a"}]
146 """Get the latest data from opendata.ch."""
148 params[
"stopid"] = self.
stopstop
151 params[
"routeid"] = self.
routeroute
153 params[
"maxresults"] = 2
154 params[
"format"] =
"json"
156 response = requests.get(_RESOURCE, params, timeout=10)
158 if response.status_code != HTTPStatus.OK:
160 {ATTR_DUE_AT:
"n/a", ATTR_ROUTE: self.
routeroute, ATTR_DUE_IN:
"n/a"}
164 result = response.json()
166 if str(result[
"errorcode"]) !=
"0":
168 {ATTR_DUE_AT:
"n/a", ATTR_ROUTE: self.
routeroute, ATTR_DUE_IN:
"n/a"}
173 for item
in result[
"results"]:
174 due_at = item.get(
"departuredatetime")
175 route = item.get(
"route")
176 if due_at
is not None and route
is not None:
182 self.
infoinfo.append(bus_data)
184 if not self.
infoinfo:
186 {ATTR_DUE_AT:
"n/a", ATTR_ROUTE: self.
routeroute, ATTR_DUE_IN:
"n/a"}
def native_unit_of_measurement(self)
def __init__(self, data, stop, route, name)
dict[str, Any]|None extra_state_attributes(self)
def __init__(self, stop, route)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
def due_in_minutes(timestamp)
def add_entities(account, async_add_entities, tracked)