diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 46fd3e5e522..c0cc5867799 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -599,7 +599,8 @@ class HomeKit: await self.hass.async_add_executor_job(self.setup, async_zc_instance) self.aid_storage = AccessoryAidStorage(self.hass, self._entry_id) await self.aid_storage.async_initialize() - await self._async_create_accessories() + if not await self._async_create_accessories(): + return self._async_register_bridge() _LOGGER.debug("Driver start for %s", self._name) await self.driver.async_start() @@ -666,6 +667,13 @@ class HomeKit: """Create the accessories.""" entity_states = await self.async_configure_accessories() if self._homekit_mode == HOMEKIT_MODE_ACCESSORY: + if not entity_states: + _LOGGER.error( + "HomeKit %s cannot startup: entity not available: %s", + self._name, + self._filter.config, + ) + return False state = entity_states[0] conf = self._config.pop(state.entity_id, {}) acc = get_accessory(self.hass, self.driver, state, STANDALONE_AID, conf) @@ -677,6 +685,7 @@ class HomeKit: # No need to load/persist as we do it in setup self.driver.accessory = acc + return True async def async_stop(self, *args): """Stop the accessory driver.""" diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index bd7af3b3596..5e9ea4fd4b6 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -54,7 +54,15 @@ from homeassistant.const import ( ) from homeassistant.core import State from homeassistant.helpers import device_registry -from homeassistant.helpers.entityfilter import generate_filter +from homeassistant.helpers.entityfilter import ( + CONF_EXCLUDE_DOMAINS, + CONF_EXCLUDE_ENTITIES, + CONF_EXCLUDE_ENTITY_GLOBS, + CONF_INCLUDE_DOMAINS, + CONF_INCLUDE_ENTITIES, + CONF_INCLUDE_ENTITY_GLOBS, + convert_filter, +) from homeassistant.setup import async_setup_component from homeassistant.util import json as json_util @@ -65,6 +73,27 @@ from tests.common import MockConfigEntry, mock_device_registry, mock_registry IP_ADDRESS = "127.0.0.1" +def generate_filter( + include_domains, + include_entities, + exclude_domains, + exclude_entites, + include_globs=None, + exclude_globs=None, +): + """Generate an entity filter using the standard method.""" + return convert_filter( + { + CONF_INCLUDE_DOMAINS: include_domains, + CONF_INCLUDE_ENTITIES: include_entities, + CONF_EXCLUDE_DOMAINS: exclude_domains, + CONF_EXCLUDE_ENTITIES: exclude_entites, + CONF_INCLUDE_ENTITY_GLOBS: include_globs or [], + CONF_EXCLUDE_ENTITY_GLOBS: exclude_globs or [], + } + ) + + @pytest.fixture(autouse=True) def always_patch_driver(hk_driver): """Load the hk_driver fixture.""" @@ -1173,6 +1202,31 @@ async def test_homekit_start_in_accessory_mode( assert homekit.status == STATUS_RUNNING +async def test_homekit_start_in_accessory_mode_missing_entity( + hass, hk_driver, mock_zeroconf, device_reg, caplog +): + """Test HomeKit start method in accessory mode when entity is not available.""" + entry = await async_init_integration(hass) + + homekit = _mock_homekit(hass, entry, HOMEKIT_MODE_ACCESSORY) + + homekit.bridge = Mock() + homekit.bridge.accessories = [] + homekit.driver = hk_driver + homekit.driver.accessory = Accessory(hk_driver, "any") + + with patch(f"{PATH_HOMEKIT}.HomeKit.add_bridge_accessory") as mock_add_acc, patch( + f"{PATH_HOMEKIT}.show_setup_message" + ), patch("pyhap.accessory_driver.AccessoryDriver.async_start"): + await homekit.async_start() + + await hass.async_block_till_done() + mock_add_acc.assert_not_called() + assert homekit.status == STATUS_WAIT + + assert "entity not available" in caplog.text + + async def test_wait_for_port_to_free(hass, hk_driver, mock_zeroconf, caplog): """Test we wait for the port to free before declaring unload success.""" await async_setup_component(hass, "persistent_notification", {})