From 50256a4a09a954326022920d4ed1837a6ff78f71 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 31 Jan 2025 19:56:38 -0600 Subject: [PATCH 1/4] Allow ignored govee-ble devices to be setup up from the user flow Every few days we get an issue report about a device a user ignored and forgot about, and than can no longer get set up. Allow ignored devices to be selected in the user step and replace the ignored entry. --- homeassistant/components/govee_ble/config_flow.py | 4 ++-- homeassistant/config_entries.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/govee_ble/config_flow.py b/homeassistant/components/govee_ble/config_flow.py index 2cc47435abf29a..3b99c7ebf8688f 100644 --- a/homeassistant/components/govee_ble/config_flow.py +++ b/homeassistant/components/govee_ble/config_flow.py @@ -71,14 +71,14 @@ async def async_step_user( if user_input is not None: address = user_input[CONF_ADDRESS] await self.async_set_unique_id(address, raise_on_progress=False) - self._abort_if_unique_id_configured() + self._abort_if_unique_id_configured(include_ignore=False) device, service_info = self._discovered_devices[address] title = device.title or device.get_device_name() or service_info.name return self.async_create_entry( title=title, data={CONF_DEVICE_TYPE: device.device_type} ) - current_addresses = self._async_current_ids() + current_addresses = self._async_current_ids(include_ignore=False) for discovery_info in async_discovered_service_info(self.hass, False): address = discovery_info.address if address in current_addresses or address in self._discovered_devices: diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 620e4bc81977f9..d18b68b7fc6f49 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -2619,6 +2619,7 @@ def _abort_if_unique_id_configured( reload_on_update: bool = True, *, error: str = "already_configured", + include_ignore: bool = True, ) -> None: """Abort if the unique ID is already configured. @@ -2635,6 +2636,9 @@ def _abort_if_unique_id_configured( ): return + if include_ignore and entry.source == SOURCE_IGNORE: + return + should_reload = False if ( updates is not None From adf4314871bb441dbb9d4dd176385d7c6ebf4b24 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 31 Jan 2025 20:04:04 -0600 Subject: [PATCH 2/4] Add the ability to skip ignored config entries when calling _abort_if_unique_id_configured see https://github.com/home-assistant/core/pull/137052 --- homeassistant/config_entries.py | 4 ++++ tests/test_config_entries.py | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 620e4bc81977f9..80c3c11f839c6f 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -2619,6 +2619,7 @@ def _abort_if_unique_id_configured( reload_on_update: bool = True, *, error: str = "already_configured", + include_ignore: bool = True, ) -> None: """Abort if the unique ID is already configured. @@ -2635,6 +2636,9 @@ def _abort_if_unique_id_configured( ): return + if include_ignore is False and entry.source == SOURCE_IGNORE: + return + should_reload = False if ( updates is not None diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 3ea1a16e89835c..3646c4e2d0e5af 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -4693,6 +4693,45 @@ async def async_step_user(self, user_input=None): assert entry.state is config_entries.ConfigEntryState.LOADED +async def test_abort_if_unique_id_configured_can_skip_ignored( + hass: HomeAssistant, manager: config_entries.ConfigEntries +) -> None: + """Test _abort_if_unique_id_configured can skip ignored entries.""" + hass.config.components.add("comp") + mock_platform(hass, "comp.config_flow", None) + + entry = MockConfigEntry( + domain="comp", + data={}, + unique_id="mock-unique-id", + source=config_entries.SOURCE_IGNORE, + state=config_entries.ConfigEntryState.NOT_LOADED, + ) + entry.add_to_hass(hass) + + class TestFlow(config_entries.ConfigFlow): + """Test flow.""" + + VERSION = 1 + + async def async_step_user(self, user_input=None): + """Test user step.""" + await self.async_set_unique_id("mock-unique-id") + self._abort_if_unique_id_configured(include_ignore=False) + return self.async_abort(reason="made_it_past_abort_if_unique_id_configured") + + with ( + mock_config_flow("comp", TestFlow), + ): + result = await manager.flow.async_init( + "comp", context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "made_it_past_abort_if_unique_id_configured" + + async def test_disallow_entry_reload_with_setup_in_progress( hass: HomeAssistant, manager: config_entries.ConfigEntries ) -> None: From 3735b015e46e81c7ea76677ddb8d193cedc3b3ab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 31 Jan 2025 20:10:24 -0600 Subject: [PATCH 3/4] coverage --- .../components/govee_ble/test_config_flow.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/components/govee_ble/test_config_flow.py b/tests/components/govee_ble/test_config_flow.py index eb0719f832cd26..ac8970ca9772bd 100644 --- a/tests/components/govee_ble/test_config_flow.py +++ b/tests/components/govee_ble/test_config_flow.py @@ -79,6 +79,38 @@ async def test_async_step_user_with_found_devices(hass: HomeAssistant) -> None: assert result2["result"].unique_id == "4125DDBA-2774-4851-9889-6AADDD4CAC3D" +async def test_async_step_user_replace_ignored_device(hass: HomeAssistant) -> None: + """Test setup user step can replace an ignored device.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=GVH5177_SERVICE_INFO.address, + data={}, + source=config_entries.SOURCE_IGNORE, + ) + entry.add_to_hass(hass) + with patch( + "homeassistant.components.govee_ble.config_flow.async_discovered_service_info", + return_value=[GVH5177_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + with patch( + "homeassistant.components.govee_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": "4125DDBA-2774-4851-9889-6AADDD4CAC3D"}, + ) + assert result2["type"] is FlowResultType.CREATE_ENTRY + assert result2["title"] == "H5177 2EC8" + assert result2["data"] == {CONF_DEVICE_TYPE: "H5177"} + assert result2["result"].unique_id == "4125DDBA-2774-4851-9889-6AADDD4CAC3D" + + async def test_async_step_user_device_added_between_steps(hass: HomeAssistant) -> None: """Test the device gets added via another flow between steps.""" with patch( From 4ac70fa24087d24a125a528211bae2ef0d36a1bd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 31 Jan 2025 20:13:02 -0600 Subject: [PATCH 4/4] revert --- .../components/govee_ble/config_flow.py | 2 +- homeassistant/config_entries.py | 4 -- tests/test_config_entries.py | 39 ------------------- 3 files changed, 1 insertion(+), 44 deletions(-) diff --git a/homeassistant/components/govee_ble/config_flow.py b/homeassistant/components/govee_ble/config_flow.py index 3b99c7ebf8688f..d48fffdd633d52 100644 --- a/homeassistant/components/govee_ble/config_flow.py +++ b/homeassistant/components/govee_ble/config_flow.py @@ -71,7 +71,7 @@ async def async_step_user( if user_input is not None: address = user_input[CONF_ADDRESS] await self.async_set_unique_id(address, raise_on_progress=False) - self._abort_if_unique_id_configured(include_ignore=False) + self._abort_if_unique_id_configured() device, service_info = self._discovered_devices[address] title = device.title or device.get_device_name() or service_info.name return self.async_create_entry( diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 80c3c11f839c6f..620e4bc81977f9 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -2619,7 +2619,6 @@ def _abort_if_unique_id_configured( reload_on_update: bool = True, *, error: str = "already_configured", - include_ignore: bool = True, ) -> None: """Abort if the unique ID is already configured. @@ -2636,9 +2635,6 @@ def _abort_if_unique_id_configured( ): return - if include_ignore is False and entry.source == SOURCE_IGNORE: - return - should_reload = False if ( updates is not None diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 3646c4e2d0e5af..3ea1a16e89835c 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -4693,45 +4693,6 @@ async def async_step_user(self, user_input=None): assert entry.state is config_entries.ConfigEntryState.LOADED -async def test_abort_if_unique_id_configured_can_skip_ignored( - hass: HomeAssistant, manager: config_entries.ConfigEntries -) -> None: - """Test _abort_if_unique_id_configured can skip ignored entries.""" - hass.config.components.add("comp") - mock_platform(hass, "comp.config_flow", None) - - entry = MockConfigEntry( - domain="comp", - data={}, - unique_id="mock-unique-id", - source=config_entries.SOURCE_IGNORE, - state=config_entries.ConfigEntryState.NOT_LOADED, - ) - entry.add_to_hass(hass) - - class TestFlow(config_entries.ConfigFlow): - """Test flow.""" - - VERSION = 1 - - async def async_step_user(self, user_input=None): - """Test user step.""" - await self.async_set_unique_id("mock-unique-id") - self._abort_if_unique_id_configured(include_ignore=False) - return self.async_abort(reason="made_it_past_abort_if_unique_id_configured") - - with ( - mock_config_flow("comp", TestFlow), - ): - result = await manager.flow.async_init( - "comp", context={"source": config_entries.SOURCE_USER} - ) - await hass.async_block_till_done() - - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "made_it_past_abort_if_unique_id_configured" - - async def test_disallow_entry_reload_with_setup_in_progress( hass: HomeAssistant, manager: config_entries.ConfigEntries ) -> None: