@@ -81,47 +81,59 @@ def __init__(self, stop_code, maximum, timelimit, lines) -> None:
8181 self ._last_update_time = None
8282
8383 def _remove_unwanted_departures (self , departures ):
84- removed_departures_count = 0
84+ try :
85+ removed_departures_count = 0
8586
86- # Remove unwanted departures based on departure time and line number
87- for departure in departures [:]:
88- departure_local = dt_util .as_local (
89- parser .parse (departure ["departure_time" ])
90- )
91- if (
92- departure_local
93- < self ._last_update_time + timedelta (minutes = self ._timelimit )
94- or departure ["route_id" ] not in self ._lines
95- ):
96- departures .remove (departure )
97- removed_departures_count += 1
98-
99- if removed_departures_count > 0 :
100- _LOGGER .debug (
101- "%s: Removed %s stale or unwanted departures" ,
87+ # Remove unwanted departures based on departure time and line number
88+ for departure in departures [:]:
89+ departure_local = dt_util .as_local (
90+ parser .parse (departure ["departure_time" ])
91+ )
92+ if (
93+ departure_local
94+ < self ._last_update_time + timedelta (minutes = self ._timelimit )
95+ or departure ["route_id" ] not in self ._lines
96+ ):
97+ departures .remove (departure )
98+ removed_departures_count += 1
99+
100+ if removed_departures_count > 0 :
101+ _LOGGER .debug (
102+ "%s: Removed %s stale or unwanted departures" ,
103+ self ._stop_code ,
104+ removed_departures_count ,
105+ )
106+
107+ return departures [: self ._max_items ]
108+ except (KeyError , OSError ) as err :
109+ _LOGGER .info (
110+ "%s: Failed to process realtime departures: %s" ,
102111 self ._stop_code ,
103- removed_departures_count ,
112+ err ,
104113 )
105-
106- return departures [: self ._max_items ]
114+ return []
107115
108116 async def _fetch_departures (self ):
109- url = STOP_URL .format (self ._stop_code )
110- _LOGGER .debug (
111- "%s: Fectching departures from %s" ,
112- self ._stop_code ,
113- url + "&indent=yes" ,
114- )
115- data = await get (url )
116- if not data :
117- _LOGGER .warning (
118- "%s: Nysse API error: failed to fetch realtime data: no data received from %s" ,
117+ try :
118+ url = STOP_URL .format (self ._stop_code )
119+ _LOGGER .debug (
120+ "%s: Fectching departures from %s" ,
119121 self ._stop_code ,
120- url ,
122+ url + "&indent=yes" ,
121123 )
122- return
123- unformatted_departures = json .loads (data )
124- return self ._format_departures (unformatted_departures )
124+ data = await get (url )
125+ if not data :
126+ _LOGGER .warning (
127+ "%s: Nysse API error: failed to fetch realtime data: no data received from %s" ,
128+ self ._stop_code ,
129+ url ,
130+ )
131+ return
132+ unformatted_departures = json .loads (data )
133+ return self ._format_departures (unformatted_departures )
134+ except OSError as err :
135+ _LOGGER .error ("%s: Failed to fetch realtime data: %s" , self ._stop_code , err )
136+ return []
125137
126138 def _format_departures (self , departures ):
127139 try :
@@ -158,12 +170,19 @@ def _format_departures(self, departures):
158170 err ,
159171 )
160172 return []
173+ except OSError as err :
174+ _LOGGER .info (
175+ "%s: failed to process realtime data: %s" ,
176+ self ._stop_code ,
177+ err ,
178+ )
179+ return []
161180
162181 async def async_update (self ) -> None :
163182 """Fetch new state data for the sensor."""
164- self ._last_update_time = dt_util .now ()
165-
166183 try :
184+ self ._last_update_time = dt_util .now ()
185+
167186 if len (self ._stops ) == 0 :
168187 _LOGGER .debug ("Getting stops" )
169188 self ._stops = await get_stops ()
@@ -203,36 +222,60 @@ async def async_update(self) -> None:
203222 _LOGGER .error ("%s: Failed to update sensor: %s" , self ._stop_code , err )
204223
205224 def _data_to_display_format (self , data ):
206- formatted_data = []
207- for item in data :
208- departure = {
209- "destination" : item ["trip_headsign" ],
210- "line" : item ["route_id" ],
211- "departure" : parser .parse (item ["departure_time" ]).strftime ("%H:%M" ),
212- "time_to_station" : self ._time_to_station (item ),
213- "icon" : self ._get_line_icon (item ["route_id" ]),
214- "realtime" : item ["realtime" ] if "realtime" in item else False ,
215- }
216- formatted_data .append (departure )
217- return sorted (formatted_data , key = lambda x : x ["time_to_station" ])
225+ try :
226+ formatted_data = []
227+ for item in data :
228+ departure = {
229+ "destination" : item ["trip_headsign" ],
230+ "line" : item ["route_id" ],
231+ "departure" : parser .parse (item ["departure_time" ]).strftime ("%H:%M" ),
232+ "time_to_station" : self ._time_to_station (item ),
233+ "icon" : self ._get_line_icon (item ["route_id" ]),
234+ "realtime" : item ["realtime" ] if "realtime" in item else False ,
235+ }
236+ formatted_data .append (departure )
237+ return sorted (formatted_data , key = lambda x : x ["time_to_station" ])
238+ except OSError as err :
239+ _LOGGER .debug ("%s: Failed to format data: %s" , self ._stop_code , err )
240+ return []
218241
219242 def _get_line_icon (self , line_no ):
220243 if line_no in TRAM_LINES :
221244 return "mdi:tram"
222245 return "mdi:bus"
223246
224247 def _time_to_station (self , item ):
225- departure_local = dt_util .as_local (parser .parse (item ["departure_time" ]))
226- if "delta_days" in item :
227- departure_local += timedelta (days = item ["delta_days" ])
228- next_departure_time = (departure_local - self ._last_update_time ).seconds
229- return int (next_departure_time / 60 )
248+ try :
249+ departure_local = dt_util .as_local (parser .parse (item ["departure_time" ]))
250+ if "delta_days" in item :
251+ departure_local += timedelta (days = item ["delta_days" ])
252+ next_departure_time = (departure_local - self ._last_update_time ).seconds
253+ return int (next_departure_time / 60 )
254+ except OSError as err :
255+ _LOGGER .debug (
256+ "%s: Failed to calculate time to station: %s" ,
257+ self ._stop_code ,
258+ err ,
259+ )
260+ return 0
230261
231262 def _get_stop_name (self , stop_id ):
232- return next (
233- (stop ["stop_name" ] for stop in self ._stops if stop ["stop_id" ] == stop_id ),
234- "unknown stop" ,
235- )
263+ try :
264+ return next (
265+ (
266+ stop ["stop_name" ]
267+ for stop in self ._stops
268+ if stop ["stop_id" ] == stop_id
269+ ),
270+ "unknown stop" ,
271+ )
272+ except (OSError , KeyError ) as err :
273+ _LOGGER .debug (
274+ "%s: Failed to get stop name: %s" ,
275+ self ._stop_code ,
276+ err ,
277+ )
278+ return "unknown stop"
236279
237280 @property
238281 def unique_id (self ) -> str :
@@ -278,8 +321,12 @@ def __init__(self) -> None:
278321 self ._empty_response_counter = 0
279322
280323 def _timestamp_to_local (self , timestamp ):
281- utc = dt_util .utc_from_timestamp (int (str (timestamp )[:10 ]))
282- return dt_util .as_local (utc )
324+ try :
325+ utc = dt_util .utc_from_timestamp (int (str (timestamp )[:10 ]))
326+ return dt_util .as_local (utc )
327+ except OSError as err :
328+ _LOGGER .error ("Failed to convert timestamp to local time: %s" , err )
329+ return ""
283330
284331 def _conditionally_clear_alerts (self ):
285332 # TODO: Individual alerts may never be removed
0 commit comments