mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 22:38:02 -04:00
Fix Reolink reconfigure and reauth for a device that moved to its other network interface (#182641)
This commit is contained in:
@@ -322,16 +322,27 @@ class ReolinkFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
|
||||
mac_address = format_mac(host.api.mac_address)
|
||||
await self.async_set_unique_id(mac_address, raise_on_progress=False)
|
||||
if self.source == SOURCE_REAUTH:
|
||||
self._abort_if_unique_id_mismatch()
|
||||
return self.async_update_reload_and_abort(
|
||||
entry=self._get_reauth_entry(), data=user_input
|
||||
existing_entry = await self.async_set_unique_id(
|
||||
mac_address, raise_on_progress=False
|
||||
)
|
||||
if self.source in (SOURCE_REAUTH, SOURCE_RECONFIGURE):
|
||||
entry = (
|
||||
self._get_reauth_entry()
|
||||
if self.source == SOURCE_REAUTH
|
||||
else self._get_reconfigure_entry()
|
||||
)
|
||||
if self.source == SOURCE_RECONFIGURE:
|
||||
self._abort_if_unique_id_mismatch()
|
||||
# a device moved to its other network interface reports a new
|
||||
# MAC address, its UID tells it is still the same device
|
||||
same_device = (
|
||||
host.api.supported(None, "UID")
|
||||
and entry.data.get(CONF_UID) == host.api.uid
|
||||
)
|
||||
if not same_device:
|
||||
self._abort_if_unique_id_mismatch()
|
||||
if existing_entry is not None and existing_entry is not entry:
|
||||
return self.async_abort(reason="already_configured")
|
||||
return self.async_update_reload_and_abort(
|
||||
entry=self._get_reconfigure_entry(), data=user_input
|
||||
entry=entry, data=user_input, unique_id=mac_address
|
||||
)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ from .conftest import (
|
||||
TEST_HOST,
|
||||
TEST_HOST2,
|
||||
TEST_MAC,
|
||||
TEST_MAC2,
|
||||
TEST_MAC_CAM,
|
||||
TEST_NVR_NAME,
|
||||
TEST_PASSWORD,
|
||||
TEST_PASSWORD2,
|
||||
@@ -461,58 +463,6 @@ async def test_reauth(hass: HomeAssistant) -> None:
|
||||
assert config_entry.data[CONF_PASSWORD] == TEST_PASSWORD2
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_reauth_abort_unique_id_mismatch(
|
||||
hass: HomeAssistant, reolink_host: MagicMock
|
||||
) -> None:
|
||||
"""Test a reauth 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,
|
||||
CONF_BC_PORT: TEST_BC_PORT,
|
||||
CONF_BC_CONNECT: TEST_BC_CON,
|
||||
CONF_BC_ONLY: False,
|
||||
CONF_UID: TEST_UID,
|
||||
},
|
||||
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()
|
||||
|
||||
reolink_host.mac_address = "aa:aa:aa:aa:aa:aa"
|
||||
|
||||
result = await config_entry.start_reauth_flow(hass)
|
||||
|
||||
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_USERNAME: TEST_USERNAME2,
|
||||
CONF_PASSWORD: TEST_PASSWORD2,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "unique_id_mismatch"
|
||||
assert config_entry.data[CONF_HOST] == TEST_HOST
|
||||
assert config_entry.data[CONF_USERNAME] == TEST_USERNAME
|
||||
assert config_entry.data[CONF_PASSWORD] == TEST_PASSWORD
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_dhcp_flow(hass: HomeAssistant) -> None:
|
||||
"""Successful flow from DHCP discovery."""
|
||||
@@ -885,10 +835,72 @@ async def test_reconfig(hass: HomeAssistant) -> None:
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_reconfig_abort_unique_id_mismatch(
|
||||
hass: HomeAssistant, reolink_host: MagicMock
|
||||
@pytest.mark.parametrize(
|
||||
("source", "user_input", "success_reason"),
|
||||
[
|
||||
pytest.param(
|
||||
config_entries.SOURCE_REAUTH,
|
||||
{CONF_USERNAME: TEST_USERNAME2, CONF_PASSWORD: TEST_PASSWORD2},
|
||||
"reauth_successful",
|
||||
id="reauth",
|
||||
),
|
||||
pytest.param(
|
||||
config_entries.SOURCE_RECONFIGURE,
|
||||
{
|
||||
CONF_HOST: TEST_HOST2,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
},
|
||||
"reconfigure_successful",
|
||||
id="reconfigure",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("uid_supported", "device_uid", "other_entry_mac", "abort_reason"),
|
||||
[
|
||||
pytest.param(
|
||||
True,
|
||||
TEST_UID,
|
||||
TEST_MAC_CAM,
|
||||
None,
|
||||
id="same_device_on_other_interface",
|
||||
),
|
||||
pytest.param(
|
||||
True,
|
||||
"OTHER0123456789A",
|
||||
TEST_MAC_CAM,
|
||||
"unique_id_mismatch",
|
||||
id="different_device",
|
||||
),
|
||||
pytest.param(
|
||||
False,
|
||||
TEST_UID,
|
||||
TEST_MAC_CAM,
|
||||
"unique_id_mismatch",
|
||||
id="uid_not_supported",
|
||||
),
|
||||
pytest.param(
|
||||
True,
|
||||
TEST_UID,
|
||||
TEST_MAC2,
|
||||
"already_configured",
|
||||
id="other_interface_has_its_own_entry",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_reauth_reconfig_mac_change(
|
||||
hass: HomeAssistant,
|
||||
reolink_host: MagicMock,
|
||||
source: str,
|
||||
user_input: dict[str, str],
|
||||
success_reason: str,
|
||||
uid_supported: bool,
|
||||
device_uid: str,
|
||||
other_entry_mac: str,
|
||||
abort_reason: str | None,
|
||||
) -> None:
|
||||
"""Test a reconfiguration flow aborts if the unique id does not match."""
|
||||
"""Test a reauth or reconfigure flow when the device reports another MAC address."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id=format_mac(TEST_MAC),
|
||||
@@ -909,29 +921,44 @@ async def test_reconfig_abort_unique_id_mismatch(
|
||||
title=TEST_NVR_NAME,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
MockConfigEntry(domain=DOMAIN, unique_id=format_mac(other_entry_mac)).add_to_hass(
|
||||
hass
|
||||
)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
reolink_host.mac_address = "aa:aa:aa:aa:aa:aa"
|
||||
reolink_host.mac_address = TEST_MAC2
|
||||
reolink_host.uid = device_uid
|
||||
reolink_host.supported.side_effect = lambda channel, capability: (
|
||||
capability != "UID" or uid_supported
|
||||
)
|
||||
|
||||
result = await config_entry.start_reconfigure_flow(hass)
|
||||
if source == config_entries.SOURCE_REAUTH:
|
||||
result = await config_entry.start_reauth_flow(hass)
|
||||
else:
|
||||
result = await config_entry.start_reconfigure_flow(hass)
|
||||
|
||||
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,
|
||||
},
|
||||
result["flow_id"], user_input
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "unique_id_mismatch"
|
||||
if abort_reason is None:
|
||||
# the entry follows the device to its new MAC address
|
||||
assert result["reason"] == success_reason
|
||||
assert config_entry.data[CONF_HOST] == user_input.get(CONF_HOST, TEST_HOST)
|
||||
assert config_entry.data[CONF_USERNAME] == user_input[CONF_USERNAME]
|
||||
assert config_entry.data[CONF_PASSWORD] == user_input[CONF_PASSWORD]
|
||||
assert config_entry.unique_id == format_mac(TEST_MAC2)
|
||||
return
|
||||
|
||||
assert result["reason"] == abort_reason
|
||||
assert config_entry.data[CONF_HOST] == TEST_HOST
|
||||
assert config_entry.data[CONF_USERNAME] == TEST_USERNAME
|
||||
assert config_entry.data[CONF_PASSWORD] == TEST_PASSWORD
|
||||
assert config_entry.unique_id == format_mac(TEST_MAC)
|
||||
|
||||
Reference in New Issue
Block a user