data grand lyon: implement reconfiguration flow (#170210)

This commit is contained in:
Crocmagnon
2026-05-11 09:17:31 +02:00
committed by GitHub
parent f96afda959
commit d5f2cd8b17
4 changed files with 122 additions and 2 deletions
@@ -30,6 +30,12 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
}
)
STEP_RECONFIGURE_SCHEMA = vol.Schema(
{
vol.Required(CONF_PASSWORD): str,
}
)
STEP_STOP_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_LINE): str,
@@ -103,6 +109,34 @@ class DataGrandLyonConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of credentials."""
errors: dict[str, str] = {}
reconfigure_entry = self._get_reconfigure_entry()
if user_input is not None:
creds = {
CONF_USERNAME: reconfigure_entry.data.get(CONF_USERNAME),
CONF_PASSWORD: user_input[CONF_PASSWORD],
}
if error := await self._test_connection(creds):
errors["base"] = error
else:
return self.async_update_reload_and_abort(
reconfigure_entry, data_updates=user_input
)
return self.async_show_form(
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
STEP_RECONFIGURE_SCHEMA,
user_input or reconfigure_entry.data,
),
errors=errors,
)
async def _test_connection(self, user_input: dict[str, Any]) -> str | None:
"""Test connectivity by making a dummy API call.
@@ -62,7 +62,7 @@ rules:
entity-translations: done
exception-translations: done
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: no known use cases for repair issues or flows, yet
@@ -2,7 +2,8 @@
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@@ -20,6 +21,16 @@
"username": "[%key:component::data_grand_lyon::config::step::user::data_description::username%]"
}
},
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]"
},
"data_description": {
"password": "[%key:component::data_grand_lyon::config::step::user::data_description::password%]",
"username": "[%key:component::data_grand_lyon::config::step::user::data_description::username%]"
}
},
"user": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
@@ -193,6 +193,81 @@ async def test_form_already_configured(
assert result["reason"] == "already_configured"
async def test_reconfigure_flow(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_get_tcl_passages: AsyncMock,
) -> None:
"""Test the reconfigure flow updates credentials and preserves subentries."""
mock_config_entry.add_to_hass(hass)
original_subentries = dict(mock_config_entry.subentries)
result = await mock_config_entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PASSWORD: "new-pass"},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config_entry.data == {
CONF_USERNAME: "user",
CONF_PASSWORD: "new-pass",
}
assert dict(mock_config_entry.subentries) == original_subentries
@pytest.mark.parametrize(
("side_effect", "error"),
[
(ClientConnectionError(), "cannot_connect"),
(ClientResponseError(None, None, status=401), "invalid_auth"),
(ClientResponseError(None, None, status=500), "cannot_connect"),
(RuntimeError("unexpected"), "unknown"),
],
ids=["connection-error", "auth-401", "http-500", "unknown"],
)
async def test_reconfigure_flow_errors(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_get_tcl_passages: AsyncMock,
side_effect: Exception,
error: str,
) -> None:
"""Test the reconfigure flow shows errors and recovers."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reconfigure_flow(hass)
mock_get_tcl_passages.side_effect = side_effect
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PASSWORD: "new-pass"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
assert result["errors"] == {"base": error}
mock_get_tcl_passages.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PASSWORD: "new-pass"},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config_entry.data == {
CONF_USERNAME: "user",
CONF_PASSWORD: "new-pass",
}
# Stop subentry tests