77import logging
88
99from dateutil import parser
10+ from dateutil .parser import ParserError
1011import isodate
1112
1213from homeassistant import config_entries , core
2425 STOP_URL ,
2526 TRAM_LINES ,
2627)
27- from .fetch_api import get_stop_times , get_stops
28+ from .fetch_api import StopTime , get_stop_times , get_stops
2829from .network import get
2930
3031_LOGGER = logging .getLogger (__name__ )
@@ -81,19 +82,17 @@ def __init__(self, stop_code, maximum, timelimit, lines) -> None:
8182
8283 self ._last_update_time = None
8384
84- def _remove_unwanted_departures (self , departures ):
85+ def _remove_unwanted_departures (self , departures : list [ StopTime ] ):
8586 try :
8687 removed_departures_count = 0
8788
8889 # Remove unwanted departures based on departure time and line number
8990 for departure in departures [:]:
90- departure_local = dt_util .as_local (
91- parser .parse (departure ["departure_time" ])
92- )
91+ departure_local = dt_util .as_local (departure .departure_time )
9392 if (
9493 departure_local
9594 < self ._last_update_time + timedelta (minutes = self ._timelimit )
96- or departure [ " route_id" ] not in self ._lines
95+ or departure . route_id not in self ._lines
9796 ):
9897 departures .remove (departure )
9998 removed_departures_count += 1
@@ -129,7 +128,7 @@ async def _fetch_departures(self):
129128 self ._stop_code ,
130129 url ,
131130 )
132- return
131+ return None
133132 unformatted_departures = json .loads (data )
134133 return self ._format_departures (unformatted_departures )
135134 except OSError as err :
@@ -139,25 +138,20 @@ async def _fetch_departures(self):
139138 def _format_departures (self , departures ):
140139 try :
141140 body = departures ["body" ][self ._stop_code ]
142- formatted_data = []
141+ formatted_data : list [ StopTime ] = []
143142 for departure in body :
144143 try :
145- formatted_departure = {
146- "route_id" : departure ["lineRef" ],
147- "trip_headsign" : self ._get_stop_name (
148- departure ["destinationShortName" ]
149- ),
150- "departure_time" : departure ["call" ]["expectedDepartureTime" ],
151- "aimed_departure_time" : departure ["call" ]["aimedDepartureTime" ],
152- "delay" : departure ["delay" ],
153- "realtime" : True ,
154- }
155- if (
156- formatted_departure ["departure_time" ] is not None
157- and formatted_departure ["aimed_departure_time" ] is not None
158- ):
159- formatted_data .append (formatted_departure )
160- except KeyError as err :
144+ formatted_departure = StopTime (
145+ departure ["lineRef" ],
146+ self ._get_stop_name (departure ["destinationShortName" ]),
147+ parser .parse (departure ["call" ]["expectedDepartureTime" ]),
148+ parser .parse (departure ["call" ]["aimedDepartureTime" ]),
149+ self ._delay_to_display_format (departure ["delay" ]),
150+ 0 ,
151+ True ,
152+ )
153+ formatted_data .append (formatted_departure )
154+ except (KeyError , ParserError ) as err :
161155 _LOGGER .info (
162156 "%s: Failed to process realtime departure: %s" ,
163157 self ._stop_code ,
@@ -200,13 +194,9 @@ async def async_update(self) -> None:
200194 )
201195 for journey in self ._journeys [:]:
202196 for departure in departures :
203- departure_time = parser .parse (departure ["aimed_departure_time" ])
204- journey_time = dt_util .as_local (
205- parser .parse (journey ["departure_time" ])
206- )
207197 if (
208- journey_time == departure_time
209- and journey [ " route_id" ] == departure [ " route_id" ]
198+ journey . departure_time == departure . aimed_departure_time
199+ and journey . route_id == departure . route_id
210200 ):
211201 self ._journeys .remove (journey )
212202 else :
@@ -223,24 +213,24 @@ async def async_update(self) -> None:
223213 except (OSError , ValueError ) as err :
224214 _LOGGER .error ("%s: Failed to update sensor: %s" , self ._stop_code , err )
225215
226- def _data_to_display_format (self , data ):
216+ def _data_to_display_format (self , data : list [ StopTime ] ):
227217 try :
228218 formatted_data = []
229219 for item in data :
230220 departure = {
231- "destination" : item [ " trip_headsign" ] ,
232- "line" : item [ " route_id" ] ,
233- "departure" : parser . parse ( item [ " departure_time" ]) .strftime ("%H:%M" ),
221+ "destination" : item . trip_headsign ,
222+ "line" : item . route_id ,
223+ "departure" : item . departure_time .strftime ("%H:%M" ),
234224 "time_to_station" : self ._time_to_station (item ),
235- "icon" : self ._get_line_icon (item [ " route_id" ] ),
236- "realtime" : item [ " realtime" ] if "realtime" in item else False ,
225+ "icon" : self ._get_line_icon (item . route_id ),
226+ "realtime" : item . realtime ,
237227 }
238- if " aimed_departure_time" in item :
239- departure ["aimed_departure" ] = parser . parse (
240- item [ "aimed_departure_time" ]
241- ). strftime ( "%H:%M" )
242- if " delay" in item :
243- departure ["delay" ] = self . _delay_to_display_format ( item [ " delay" ])
228+ if item . aimed_departure_time is not None :
229+ departure ["aimed_departure" ] = item . aimed_departure_time . strftime (
230+ "%H:%M"
231+ )
232+ if item . delay is not None :
233+ departure ["delay" ] = item . delay
244234 formatted_data .append (departure )
245235 return sorted (formatted_data , key = lambda x : x ["time_to_station" ])
246236 except (OSError , ValueError ) as err :
@@ -252,11 +242,11 @@ def _get_line_icon(self, line_no):
252242 return "mdi:tram"
253243 return "mdi:bus"
254244
255- def _time_to_station (self , item ):
245+ def _time_to_station (self , item : StopTime ):
256246 try :
257- departure_local = dt_util .as_local (parser . parse ( item [ " departure_time" ]) )
258- if " delta_days" in item :
259- departure_local += timedelta (days = item [ " delta_days" ] )
247+ departure_local = dt_util .as_local (item . departure_time )
248+ if item . delta_days > 0 :
249+ departure_local += timedelta (days = item . delta_days )
260250 next_departure_time = (departure_local - self ._last_update_time ).seconds
261251 return int (next_departure_time / 60 )
262252 except OSError as err :
@@ -323,12 +313,12 @@ def state(self) -> str:
323313 @property
324314 def extra_state_attributes (self ):
325315 """Sensor attributes."""
326- attributes = {
316+ return {
327317 "last_refresh" : self ._last_update_time ,
328318 "departures" : self ._all_data ,
329319 "station_name" : self ._get_stop_name (self ._stop_code ),
320+ "station_id" : self ._stop_code ,
330321 }
331- return attributes
332322
333323
334324class ServiceAlertSensor (SensorEntity ):
@@ -363,7 +353,7 @@ async def _fetch_service_alerts(self):
363353 "Nysse API error: failed to fetch service alerts: no data received from %s" ,
364354 SERVICE_ALERTS_URL ,
365355 )
366- return
356+ return None
367357 json_data = json .loads (data )
368358
369359 self ._last_update = self ._timestamp_to_local (
@@ -428,8 +418,7 @@ def state(self) -> str:
428418 @property
429419 def extra_state_attributes (self ):
430420 """Sensor attributes."""
431- attributes = {
421+ return {
432422 "last_refresh" : self ._last_update ,
433423 "alerts" : self ._alerts ,
434424 }
435- return attributes
0 commit comments