Fix Nederlandse Spoorwegen to ignore trains in the past (#138331)

* Update NS integration to show first next train instead of just the first.

* Handle no first or next trip.

* Remove debug statement.

* Remove seconds and revert back to minutes.

* Make use of dt_util.now().

* Fix issue with next train if no first train.
This commit is contained in:
Martreides 2025-03-02 14:32:10 +01:00 committed by GitHub
parent 0694f9e164
commit b7bedd4b8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -20,7 +20,7 @@ from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import Throttle from homeassistant.util import Throttle, dt as dt_util
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -119,6 +119,8 @@ class NSDepartureSensor(SensorEntity):
self._time = time self._time = time
self._state = None self._state = None
self._trips = None self._trips = None
self._first_trip = None
self._next_trip = None
@property @property
def name(self): def name(self):
@ -133,44 +135,44 @@ class NSDepartureSensor(SensorEntity):
@property @property
def extra_state_attributes(self): def extra_state_attributes(self):
"""Return the state attributes.""" """Return the state attributes."""
if not self._trips: if not self._trips or self._first_trip is None:
return None return None
if self._trips[0].trip_parts: if self._first_trip.trip_parts:
route = [self._trips[0].departure] route = [self._first_trip.departure]
route.extend(k.destination for k in self._trips[0].trip_parts) route.extend(k.destination for k in self._first_trip.trip_parts)
# Static attributes # Static attributes
attributes = { attributes = {
"going": self._trips[0].going, "going": self._first_trip.going,
"departure_time_planned": None, "departure_time_planned": None,
"departure_time_actual": None, "departure_time_actual": None,
"departure_delay": False, "departure_delay": False,
"departure_platform_planned": self._trips[0].departure_platform_planned, "departure_platform_planned": self._first_trip.departure_platform_planned,
"departure_platform_actual": self._trips[0].departure_platform_actual, "departure_platform_actual": self._first_trip.departure_platform_actual,
"arrival_time_planned": None, "arrival_time_planned": None,
"arrival_time_actual": None, "arrival_time_actual": None,
"arrival_delay": False, "arrival_delay": False,
"arrival_platform_planned": self._trips[0].arrival_platform_planned, "arrival_platform_planned": self._first_trip.arrival_platform_planned,
"arrival_platform_actual": self._trips[0].arrival_platform_actual, "arrival_platform_actual": self._first_trip.arrival_platform_actual,
"next": None, "next": None,
"status": self._trips[0].status.lower(), "status": self._first_trip.status.lower(),
"transfers": self._trips[0].nr_transfers, "transfers": self._first_trip.nr_transfers,
"route": route, "route": route,
"remarks": None, "remarks": None,
} }
# Planned departure attributes # Planned departure attributes
if self._trips[0].departure_time_planned is not None: if self._first_trip.departure_time_planned is not None:
attributes["departure_time_planned"] = self._trips[ attributes["departure_time_planned"] = (
0 self._first_trip.departure_time_planned.strftime("%H:%M")
].departure_time_planned.strftime("%H:%M") )
# Actual departure attributes # Actual departure attributes
if self._trips[0].departure_time_actual is not None: if self._first_trip.departure_time_actual is not None:
attributes["departure_time_actual"] = self._trips[ attributes["departure_time_actual"] = (
0 self._first_trip.departure_time_actual.strftime("%H:%M")
].departure_time_actual.strftime("%H:%M") )
# Delay departure attributes # Delay departure attributes
if ( if (
@ -182,16 +184,16 @@ class NSDepartureSensor(SensorEntity):
attributes["departure_delay"] = True attributes["departure_delay"] = True
# Planned arrival attributes # Planned arrival attributes
if self._trips[0].arrival_time_planned is not None: if self._first_trip.arrival_time_planned is not None:
attributes["arrival_time_planned"] = self._trips[ attributes["arrival_time_planned"] = (
0 self._first_trip.arrival_time_planned.strftime("%H:%M")
].arrival_time_planned.strftime("%H:%M") )
# Actual arrival attributes # Actual arrival attributes
if self._trips[0].arrival_time_actual is not None: if self._first_trip.arrival_time_actual is not None:
attributes["arrival_time_actual"] = self._trips[ attributes["arrival_time_actual"] = (
0 self._first_trip.arrival_time_actual.strftime("%H:%M")
].arrival_time_actual.strftime("%H:%M") )
# Delay arrival attributes # Delay arrival attributes
if ( if (
@ -202,15 +204,14 @@ class NSDepartureSensor(SensorEntity):
attributes["arrival_delay"] = True attributes["arrival_delay"] = True
# Next attributes # Next attributes
if len(self._trips) > 1: if self._next_trip.departure_time_actual is not None:
if self._trips[1].departure_time_actual is not None: attributes["next"] = self._next_trip.departure_time_actual.strftime("%H:%M")
attributes["next"] = self._trips[1].departure_time_actual.strftime( elif self._next_trip.departure_time_planned is not None:
"%H:%M" attributes["next"] = self._next_trip.departure_time_planned.strftime(
) "%H:%M"
elif self._trips[1].departure_time_planned is not None: )
attributes["next"] = self._trips[1].departure_time_planned.strftime( else:
"%H:%M" attributes["next"] = None
)
return attributes return attributes
@ -225,6 +226,7 @@ class NSDepartureSensor(SensorEntity):
): ):
self._state = None self._state = None
self._trips = None self._trips = None
self._first_trip = None
return return
# Set the search parameter to search from a specific trip time # Set the search parameter to search from a specific trip time
@ -236,19 +238,51 @@ class NSDepartureSensor(SensorEntity):
.strftime("%d-%m-%Y %H:%M") .strftime("%d-%m-%Y %H:%M")
) )
else: else:
trip_time = datetime.now().strftime("%d-%m-%Y %H:%M") trip_time = dt_util.now().strftime("%d-%m-%Y %H:%M")
try: try:
self._trips = self._nsapi.get_trips( self._trips = self._nsapi.get_trips(
trip_time, self._departure, self._via, self._heading, True, 0, 2 trip_time, self._departure, self._via, self._heading, True, 0, 2
) )
if self._trips: if self._trips:
if self._trips[0].departure_time_actual is None: all_times = []
planned_time = self._trips[0].departure_time_planned
self._state = planned_time.strftime("%H:%M") # If a train is delayed we can observe this through departure_time_actual.
for trip in self._trips:
if trip.departure_time_actual is None:
all_times.append(trip.departure_time_planned)
else:
all_times.append(trip.departure_time_actual)
# Remove all trains that already left.
filtered_times = [
(i, time)
for i, time in enumerate(all_times)
if time > dt_util.now()
]
if len(filtered_times) > 0:
sorted_times = sorted(filtered_times, key=lambda x: x[1])
self._first_trip = self._trips[sorted_times[0][0]]
self._state = sorted_times[0][1].strftime("%H:%M")
# Filter again to remove trains that leave at the exact same time.
filtered_times = [
(i, time)
for i, time in enumerate(all_times)
if time > sorted_times[0][1]
]
if len(filtered_times) > 0:
sorted_times = sorted(filtered_times, key=lambda x: x[1])
self._next_trip = self._trips[sorted_times[0][0]]
else:
self._next_trip = None
else: else:
actual_time = self._trips[0].departure_time_actual self._first_trip = None
self._state = actual_time.strftime("%H:%M") self._state = None
except ( except (
requests.exceptions.ConnectionError, requests.exceptions.ConnectionError,
requests.exceptions.HTTPError, requests.exceptions.HTTPError,