Skip to content

Commit

Permalink
Align for realtime
Browse files Browse the repository at this point in the history
  • Loading branch information
vingerha committed Nov 11, 2023
1 parent 1ada7a9 commit aaa00ac
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 122 deletions.
30 changes: 23 additions & 7 deletions custom_components/gtfs2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from datetime import timedelta

from .const import DOMAIN, PLATFORMS, DEFAULT_PATH, DEFAULT_REFRESH_INTERVAL
from .coordinator import GTFSUpdateCoordinator, GTFSRealtimeUpdateCoordinator
from .coordinator import GTFSUpdateCoordinator
import voluptuous as vol
from .gtfs_helper import get_gtfs

Expand All @@ -20,12 +20,25 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry) -> bool:

if config_entry.version == 1:

new = {**config_entry.data}
new['extract_from'] = 'url'
new.pop('refresh_interval')

config_entry.version = 2
new_data = {**config_entry.data}
new_data['extract_from'] = 'url'
new_data.pop('refresh_interval')

new_options = {**config_entry.options}
new_options['real_time'] = False
new_options['refresh_interval'] = 15

config_entry.version = 3
hass.config_entries.async_update_entry(config_entry, data=new)
hass.config_entries.async_update_entry(config_entry, options=new_options)

if config_entry.version == 2:

new_options = {**config_entry.options}
new_options['real_time'] = False

config_entry.version = 3
hass.config_entries.async_update_entry(config_entry, options=new_options)

_LOGGER.debug("Migration to version %s successful", config_entry.version)

Expand All @@ -40,6 +53,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

#await coordinator.async_config_entry_first_refresh()

if not coordinator.last_update_success:
raise ConfigEntryNotReady

hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator,
}
Expand Down Expand Up @@ -74,6 +90,6 @@ def update_gtfs(call):

async def update_listener(hass: HomeAssistant, entry: ConfigEntry):
"""Handle options update."""
hass.data[DOMAIN][entry.entry_id]['coordinator'].update_interval = timedelta(minutes=entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL))
hass.data[DOMAIN][entry.entry_id]['coordinator'].update_interval = timedelta(minutes=1)

return True
5 changes: 2 additions & 3 deletions custom_components/gtfs2/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for GTFS."""

VERSION = 2
VERSION = 3

def __init__(self) -> None:
"""Init ConfigFlow."""
Expand Down Expand Up @@ -246,7 +246,6 @@ async def async_step_init(
) -> FlowResult:
"""Manage the options."""
if user_input is not None:
user_input['real_time'] = False
if user_input['real_time']:
self._user_inputs.update(user_input)
_LOGGER.debug(f"GTFS Options with realtime: {self._user_inputs}")
Expand All @@ -261,7 +260,7 @@ async def async_step_init(
data_schema=vol.Schema(
{
vol.Optional("refresh_interval", default=self.config_entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL)): int,
# vol.Required("real_time"): vol.In({False: "No", True: "Yes"}),
vol.Required("real_time"): vol.In({False: "No", True: "Yes"}),
}
),
)
Expand Down
141 changes: 67 additions & 74 deletions custom_components/gtfs2/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from datetime import timedelta
import logging


from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
import homeassistant.util.dt as dt_util

from .const import DEFAULT_PATH, DEFAULT_REFRESH_INTERVAL
from .gtfs_helper import get_gtfs, get_next_departure, check_datasource_index
Expand All @@ -26,7 +28,7 @@ def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
hass=hass,
logger=_LOGGER,
name=entry.entry_id,
update_interval=timedelta(minutes=entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL)),
update_interval=timedelta(minutes=1),
)
self.config_entry = entry
self.hass = hass
Expand All @@ -35,87 +37,78 @@ def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
self._data: dict[str, str] = {}

async def _async_update_data(self) -> dict[str, str]:
"""Update."""
"""Get the latest data from GTFS and GTFS relatime, depending refresh interval"""
data = self.config_entry.data
options = self.config_entry.options
self._pygtfs = get_gtfs(
self.hass, DEFAULT_PATH, data, False
)
self._data = {
"schedule": self._pygtfs,
"origin": data["origin"].split(": ")[0],
"destination": data["destination"].split(": ")[0],
"offset": data["offset"],
"include_tomorrow": data["include_tomorrow"],
"gtfs_dir": DEFAULT_PATH,
"name": data["name"],
}
previous_data = None if self.data is None else self.data.copy()
_LOGGER.debug("Previous data: %s", previous_data)

check_index = await self.hass.async_add_executor_job(
check_datasource_index, self._pygtfs
)

try:
self._data["next_departure"] = await self.hass.async_add_executor_job(
get_next_departure, self
)
except Exception as ex: # pylint: disable=broad-except
_LOGGER.error("Error getting gtfs data from generic helper: %s", ex)
_LOGGER.debug("GTFS coordinator data from helper: %s", self._data["next_departure"])
return self._data
if previous_data is not None and (previous_data["next_departure"]["gtfs_updated_at"] + timedelta(minutes=options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL))) > dt_util.now().replace(tzinfo=None):
_LOGGER.debug("Do nothing")
self._data = previous_data

if previous_data is None or (previous_data["next_departure"]["gtfs_updated_at"] + timedelta(minutes=options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL))) < dt_util.now().replace(tzinfo=None):
self._data = {
"schedule": self._pygtfs,
"origin": data["origin"].split(": ")[0],
"destination": data["destination"].split(": ")[0],
"offset": data["offset"],
"include_tomorrow": data["include_tomorrow"],
"gtfs_dir": DEFAULT_PATH,
"name": data["name"],
}

class GTFSRealtimeUpdateCoordinator(DataUpdateCoordinator):
"""Data update coordinator for the GTFSRT integration."""

config_entry: ConfigEntry


def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Initialize the coordinator."""
_LOGGER.debug("GTFS RT: coordinator init")
super().__init__(
hass=hass,
logger=_LOGGER,
name=entry.entry_id,
update_interval=timedelta(minutes=entry.options.get("refresh_interval_rt", DEFAULT_REFRESH_INTERVAL_RT)),
)
self.config_entry = entry
self.hass = hass
self._data: dict[str, str] = {}

