Implement Reolink reconfiguration flow (#126004)

Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
starkillerOG 2024-09-16 09:21:23 +02:00 committed by GitHub
parent 8dbca0fa0b
commit 2174ee18dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 85 additions and 14 deletions

View File

@ -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,

View File

@ -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(

View File

@ -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": {

View File

@ -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