From 1a646f6011c34e79a1203f34737f5c2fa0c03e65 Mon Sep 17 00:00:00 2001 From: Colin Summers Date: Mon, 20 Jul 2026 12:48:15 -0400 Subject: [PATCH 1/2] Fix KeyError in device cleanup on Python 3.14 async_remove_device raises KeyError when the device has already been removed from the registry (e.g. by a prior iteration of the cleanup loop). Python 3.14's ReadOnlyDict.pop() surfaces this as an unhandled exception, causing the entire integration setup to fail with setup_error. Wrap the call in try/except to handle the race gracefully. Co-Authored-By: Claude Opus 4.6 --- custom_components/eero/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/custom_components/eero/__init__.py b/custom_components/eero/__init__.py index aff8889..615a183 100755 --- a/custom_components/eero/__init__.py +++ b/custom_components/eero/__init__.py @@ -346,7 +346,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b device_entry.name, device_entry.model, ) - device_registry.async_remove_device(device_entry.id) + try: + device_registry.async_remove_device(device_entry.id) + except (KeyError, ValueError): + pass else: for entity_entry in er.async_entries_for_device( entity_registry, device_entry.id From 7488d0783a3f95a24ecbeb46206114f51bd3c2cf Mon Sep 17 00:00:00 2001 From: Colin Summers Date: Mon, 20 Jul 2026 17:48:02 -0400 Subject: [PATCH 2/2] Fix device tracker zone counting by switching to BaseScannerEntity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HA 2026.7 changed zone/person tracking to rely on the in_zones state attribute. The old manual state/state_attributes approach doesn't set in_zones, so zone.home reports 0 persons for router-based trackers. BaseScannerEntity is the intended base class for connection-based trackers — it derives state from is_connected and handles in_zones automatically. The existing is_connected, source_type, ip_address, mac_address, and hostname properties are exactly what it needs. Closes #3 Co-Authored-By: Claude Opus 4.6 --- custom_components/eero/device_tracker.py | 32 +++--------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/custom_components/eero/device_tracker.py b/custom_components/eero/device_tracker.py index 1cc2766..478395f 100755 --- a/custom_components/eero/device_tracker.py +++ b/custom_components/eero/device_tracker.py @@ -5,21 +5,17 @@ from collections.abc import Mapping from dataclasses import dataclass from datetime import datetime, timedelta -from typing import Any, final +from typing import Any from homeassistant.components.device_tracker import ( - ATTR_HOST_NAME, - ATTR_IP, - ATTR_MAC, - ATTR_SOURCE_TYPE, + BaseScannerEntity, SourceType, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_MANUFACTURER, STATE_HOME, STATE_NOT_HOME +from homeassistant.const import ATTR_MANUFACTURER from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.util import dt as dt_util @@ -100,7 +96,7 @@ async def async_setup_entry( async_add_entities(entities) -class EeroDeviceTrackerEntity(EeroEntity): +class EeroDeviceTrackerEntity(EeroEntity, BaseScannerEntity): """Representation of an Eero device tracker entity.""" _attr_entity_category = EntityCategory.DIAGNOSTIC @@ -176,26 +172,6 @@ def hostname(self) -> str | None: return self.resource.hostname return None - @property - def state(self) -> str: - """Return the state of the device.""" - if self.is_connected: - return STATE_HOME - return STATE_NOT_HOME - - @final - @property - def state_attributes(self) -> dict[str, StateType]: - """Return the device state attributes.""" - attr: dict[str, StateType] = {ATTR_SOURCE_TYPE: self.source_type} - if ip_address := self.ip_address: - attr[ATTR_IP] = ip_address - if mac_address := self.mac_address: - attr[ATTR_MAC] = mac_address - if hostname := self.hostname: - attr[ATTR_HOST_NAME] = hostname - return attr - @property def extra_state_attributes(self) -> Mapping[str, Any] | None: """Return entity specific state attributes.