Skip to content

Commit

Permalink
Fixed bugs relating to sensor updating
Browse files Browse the repository at this point in the history
  • Loading branch information
warrior25 committed Nov 11, 2022
1 parent d8bc81d commit 8b791a0
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 69 deletions.
5 changes: 2 additions & 3 deletions custom_components/nysse/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from http.client import CannotSendRequest
from typing import Any, Optional

from homeassistant import config_entries
from .fetch_stop_points import fetch_stop_points
import homeassistant.helpers.config_validation as cv
Expand Down Expand Up @@ -37,12 +35,13 @@ async def async_step_user(self, user_input: Optional[dict[str, Any]] = None):
"max": user_input[CONF_MAX],
}
)

# If user ticked the box show this form again so they can add an
# additional station.

if user_input.get("add_another", True):
return await self.async_step_user()

# Show "Many stations" as the configuration name if it contains multiple stations
if len(self.data[CONF_STOPS]) > 1:
return self.async_create_entry(title="Many stations", data=self.data)

Expand Down
1 change: 1 addition & 0 deletions custom_components/nysse/fetch_stop_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


async def fetch_stop_points(has_id):
"""Fetches stop point names """
stations = {}
if len(stations) == 0:
try:
Expand Down
85 changes: 45 additions & 40 deletions custom_components/nysse/nysse_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,73 +9,81 @@

class NysseData:
def __init__(self):
self._arrival_data = []
self._journey_data = []
# Last update timestamp for the sensor
self._last_update = None
self._api_json = []
self._station_name = ""
self._station = ""

# Variable to store all fetched data
self._json_data = []

self._station_id = ""
self._stops = []

def populate(self, arrival_data, journey_data, station_no, stop_points):
self._station = station_no
self._stops = stop_points
def populate(self, departures, journeys, station_id, stops, max_items):
"""Collect sensor data to corresponding variables."""
departures2 = []
self._station_id = station_id
self._stops = stops
self._last_update = datetime.now().astimezone(LOCAL)

if self._station in arrival_data["body"]:
self._arrival_data = arrival_data["body"][self._station]
if self._station_id in departures["body"]:
departures2 = departures["body"][self._station_id]

self._journey_data = journey_data
# Store realtime arrival data
self._json_data = departures2[:max_items]

# Append static timetable data if not enough realtime data
if len(self._json_data) < max_items:
for i in range(len(self._json_data), max_items):
if len(journeys) > i:
self._json_data.append(journeys[i])

def remove_stale_data(self):
for item in self._api_json:
"""Remove old or unavailable departures."""
for item in self._json_data:
if self.get_departure_time(item, True) == "unavailable":
_LOGGER.info("Removing unavailable departures")
self._api_json.remove(item)
self._json_data.remove(item)

elif (self.get_departure_time(item, False)) < datetime.now().astimezone(LOCAL):
_LOGGER.info("Removing stale departures")
self._api_json.remove(item)

def sort_data(self, max_items):
self._api_json = self._arrival_data[:max_items]
#_LOGGER.warning("self._journey_data:\n %s", self._journey_data)
if len(self._api_json) < max_items:
for i in range(len(self._api_json), max_items):
self._api_json.append(self._journey_data[i])
#_LOGGER.info("self._api_json:\n %s", self._api_json)
self._json_data.remove(item)

def get_state(self):
if len(self._api_json) > 0:
depart_time = self.get_departure_time(self._api_json[0], True)
"""Get next departure time as the sensor state."""
if len(self._json_data) > 0:
depart_time = self.get_departure_time(self._json_data[0], True)
if depart_time != "unavailable":
return parser.parse(depart_time).strftime("%H:%M")

def is_empty(self):
return len(self._api_json) == 0

def get_departures(self):
"""Format departure data to show in sensor attributes."""
departures = []
for item in self._api_json:
for item in self._json_data:
departure = {
"destination": self.get_destination(item),
"destination": self.get_destination_name(item),
"line": item["lineRef"],
"departure": self.get_departure_time(item, True),
"time_to_station": self.time_to_station(item, False, "{0}"),
"time_to_station": self.time_to_station(item),
"icon": self.get_line_icon(item["lineRef"]),
"realtime": self.is_realtime(item),
}

# Append only valid departures
if departure["time_to_station"] != "unavailable":
departures.append(departure)

# Sort departures according to their departure times
departures = sorted(departures, key=lambda d: d['time_to_station'])
return departures

def is_realtime(self, item):
"""Check if departure data is from realtime data source"""
if "non-realtime" in item:
return False
return True

def get_departure_time(self, item, stringify):
"""Get departure time from json data"""
if "expectedArrivalTime" in item["call"]:
parsed = parser.parse(item["call"]["expectedArrivalTime"])
if stringify:
Expand All @@ -97,27 +105,24 @@ def get_line_icon(self, line_no):
return "mdi:bus"

def get_station_name(self):
if len(self._station_name) == 0:
self._station_name = self._stops[self._station]
return self._station_name
return self._stops[self._station_id]

def get_last_update(self):
return self._last_update

def get_destination(self, entry):
def get_destination_name(self, entry):
if "destinationShortName" in entry:
return self._stops[entry["destinationShortName"]]
return ""
return "unavailable"

