From 758e60a58de35247f6a71e87c003693129bb795d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 10 Sep 2020 16:19:11 -0500 Subject: [PATCH] Prevent missing integration from failing HomeKit startup (#39918) --- homeassistant/components/homekit/__init__.py | 11 ++- tests/components/homekit/test_homekit.py | 93 +++++++++++++++++++- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index e77d4ef134e..acc96397601 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -38,7 +38,7 @@ from homeassistant.helpers import device_registry, entity_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entityfilter import BASE_FILTER_SCHEMA, FILTER_SCHEMA from homeassistant.helpers.reload import async_integration_yaml_config -from homeassistant.loader import async_get_integration +from homeassistant.loader import IntegrationNotFound, async_get_integration from homeassistant.util import get_local_ip from .accessories import get_accessory @@ -712,8 +712,13 @@ class HomeKit: if dev_reg_ent.sw_version: ent_cfg[ATTR_SOFTWARE_VERSION] = dev_reg_ent.sw_version if ATTR_MANUFACTURER not in ent_cfg: - integration = await async_get_integration(self.hass, ent_reg_ent.platform) - ent_cfg[ATTR_INTERGRATION] = integration.name + try: + integration = await async_get_integration( + self.hass, ent_reg_ent.platform + ) + ent_cfg[ATTR_INTERGRATION] = integration.name + except IntegrationNotFound: + ent_cfg[ATTR_INTERGRATION] = ent_reg_ent.platform class HomeKitPairingQRView(HomeAssistantView): diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index 16473cd7b22..757281af1e9 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -807,6 +807,91 @@ async def test_homekit_finds_linked_batteries( ) +async def test_homekit_async_get_integration_fails( + hass, hk_driver, debounce_patcher, device_reg, entity_reg +): + """Test that we continue if async_get_integration fails.""" + entry = await async_init_integration(hass) + + homekit = HomeKit( + hass, + None, + None, + None, + {}, + {"light.demo": {}}, + DEFAULT_SAFE_MODE, + advertise_ip=None, + entry_id=entry.entry_id, + ) + homekit.driver = hk_driver + # pylint: disable=protected-access + homekit._filter = Mock(return_value=True) + homekit.bridge = HomeBridge(hass, hk_driver, "mock_bridge") + + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + sw_version="0.16.0", + model="Powerwall 2", + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + + binary_charging_sensor = entity_reg.async_get_or_create( + "binary_sensor", + "invalid_integration_does_not_exist", + "battery_charging", + device_id=device_entry.id, + device_class=DEVICE_CLASS_BATTERY_CHARGING, + ) + battery_sensor = entity_reg.async_get_or_create( + "sensor", + "invalid_integration_does_not_exist", + "battery", + device_id=device_entry.id, + device_class=DEVICE_CLASS_BATTERY, + ) + light = entity_reg.async_get_or_create( + "light", "invalid_integration_does_not_exist", "demo", device_id=device_entry.id + ) + + hass.states.async_set( + binary_charging_sensor.entity_id, + STATE_ON, + {ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY_CHARGING}, + ) + hass.states.async_set( + battery_sensor.entity_id, 30, {ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY} + ) + hass.states.async_set(light.entity_id, STATE_ON) + + def _mock_get_accessory(*args, **kwargs): + return [None, "acc", None] + + with patch.object(homekit.bridge, "add_accessory"), patch( + f"{PATH_HOMEKIT}.show_setup_message" + ), patch(f"{PATH_HOMEKIT}.get_accessory") as mock_get_acc, patch( + "pyhap.accessory_driver.AccessoryDriver.start_service" + ): + await homekit.async_start() + await hass.async_block_till_done() + + mock_get_acc.assert_called_with( + hass, + hk_driver, + ANY, + ANY, + { + "model": "Powerwall 2", + "sw_version": "0.16.0", + "platform": "invalid_integration_does_not_exist", + "linked_battery_charging_sensor": "binary_sensor.invalid_integration_does_not_exist_battery_charging", + "linked_battery_sensor": "sensor.invalid_integration_does_not_exist_battery", + }, + ) + + async def test_setup_imported(hass): """Test async_setup with imported config options.""" legacy_persist_file_path = hass.config.path(HOMEKIT_FILE) @@ -1222,7 +1307,13 @@ async def test_reload(hass): ) with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path), patch( f"{PATH_HOMEKIT}.HomeKit" - ) as mock_homekit2: + ) as mock_homekit2, patch.object(homekit.bridge, "add_accessory"), patch( + f"{PATH_HOMEKIT}.show_setup_message" + ), patch( + f"{PATH_HOMEKIT}.get_accessory" + ), patch( + "pyhap.accessory_driver.AccessoryDriver.start_service" + ): mock_homekit2.return_value = homekit = Mock() type(homekit).async_start = AsyncMock() await hass.services.async_call(