Skip to main content

Events reference

Home Keeper fires a Home Assistant bus event for every state change: a task is created, edited, completed, deleted, or crosses into overdue or due-soon. It also fires on spare-part stock transitions (low stock, out of stock, restocked) and appliance changes (added, changed, removed). This is the surface automations and other integrations build on.

You can react to these events two ways:

  1. Visual automation editor (device triggers). On a Home Keeper appliance, Add automation → When lists Home Keeper triggers like “Task became overdue” or “Spare part out of stock”. No need to know the event name. These are scoped to that device.

    Device triggers are offered on devices Home Keeper owns, meaning its appliances. They are not offered on a device another integration owns that a task is merely attached to. Home Assistant builds that menu from the integrations a device belongs to, and since HA 2026.8 a device belongs to exactly one (see DESIGN.md → "Device attachment"). For those tasks, automate on the task's own entities (binary_sensor.<task>_overdue, sensor.<task>_next_due) or use the event trigger below. Both reach the same events.

  2. Event trigger (any automation). For global automations (“any part low → add to one shopping list”), use a plain platform: event trigger on the event name.

Which events exist, what each one's payload holds, and which are offered as device triggers are all generated from the integration itself and listed in the API reference. This document is the other half: what each event means in context, and what a restart does to it.

Integrators pushing tasks into Home Keeper should also read INTEGRATING.md.

What the events mean

All event names follow home_keeper_<noun>_<verb>. Task events share a common spine. Stock events share one shape and asset events share another. The API reference lists every event with its payload. This section covers the behaviour a table can't express.

Task lifecycle

Only next_due moves when a task is snoozed. The recurrence stays the same. Setting a task due today is the mirror image: next_due moves to now instead of later, with the same "recurrence untouched" guarantee.

A skip advances the schedule itself. The step depends on the kind of task:

  • floating starts a new interval from now
  • fixed moves to the next scheduled occurrence
  • one-off, triggered and sensor tasks go dormant

A snooze, a due-today and a skip all re-arm the edge-triggered overdue and due-soon announcements for the new date.

A skip is also recorded. It goes in a skips list, beside the completions list. A skip records an occurrence that was passed over. It never sets last_completed, and nothing derived from the completion log counts it.

home_keeper_task_skipped includes the new entry's ts. The ts value identifies that entry for these services:

  • update_skip
  • move_skip
  • delete_skip

The _skip_updated and _skip_removed events use ts the same way.

A skip on a usage task resets its meter, the same way a completion does. The next interval is then measured from the reading at the skip.

The time backstop, also_every, measures from that same point. The default combinator is "any", so the meter or the backstop can re-arm the task on its own. A backstop left at its old point can re-arm the task soon after a skip.

When a user edits the reading on whichever completion or skip anchors the meter, Home Keeper re-anchors the meter. The _completion_updated and _skip_updated events can then add a meter_baseline.

NFC/RFID tag scans ride these same events: completing a task by scanning its linked tag fires an ordinary home_keeper_task_completed carrying origin: home_keeper_tag_scan. Match on that origin to tell a physical scan from a press of Done. Passing that origin to complete_task is also the escape hatch for automations that need to complete a task whose Require tag scan toggle blocks every UI surface.

Sensor-based tasks reuse the triggered lifecycle: Home Keeper's watcher fires home_keeper_task_triggered when a bound entity meets the task's condition (a usage meter passing its target, a threshold crossing, or a state binding's entity entering its state), the task then crosses to home_keeper_task_overdue like any due task, and a normal user home_keeper_task_completed clears it (resetting a usage meter's baseline). A usage meter carrying a time backstop (sensor.also_every) arms on whichever half lands first (including while the bound entity is unavailable) through the same home_keeper_task_triggered. No new event types are introduced.

A threshold or state binding that sets clear_on_recover also clears itself when its condition goes away, and that path fires an ordinary home_keeper_task_completed carrying origin: home_keeper_sensor_recover. Match on that origin to tell a self-clearing sensor task from someone pressing Done. If the task is linked to a consumable, the auto-completion consumes one spare, potentially producing home_keeper_part_low_stock or home_keeper_part_out_of_stock the same as any other completion path. A bound entity going unavailable/unknown counts as no reading rather than a recovery, and fires nothing, so a device dropping off the network never completes a task.

The watcher's own baseline bookkeeping (anchoring a fresh meter, re-anchoring after a meter reset) stays silent, because it is internal state, not a user action. A baseline moved by hand through the set_task_meter service does fire home_keeper_task_updated with changed_fields: ["sensor"].

