-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
Use config subentries in nederlandse spoorwegen #139857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
558c139
096422c
3d32476
1a76c60
876d732
175e30c
fabf684
15d39f9
bc44366
02da1fb
356036a
0552582
f3bb12e
b4da277
78c4d4b
754873a
2943d8b
e5b119c
58e192f
a98d671
b567343
a03380e
10fa9a9
5be0477
2cfcb8a
ee1b308
29603c2
fb7fccf
71af37b
df500fa
a9b9860
e21b90d
9c93c3d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,38 @@ | ||
"""The nederlandse_spoorwegen component.""" | ||
|
||
import ns_api | ||
from ns_api import RequestParametersError | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import CONF_API_KEY, Platform | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.exceptions import ConfigEntryAuthFailed | ||
|
||
PLATFORMS = [Platform.SENSOR] | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up NS API as config entry.""" | ||
|
||
nsapi = ns_api.NSAPI(entry.data[CONF_API_KEY]) | ||
try: | ||
await hass.async_add_executor_job(nsapi.get_stations) | ||
except RequestParametersError as ex: | ||
raise ConfigEntryAuthFailed( | ||
"Could not instantiate the Nederlandse Spoorwegen API." | ||
) from ex | ||
entry.runtime_data = nsapi | ||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) | ||
entry.async_on_unload(entry.add_update_listener(update_listener)) | ||
|
||
return True | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Unload a config entry.""" | ||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) | ||
|
||
|
||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry): | ||
"""Handle update.""" | ||
await hass.config_entries.async_reload(entry.entry_id) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
"""Adds config flow for Nederlandse Spoorwegen.""" | ||
|
||
from typing import Any | ||
|
||
import ns_api | ||
from ns_api import RequestParametersError | ||
import voluptuous as vol | ||
|
||
from homeassistant.config_entries import ( | ||
ConfigEntry, | ||
ConfigFlow, | ||
ConfigFlowResult, | ||
ConfigSubentryFlow, | ||
SubentryFlowResult, | ||
) | ||
from homeassistant.const import CONF_API_KEY, CONF_NAME | ||
from homeassistant.core import HomeAssistant, callback | ||
from homeassistant.helpers.selector import TimeSelector | ||
|
||
from .const import ( | ||
CONF_STATION_FROM, | ||
CONF_STATION_TO, | ||
CONF_STATION_VIA, | ||
CONF_TIME, | ||
DOMAIN, | ||
) | ||
|
||
|
||
async def get_stations(hass: HomeAssistant, nsapi: ns_api.NSAPI): | ||
"""Return all available stations.""" | ||
return await hass.async_add_executor_job(nsapi.get_stations) | ||
|
||
|
||
class NederlandseSpoorwegenConfigFlow(ConfigFlow, domain=DOMAIN): | ||
"""Nederlandse Spoorwegen config flow.""" | ||
|
||
@classmethod | ||
@callback | ||
def async_get_supported_subentry_types( | ||
cls, config_entry: ConfigEntry | ||
) -> dict[str, type[ConfigSubentryFlow]]: | ||
"""Return subentries supported by this integration.""" | ||
return {"trip": NederlandseSpoorwegenSubentryFlowHandler} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are they trips or routes? Because I believe we set point A to point B, so in theory it's a route. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I opted to use the same term as used by the NS in the API. However, if you think route is more suitable I am okay with changing that. |
||
|
||
async def async_step_user( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> ConfigFlowResult: | ||
"""Handle a flow initialized by the user.""" | ||
errors: dict[str, str] = {} | ||
if user_input is not None: | ||
self._async_abort_entries_match({CONF_API_KEY: user_input[CONF_API_KEY]}) | ||
try: | ||
await get_stations(self.hass, ns_api.NSAPI(user_input[CONF_API_KEY])) | ||
except RequestParametersError: | ||
errors["base"] = "invalid_api_key" | ||
else: | ||
await self.async_set_unique_id(user_input[CONF_API_KEY]) | ||
self._abort_if_unique_id_configured() | ||
return self.async_create_entry( | ||
title="NS API", data={CONF_API_KEY: user_input[CONF_API_KEY]} | ||
) | ||
|
||
return self.async_show_form( | ||
step_id="user", | ||
data_schema=vol.Schema({vol.Required(CONF_API_KEY): str}), | ||
errors=errors, | ||
) | ||
|
||
|
||
class NederlandseSpoorwegenSubentryFlowHandler(ConfigSubentryFlow): | ||
"""Handle subentry flow for adding and modifying a location.""" | ||
|
||
def __init__(self) -> None: | ||
"""Initialize the config flow.""" | ||
self._stations: dict[str, str] = {} | ||
|
||
async def async_step_user( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> SubentryFlowResult: | ||
"""User flow to add a new train.""" | ||
|
||
if len(self._stations) == 0: | ||
nsapi = self.hass.config_entries.async_get_known_entry( | ||
self.handler[0] | ||
).runtime_data | ||
|
||
self._stations = { | ||
station.code: station.names["middle"] | ||
for station in await get_stations(self.hass, nsapi) | ||
if station.country == "NL" | ||
} | ||
|
||
self._stations = dict( | ||
sorted(self._stations.items(), key=lambda item: item[1]) | ||
) | ||
|
||
errors: dict[str, str] = {} | ||
|
||
if user_input is not None: | ||
title = user_input.get(CONF_NAME, None) | ||
|
||
if title is None: | ||
title = self._stations[user_input[CONF_STATION_FROM]] | ||
if CONF_STATION_VIA in user_input: | ||
title = title + " - " + self._stations[user_input[CONF_STATION_VIA]] | ||
title = title + " - " + self._stations[user_input[CONF_STATION_TO]] | ||
if CONF_TIME in user_input: | ||
title = title + " - " + user_input[CONF_TIME] | ||
|
||
return self.async_create_entry( | ||
title=title, | ||
data={ | ||
CONF_STATION_FROM: user_input[CONF_STATION_FROM], | ||
CONF_STATION_VIA: user_input.get(CONF_STATION_VIA, None), | ||
CONF_STATION_TO: user_input[CONF_STATION_TO], | ||
CONF_TIME: user_input.get(CONF_TIME, None), | ||
}, | ||
) | ||
return self.async_show_form( | ||
step_id="user", | ||
data_schema=vol.Schema( | ||
{ | ||
vol.Optional(CONF_NAME): str, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should allow the user to rename the config entry via HA. I don't believe that is supported right now, but that would be ideal. |
||
vol.Required(CONF_STATION_FROM): vol.In(self._stations), | ||
vol.Optional(CONF_STATION_VIA): vol.In(self._stations), | ||
vol.Required(CONF_STATION_TO): vol.In(self._stations), | ||
vol.Optional(CONF_TIME): TimeSelector(), | ||
} | ||
), | ||
errors=errors, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
"""Constants for Nederlandse Spoorwegen integration.""" | ||
|
||
CONF_STATION_FROM = "from" | ||
CONF_STATION_VIA = "via" | ||
CONF_STATION_TO = "to" | ||
CONF_TIME = "time" | ||
DOMAIN = "nederlandse_spoorwegen" | ||
|
||
MIN_TIME_BETWEEN_UPDATES = 120 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
{ | ||
BF40 "domain": "nederlandse_spoorwegen", | ||
"name": "Nederlandse Spoorwegen (NS)", | ||
"codeowners": ["@YarmoM"], | ||
"codeowners": ["@martreides"], | ||
"config_flow": true, | ||
"documentation": "https://www.home-assistant.io/integrations/nederlandse_spoorwegen", | ||
"iot_class": "cloud_polling", | ||
"quality_scale": "legacy", | ||
"requirements": ["nsapi==3.1.2"] | ||
"requirements": ["nsapi==3.1.2"], | ||
"single_config_entry": true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we enforce a single confi gentry? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't remember exactly. I think my reasoning was that it makes more sense to have a single entry since it uses sub entries. The risk is that users will still add multiple entries with the same API key. On the other side, if for some reason users want to use multiple API keys, that's of course fine as well. I am not sure what the best option is, what do you prefer? |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extend ConfigEntry with the type of
entry.runtime_data
and use that throughout the code