From 393dee1524636c2d30f7d8ea67bfcde17937984c Mon Sep 17 00:00:00 2001 From: Steven B <51370195+sdb9696@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:24:44 +0000 Subject: [PATCH] Handle IP address changes properly for tplink (#108731) * Update device config for SETUP_RETRY and use CONF_HOST on startup * Make entry state checks use a constant Co-authored-by: J. Nick Koston * Update tests --------- Co-authored-by: J. Nick Koston --- homeassistant/components/tplink/__init__.py | 2 + .../components/tplink/config_flow.py | 7 ++- tests/components/tplink/test_config_flow.py | 53 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tplink/__init__.py b/homeassistant/components/tplink/__init__.py index 4b684abf280..e2342e617de 100644 --- a/homeassistant/components/tplink/__init__.py +++ b/homeassistant/components/tplink/__init__.py @@ -143,6 +143,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if not config: config = DeviceConfig(host) + else: + config.host = host config.timeout = CONNECT_TIMEOUT if config.uses_http is True: diff --git a/homeassistant/components/tplink/config_flow.py b/homeassistant/components/tplink/config_flow.py index 68a40d81415..96d720e59a0 100644 --- a/homeassistant/components/tplink/config_flow.py +++ b/homeassistant/components/tplink/config_flow.py @@ -78,8 +78,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def _update_config_if_entry_in_setup_error( self, entry: ConfigEntry, host: str, config: dict ) -> None: - """If discovery encounters a device that is in SETUP_ERROR update the device config.""" - if entry.state is not ConfigEntryState.SETUP_ERROR: + """If discovery encounters a device that is in SETUP_ERROR or SETUP_RETRY update the device config.""" + if entry.state not in ( + ConfigEntryState.SETUP_ERROR, + ConfigEntryState.SETUP_RETRY, + ): return entry_data = entry.data entry_config_dict = entry_data.get(CONF_DEVICE_CONFIG) diff --git a/tests/components/tplink/test_config_flow.py b/tests/components/tplink/test_config_flow.py index 96cfbead5e4..18e22db60f4 100644 --- a/tests/components/tplink/test_config_flow.py +++ b/tests/components/tplink/test_config_flow.py @@ -10,6 +10,7 @@ from homeassistant.components.tplink import ( DOMAIN, AuthenticationException, Credentials, + DeviceConfig, SmartDeviceException, ) from homeassistant.components.tplink.const import CONF_DEVICE_CONFIG @@ -36,6 +37,7 @@ from . import ( MAC_ADDRESS, MAC_ADDRESS2, MODULE, + _mocked_bulb, _patch_connect, _patch_discovery, _patch_single_discovery, @@ -736,6 +738,57 @@ async def test_discovered_by_dhcp_or_discovery_failed_to_get_device( assert result["reason"] == "cannot_connect" +async def test_discovery_with_ip_change( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_discovery: AsyncMock, + mock_connect: AsyncMock, +) -> None: + """Test reauth flow.""" + 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_INTEGRATION_DISCOVERY}, + data={ + CONF_HOST: "127.0.0.2", + CONF_MAC: MAC_ADDRESS, + CONF_ALIAS: ALIAS, + CONF_DEVICE_CONFIG: DEVICE_CONFIG_DICT_AUTH, + }, + ) + await hass.async_block_till_done() + assert discovery_result["type"] is FlowResultType.ABORT + assert discovery_result["reason"] == "already_configured" + assert mock_config_entry.data[CONF_DEVICE_CONFIG] == DEVICE_CONFIG_DICT_AUTH + assert mock_config_entry.data[CONF_HOST] == "127.0.0.2" + + config = DeviceConfig.from_dict(DEVICE_CONFIG_DICT_AUTH) + + mock_connect["connect"].reset_mock(side_effect=True) + bulb = _mocked_bulb( + device_config=config, + mac=mock_config_entry.unique_id, + ) + mock_connect["connect"].return_value = bulb + await hass.config_entries.async_reload(mock_config_entry.entry_id) + await hass.async_block_till_done() + assert mock_config_entry.state == config_entries.ConfigEntryState.LOADED + # Check that init set the new host correctly before calling connect + assert config.host == "127.0.0.1" + config.host = "127.0.0.2" + mock_connect["connect"].assert_awaited_once_with(config=config) + + async def test_reauth( hass: HomeAssistant, mock_added_config_entry: MockConfigEntry,