265 Dead Entities: Cleaning Up a Home Assistant Registry
Over time, your smart home accumulates ghosts. Devices you removed months ago still have entries in the entity registry. Integrations you disabled leave behind orphaned entities. After a year of tinkering with Home Assistant, I had 265 entities that either didn't exist, were duplicated, or pointed to removed devices.
The Accumulation Problem
Every time you add a Zigbee device, set up an integration, or create a helper entity, Home Assistant registers it. But when you remove the device or disable the integration, the registry entry often stays. Over time, this builds up:
- Unavailable entities: 100+ entities showing as "unavailable" because their devices were removed
- Duplicates: Entities created twice due to re-pairing or integration re-setup
- Stale integrations: Entities from integrations that were disabled months ago
- Test artifacts: Entities created during testing that were never cleaned up
The UI lets you delete entities one at a time, but 265 clicks is not how I want to spend my evening.
The WebSocket API: Your Scalpel
Home Assistant exposes a WebSocket API that gives you programmatic access to the entity registry. This is different from the REST API — it's a persistent WebSocket connection that accepts JSON commands.
The connection is straightforward:
import websocket, json
ws = websocket.create_connection(
"ws://homeassistant.local:8123/api/websocket",
header={"Authorization": f"Bearer {TOKEN}"}
)
# Auth is handled by the first message
auth_req = json.loads(ws.recv())
ws.send(json.dumps({"type": "auth", "access_token": TOKEN}))
auth_result = json.loads(ws.recv())
Listing All Entities
Once connected, you can list the entire entity registry:
ws.send(json.dumps({
"type": "config/entity_registry/list"
}))
entities = json.loads(ws.recv())["result"]
This returns every entity with its metadata: entity_id, platform, device_id, disabled_by, and more. I filtered for entities that were either unavailable, disabled, or had no corresponding device.
The Safety Checks
Bulk deletion is dangerous. Before removing anything, I ran three safety checks:
- Is the entity currently in use by any automation? Cross-referenced against all automation configs
- Is the entity referenced in any dashboard? Checked all Lovelace dashboard configurations
- Is the entity disabled vs truly orphaned? Disabled entities might be intentionally disabled; orphaned ones are the target
Entities that passed all three checks were added to the deletion list. This reduced the count from 265 to 198 actually-safe-to-remove entities. The remaining 67 were either in use or intentionally disabled.
Bulk Removal
The actual removal uses the entity registry's remove command:
for entity_id in to_remove:
ws.send(json.dumps({
"type": "config/entity_registry/remove",
"entity_id": entity_id
}))
result = json.loads(ws.recv())
if result.get("success"):
print(f"Removed: {entity_id}")
else:
print(f"Failed: {entity_id} — {result}")
The whole operation took about 30 seconds. 198 entities removed successfully, 0 failures.
The Result
After cleanup:
- Entity count dropped by 40%
- Dashboard loading time improved noticeably
- No more "unavailable" clutter in the entity list
- Zero broken automations (the safety checks worked)
Lessons
- The WebSocket API is powerful. Home Assistant's REST API gets all the attention, but the WebSocket API is where the real registry operations live.
- Always check dependencies before removing. Automations and dashboards will silently break if you remove entities they reference.
- Do this quarterly. Entity rot is inevitable. A quarterly cleanup keeps things manageable.
- Script it. 265 manual clicks is unacceptable. 30 seconds of script is not.
I clean up messy Home Assistant installations — entity registry, automations, dashboards. One session, real results.
Work with me →