From 69319a7137a507ea43a03a33086a1cfd56ce252a Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Sep 2026 18:05:20 +0200 Subject: [PATCH] Disconnect homekit reload dispatcher when start did not finish (#183303) Co-authored-by: Claude --- homeassistant/components/homekit/__init__.py | 10 +++++----- tests/components/homekit/test_config_flow.py | 1 + tests/components/homekit/test_homekit.py | 16 ++++++++++++++++ tests/components/homekit/test_init.py | 1 + 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 243d52d12b6d..941f44876516 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -424,9 +424,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: HomeKitConfigEntry) -> async_dismiss_setup_message(hass, entry.entry_id) entry_data = entry.runtime_data homekit = entry_data.homekit - - if homekit.status == STATUS_RUNNING: - await homekit.async_stop() + await homekit.async_stop() logged_shutdown_wait = False for _ in range(SHUTDOWN_TIMEOUT): @@ -1144,12 +1142,14 @@ class HomeKit: async def async_stop(self, *args: Any) -> None: """Stop the accessory driver.""" + # The dispatcher is connected before the start can bail out early. + if self._cancel_reload_dispatcher: + self._cancel_reload_dispatcher() + self._cancel_reload_dispatcher = None if self.status != STATUS_RUNNING: return async with self._reset_lock: self.status = STATUS_STOPPED - assert self._cancel_reload_dispatcher is not None - self._cancel_reload_dispatcher() _LOGGER.debug("Driver stop for %s", self._name) if self.driver: await self.driver.async_stop() diff --git a/tests/components/homekit/test_config_flow.py b/tests/components/homekit/test_config_flow.py index d3fa5699beba..5c473f353521 100644 --- a/tests/components/homekit/test_config_flow.py +++ b/tests/components/homekit/test_config_flow.py @@ -401,6 +401,7 @@ async def test_options_flow_devices( with patch("homeassistant.components.homekit.HomeKit") as mock_homekit: mock_homekit.return_value = homekit = Mock(bridge=None, driver=None) type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await async_setup_component(hass, DOMAIN, {"homekit": {}}) assert await async_setup_component(hass, "homeassistant", {}) assert await async_setup_component(hass, "demo", {"demo": {}}) diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index 4866068315be..d2121e6b2ee4 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -33,6 +33,7 @@ from homeassistant.components.homekit.const import ( HOMEKIT_MODE_BRIDGE, SERVICE_HOMEKIT_RESET_ACCESSORY, SERVICE_HOMEKIT_UNPAIR, + SIGNAL_RELOAD_ENTITIES, ) from homeassistant.components.homekit.models import HomeKitEntryData from homeassistant.components.homekit.type_triggers import DeviceTriggerAccessory @@ -67,6 +68,7 @@ from homeassistant.helpers import ( entity_registry as er, instance_id, ) +from homeassistant.helpers.dispatcher import DATA_DISPATCHER from homeassistant.helpers.entityfilter import ( CONF_EXCLUDE_DOMAINS, CONF_EXCLUDE_ENTITIES, @@ -184,6 +186,7 @@ async def test_setup_min(hass: HomeAssistant) -> None: ): mock_homekit.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() @@ -229,6 +232,7 @@ async def test_removing_entry(port_mock, hass: HomeAssistant) -> None: ): mock_homekit.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() @@ -1849,6 +1853,7 @@ async def test_yaml_updates_update_config_entry_for_name(hass: HomeAssistant) -> ): mock_homekit.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await async_setup_component( hass, DOMAIN, {"homekit": {CONF_NAME: BRIDGE_NAME, CONF_PORT: 12345}} ) @@ -1897,6 +1902,7 @@ async def test_yaml_can_link_with_default_name(hass: HomeAssistant) -> None: ): mock_homekit.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await async_setup_component( hass, DOMAIN, @@ -1943,6 +1949,7 @@ async def test_yaml_can_link_with_port(hass: HomeAssistant) -> None: ): mock_homekit.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await async_setup_component( hass, DOMAIN, @@ -2432,11 +2439,13 @@ async def test_reload(mock_port_available: MagicMock, hass: HomeAssistant) -> No ): mock_homekit.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await async_setup_component( hass, DOMAIN, {"homekit": {CONF_NAME: "reloadable", CONF_PORT: 12345}} ) await hass.async_block_till_done() + old_homekit = homekit mock_homekit.assert_any_call( hass, "reloadable", @@ -2469,6 +2478,7 @@ async def test_reload(mock_port_available: MagicMock, hass: HomeAssistant) -> No ): mock_homekit2.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() await hass.services.async_call( "homekit", SERVICE_RELOAD, @@ -2477,6 +2487,7 @@ async def test_reload(mock_port_available: MagicMock, hass: HomeAssistant) -> No ) await hass.async_block_till_done() + old_homekit.async_stop.assert_awaited_once() mock_homekit2.assert_any_call( hass, "reloadable", @@ -2604,6 +2615,11 @@ async def test_homekit_start_in_accessory_mode_missing_entity( assert "entity not available" in caplog.text + signal = SIGNAL_RELOAD_ENTITIES.format(entry.entry_id) + assert hass.data[DATA_DISPATCHER][signal] + await homekit.async_stop() + assert not hass.data[DATA_DISPATCHER][signal] + @pytest.mark.usefixtures("mock_async_zeroconf") async def test_wait_for_port_to_free( diff --git a/tests/components/homekit/test_init.py b/tests/components/homekit/test_init.py index 1dc4ce16c053..f90734fa6a18 100644 --- a/tests/components/homekit/test_init.py +++ b/tests/components/homekit/test_init.py @@ -32,6 +32,7 @@ async def test_humanify_homekit_changed_event(hass: HomeAssistant, hk_driver) -> with patch("homeassistant.components.homekit.HomeKit") as mock_homekit: mock_homekit.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() + type(homekit).async_stop = AsyncMock() assert await async_setup_component(hass, DOMAIN, {"homekit": {}}) assert await async_setup_component(hass, "logbook", {}) await hass.async_block_till_done()