diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 0a208e012fb..adbf79128e3 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -627,12 +627,14 @@ class HomeKit: ent_cfg = self._config.setdefault(entity_id, {}) if ent_reg_ent.device_id: dev_reg_ent = dev_reg.async_get(ent_reg_ent.device_id) - if dev_reg_ent.manufacturer: - ent_cfg[ATTR_MANUFACTURER] = dev_reg_ent.manufacturer - if dev_reg_ent.model: - ent_cfg[ATTR_MODEL] = dev_reg_ent.model - if dev_reg_ent.sw_version: - ent_cfg[ATTR_SOFTWARE_VERSION] = dev_reg_ent.sw_version + if dev_reg_ent is not None: + # Handle missing devices + if dev_reg_ent.manufacturer: + ent_cfg[ATTR_MANUFACTURER] = dev_reg_ent.manufacturer + if dev_reg_ent.model: + ent_cfg[ATTR_MODEL] = dev_reg_ent.model + 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 diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index 8a4ac87f21b..c0e2ea90fba 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -913,3 +913,83 @@ def _write_data(path: str, data: Dict) -> None: if not os.path.isdir(os.path.dirname(path)): os.makedirs(os.path.dirname(path)) json_util.save_json(path, data) + + +async def test_homekit_ignored_missing_devices( + hass, hk_driver, debounce_patcher, device_reg, entity_reg +): + """Test HomeKit handles a device in the entity registry but missing from the device registry.""" + entry = await async_init_integration(hass) + + homekit = HomeKit( + hass, + None, + None, + None, + {}, + {"light.demo": {}}, + DEFAULT_SAFE_MODE, + advertise_ip=None, + interface_choice=None, + entry_id=entry.entry_id, + ) + homekit.driver = hk_driver + 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", + manufacturer="Tesla", + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + + entity_reg.async_get_or_create( + "binary_sensor", + "powerwall", + "battery_charging", + device_id=device_entry.id, + device_class=DEVICE_CLASS_BATTERY_CHARGING, + ) + entity_reg.async_get_or_create( + "sensor", + "powerwall", + "battery", + device_id=device_entry.id, + device_class=DEVICE_CLASS_BATTERY, + ) + light = entity_reg.async_get_or_create( + "light", "powerwall", "demo", device_id=device_entry.id + ) + + # Delete the device to make sure we fallback + # to using the platform + device_reg.async_remove_device(device_entry.id) + + 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" + ): + await homekit.async_start() + await hass.async_block_till_done() + + mock_get_acc.assert_called_with( + hass, + hk_driver, + ANY, + ANY, + { + "platform": "Tesla Powerwall", + "linked_battery_charging_sensor": "binary_sensor.powerwall_battery_charging", + "linked_battery_sensor": "sensor.powerwall_battery", + }, + )