diff --git a/homeassistant/components/reolink/config_flow.py b/homeassistant/components/reolink/config_flow.py index 5b316662a2c..489597e7764 100644 --- a/homeassistant/components/reolink/config_flow.py +++ b/homeassistant/components/reolink/config_flow.py @@ -11,7 +11,13 @@ from reolink_aio.exceptions import ApiError, CredentialsInvalidError, ReolinkErr import voluptuous as vol from homeassistant.components import dhcp -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow +from homeassistant.config_entries import ( + SOURCE_REAUTH, + SOURCE_RECONFIGURE, + ConfigFlow, + ConfigFlowResult, + OptionsFlow, +) from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, @@ -94,7 +100,6 @@ class ReolinkFlowHandler(ConfigFlow, domain=DOMAIN): self._host: str | None = None self._username: str = "admin" self._password: str | None = None - self._reauth: bool = False @staticmethod @callback @@ -111,7 +116,6 @@ class ReolinkFlowHandler(ConfigFlow, domain=DOMAIN): self._host = entry_data[CONF_HOST] self._username = entry_data[CONF_USERNAME] self._password = entry_data[CONF_PASSWORD] - self._reauth = True self.context["title_placeholders"]["ip_address"] = entry_data[CONF_HOST] self.context["title_placeholders"]["hostname"] = self.context[ "title_placeholders" @@ -129,6 +133,19 @@ class ReolinkFlowHandler(ConfigFlow, domain=DOMAIN): step_id="reauth_confirm", description_placeholders=placeholders ) + async def async_step_reconfigure( + self, entry_data: Mapping[str, Any] + ) -> ConfigFlowResult: + """Perform a reconfiguration.""" + config_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + assert config_entry is not None + self._host = config_entry.data[CONF_HOST] + self._username = config_entry.data[CONF_USERNAME] + self._password = config_entry.data[CONF_PASSWORD] + return await self.async_step_user() + async def async_step_dhcp( self, discovery_info: dhcp.DhcpServiceInfo ) -> ConfigFlowResult: @@ -244,14 +261,15 @@ class ReolinkFlowHandler(ConfigFlow, domain=DOMAIN): existing_entry = await self.async_set_unique_id( mac_address, raise_on_progress=False ) - if existing_entry and self._reauth: - if self.hass.config_entries.async_update_entry( - existing_entry, data=user_input - ): - await self.hass.config_entries.async_reload( - existing_entry.entry_id - ) - return self.async_abort(reason="reauth_successful") + if existing_entry and self.init_step in ( + SOURCE_REAUTH, + SOURCE_RECONFIGURE, + ): + return self.async_update_reload_and_abort( + entry=existing_entry, + data=user_input, + reason=f"{self.init_step}_successful", + ) self._abort_if_unique_id_configured(updates=user_input) return self.async_create_entry( @@ -266,7 +284,7 @@ class ReolinkFlowHandler(ConfigFlow, domain=DOMAIN): vol.Required(CONF_PASSWORD, default=self._password): str, } ) - if self._host is None or errors: + if self._host is None or self.init_step == SOURCE_RECONFIGURE or errors: data_schema = data_schema.extend( { vol.Required(CONF_HOST, default=self._host): str, diff --git a/homeassistant/components/reolink/host.py b/homeassistant/components/reolink/host.py index 0df4918be76..58ae191eb9f 100644 --- a/homeassistant/components/reolink/host.py +++ b/homeassistant/components/reolink/host.py @@ -561,7 +561,9 @@ class ReolinkHost: def register_webhook(self) -> None: """Register the webhook for motion events.""" - self.webhook_id = f"{DOMAIN}_{self.unique_id.replace(':', '')}_ONVIF" + self.webhook_id = ( + f"{DOMAIN}_{self.unique_id.replace(':', '')}_{webhook.async_generate_id()}" + ) event_id = self.webhook_id webhook.async_register( diff --git a/homeassistant/components/reolink/strings.json b/homeassistant/components/reolink/strings.json index 3710c3743fa..9f18f4afe15 100644 --- a/homeassistant/components/reolink/strings.json +++ b/homeassistant/components/reolink/strings.json @@ -35,7 +35,8 @@ }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", - "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%]" } }, "options": { diff --git a/tests/components/reolink/test_config_flow.py b/tests/components/reolink/test_config_flow.py index 40695861aaf..4c362e150ca 100644 --- a/tests/components/reolink/test_config_flow.py +++ b/tests/components/reolink/test_config_flow.py @@ -503,3 +503,53 @@ async def test_dhcp_ip_update( await hass.async_block_till_done() assert config_entry.data[CONF_HOST] == expected + + +async def test_reconfig(hass: HomeAssistant, mock_setup_entry: MagicMock) -> None: + """Test a reconfiguration flow.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + unique_id=format_mac(TEST_MAC), + data={ + CONF_HOST: TEST_HOST, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_PORT: TEST_PORT, + CONF_USE_HTTPS: TEST_USE_HTTPS, + }, + options={ + CONF_PROTOCOL: DEFAULT_PROTOCOL, + }, + title=TEST_NVR_NAME, + ) + config_entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_RECONFIGURE, + "entry_id": config_entry.entry_id, + }, + ) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: TEST_HOST2, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + }, + ) + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + assert config_entry.data[CONF_HOST] == TEST_HOST2 + assert config_entry.data[CONF_USERNAME] == TEST_USERNAME + assert config_entry.data[CONF_PASSWORD] == TEST_PASSWORD