Compare commits

...

1 Commits

Author SHA1 Message Date
Franck Nijhof
ed69f81ec4 Add reconfiguration flow to Twente Milieu 2026-04-12 10:23:32 +00:00
4 changed files with 189 additions and 2 deletions

View File

@@ -78,3 +78,62 @@ class TwenteMilieuFlowHandler(ConfigFlow, domain=DOMAIN):
CONF_HOUSE_LETTER: user_input.get(CONF_HOUSE_LETTER),
},
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of a Twente Milieu address."""
errors: dict[str, str] = {}
reconfigure_entry = self._get_reconfigure_entry()
if user_input is not None:
twentemilieu = TwenteMilieu(
post_code=user_input[CONF_POST_CODE],
house_number=user_input[CONF_HOUSE_NUMBER],
house_letter=user_input.get(CONF_HOUSE_LETTER, ""),
session=async_get_clientsession(self.hass),
)
try:
unique_id = await twentemilieu.unique_id()
except TwenteMilieuConnectionError:
errors["base"] = "cannot_connect"
except TwenteMilieuAddressError:
errors["base"] = "invalid_address"
else:
await self.async_set_unique_id(str(unique_id))
self._abort_if_unique_id_mismatch(reason="different_address")
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates={
CONF_ID: unique_id,
CONF_POST_CODE: user_input[CONF_POST_CODE],
CONF_HOUSE_NUMBER: user_input[CONF_HOUSE_NUMBER],
CONF_HOUSE_LETTER: user_input.get(CONF_HOUSE_LETTER),
},
)
return self.async_show_form(
step_id="reconfigure",
data_schema=vol.Schema(
{
vol.Required(
CONF_POST_CODE,
default=reconfigure_entry.data[CONF_POST_CODE],
): str,
vol.Required(
CONF_HOUSE_NUMBER,
default=reconfigure_entry.data[CONF_HOUSE_NUMBER],
): str,
vol.Optional(
CONF_HOUSE_LETTER,
description={
"suggested_value": reconfigure_entry.data.get(
CONF_HOUSE_LETTER
)
},
): str,
}
),
errors=errors,
)

View File

@@ -72,7 +72,7 @@ rules:
diagnostics: done
exception-translations: done
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
dynamic-devices:
status: exempt
comment: |

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
"different_address": "The entered address resolves to a different Twente Milieu service area.",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_address": "Address not found in Twente Milieu service area."
},
"step": {
"reconfigure": {
"data": {
"house_letter": "[%key:component::twentemilieu::config::step::user::data::house_letter%]",
"house_number": "[%key:component::twentemilieu::config::step::user::data::house_number%]",
"post_code": "[%key:component::twentemilieu::config::step::user::data::post_code%]"
},
"data_description": {
"house_letter": "[%key:component::twentemilieu::config::step::user::data_description::house_letter%]",
"house_number": "[%key:component::twentemilieu::config::step::user::data_description::house_number%]",
"post_code": "[%key:component::twentemilieu::config::step::user::data_description::post_code%]"
},
"description": "Update the address of your Twente Milieu integration."
},
"user": {
"data": {
"house_letter": "House letter/additional",

View File

@@ -171,3 +171,116 @@ async def test_address_already_set_up(
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@pytest.mark.usefixtures("mock_twentemilieu")
async def test_reconfigure_flow(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test reconfiguring an existing Twente Milieu address."""
mock_config_entry.add_to_hass(hass)
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"],
user_input={
CONF_POST_CODE: "1234AB",
CONF_HOUSE_NUMBER: "2",
CONF_HOUSE_LETTER: "B",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config_entry.data == {
CONF_HOUSE_LETTER: "B",
CONF_HOUSE_NUMBER: "2",
CONF_ID: 12345,
CONF_POST_CODE: "1234AB",
}
@pytest.mark.parametrize(
("side_effect", "expected_error"),
[
(TwenteMilieuConnectionError, {"base": "cannot_connect"}),
(TwenteMilieuAddressError, {"base": "invalid_address"}),
],
)
async def test_reconfigure_flow_errors(
hass: HomeAssistant,
mock_twentemilieu: MagicMock,
mock_config_entry: MockConfigEntry,
side_effect: type[Exception],
expected_error: dict[str, str],
) -> None:
"""Test reconfigure flow recovers from errors."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
mock_twentemilieu.unique_id.side_effect = side_effect
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_POST_CODE: "1234AB",
CONF_HOUSE_NUMBER: "2",
CONF_HOUSE_LETTER: "B",
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
assert result["errors"] == expected_error
mock_twentemilieu.unique_id.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_POST_CODE: "1234AB",
CONF_HOUSE_NUMBER: "2",
CONF_HOUSE_LETTER: "B",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
async def test_reconfigure_flow_different_address(
hass: HomeAssistant,
mock_twentemilieu: MagicMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test reconfigure aborts when the address resolves to a different ID."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reconfigure_flow(hass)
mock_twentemilieu.unique_id.return_value = 67890
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_POST_CODE: "9999ZZ",
CONF_HOUSE_NUMBER: "42",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "different_address"
# Original data is preserved
assert mock_config_entry.data == {
CONF_HOUSE_LETTER: "A",
CONF_HOUSE_NUMBER: "1",
CONF_ID: 12345,
CONF_POST_CODE: "1234AB",
}