Add reconfigure step for here_travel_time (#114667)
* Add reconfigure step for here_travel_time * Add comments, reuse step_user, TYPE_CHECKING, remove defaults
This commit is contained in:
parent
e64e3c2778
commit
cddb3bb668
3 changed files with 235 additions and 53 deletions
|
@ -3,7 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from here_routing import (
|
||||
HERERoutingApi,
|
||||
|
@ -104,6 +104,8 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
def __init__(self) -> None:
|
||||
"""Init Config Flow."""
|
||||
self._config: dict[str, Any] = {}
|
||||
self._entry: ConfigEntry | None = None
|
||||
self._is_reconfigure_flow: bool = False
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
|
@ -119,21 +121,36 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
user_input = user_input or {}
|
||||
if user_input:
|
||||
try:
|
||||
await async_validate_api_key(user_input[CONF_API_KEY])
|
||||
except HERERoutingUnauthorizedError:
|
||||
errors["base"] = "invalid_auth"
|
||||
except (HERERoutingError, HERETransitError):
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
if not errors:
|
||||
self._config = user_input
|
||||
return await self.async_step_origin_menu()
|
||||
if not self._is_reconfigure_flow: # Always show form first for reconfiguration
|
||||
if user_input:
|
||||
try:
|
||||
await async_validate_api_key(user_input[CONF_API_KEY])
|
||||
except HERERoutingUnauthorizedError:
|
||||
errors["base"] = "invalid_auth"
|
||||
except (HERERoutingError, HERETransitError):
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
if not errors:
|
||||
self._config[CONF_NAME] = user_input[CONF_NAME]
|
||||
self._config[CONF_API_KEY] = user_input[CONF_API_KEY]
|
||||
self._config[CONF_MODE] = user_input[CONF_MODE]
|
||||
return await self.async_step_origin_menu()
|
||||
self._is_reconfigure_flow = False
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=get_user_step_schema(user_input), errors=errors
|
||||
)
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self, _: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle reconfiguration."""
|
||||
self._is_reconfigure_flow = True
|
||||
self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
|
||||
if TYPE_CHECKING:
|
||||
assert self._entry
|
||||
self._config = self._entry.data.copy()
|
||||
return await self.async_step_user(self._config)
|
||||
|
||||
async def async_step_origin_menu(self, _: None = None) -> ConfigFlowResult:
|
||||
"""Show the origin menu."""
|
||||
return self.async_show_menu(
|
||||
|
@ -150,37 +167,57 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
self._config[CONF_ORIGIN_LONGITUDE] = user_input[CONF_ORIGIN][
|
||||
CONF_LONGITUDE
|
||||
]
|
||||
# Remove possible previous configuration using an entity_id
|
||||
self._config.pop(CONF_ORIGIN_ENTITY_ID, None)
|
||||
return await self.async_step_destination_menu()
|
||||
schema = vol.Schema(
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_ORIGIN,
|
||||
): LocationSelector()
|
||||
}
|
||||
),
|
||||
{
|
||||
vol.Required(
|
||||
CONF_ORIGIN,
|
||||
default={
|
||||
CONF_LATITUDE: self.hass.config.latitude,
|
||||
CONF_LONGITUDE: self.hass.config.longitude,
|
||||
},
|
||||
): LocationSelector()
|
||||
}
|
||||
CONF_ORIGIN: {
|
||||
CONF_LATITUDE: self._config.get(CONF_ORIGIN_LATITUDE)
|
||||
or self.hass.config.latitude,
|
||||
CONF_LONGITUDE: self._config.get(CONF_ORIGIN_LONGITUDE)
|
||||
or self.hass.config.longitude,
|
||||
}
|
||||
},
|
||||
)
|
||||
return self.async_show_form(step_id="origin_coordinates", data_schema=schema)
|
||||
|
||||
async def async_step_destination_menu(self, _: None = None) -> ConfigFlowResult:
|
||||
"""Show the destination menu."""
|
||||
return self.async_show_menu(
|
||||
step_id="destination_menu",
|
||||
menu_options=["destination_coordinates", "destination_entity"],
|
||||
)
|
||||
|
||||
async def async_step_origin_entity(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Configure origin by using an entity."""
|
||||
if user_input is not None:
|
||||
self._config[CONF_ORIGIN_ENTITY_ID] = user_input[CONF_ORIGIN_ENTITY_ID]
|
||||
# Remove possible previous configuration using coordinates
|
||||
self._config.pop(CONF_ORIGIN_LATITUDE, None)
|
||||
self._config.pop(CONF_ORIGIN_LONGITUDE, None)
|
||||
return await self.async_step_destination_menu()
|
||||
schema = vol.Schema({vol.Required(CONF_ORIGIN_ENTITY_ID): EntitySelector()})
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_ORIGIN_ENTITY_ID,
|
||||
): EntitySelector()
|
||||
}
|
||||
),
|
||||
{CONF_ORIGIN_ENTITY_ID: self._config.get(CONF_ORIGIN_ENTITY_ID)},
|
||||
)
|
||||
return self.async_show_form(step_id="origin_entity", data_schema=schema)
|
||||
|
||||
async def async_step_destination_menu(self, _: None = None) -> ConfigFlowResult:
|
||||
"""Show the destination menu."""
|
||||
return self.async_show_menu(
|
||||
step_id="destination_menu",
|
||||
menu_options=["destination_coordinates", "destination_entity"],
|
||||
)
|
||||
|
||||
async def async_step_destination_coordinates(
|
||||
self,
|
||||
user_input: dict[str, Any] | None = None,
|
||||
|
@ -193,21 +230,36 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
self._config[CONF_DESTINATION_LONGITUDE] = user_input[CONF_DESTINATION][
|
||||
CONF_LONGITUDE
|
||||
]
|
||||
# Remove possible previous configuration using an entity_id
|
||||
self._config.pop(CONF_DESTINATION_ENTITY_ID, None)
|
||||
if self._entry:
|
||||
return self.async_update_reload_and_abort(
|
||||
self._entry,
|
||||
title=self._config[CONF_NAME],
|
||||
data=self._config,
|
||||
reason="reconfigure_successful",
|
||||
)
|
||||
return self.async_create_entry(
|
||||
title=self._config[CONF_NAME],
|
||||
data=self._config,
|
||||
options=DEFAULT_OPTIONS,
|
||||
)
|
||||
schema = vol.Schema(
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_DESTINATION,
|
||||
): LocationSelector()
|
||||
}
|
||||
),
|
||||
{
|
||||
vol.Required(
|
||||
CONF_DESTINATION,
|
||||
default={
|
||||
CONF_LATITUDE: self.hass.config.latitude,
|
||||
CONF_LONGITUDE: self.hass.config.longitude,
|
||||
},
|
||||
): LocationSelector()
|
||||
}
|
||||
CONF_DESTINATION: {
|
||||
CONF_LATITUDE: self._config.get(CONF_DESTINATION_LATITUDE)
|
||||
or self.hass.config.latitude,
|
||||
CONF_LONGITUDE: self._config.get(CONF_DESTINATION_LONGITUDE)
|
||||
or self.hass.config.longitude,
|
||||
},
|
||||
},
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="destination_coordinates", data_schema=schema
|
||||
|
@ -222,13 +274,27 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
self._config[CONF_DESTINATION_ENTITY_ID] = user_input[
|
||||
CONF_DESTINATION_ENTITY_ID
|
||||
]
|
||||
# Remove possible previous configuration using coordinates
|
||||
self._config.pop(CONF_DESTINATION_LATITUDE, None)
|
||||
self._config.pop(CONF_DESTINATION_LONGITUDE, None)
|
||||
if self._entry:
|
||||
return self.async_update_reload_and_abort(
|
||||
self._entry, data=self._config, reason="reconfigure_successful"
|
||||
)
|
||||
return self.async_create_entry(
|
||||
title=self._config[CONF_NAME],
|
||||
data=self._config,
|
||||
options=DEFAULT_OPTIONS,
|
||||
)
|
||||
schema = vol.Schema(
|
||||
{vol.Required(CONF_DESTINATION_ENTITY_ID): EntitySelector()}
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_DESTINATION_ENTITY_ID,
|
||||
): EntitySelector()
|
||||
}
|
||||
),
|
||||
{CONF_DESTINATION_ENTITY_ID: self._config.get(CONF_DESTINATION_ENTITY_ID)},
|
||||
)
|
||||
return self.async_show_form(step_id="destination_entity", data_schema=schema)
|
||||
|
||||
|
@ -249,15 +315,22 @@ class HERETravelTimeOptionsFlow(OptionsFlow):
|
|||
self._config = user_input
|
||||
return await self.async_step_time_menu()
|
||||
|
||||
schema = vol.Schema(
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_ROUTE_MODE,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_ROUTE_MODE, DEFAULT_OPTIONS[CONF_ROUTE_MODE]
|
||||
),
|
||||
): vol.In(ROUTE_MODES),
|
||||
}
|
||||
),
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_ROUTE_MODE,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_ROUTE_MODE, DEFAULT_OPTIONS[CONF_ROUTE_MODE]
|
||||
),
|
||||
): vol.In(ROUTE_MODES),
|
||||
}
|
||||
CONF_ROUTE_MODE: self.config_entry.options.get(
|
||||
CONF_ROUTE_MODE, DEFAULT_OPTIONS[CONF_ROUTE_MODE]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="init", data_schema=schema)
|
||||
|
@ -283,8 +356,11 @@ class HERETravelTimeOptionsFlow(OptionsFlow):
|
|||
self._config[CONF_ARRIVAL_TIME] = user_input[CONF_ARRIVAL_TIME]
|
||||
return self.async_create_entry(title="", data=self._config)
|
||||
|
||||
schema = vol.Schema(
|
||||
{vol.Required(CONF_ARRIVAL_TIME, default="00:00:00"): TimeSelector()}
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{vol.Required(CONF_ARRIVAL_TIME, default="00:00:00"): TimeSelector()}
|
||||
),
|
||||
{CONF_ARRIVAL_TIME: "00:00:00"},
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="arrival_time", data_schema=schema)
|
||||
|
@ -297,8 +373,11 @@ class HERETravelTimeOptionsFlow(OptionsFlow):
|
|||
self._config[CONF_DEPARTURE_TIME] = user_input[CONF_DEPARTURE_TIME]
|
||||
return self.async_create_entry(title="", data=self._config)
|
||||
|
||||
schema = vol.Schema(
|
||||
{vol.Required(CONF_DEPARTURE_TIME, default="00:00:00"): TimeSelector()}
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{vol.Required(CONF_DEPARTURE_TIME, default="00:00:00"): TimeSelector()}
|
||||
),
|
||||
{CONF_DEPARTURE_TIME: "00:00:00"},
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="departure_time", data_schema=schema)
|
||||
|
|
|
@ -52,7 +52,8 @@
|
|||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
|
|
@ -6,17 +6,20 @@ from here_routing import HERERoutingError, HERERoutingUnauthorizedError
|
|||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS
|
||||
from homeassistant.components.here_travel_time.const import (
|
||||
CONF_ARRIVAL_TIME,
|
||||
CONF_DEPARTURE_TIME,
|
||||
CONF_DESTINATION_ENTITY_ID,
|
||||
CONF_DESTINATION_LATITUDE,
|
||||
CONF_DESTINATION_LONGITUDE,
|
||||
CONF_ORIGIN_ENTITY_ID,
|
||||
CONF_ORIGIN_LATITUDE,
|
||||
CONF_ORIGIN_LONGITUDE,
|
||||
CONF_ROUTE_MODE,
|
||||
DOMAIN,
|
||||
ROUTE_MODE_FASTEST,
|
||||
TRAVEL_MODE_BICYCLE,
|
||||
TRAVEL_MODE_CAR,
|
||||
TRAVEL_MODE_PUBLIC,
|
||||
)
|
||||
|
@ -249,6 +252,105 @@ async def test_step_destination_entity(
|
|||
}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("valid_response")
|
||||
async def test_reconfigure_destination_entity(hass: HomeAssistant) -> None:
|
||||
"""Test reconfigure flow when choosing a destination entity."""
|
||||
origin_entity_selector_result = await do_common_reconfiguration_steps(hass)
|
||||
menu_result = await hass.config_entries.flow.async_configure(
|
||||
origin_entity_selector_result["flow_id"], {"next_step_id": "destination_entity"}
|
||||
)
|
||||
assert menu_result["type"] is FlowResultType.FORM
|
||||
|
||||
destination_entity_selector_result = await hass.config_entries.flow.async_configure(
|
||||
menu_result["flow_id"],
|
||||
{"destination_entity_id": "zone.home"},
|
||||
)
|
||||
assert destination_entity_selector_result["type"] is FlowResultType.ABORT
|
||||
assert destination_entity_selector_result["reason"] == "reconfigure_successful"
|
||||
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||
assert entry.data == {
|
||||
CONF_NAME: "test",
|
||||
CONF_API_KEY: API_KEY,
|
||||
CONF_ORIGIN_ENTITY_ID: "zone.home",
|
||||
CONF_DESTINATION_ENTITY_ID: "zone.home",
|
||||
CONF_MODE: TRAVEL_MODE_BICYCLE,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("valid_response")
|
||||
async def test_reconfigure_destination_coordinates(hass: HomeAssistant) -> None:
|
||||
"""Test reconfigure flow when choosing destination coordinates."""
|
||||
origin_entity_selector_result = await do_common_reconfiguration_steps(hass)
|
||||
menu_result = await hass.config_entries.flow.async_configure(
|
||||
origin_entity_selector_result["flow_id"],
|
||||
{"next_step_id": "destination_coordinates"},
|
||||
)
|
||||
assert menu_result["type"] is FlowResultType.FORM
|
||||
|
||||
destination_entity_selector_result = await hass.config_entries.flow.async_configure(
|
||||
menu_result["flow_id"],
|
||||
{
|
||||
"destination": {
|
||||
"latitude": 43.0,
|
||||
"longitude": -80.3,
|
||||
"radius": 5.0,
|
||||
}
|
||||
},
|
||||
)
|
||||
assert destination_entity_selector_result["type"] is FlowResultType.ABORT
|
||||
assert destination_entity_selector_result["reason"] == "reconfigure_successful"
|
||||
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||
assert entry.data == {
|
||||
CONF_NAME: "test",
|
||||
CONF_API_KEY: API_KEY,
|
||||
CONF_ORIGIN_ENTITY_ID: "zone.home",
|
||||
CONF_DESTINATION_LATITUDE: 43.0,
|
||||
CONF_DESTINATION_LONGITUDE: -80.3,
|
||||
CONF_MODE: TRAVEL_MODE_BICYCLE,
|
||||
}
|
||||
|
||||
|
||||
async def do_common_reconfiguration_steps(hass: HomeAssistant) -> None:
|
||||
"""Walk through common flow steps for reconfiguring."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="0123456789",
|
||||
data=DEFAULT_CONFIG,
|
||||
options=DEFAULT_OPTIONS,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
reconfigure_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": config_entries.SOURCE_RECONFIGURE,
|
||||
"entry_id": entry.entry_id,
|
||||
},
|
||||
)
|
||||
assert reconfigure_result["type"] is FlowResultType.FORM
|
||||
assert reconfigure_result["step_id"] == "user"
|
||||
|
||||
user_step_result = await hass.config_entries.flow.async_configure(
|
||||
reconfigure_result["flow_id"],
|
||||
{
|
||||
CONF_API_KEY: API_KEY,
|
||||
CONF_MODE: TRAVEL_MODE_BICYCLE,
|
||||
CONF_NAME: "test",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
menu_result = await hass.config_entries.flow.async_configure(
|
||||
user_step_result["flow_id"], {"next_step_id": "origin_entity"}
|
||||
)
|
||||
return await hass.config_entries.flow.async_configure(
|
||||
menu_result["flow_id"],
|
||||
{"origin_entity_id": "zone.home"},
|
||||
)
|
||||
|
||||
|
||||
async def test_form_invalid_auth(hass: HomeAssistant) -> None:
|
||||
"""Test we handle invalid auth."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
|
Loading…
Add table
Reference in a new issue