diff --git a/homeassistant/components/tado/config_flow.py b/homeassistant/components/tado/config_flow.py index 38110f6749e..e52b87796f7 100644 --- a/homeassistant/components/tado/config_flow.py +++ b/homeassistant/components/tado/config_flow.py @@ -74,6 +74,7 @@ class TadoConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for Tado.""" VERSION = 1 + config_entry: ConfigEntry | None async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -159,6 +160,56 @@ class TadoConfigFlow(ConfigFlow, domain=DOMAIN): }, ) + async def async_step_reconfigure( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle a reconfiguration flow initialized by the user.""" + self.config_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + 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: dict[str, str] = {} + assert self.config_entry + + if user_input is not None: + user_input[CONF_USERNAME] = self.config_entry.data[CONF_USERNAME] + try: + await validate_input(self.hass, user_input) + except CannotConnect: + errors["base"] = "cannot_connect" + except PyTado.exceptions.TadoWrongCredentialsException: + errors["base"] = "invalid_auth" + except NoHomes: + errors["base"] = "no_homes" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + + if not errors: + return self.async_update_reload_and_abort( + self.config_entry, + data={**self.config_entry.data, **user_input}, + reason="reconfigure_successful", + ) + + return self.async_show_form( + step_id="reconfigure_confirm", + data_schema=vol.Schema( + { + vol.Required(CONF_PASSWORD): str, + } + ), + errors=errors, + description_placeholders={ + CONF_USERNAME: self.config_entry.data[CONF_USERNAME] + }, + ) + @staticmethod @callback def async_get_options_flow( diff --git a/homeassistant/components/tado/strings.json b/homeassistant/components/tado/strings.json index 267cbbe6fee..51e36fe5355 100644 --- a/homeassistant/components/tado/strings.json +++ b/homeassistant/components/tado/strings.json @@ -1,7 +1,8 @@ { "config": { "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%]" }, "step": { "user": { @@ -10,6 +11,16 @@ "username": "[%key:common::config_flow::data::username%]" }, "title": "Connect to your Tado account" + }, + "reconfigure_confirm": { + "title": "Reconfigure your Tado", + "description": "Reconfigure the entry, for your account: `{username}`.", + "data": { + "password": "[%key:common::config_flow::data::password%]" + }, + "data_description": { + "password": "Enter the (new) password for Tado." + } } }, "error": { diff --git a/tests/components/tado/test_config_flow.py b/tests/components/tado/test_config_flow.py index 6f44bee8960..a8883f47fe2 100644 --- a/tests/components/tado/test_config_flow.py +++ b/tests/components/tado/test_config_flow.py @@ -10,6 +10,7 @@ import requests from homeassistant import config_entries from homeassistant.components import zeroconf +from homeassistant.components.tado.config_flow import NoHomes from homeassistant.components.tado.const import ( CONF_FALLBACK, CONST_OVERLAY_TADO_DEFAULT, @@ -409,3 +410,83 @@ async def test_import_step_unique_id_configured(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" assert mock_setup_entry.call_count == 0 + + +@pytest.mark.parametrize( + ("exception", "error"), + [ + (PyTado.exceptions.TadoWrongCredentialsException, "invalid_auth"), + (RuntimeError, "cannot_connect"), + (NoHomes, "no_homes"), + (ValueError, "unknown"), + ], +) +async def test_reconfigure_flow( + hass: HomeAssistant, exception: Exception, error: str +) -> None: + """Test re-configuration flow.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={ + "username": "test-username", + "password": "test-password", + "home_id": 1, + }, + unique_id="unique_id", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_RECONFIGURE, + "entry_id": entry.entry_id, + }, + ) + + assert result["type"] is FlowResultType.FORM + + with patch( + "homeassistant.components.tado.config_flow.Tado", + side_effect=exception, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_PASSWORD: "test-password", + }, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": error} + + mock_tado_api = _get_mock_tado_api(getMe={"homes": [{"id": 1, "name": "myhome"}]}) + with ( + patch( + "homeassistant.components.tado.config_flow.Tado", + return_value=mock_tado_api, + ), + patch( + "homeassistant.components.tado.async_setup_entry", + return_value=True, + ), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_PASSWORD: "test-password", + }, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + entry = hass.config_entries.async_get_entry(entry.entry_id) + assert entry + assert entry.title == "Mock Title" + assert entry.data == { + "username": "test-username", + "password": "test-password", + "home_id": 1, + }