def time_to_station(self, entry, with_destination=True, style="{0}m {1}s"):
def time_to_station(self, entry):
"""Get time until departure in minutes"""
time = self.get_departure_time(entry, True)
if time != "unavailable":
# Convert departure time to UTC
naive = parser.parse(self.get_departure_time(entry, True)).replace(tzinfo=None)
local_dt = LOCAL.localize(naive, is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc)
next_departure_time = (utc_dt - datetime.now().astimezone(pytz.utc)).seconds
next_departure_dest = self.get_destination(entry)
return int(style.format(
int(next_departure_time / 60), int(next_departure_time % 60)
) + (" to " + next_departure_dest if with_destination else ""))
return int(next_departure_time / 60)
return "unavailable"
55 changes: 29 additions & 26 deletions custom_components/nysse/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def __init__(self, name, station, maximum):
self.station_no = station
self.max_items = int(maximum)

self._station_name = ""
self._state = None
self._destination = ""
self._nysse_data = NysseData()
Expand All @@ -117,6 +118,7 @@ def __init__(self, name, station, maximum):
self._journeys = []
self._journeys_modified = []
self._journeys_date = datetime.now().astimezone(LOCAL).strftime("%A")
self._skip_journey_lookup = False

def modify_journey_data(self, journeys, next_day):
journeys_data = []
Expand Down Expand Up @@ -160,8 +162,8 @@ def unique_id(self):

@property
def name(self) -> str:
station = self._nysse_data.get_station_name()
return "{0} - {1} ({2})".format(self._platformname, station, self.station_no)
self._station_name = self._nysse_data.get_station_name()
return "{0} - {1} ({2})".format(self._platformname, self._station_name, self.station_no)

#@property
#def icon(self):
Expand All @@ -178,42 +180,46 @@ async def async_update(self):
This is the only method that should fetch new data for Home Assistant.
"""
if len(self._stops) == 0:
_LOGGER.info("Fectching stop points")
_LOGGER.info("Fectching stops")
self._stops = await fetch_stop_points(False)

journeys_index = 0

arrival_url = NYSSE_STOP_URL.format(self.station_no)

try:
_LOGGER.info("Fectching arrivals from %s", arrival_url)
arrivals = await request(arrival_url)
_LOGGER.info("%s: Fectching departures from %s", self.station_no, arrival_url)
departures = await request(arrival_url)

if not arrivals:
_LOGGER.warning("Can't fetch arrivals. Incorrect response from %s", arrival_url)
arrivals = []
if not departures:
_LOGGER.warning("Can't fetch departures for station %s. Incorrect response from %s", self.station_no, arrival_url)
departures = []
else:
arrivals = json.loads(arrivals)
departures = json.loads(departures)

next_day = self._journeys_date != datetime.now().astimezone(LOCAL).strftime("%A")
if self._journeys_date == datetime.now().astimezone(LOCAL).strftime("%A"):
next_day = False
self._skip_journey_lookup = False

self.remove_stale_journeys()

if len(self._journeys_modified) < self.max_items + len(arrivals):
_LOGGER.info("Not enough timetable data")
if (len(self._journeys_modified) < self.max_items) and (not self._skip_journey_lookup):
self._journeys_modified.clear()
self._journeys.clear()

while True:
if self._skip_journey_lookup:
break

journeys_url = NYSSE_JOURNEYS_URL.format(
self.station_no, self._journeys_date, journeys_index
)

_LOGGER.info("Fetching timetable data for %s, from %s", self._journeys_date, journeys_url)
_LOGGER.info("%s: Fetching timetable data from %s", self.station_no, journeys_url)
journeys_data = await request(journeys_url)

if not journeys_data:
_LOGGER.error("Can't fetch timetables. Incorrect response from %s", journeys_url)
_LOGGER.error("%s: Can't fetch timetables. Incorrect response from %s", self.station_no, journeys_url)
return

journeys_data_json = json.loads(journeys_data)
Expand All @@ -227,15 +233,15 @@ async def async_update(self):
modified_journey_data = self.modify_journey_data(
self._journeys, next_day
)

for journey in modified_journey_data:
self._journeys_modified.append(journey)

if len(self._journeys_modified) < self.max_items:
if next_day:
_LOGGER.warning("Not enough timetable data was found. Reduce the amount of requested departures")
return
_LOGGER.info("Not enough timetable data remaining for today")
self._skip_journey_lookup = True
_LOGGER.info("%s: Not enough timetable data was found. Aborting further timetable lookup until the next day", self.station_no)
break
_LOGGER.info("%s: Not enough timetable data remaining today", self.station_no)
self._journeys.clear()
journeys_index = 0
self._journeys_date = (
Expand All @@ -252,10 +258,9 @@ async def async_update(self):
self._nysse_data.remove_stale_data()

self._nysse_data.populate(
arrivals, self._journeys_modified, self.station_no, self._stops
departures, self._journeys_modified, self.station_no, self._stops, self.max_items
)

self._nysse_data.sort_data(self.max_items)
self._state = self._nysse_data.get_state()
self._departures = self._nysse_data.get_departures()

Expand All @@ -264,11 +269,9 @@ def extra_state_attributes(self):
attributes = {}
attributes["last_refresh"] = self._nysse_data.get_last_update()

if self._nysse_data.is_empty():
return attributes

attributes["departures"] = self._departures
self._destination = self._departures[0]["destination"]
attributes["station_name"] = self._nysse_data.get_station_name()
if len(self._departures) != 0:
attributes["departures"] = self._departures
self._destination = self._departures[0]["destination"]
attributes["station_name"] = self._station_name

return attributes

0 comments on commit 8b791a0

Please sign in to comment.