Published 2026-10-07 · Reviewed 2026-10-07
When a person moves the switch, the automation should yield
A home automation that re-asserts its own state after you touch a switch is not being helpful; it is arguing. Use the cause recorded with every state change to decide who owns the final say.
- home automation
- human oversight
- reliability
The switch that keeps losing
At ten in the evening the hallway light turns itself off because the motion sensor has seen nothing for five minutes. You are still awake, so you turn it back on. A minute later the automation turns it off again. You turn it on. It turns it off. Nothing is broken: the sensor works, the rule fires, the bulb obeys. The system is in an argument, and you are losing it.
The opposite failure is just as common. The automation notices you changed something and gives up permanently. The light you fixed stays on all night, and the motion routine has quietly stopped doing its job.
Both failures come from one missing decision: when a person and an automation act on the same device, who has the final say?
Every state change carries a cause
Feeling the argument is easy; fixing it needs a way to tell who caused a change. Home Assistant builds that into its data model. Every state object — the snapshot holding an entity's state, attributes and timestamps — also carries a **context**. The documentation describes context as the link between states and events belonging to the same action: when you or an automation make something happen, Home Assistant creates a new context, and the resulting changes carry it.
It has three useful fields:
If a person turns a light on from the app, the change's context names them. If an automation turns it on, the context has no user but has a parent pointing at the trigger. A template can read the cause directly: `{{ states.switch.hallway.context.user_id }}` returns a user identifier or `None`.
- `id` — a unique identifier.
- `user_id` — the user who started the change, and `None` when no user did, "for example, when an automation did".
- `parent_id` — the context that caused this one, so a chain of changes can be followed back to its trigger.
Value is not cause
The usual way to filter a trigger is by value. The state trigger can react only to a change **to** a value, only **from** a value, or exclude a value with `not_from` and `not_to`.
That answers a different question. A change to `on` looks identical whether a person or an automation produced it. Look only at the new state and you cannot separate a deliberate instruction from the automation re-applying its policy. To tell them apart you read the cause, not the result.
The event trigger goes further: it documents an optional context filter, and the interface offers a "Limit to events triggered by" a selected user. Some triggers can be set to fire only when a particular person acted.
Choose one writer and one policy
For each device, name the automation that owns it, then decide the winner when a person acts:
- **The automation wins.** It re-asserts its state. That is what produces the fight, and it is only defensible when the automation enforces something that must not be overridden, such as a safety shut-off.
- **The person wins, forever.** Touch the device and the automation stops. This ends the argument but turns a temporary correction into a silent permanent failure.
- **The person wins until a stated reset.** The automation steps back and resumes later on a condition you can see and explain. This is usually the right default, because it treats a human action as an instruction with a lifetime rather than noise to correct or a fault that disables the routine.
Make the override explicit, bounded and visible
A deference rule hidden in template logic is hard to reason about and harder to debug. Give it a name and an end. A common shape uses a small helper — a boolean flag or equivalent — that the automation sets when it sees a human change on the device it manages:
1. A state-change trigger watches the device. 2. A condition checks the change came from a person, not from the automation. 3. The automation sets the override, records when it started, and while it is set does not re-apply its own state; a declined action is left in the trace. 4. A reset clears the override — at the next scheduled event, after a bounded time, or when an "everything off" command runs.
The reset is the part people forget. An override with no expiry is an automation switched off without anyone noticing.
A worked example
The following is invented. A hallway light has one owner: a motion automation that turns it on when movement is seen and off after five minutes of quiet.
Still awake, the person turns it back on. Because the change names a user, the automation treats it as an override: it records the time and declines its own "turn off" step. The light stays on. The override clears at the next reset — when the night routine runs, say, or once the sensor has been clear for a longer window — and the automation takes over again.
Always-win produces the fight. Never-win leaves the light on and the routine dead. Bounded deference gives a light that obeys the person now and the schedule later, with a visible reason for both.
Two automations claiming one device fail the same way; the fix is one owner, one priority.
A checklist for shared control
Run these before you call the automation finished:
- Name the entity the automation believes it owns, and the one automation allowed to write to it.
- Decide, per device, who wins when a person acts — and say why.
- Detect the human action from its cause, not its value.
- Give the override a name, a start time and a reset condition.
- Make a declined action visible in the trace, so "the automation did nothing" has a reason.
- Keep a manual path that works even when the automation is offline.
- Re-test by acting as the person, not only by firing the automation.
Where this stops helping
Context tells you who, inside Home Assistant, started a change. It proves neither intent nor that the physical world changed. It can be ambiguous: a change with no `user_id` may be an automation, an integration polling a device, or a physical switch reporting through a hub. A wall switch that is not a Home Assistant user will never carry a user id, however deliberately it was pressed. Treat the cause as a hint about ownership, not a claim about who was present.
Deference has a cost: if every automation yields to the nearest button, some stop doing their job. Where an automation protects against real harm, it may need to win.
And context cannot make two owners agree — it only records what happened. The priority still has to be written down. Making home automations fail safe first covers the wider design discipline; a missing sensor reading is not zero is the companion problem when the input, not the owner, is the thing you cannot trust.
What we would do next
Pick one device you and an automation share. Write down who should win, and until when. Add the cause check, name the override, give it a reset. Then reproduce the argument on purpose — touch the switch, watch the trace — and confirm it yielded for a stated reason, not by accident.
Sources and limits
This article synthesises the sources below into a practical explanation. It is not a security standard, legal advice, or a guarantee that guidance current at review time still applies — check the review date above against your own situation.
- State and state object — Home Assistant. Defines the state object's context, describes user_id as the user who started the change and None when an automation did, explains parent_id as the link back to a trigger, and shows templates that read context.user_id.
- Automation templating — Home Assistant. States that state objects carry context data that can identify the user who caused a script or automation to execute, and that trigger.to_state is the new state object.
- State changed — Home Assistant. Documents the state trigger's not_from and not_to value exclusions and states that, with none of from, to, not_from or not_to set, the trigger fires on every state change.
- Manual event received — Home Assistant. Documents the event trigger's optional context filter and the UI option to limit a trigger to events triggered by a selected user.
More from Server Attic
Before your automation retries, make the action idempotent
Retries turn a brief failure into a duplicate side effect unless the action can be repeated safely. A practical checklist for stable operation identifiers, precondition checks, outcome records and bounded retries, and an honest account of what it cannot promise.
A missing sensor reading is not zero
Unknown, unavailable and stale sensor values are not measurements. A practical way to keep missing data out of averages, thresholds and automations, and to leave a household with a safe manual path.
Make home automations fail safe first
A practical checklist for designing Home Assistant automations that recover clearly, leave evidence and avoid doing the wrong thing twice.

Community comments
Comments are reviewed before publication. Keep discussion constructive: no harassment, hate, threats, doxxing, spam, illegal material, or attempts to evade moderation.
No approved comments yet.
Sign in to join the discussion.