async def _async_update_data(self) -> dict[str, str]:
"""Update."""
data = self.config_entry.data
options = self.config_entry.options
_LOGGER.debug("GTFS RT: coordinator async_update_data: %s", data)
_LOGGER.debug("GTFS RT: coordinator async_update_data options: %s", options)
#add real_time if setup

check_index = await self.hass.async_add_executor_job(
check_datasource_index, self._pygtfs
)

try:
self._data["next_departure"] = await self.hass.async_add_executor_job(
get_next_departure, self
)
except Exception as ex: # pylint: disable=broad-except
_LOGGER.error("Error getting gtfs data from generic helper: %s", ex)
return None
_LOGGER.debug("GTFS coordinator data from helper: %s", self._data["next_departure"])


if "real_time" in options:

"""Initialize the info object."""
self._trip_update_url = options["trip_update_url"]
self._vehicle_position_url = options["vehicle_position_url"]
self._route_delimiter = "-"
# if options["CONF_API_KEY"] is not None:
# self._headers = {"Authorization": options["CONF_API_KEY"]}
# elif options["CONF_X_API_KEY"] is not None:
# self._headers = {"x-api-key": options["CONF_X_API_KEY"]}
# else:
# self._headers = None
self._headers = None
self.info = {}
self._route_id = data["route"].split(": ")[0]
self._stop_id = data["origin"].split(": ")[0]
self._direction = data["direction"]
self._relative = False
#_LOGGER.debug("GTFS RT: Realtime data: %s", self._data)
self._data = await self.hass.async_add_executor_job(get_rt_route_statuses, self)
self._get_next_service = await self.hass.async_add_executor_job(get_next_services, self)
_LOGGER.debug("GTFS RT: Realtime next service: %s", self._get_next_service)
if options["real_time"]:

