Skip to content

Commit

Permalink
Fix tplink not updating IP from DHCP discovery and discovering twice (#…
Browse files Browse the repository at this point in the history
…110557)

We only called format_mac on the mac address if we connected
to the device during entry creation. Since the format of the
mac address from DHCP discovery did not match the format saved
in the unique id, the IP would not get updated and a second
discovery would appear

Thankfully the creation path does format the mac so we did not
create any entries with an inconsistantly formatted unique id

fixes #110460
  • Loading branch information
bdraco committed Feb 14, 2024
1 parent c045e23 commit cd1c633
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/tplink/config_flow.py
Expand Up @@ -61,7 +61,7 @@ def __init__(self) -> None:
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
"""Handle discovery via dhcp."""
return await self._async_handle_discovery(
discovery_info.ip, discovery_info.macaddress
discovery_info.ip, dr.format_mac(discovery_info.macaddress)
)

async def async_step_integration_discovery(
Expand Down
1 change: 1 addition & 0 deletions tests/components/tplink/__init__.py
Expand Up @@ -36,6 +36,7 @@
ALIAS = "My Bulb"
MODEL = "HS100"
MAC_ADDRESS = "aa:bb:cc:dd:ee:ff"
DHCP_FORMATTED_MAC_ADDRESS = MAC_ADDRESS.replace(":", "")
MAC_ADDRESS2 = "11:22:33:44:55:66"
DEFAULT_ENTRY_TITLE = f"{ALIAS} {MODEL}"
CREDENTIALS_HASH_LEGACY = ""
Expand Down
60 changes: 54 additions & 6 deletions tests/components/tplink/test_config_flow.py
Expand Up @@ -33,6 +33,7 @@
DEFAULT_ENTRY_TITLE,
DEVICE_CONFIG_DICT_AUTH,
DEVICE_CONFIG_DICT_LEGACY,
DHCP_FORMATTED_MAC_ADDRESS,
IP_ADDRESS,
MAC_ADDRESS,
MAC_ADDRESS2,
Expand Down Expand Up @@ -144,6 +145,7 @@ async def test_discovery_auth(
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == DEFAULT_ENTRY_TITLE
assert result2["data"] == CREATE_ENTRY_DATA_AUTH
assert result2["context"]["unique_id"] == MAC_ADDRESS


@pytest.mark.parametrize(
Expand Down Expand Up @@ -206,6 +208,7 @@ async def test_discovery_auth_errors(
)
assert result3["type"] is FlowResultType.CREATE_ENTRY
assert result3["data"] == CREATE_ENTRY_DATA_AUTH
assert result3["context"]["unique_id"] == MAC_ADDRESS


async def test_discovery_new_credentials(
Expand Down Expand Up @@ -254,6 +257,7 @@ async def test_discovery_new_credentials(
)
assert result3["type"] is FlowResultType.CREATE_ENTRY
assert result3["data"] == CREATE_ENTRY_DATA_AUTH
assert result3["context"]["unique_id"] == MAC_ADDRESS


async def test_discovery_new_credentials_invalid(
Expand Down Expand Up @@ -309,6 +313,7 @@ async def test_discovery_new_credentials_invalid(
)
assert result3["type"] is FlowResultType.CREATE_ENTRY
assert result3["data"] == CREATE_ENTRY_DATA_AUTH
assert result3["context"]["unique_id"] == MAC_ADDRESS


async def test_discovery_with_existing_device_present(hass: HomeAssistant) -> None:
Expand Down Expand Up @@ -365,6 +370,7 @@ async def test_discovery_with_existing_device_present(hass: HomeAssistant) -> No
assert result3["type"] is FlowResultType.CREATE_ENTRY
assert result3["title"] == DEFAULT_ENTRY_TITLE
assert result3["data"] == CREATE_ENTRY_DATA_LEGACY
assert result3["context"]["unique_id"] == MAC_ADDRESS
await hass.async_block_till_done()

mock_setup_entry.assert_called_once()
Expand Down Expand Up @@ -432,6 +438,7 @@ async def test_manual(hass: HomeAssistant) -> None:
assert result4["type"] is FlowResultType.CREATE_ENTRY
assert result4["title"] == DEFAULT_ENTRY_TITLE
assert result4["data"] == CREATE_ENTRY_DATA_LEGACY
assert result4["context"]["unique_id"] == MAC_ADDRESS

# Duplicate
result = await hass.config_entries.flow.async_init(
Expand Down Expand Up @@ -470,6 +477,7 @@ async def test_manual_no_capabilities(hass: HomeAssistant) -> None:

assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == CREATE_ENTRY_DATA_LEGACY
assert result["context"]["unique_id"] == MAC_ADDRESS


async def test_manual_auth(
Expand Down Expand Up @@ -510,6 +518,7 @@ async def test_manual_auth(
assert result3["type"] is FlowResultType.CREATE_ENTRY
assert result3["title"] == DEFAULT_ENTRY_TITLE
assert result3["data"] == CREATE_ENTRY_DATA_AUTH
assert result3["context"]["unique_id"] == MAC_ADDRESS


@pytest.mark.parametrize(
Expand Down Expand Up @@ -572,6 +581,7 @@ async def test_manual_auth_errors(
)
assert result4["type"] is FlowResultType.CREATE_ENTRY
assert result4["data"] == CREATE_ENTRY_DATA_AUTH
assert result4["context"]["unique_id"] == MAC_ADDRESS

await hass.async_block_till_done()

Expand Down Expand Up @@ -599,7 +609,7 @@ async def test_discovered_by_discovery_and_dhcp(hass: HomeAssistant) -> None:
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress=MAC_ADDRESS, hostname=ALIAS
ip=IP_ADDRESS, macaddress=DHCP_FORMATTED_MAC_ADDRESS, hostname=ALIAS
),
)
await hass.async_block_till_done()
Expand All @@ -611,7 +621,7 @@ async def test_discovered_by_discovery_and_dhcp(hass: HomeAssistant) -> None:
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress="00:00:00:00:00:00", hostname="mock_hostname"
ip=IP_ADDRESS, macaddress="000000000000", hostname="mock_hostname"
),
)
await hass.async_block_till_done()
Expand All @@ -625,7 +635,7 @@ async def test_discovered_by_discovery_and_dhcp(hass: HomeAssistant) -> None:
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(
ip="1.2.3.5", macaddress="00:00:00:00:00:01", hostname="mock_hostname"
ip="1.2.3.5", macaddress="000000000001", hostname="mock_hostname"
),
)
await hass.async_block_till_done()
Expand All @@ -638,7 +648,9 @@ async def test_discovered_by_discovery_and_dhcp(hass: HomeAssistant) -> None:
[
(
config_entries.SOURCE_DHCP,
dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress=MAC_ADDRESS, hostname=ALIAS),
dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress=DHCP_FORMATTED_MAC_ADDRESS, hostname=ALIAS
),
),
(
config_entries.SOURCE_INTEGRATION_DISCOVERY,
Expand Down Expand Up @@ -675,6 +687,8 @@ async def test_discovered_by_dhcp_or_discovery(

assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["data"] == CREATE_ENTRY_DATA_LEGACY
assert result2["context"]["unique_id"] == MAC_ADDRESS

assert mock_async_setup.called
assert mock_async_setup_entry.called

Expand All @@ -684,7 +698,9 @@ async def test_discovered_by_dhcp_or_discovery(
[
(
config_entries.SOURCE_DHCP,
dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress=MAC_ADDRESS, hostname=ALIAS),
dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress=DHCP_FORMATTED_MAC_ADDRESS, hostname=ALIAS
),
),
(
config_entries.SOURCE_INTEGRATION_DISCOVERY,
Expand Down Expand Up @@ -713,7 +729,7 @@ async def test_discovered_by_dhcp_or_discovery_failed_to_get_device(
assert result["reason"] == "cannot_connect"


async def test_discovery_with_ip_change(
async def test_integration_discovery_with_ip_change(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_discovery: AsyncMock,
Expand Down Expand Up @@ -764,6 +780,36 @@ async def test_discovery_with_ip_change(
mock_connect["connect"].assert_awaited_once_with(config=config)


async def test_dhcp_discovery_with_ip_change(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_discovery: AsyncMock,
mock_connect: AsyncMock,
) -> None:
"""Test dhcp discovery with an IP change."""
mock_connect["connect"].side_effect = SmartDeviceException()
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state == config_entries.ConfigEntryState.SETUP_RETRY

flows = hass.config_entries.flow.async_progress()
assert len(flows) == 0
assert mock_config_entry.data[CONF_DEVICE_CONFIG] == DEVICE_CONFIG_DICT_LEGACY
assert mock_config_entry.data[CONF_DEVICE_CONFIG].get(CONF_HOST) == "127.0.0.1"

discovery_result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(
ip="127.0.0.2", macaddress=DHCP_FORMATTED_MAC_ADDRESS, hostname=ALIAS
),
)
assert discovery_result["type"] is FlowResultType.ABORT
assert discovery_result["reason"] == "already_configured"
assert mock_config_entry.data[CONF_HOST] == "127.0.0.2"


async def test_reauth(
hass: HomeAssistant,
mock_added_config_entry: MockConfigEntry,
Expand Down Expand Up @@ -1025,6 +1071,7 @@ async def test_pick_device_errors(
},
)
assert result4["type"] == FlowResultType.CREATE_ENTRY
assert result4["context"]["unique_id"] == MAC_ADDRESS


async def test_discovery_timeout_connect(
Expand All @@ -1049,6 +1096,7 @@ async def test_discovery_timeout_connect(
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["context"]["unique_id"] == MAC_ADDRESS
assert mock_connect["connect"].call_count == 1


Expand Down

0 comments on commit cd1c633

Please sign in to comment.