condition-trigger
The runtime behind automations:, overrides: and conditional
picture-elements:, built to mirror Home Assistant's conditions and triggers.
User-facing reference: conditions-triggers.md.
Condition vs trigger
The same type (state, camera, ...) exists as both, but they are opposite shapes:
| Condition | Trigger | |
|---|---|---|
| Asks | "is this true right now?" | "did this just become true?" |
| Is a | level / predicate, pulled via evaluate() |
edge / event, pushed via subscribe() |
| Used in | automations conditions, overrides, elements |
automations triggers only |
Both read one source of truth, the ConditionStateManager -- the card's
live state (camera/view/config/hass/...), which notifies on change (the
lone exception is screen, which watches a window.matchMedia query). The Zod
schema
(config/schema/condition-trigger/)
shares each type's fields between its condition and trigger (common/) so the
two cannot drift.
Conditions
Each type has a pure level-predicate evaluator (evaluate(state) -> result),
built by createConditionEvaluator. An evaluator may also declare
externalSources -- change sources outside ConditionState (currently only
screen's matchMedia, via a MediaQueryWatcher) -- which ConditionsManager
subscribes to so a change there triggers a re-evaluation. The manager ANDs a set
of evaluators and notifies when the combined result flips; it backs
overrides/elements and the automation ongoing-conditions pull (below).
The bridge
A type's meaning lives in exactly one place -- its condition evaluator. A
card-state trigger reuses that same evaluator as a point-in-time value-filter,
built directly from the trigger by createConditionEvaluatorForTrigger (the
trigger and condition schemas share a common/ base, so no discriminator-swap
or cast). One definition of meaning, two readings: the condition asks the
predicate, the trigger watches for change and filters it through the predicate.
Triggers: four kinds
createTriggerEvaluator picks one. Each emits a TriggerData payload (the
trigger.* template variable); stock triggers report their HA platform, card
triggers report platform: acc + the kind in type.
- Stock entity (
state,numeric_state) --EntityStateTriggerBase: per-entity_idfan-out, HAfrom_state/to_state,for:via aTimer. - Stock template (
template) -- the non-true -> true edge ofvalue_template. - Screen (
screen) --ScreenTrigger: watches amatchMediaquery, whose state lives outsideConditionState, so it owns a sharedMediaQueryWatcher(the same watcher the screen condition uses) and fires on the rising edge of the match. - Card-state (every other type:
camera,view,config,fullscreen, ...) --ConditionStateTriggerBase: subscribe to theConditionStateManager, fire when the watched field (_getValue) changes, and emitfrom_acc/to_accsnapshots. A value filters the change through the matching condition (the bridge); no value fires on any change;configis trigger-only (no matching condition).
Automations: push, then pull
AutomationsManager runs a TriggersManager per automation and, when a trigger
pushes, pull-evaluates the ongoing conditions against the current state
(this matches Home Assistant actions). The state store updates before
dispatching, so a pulled condition already sees the triggering change -- no
ordering needed between the two.