"""Initialize the info object."""
self._trip_update_url = options["trip_update_url"]
self._vehicle_position_url = options["vehicle_position_url"]
self._route_delimiter = "-"
# if options["CONF_API_KEY"] is not None:
# self._headers = {"Authorization": options["CONF_API_KEY"]}
# elif options["CONF_X_API_KEY"] is not None:
# self._headers = {"x-api-key": options["CONF_X_API_KEY"]}
# else:
# self._headers = None
self._headers = None
self.info = {}
self._route_id = self._data["next_departure"]["route_id"]
self._stop_id = data["origin"].split(": ")[0]
self._direction = data["direction"]
self._relative = False
#_LOGGER.debug("GTFS RT: Realtime data: %s", self._data)
try:
self._get_rt_route_statuses = await self.hass.async_add_executor_job(get_rt_route_statuses, self)
self._get_next_service = await self.hass.async_add_executor_job(get_next_services, self)
except Exception as ex: # pylint: disable=broad-except
_LOGGER.error("Error getting gtfs realtime data: %s", ex)
self._get_next_service = "error"
else:
_LOGGER.info("GTFS RT: RealTime = false, selected in entity options")
self._get_next_service = "n.a."
else:
_LOGGER.error("GTFS RT: Issue with entity options")
return "---"
_LOGGER.debug("GTFS RT: RealTime not selected in entity options")
self._get_next_service = "n.a."
self._data["next_departure"]["next_departure_realtime"] = self._get_next_service
self._data["next_departure"]["gtfs_rt_updated_at"] = dt_util.now().replace(tzinfo=None)

return self._data

return self._get_next_service
1 change: 1 addition & 0 deletions custom_components/gtfs2/gtfs_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ def get_next_departure(self):
"next_departures": timetable_remaining,
"next_departures_lines": timetable_remaining_line,
"next_departures_headsign": timetable_remaining_headsign,
"gtfs_updated_at": dt_util.now().replace(tzinfo=None),
}


Expand Down
2 changes: 1 addition & 1 deletion custom_components/gtfs2/gtfs_rt_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def get_gtfs_feed_entities(url: str, headers, label: str):
## reworked for gtfs2

def get_next_services(self):
self.data = self._data
self.data = self._get_rt_route_statuses
self._stop = self._stop_id
self._route = self._route_id
self._direction = self._direction
Expand Down
49 changes: 12 additions & 37 deletions custom_components/gtfs2/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
from homeassistant.util import slugify
import homeassistant.util.dt as dt_util

from .coordinator import GTFSRealtimeUpdateCoordinator

from .const import (
ATTR_ARRIVAL,
ATTR_BICYCLE,
Expand Down Expand Up @@ -381,8 +379,18 @@ def _update_attrs(self): # noqa: C901 PLR0911
self._attributes["next_departures_headsign"] = self._departure[
"next_departures_headsign"
][:10]

self._attributes["updated_at"] = dt_util.now().replace(tzinfo=None)

# Add next departure realtime
self._attributes["next_departure_realtime"] = self._departure[
"next_departure_realtime"
]
self._attributes["gtfs_rt_updated_at"] = self._departure[
"gtfs_rt_updated_at"
]

self._attributes["gtfs_updated_at"] = self._departure[
"gtfs_updated_at"
]
self._attr_extra_state_attributes = self._attributes
return self._attr_extra_state_attributes

Expand Down Expand Up @@ -412,36 +420,3 @@ def remove_keys(self, prefix: str) -> None:
}


class GTFSRealtimeDepartureSensor(CoordinatorEntity):
"""Implementation of a GTFS departure sensor."""

def __init__(self, coordinator: GTFSRealtimeUpdateCoordinator) -> None:
"""Initialize the GTFSsensor."""
super().__init__(coordinator)
self._name = coordinator.data["name"] + "_rt"
self._attributes: dict[str, Any] = {}

self._attr_unique_id = f"gtfs-{self._name}_rt"
self._attr_device_info = DeviceInfo(
name=f"GTFS - {self._name}",
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, f"GTFS - {self._name}_rt")},
manufacturer="GTFS",
model=self._name,
)
_LOGGER.debug("GTFS RT Sensor: coordinator data: %s", coordinator.data )
self._coordinator = coordinator
self._attributes = self._update_attrs_rt()
self._attr_extra_state_attributes = self._attributes

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._update_attrs_rt()
super()._handle_coordinator_update()

def _update_attrs_rt(self): # noqa: C901 PLR0911
_LOGGER.debug(f"GTFS RT Sensor update attr DATA: {self._coordinator.data}")
self._attr_native_value = coordinator.data
self._attributes["next_departure_realtime"] = self._coordinator.data
return self._attributes

0 comments on commit aaa00ac

Please sign in to comment.