Skip to content

02 โ€” The Home Assistant Surface

Scope. Everything this integration exposes to Home Assistant and everything it takes from it: the four contract functions, the six entity platforms, the eighty services, and the eleven events โ€” which are the one part of that surface with no other owning document.

The rest of this page is a chooser. Each surface has its own document; what belongs here is how they relate and when to reach for which.


1. Four ways in, one way out

surface what it is for doc
config entry install, reload, unload, delete 39 ยท 44
services do something, or ask something โ€” eighty of them 36
entities continuous state, across six platforms 37
panels one sidebar entry per vacuum 44 ยง3
events this happened โ€” eleven, fire-and-forget ยง2 below

Services and entities are the two a person touches. Events are the one surface that is purely outbound: nothing in this integration listens to its own events, and nothing waits on one.


2. The event surface

Eleven distinct events, fired from 26 sites across the tree:

event fired meaning
eufy_vacuum_job_finished 6ร— a run reached a terminal state
eufy_vacuum_run_incomplete 5ร— it ended without covering the queue
eufy_vacuum_stall_detected 3ร— the robot appears stuck
eufy_vacuum_room_started 3ร— a room began
eufy_vacuum_room_finished 2ร— a room ended
eufy_vacuum_path_blocked 2ร— a route the run needed is closed
eufy_vacuum_room_skipped 1ร— a queued room was passed over
eufy_vacuum_room_completed 1ร— (see ยง3)
eufy_vacuum_external_run_pending 1ร— an app-started run is waiting for review โ€” 30
eufy_vacuum_job_progress_tick 1ร— a lightweight polling signal
eufy_vacuum_stall_captured 1ร— a stall capture was written โ€” 15

The card subscribes to five of the eleven โ€” job finished, run incomplete, room started, room finished, room completed. The other six exist for automations and for diagnosis, which is the point: the event surface is wider than the card's needs on purpose, because an automation is a first-class consumer and not an afterthought.

Two payload builders are shared rather than per-site โ€” services/_common.py::job_finished_event_payload and services/_common.py::run_incomplete_event_payload โ€” because the same event fires from several places and a per-site payload would drift into several shapes under one name.

A run's most important events fire more than once from different code paths. Job-finished has six fire sites because a run can end six ways (06); the shared builder is what keeps those six agreeing on what a finished job looks like.


3. Ten event names derive from the domain and one does not

Ten of the eleven are built as f"{DOMAIN}_โ€ฆ" in const.py. EVENT_ROOM_COMPLETED is a hardcoded string literal, and it lives in mapping/tracker.py rather than with the others.

โœ… CORRECTED 2026-08-23. EVENT_ROOM_COMPLETED and its neighbour EVENT_BOUNDARY_SAVED now live in const.py, derived from DOMAIN like their nine siblings. The derived strings are byte-identical to the literals they replace, so nothing changed on the wire and the card's subscription is unaffected โ€” verified by asserting the resolved values. mapping/tracker.py re-exports both, so existing importers keep working. All eleven outbound events now derive from one place.

Nothing is broken today โ€” the literal matches what the pattern would produce. But it is the one event name that would not follow if the domain changed, and it is the one that is not visible when reading the event block in const.py. Being defined away from its siblings is why it drifted from their shape.

โš  There is a second-order point worth stating, because it is easy to miss: the domain those ten derive from is itself re-exported from the brand package (45 ยง1). So ten public event names, subscribed to by automations in users' homes, are ultimately derived from a constant filed under one brand's folder.


4. Which surface answers which question

you want to use not
know a value continuously an entity polling a service
know the moment something happens an event watching an entity for a transition
change something a service writing to the store
ask something complex a service with a response โ€” 77 of 80 return one (36 ยง4) reading .storage
see everything at once the diagnostics download (40 ยง1) the dashboard snapshot, which has side effects

The last row is the one that catches people. The richest object in the system is the dashboard snapshot, and computing it can advance room timing and fire room-transition events during a live run โ€” so it is deliberately excluded from diagnostics, and reaching for it as a debugging shortcut perturbs the thing being debugged.


5. What Home Assistant owns, and what that costs

Three things this integration depends on and does not control:

  • The state machine. Every fact about the vacuum arrives as an entity state published by another integration. That integration may not have finished starting when setup runs, which is why setup runs a second time (39 ยง2).
  • The entity registry. Entity ids are a naming convention, not a contract โ€” users rename them and integrations change them โ€” which is why roles are resolved and rescued rather than assumed (34).
  • The store helper. It is the only supported path to disk; Home Assistant rewrites .storage from its own memory on shutdown, so a hand-edit is overwritten rather than merged (32 ยง1).

One thing worth knowing about HA's own behaviour, because a guard depended on the opposite: a service call naming an entity that does not exist does not raise. It logs a warning. A safety abort wrapped in except Exception around such a call is inert (42 ยง4).


6. Common wrong assumptions

assumption reality
the events are the card's API the card subscribes to five of eleven; the rest are for automations โ€” ยง2
an event fires from one place job-finished fires from six, which is why the payload builder is shared โ€” ยง2
the event names are all built the same way ten derive from the domain; one is a hardcoded literal in another file โ€” ยง3
the domain is the framework's it is re-exported from the brand package, and ten public event names derive from it โ€” ยง3
the dashboard snapshot is a good debugging read computing it has side effects during a live run โ€” ยง4
a missing service target raises HA logs a warning, and a guard relying on the exception never fires โ€” ยง5

Registries

00b-invariants.md ยท 00c-replicas.md