diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 14d69e278fa..a52b869b830 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -29,6 +29,7 @@ from .exceptions import ( HomeAssistantError, ) from .helpers import device_registry, entity_registry, storage +from .helpers.debounce import Debouncer from .helpers.dispatcher import async_dispatcher_send from .helpers.event import ( RANDOM_MICROSECOND_MAX, @@ -88,6 +89,8 @@ PATH_CONFIG = ".config_entries.json" SAVE_DELAY = 1 +DISCOVERY_COOLDOWN = 1 + _R = TypeVar("_R") @@ -808,6 +811,13 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager): self._hass_config = hass_config self._pending_import_flows: dict[str, dict[str, asyncio.Future[None]]] = {} self._initialize_tasks: dict[str, list[asyncio.Task]] = {} + self._discovery_debouncer = Debouncer( + hass, + _LOGGER, + cooldown=DISCOVERY_COOLDOWN, + immediate=True, + function=self._async_discovery, + ) async def async_wait_import_flow_initialized(self, handler: str) -> None: """Wait till all import flows in progress are initialized.""" @@ -883,6 +893,7 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager): for task_list in self._initialize_tasks.values(): for task in task_list: task.cancel() + await self._discovery_debouncer.async_shutdown() async def async_finish_flow( self, flow: data_entry_flow.FlowHandler, result: data_entry_flow.FlowResult @@ -979,16 +990,7 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager): # Create notification. if source in DISCOVERY_SOURCES: - self.hass.bus.async_fire(EVENT_FLOW_DISCOVERED) - persistent_notification.async_create( - self.hass, - title="New devices discovered", - message=( - "We have discovered new devices on your network. " - "[Check it out](/config/integrations)." - ), - notification_id=DISCOVERY_NOTIFICATION_ID, - ) + await self._discovery_debouncer.async_call() elif source == SOURCE_REAUTH: persistent_notification.async_create( self.hass, @@ -1000,6 +1002,20 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager): notification_id=RECONFIGURE_NOTIFICATION_ID, ) + @callback + def _async_discovery(self) -> None: + """Handle discovery.""" + self.hass.bus.async_fire(EVENT_FLOW_DISCOVERED) + persistent_notification.async_create( + self.hass, + title="New devices discovered", + message=( + "We have discovered new devices on your network. " + "[Check it out](/config/integrations)." + ), + notification_id=DISCOVERY_NOTIFICATION_ID, + ) + class ConfigEntries: """Manage the configuration entries. diff --git a/tests/common.py b/tests/common.py index 38b7cf79b75..9c8ed5bb56e 100644 --- a/tests/common.py +++ b/tests/common.py @@ -248,6 +248,9 @@ async def async_test_home_assistant(event_loop, load_registries=True): ) }, ) + hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, hass.config_entries._async_shutdown + ) # Load the registries entity.async_setup(hass) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index b20f2af7669..68e6fc59987 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -68,9 +68,10 @@ def mock_handlers() -> Generator[None, None, None]: @pytest.fixture -def manager(hass: HomeAssistant) -> config_entries.ConfigEntries: +async def manager(hass: HomeAssistant) -> config_entries.ConfigEntries: """Fixture of a loaded config manager.""" manager = config_entries.ConfigEntries(hass, {}) + await manager.async_initialize() hass.config_entries = manager return manager @@ -712,7 +713,9 @@ async def test_forward_entry_does_not_setup_entry_if_setup_fails( assert len(mock_setup_entry.mock_calls) == 0 -async def test_discovery_notification(hass: HomeAssistant) -> None: +async def test_discovery_notification( + hass: HomeAssistant, manager: config_entries.ConfigEntries +) -> None: """Test that we create/dismiss a notification when source is discovery.""" mock_integration(hass, MockModule("test")) mock_entity_platform(hass, "config_flow.test", None) @@ -1052,7 +1055,9 @@ async def test_setup_does_not_retry_during_shutdown(hass: HomeAssistant) -> None assert len(mock_setup_entry.mock_calls) == 1 -async def test_create_entry_options(hass: HomeAssistant) -> None: +async def test_create_entry_options( + hass: HomeAssistant, manager: config_entries.ConfigEntries +) -> None: """Test a config entry being created with options.""" async def mock_async_setup(hass, config): @@ -2482,7 +2487,9 @@ async def test_partial_flows_hidden( assert "config_entry_discovery" in notifications -async def test_async_setup_init_entry(hass: HomeAssistant) -> None: +async def test_async_setup_init_entry( + hass: HomeAssistant, manager: config_entries.ConfigEntries +) -> None: """Test a config entry being initialized during integration setup.""" async def mock_async_setup(hass: HomeAssistant, config: ConfigType) -> bool: @@ -2527,7 +2534,7 @@ async def test_async_setup_init_entry(hass: HomeAssistant) -> None: async def test_async_setup_init_entry_completes_before_loaded_event_fires( - hass: HomeAssistant, + hass: HomeAssistant, manager: config_entries.ConfigEntries ) -> None: """Test a config entry being initialized during integration setup before the loaded event fires.""" load_events: list[Event] = []