Skip to main content

Clean up stale devices

Reasoning

When a device is removed from a hub or account, it should also be removed from Home Assistant. This way, the user interface will not show devices that are no longer available.

We should only remove devices that we are sure are no longer available. If you can't be sure if a device is still available, be sure to implement async_remove_config_entry_device. This allows the user to delete the device from the device registry manually.

Example implementation

In this example, we have a coordinator that fetches data from a service. When the data is updated, we check if any devices have been removed. If so, we remove them from the device registry. This also causes all entities associated with the device to be removed.

coordinator.py

class MyCoordinator(DataUpdateCoordinator[dict[str, MyDevice]]):
"""Class to manage fetching data."""

def __init__(self, hass: HomeAssistant, client: MyClient) -> None:
"""Initialize coordinator."""
super().__init__(
hass,
logger=LOGGER,
name=DOMAIN,
update_interval=timedelta(minutes=1),
)
self.client = client
self.previous_devices: set[str] = set()

async def _async_update_data(self) -> dict[str, MyDevice]:
try:
data = await self.client.get_data()
except MyException as ex:
raise UpdateFailed(f"The service is unavailable: {ex}")
current_devices = set(data)
if (stale_devices := self.previous_devices - current_devices):
device_registry = dr.async_get(self.hass)
for device_id in stale_devices:
device = device_registry.async_get(identifiers={(DOMAIN, device_id)})
if device:
device_registry.async_update_device(
device_id=device.id,
remove_config_entry_id=self.config_entry.entry_id,
)
self.previous_devices = current_devices
return data

To show a second example where someone can delete the device from the device registry manually, we implement async_remove_config_entry_device in __init__.py. Having this function defined will enable the delete button on the device page in the UI. In this example, the integration is only able to get updates for a device and not get a full list of connected devices, hence it can't automatically delete devices. In async_remove_config_entry_device, we should implement a function that checks if the device is still available. If it is not, we return True to allow the user to delete the device manually. Here, we assume that the device is not working if we haven't got any updates for it in a while.

__init__.py

async def async_remove_config_entry_device(
hass: HomeAssistant, config_entry: MyConfigEntry, device_entry: dr.DeviceEntry
) -> bool:
"""Remove a config entry from a device."""
return not any(
identifier
for identifier in device_entry.identifiers
if identifier[0] == DOMAIN
and identifier[1] in config_entry.runtime_data.data
)

Additional resources

For more info on devices, checkout the device registry documentation.

Exceptions

There are no exceptions to this rule.