Add reconfigure flow to AVM Fritz!SmartHome (#116047)

This commit is contained in:
Michael 2024-04-24 13:06:14 +02:00 committed by GitHub
parent d17e9bfc99
commit 9fcb774252
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 132 additions and 2 deletions

View File

@ -221,3 +221,44 @@ class FritzboxConfigFlow(ConfigFlow, domain=DOMAIN):
description_placeholders={"name": self._name},
errors=errors,
)
async def async_step_reconfigure(
self, _: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a reconfiguration flow initialized by the user."""
entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
assert entry is not None
self._entry = entry
self._name = self._entry.data[CONF_HOST]
self._host = self._entry.data[CONF_HOST]
self._username = self._entry.data[CONF_USERNAME]
self._password = self._entry.data[CONF_PASSWORD]
return await self.async_step_reconfigure_confirm()
async def async_step_reconfigure_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a reconfiguration flow initialized by the user."""
errors = {}
if user_input is not None:
self._host = user_input[CONF_HOST]
result = await self.hass.async_add_executor_job(self._try_connect)
if result == RESULT_SUCCESS:
await self._update_entry()
return self.async_abort(reason="reconfigure_successful")
errors["base"] = result
return self.async_show_form(
step_id="reconfigure_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_HOST, default=self._host): str,
}
),
description_placeholders={"name": self._name},
errors=errors,
)

View File

@ -26,6 +26,15 @@
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
}
},
"reconfigure_confirm": {
"description": "Update your configuration information for {name}.",
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your FRITZ!Box router."
}
}
},
"abort": {
@ -34,7 +43,8 @@
"ignore_ip6_link_local": "IPv6 link local address is not supported.",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"not_supported": "Connected to AVM FRITZ!Box but it's unable to control Smart Home devices.",
"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": {
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"

View File

@ -12,7 +12,12 @@ from requests.exceptions import HTTPError
from homeassistant.components import ssdp
from homeassistant.components.fritzbox.const import DOMAIN
from homeassistant.components.ssdp import ATTR_UPNP_FRIENDLY_NAME, ATTR_UPNP_UDN
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_SSDP, SOURCE_USER
from homeassistant.config_entries import (
SOURCE_REAUTH,
SOURCE_RECONFIGURE,
SOURCE_SSDP,
SOURCE_USER,
)
from homeassistant.const import CONF_DEVICES, CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
@ -202,6 +207,80 @@ async def test_reauth_not_successful(hass: HomeAssistant, fritz: Mock) -> None:
assert result["reason"] == "no_devices_found"
async def test_reconfigure_success(hass: HomeAssistant, fritz: Mock) -> None:
"""Test starting a reconfigure flow."""
mock_config = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
mock_config.add_to_hass(hass)
assert mock_config.data[CONF_HOST] == "10.0.0.1"
assert mock_config.data[CONF_USERNAME] == "fake_user"
assert mock_config.data[CONF_PASSWORD] == "fake_pass"
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_RECONFIGURE, "entry_id": mock_config.entry_id},
data=mock_config.data,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure_confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: "new_host",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config.data[CONF_HOST] == "new_host"
assert mock_config.data[CONF_USERNAME] == "fake_user"
assert mock_config.data[CONF_PASSWORD] == "fake_pass"
async def test_reconfigure_failed(hass: HomeAssistant, fritz: Mock) -> None:
"""Test starting a reconfigure flow with failure."""
fritz().login.side_effect = [OSError("Boom"), None]
mock_config = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
mock_config.add_to_hass(hass)
assert mock_config.data[CONF_HOST] == "10.0.0.1"
assert mock_config.data[CONF_USERNAME] == "fake_user"
assert mock_config.data[CONF_PASSWORD] == "fake_pass"
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_RECONFIGURE, "entry_id": mock_config.entry_id},
data=mock_config.data,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure_confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: "new_host",
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure_confirm"
assert result["errors"]["base"] == "no_devices_found"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: "new_host",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config.data[CONF_HOST] == "new_host"
assert mock_config.data[CONF_USERNAME] == "fake_user"
assert mock_config.data[CONF_PASSWORD] == "fake_pass"
@pytest.mark.parametrize(
("test_data", "expected_result"),
[