8000 Use config subentries in nederlandse spoorwegen by Martreides · Pull Request #139857 · home-assistant/core · GitHub
[go: up one dir, main page]

Skip to content

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

Draft
wants to merge 33 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
558c139
Initial commit.
Martreides Mar 4, 2025
096422c
Add type hints.
Martreides Mar 4, 2025
3d32476
Improve config flow.
Martreides Mar 4, 2025
1a76c60
Add time to config flow.
Martreides Mar 4, 2025
876d732
Fix searching for trains at specific time.
Martreides Mar 4, 2025
175e30c
Add quality scale.
Martreides Mar 5, 2025
fabf684
Change code owner.
Martreides Mar 5, 2025
15d39f9
Implement sub-entry.
Martreides Mar 5, 2025
bc44366
Fix sub-entry implementation.
Martreides Mar 5, 2025
02da1fb
Cleanup.
Martreides Mar 5, 2025
356036a
Remove unused code.
Martreides Mar 5, 2025
0552582
Use timedelta for polling throttle.
Martreides Mar 5, 2025
f3bb12e
Clean up.
Martreides Mar 6, 2025
b4da277
Change subentry type to trip.
Martreides Mar 8, 2025
78c4d4b
Move setting up API to init.
Martreides Mar 8, 2025
754873a
Use api key as unique ID.
Martreides Mar 8, 2025
2943d8b
Add possibility to reload integration.
Martreides Mar 8, 2025
e5b119c
Fix not updating after adding sub entry.
Martreides Mar 8, 2025
58e192f
Improve subentry config flow..
Martreides Mar 8, 2025
a98d671
Minor cleanup.
Martreides Mar 11, 2025
b567343
Minor cleanup.
Martreides Mar 16, 2025
a03380e
Update sensor.py
Martreides Apr 25, 2025
10fa9a9
Add ignore type for mypy
Martreides Apr 25, 2025
5be0477
Ruff change
Martreides Apr 25, 2025
2cfcb8a
Remove commented code.
Martreides Jun 1, 2025
ee1b308
Remove legacy quality scale.
Martreides Jun 1, 2025
29603c2
Remove initialization of self.hass
Martreides Jun 19, 2025
fb7fccf
Remove not needed else
Martreides Jun 19, 2025
71af37b
Use async_add_executor_job for using the api.
Martreides Jun 19, 2025
df500fa
Move code out of try block.
Martreides Jun 19, 2025
a9b9860
Remove quality scale from PR.
Martreides Jun 19, 2025
e21b90d
Remove data class.
Martreides Jun 19, 2025
9c93c3d
Do not use api key as unique id.
Martreides Jun 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions homeassistant/components/nederlandse_spoorwegen/__init__.py
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:
Copy link
Member

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

"""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)
131 changes: 131 additions & 0 deletions homeassistant/components/nederlandse_spoorwegen/config_flow.py
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}
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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,
Copy link
Member

Choose a reason for hiding this comment

The 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,
)
9 changes: 9 additions & 0 deletions homeassistant/components/nederlandse_spoorwegen/const.py
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
7 changes: 4 additions & 3 deletions homeassistant/components/nederlandse_spoorwegen/manifest.json
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we enforce a single confi gentry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

}
Loading
Loading
0