Buy reminders ticked off on a synced shopping list ride these same events too. When Settings → Shopping list points at a to-do list, each auto-created "Buy {part}" reminder is put on it. Ticking that line off there fires an ordinary home_keeper_task_completed carrying origin: home_keeper_shopping_list and source: {"buy": {"asset_id": …, "part_id": …}}. Match on that origin to tell "bought at the shop" from a press of Done. Like any buy-reminder completion it restocks the part by its restock quantity. A home_keeper_part_restocked normally follows. The reminder is then retired with a home_keeper_task_deleted. The sync's own bookkeeping (which line on which list stands for which reminder) stays silent, the same reasoning as the sensor watcher's baselines above.

Tasks ticked off on a synced to-do list ride these same events. When a Profile in Settings → Profiles names an external to-do list, the tasks it selects are put on that list while they qualify. Ticking an item off fires an ordinary home_keeper_task_completed carrying origin: home_keeper_todo_sync (so does an item that disappears from a list whose provider drops completed items, while the sync's treat removed items as completed toggle is on). Match on that origin to tell "checked off on the list" from a press of Done. Completing the task in Home Keeper instead ticks the synced item off and leaves it there as the record; when a recurring task next falls due, a fresh item is added beside it. The sync's bookkeeping (which item on which list stands for which task) stays silent, like the shopping list's.

Synced problem binary sensors (when Sync problem sensors is on) ride these same events: a synced task is created for each device_class: problem sensor, triggered when the sensor reports a problem, and completed when it clears. The completion event carries origin: home_keeper_problem_sensor_sync and source: {"problem_sensor": {"entity_id": …}} so an automation can tell a self-clearing problem from a user-completed chore. (These tasks can’t be completed by hand. See the README.)

Time-based transitions (edge-triggered)

home_keeper_task_overdue fires when a task first crosses its due date (now ≥ next_due), and home_keeper_task_due_soon when it enters the three-day window before it. These are detected by the coordinator’s periodic refresh (every 5 minutes) and are edge-triggered: each fires at most once per next_due value. A task that stays overdue does not re-fire. Completing or rescheduling it re-arms the next announcement.

Restart behaviour. On startup Home Keeper baselines the current state silently. A restart never replays an “overdue” storm for tasks that were already overdue. Only transitions observed while Home Assistant is running fire. (The per-task overdue binary_sensor always reflects the steady state regardless.)

Stock transitions (edge-triggered)

Spare stock crossing to reorder_at fires home_keeper_part_low_stock. Reaching 0 fires home_keeper_part_out_of_stock instead. Recovering back above the threshold fires home_keeper_part_restocked.

Edge-triggered the same way: one event per crossing, never on every step while already low. A part must track both stock and reorder_at to fire anything. A single change that drops an already-low part to zero fires out_of_stock (the more specific event), not low_stock.

Stock is drawn down (and these events fire) whenever a task linked to that part is completed. Both an auto-generated wear-part replacement task and a task you manually linked to a consumable (via home_keeper.set_task_consumable) count. This is how a sensor-armed "replace the fridge filter" task draws down inventory and signals a reorder when you mark it done.

Each completion takes off the part's Used per completion amount. A part that leaves it unset gives up one whole spare. A bottle that sets 0.33 lasts three refills.

A part with Auto-create buy task enabled goes one step further: crossing the reorder threshold auto-creates a one-off "Buy {part}" task (a home_keeper_task_created event) and restocking removes it (home_keeper_task_deleted). No new event type is involved, just the ordinary task lifecycle. Completing that buy task restocks the part by its restock_quantity, which fires home_keeper_part_restocked like any other restock.

Asset (appliance) lifecycle

Attaching or removing an appliance document (a manual/warranty/receipt link, or an uploaded file) is an appliance change, so it surfaces as home_keeper_asset_updated with changed_fields: ["documents"]. There is no separate document event. Attaching or removing a part's single file works the same way, with changed_fields: ["parts"].

Deleting an entry from an appliance's archived task history (via home_keeper.delete_archived_completion) also surfaces as home_keeper_asset_updated with changed_fields: ["archived_history"].

Archiving (home_keeper.archive_asset) and restoring (home_keeper.restore_asset) an appliance fire their own dedicated events rather than home_keeper_asset_updated, since they're a distinct lifecycle action of their own. Archiving only hides the appliance from the panel's default list; its device, entities, and any attached tasks are left running untouched, and home_keeper_asset_deleted never fires for it.

Companion discovery (edge-triggered, baselined on startup)

Home Keeper surfaces integrations that work with it (see the panel's Settings → Companions section, and INTEGRATING.md §7). Like the time-based transitions above, the current state is baselined silently at startup (companions already connected/suggested when HA starts do not fire), and an event fires only when a companion changes into that state while HA is running. A companion reaches that state by self-registering. The same event also fires when a glue is installed, or when a curated upstream is installed. State is re-detected on the coordinator's refresh cadence (~5 min), so installing an upstream surfaces a suggestion within one cycle. These never fire from a read (opening the panel or calling list_companions fires nothing).

A suggestion names the glue in its domain and the upstream it was detected from in upstream_domain.

There is also a fire-and-forget request event, home_keeper_register_companions, emitted at setup and on reload to ask companions to announce themselves again. Its data is empty. A companion answers it by calling home_keeper.register_companion.

Declarative companion CRUD

A declarative companion is a Home-Keeper-owned recipe (target integration + entity filters + Jinja-templated task name/notes; see INTEGRATING.md §7) that materializes one managed sensor task per matching entity. Spec-level CRUD fires its own bus events so an automation can react to the recipe list changing; the materialized sensor tasks themselves emit the ordinary home_keeper_task_created / _updated / _deleted / _triggered / _completed events, so an automation that already listens to home_keeper_task_completed just works. Automations that want to filter to declarative tasks read managed_by.integration == "home_keeper" and source.declarative_companion.spec_id.

EventFires when
home_keeper_declarative_companion_addeda spec was created (via the panel, home_keeper.add_declarative_companion, or the matching WS command); payload adds spec_id, name, enabled, preset_id
home_keeper_declarative_companion_updatedany field of a stored spec was changed; same payload
home_keeper_declarative_companion_removeda spec was deleted (also fires home_keeper_task_deleted once per materialized task the spec had); same payload

Payloads

Every field of every payload is generated from the builders that construct them, so the reference cannot describe a field the code doesn't send: see the API reference.

The stock numbers need a word here, though. stock and reorder_at can be fractional, so a bottle topped up a third at a time reports 0.67 at the precision it is really at. And unit is whatever the part counts itself in ("ml", "bottles"), or "" for one counted in whole spares, so a notification can read {{ trigger.event.data.stock }} {{ trigger.event.data.unit }} and be right either way.

Example automations

Notify when anything becomes overdue (event trigger)

automation:
- alias: "Maintenance overdue → notify"
trigger:
- platform: event
event_type: home_keeper_task_overdue
action:
- service: notify.mobile_app_phone
data:
message: >-
{{ trigger.event.data.name }} is overdue
({{ trigger.event.data.days_overdue }} day(s)).

Add a spare to the shopping list when it runs out (event trigger)

automation:
- alias: "Spare out of stock → shopping list"
trigger:
- platform: event
event_type: home_keeper_part_out_of_stock
action:
- service: todo.add_item
target:
entity_id: todo.shopping_list
data:
item: >-
{{ trigger.event.data.part_name }}
{{ trigger.event.data.part_number }} ({{ trigger.event.data.vendor }})

React only to a specific appliance (device trigger)

In the automation editor, choose the appliance’s device and the “Spare part low on stock” trigger. The equivalent YAML:

automation:
- alias: "Furnace filter low"
trigger:
- platform: device
domain: home_keeper
device_id: <furnace device id>
type: part_low_stock
action: ...

Device triggers filter to the chosen device automatically: an appliance/existing-device trigger matches the event’s device_id; a standalone task’s self-owned device matches its task_id (those task events carry device_id: null).

Notes for integrators

  • The home_keeper_task_completed payload now carries the full task spine as well as its long-standing completed_at/origin fields. If you only read task_id, source, origin, and completed_at, nothing changes for you.
  • Home Keeper never inspects source; use it (and the origin echo on completions) to recognise and de-dupe your own tasks. See INTEGRATING.md.
  • An import fires one event per record, never one per completion. A document read by home_keeper.import_data can hold years of history, and that history is a record of a decade rather than a decade of things happening now. So a backfilled completion fires no home_keeper_task_completed. The history is folded onto the task before it reaches the store, and the record arrives as a single home_keeper_task_created or home_keeper_task_updated like any other write. An update whose only change is added history still fires, with completions among its changed_fields. If you mirror completions, read them from the task's history on that event rather than counting completion events.