Skip to main content

Conditions

Conditions are optional checks that must all pass (AND logic) for a rule's actions to execute. The rule can have zero conditions — it fires on every matching trigger event.

Conditions are evaluated in order. The first failure short-circuits — remaining conditions are not evaluated.

Condition reference

DeviceState

Checks the current value of a device attribute in the database.

[[conditions]]
type = "device_state"
device = "entryway.front_door"
attribute = "open"
op = "Eq"
value = false # door must be closed
FieldDescription
devicePreferred device reference: canonical name, unique display name, or raw device ID
device_idBackward-compatible alias for device
attributeAttribute name
opComparison operator
valueExpected value

Operators (op):

OperatorMeaning
EqEqual to
NeNot equal to
GtGreater than
GteGreater than or equal to
LtLess than
LteLess than or equal to

Examples:

# Light is off
[[conditions]]
type = "device_state"
device = "living_room.main_light"
attribute = "on"
op = "Eq"
value = false

# Temperature above 80°F
[[conditions]]
type = "device_state"
device = "hallway.thermostat"
attribute = "temperature"
op = "Gt"
value = 80

# Motion was detected (any truthy state)
[[conditions]]
type = "device_state"
device = "hallway.motion"
attribute = "motion"
op = "Eq"
value = true

# Battery level below 20%
[[conditions]]
type = "device_state"
device = "entryway.door_sensor"
attribute = "battery"
op = "Lt"
value = 20

TimeWindow

Checks whether the current wall-clock time falls within a window. between is accepted as an alias for time_window.

[[conditions]]
type = "time_window"
start = "08:00"
end = "22:00"

# Alias form
[[conditions]]
type = "between"
start = "08:00"
end = "22:00"

Handles midnight wrap: start = "22:00", end = "06:00" correctly covers 10 PM to 6 AM.


TimeElapsed

Checks how long an attribute has held its current value. Uses an in-memory per-attribute timestamp cache — zero database I/O.

[[conditions]]
type = "time_elapsed"
device = "garage.main_door"
attribute = "open"
duration_secs = 600 # 10 minutes

Passes if the attribute has been in its current value for at least duration_secs seconds.

Common pattern — alert if door has been open for 10 minutes:

[trigger]
type = "cron"
expression = "0 * * * * *" # every minute

[[conditions]]
type = "device_state"
device_id = "yolink_garage_door"
attribute = "open"
op = "Eq"
value = true

[[conditions]]
type = "time_elapsed"
device_id = "yolink_garage_door"
attribute = "open"
duration_secs = 600

[[actions]]
type = "notify"
channel = "telegram"
message = "Garage door has been open for 10+ minutes!"

Startup behavior: At startup, the timestamp cache is pre-populated from device.last_seen as a conservative baseline. TimeElapsed may fire sooner than expected on the first evaluation after restart for attributes that have been in their current state for a long time.


ScriptExpression

Evaluates a Rhai script expression that must return true or false.

[[conditions]]
type = "script_expression"
script = 'device_state("thermostat")["temperature"] > 75 && hour() < 22'

Available Rhai functions:

FunctionReturnsDescription
device_state("device_id")mapCurrent attributes of a device
hour()intCurrent hour (0-23, local time)
minute()intCurrent minute (0-59)
weekday()intDay of week (0=Sun, 1=Mon, …, 6=Sat)
is_weekday()boolTrue if Mon-Fri
is_weekend()boolTrue if Sat-Sun

Examples:

# Complex multi-device condition
[[conditions]]
type = "script_expression"
script = '''
let garage = device_state("yolink_garage_door");
let motion = device_state("motion_garage");
garage["open"] == true && motion["motion"] == false
'''

# Time-based logic not expressible as TimeWindow
[[conditions]]
type = "script_expression"
script = 'hour() >= 22 || hour() < 6' # after 10 PM or before 6 AM

Not

Inverts the result of any wrapped condition.

[[conditions]]
type = "not"

[conditions.condition]
type = "device_state"
device_id = "virtual_switch_away_mode"
attribute = "on"
op = "Eq"
value = true

This reads: "away mode is NOT active."

Nesting: Not can wrap any condition type, including ScriptExpression, TimeWindow, or another Not (double-negation, unusual but valid).


ModeIs

Checks whether a named mode is currently on or off.

[[conditions]]
type = "mode_is"
mode_name = "mode_night"
value = true # "is mode_night active?"

Equivalent to a DeviceState check on the mode's virtual device, but more readable.


PrivateBooleanIs

Checks a rule-local boolean flag set by SetPrivateBoolean action.

[[conditions]]
type = "private_boolean_is"
name = "already_notified"
value = false

Used with SetPrivateBoolean to prevent duplicate notifications:

# Only notify once; set the flag to prevent repeated notifications
[[conditions]]
type = "private_boolean_is"
name = "already_notified"
value = false

[[actions]]
type = "notify"
channel = "telegram"
message = "Alert!"

[[actions]]
type = "set_private_boolean"
name = "already_notified"
value = true

HubVariableIs

Checks a hub variable's value. Hub variables are shared across all rules.

[[conditions]]
type = "hub_variable_is"
name = "alarm_armed"
op = "Eq"
value = true

And

Explicitly groups conditions with AND logic. All wrapped conditions must pass. This is equivalent to listing conditions at the top level (which also AND together), but useful for nesting inside Or or Xor.

[[conditions]]
type = "and"

[[conditions.conditions]]
type = "device_state"
device_id = "mode_night"
attribute = "on"
op = "Eq"
value = true

[[conditions.conditions]]
type = "time_window"
start = "22:00"
end = "06:00"

Or

At least one of the wrapped conditions must pass.

[[conditions]]
type = "or"

[[conditions.conditions]]
type = "device_state"
device_id = "yolink_front_door"
attribute = "open"
op = "Eq"
value = true

[[conditions.conditions]]
type = "device_state"
device_id = "yolink_back_door"
attribute = "open"
op = "Eq"
value = true

Xor

Exactly one of the wrapped conditions must pass (exclusive or).

[[conditions]]
type = "xor"

[[conditions.conditions]]
type = "device_state"
device_id = "switch_away_mode"
attribute = "on"
op = "Eq"
value = true

[[conditions.conditions]]
type = "device_state"
device_id = "switch_vacation_mode"
attribute = "on"
op = "Eq"
value = true

This fires only if exactly one of away mode or vacation mode is active, but not both.


Combining conditions

All top-level conditions AND together by default. Use Or, And, and Xor for compound logic, or use a ScriptExpression for complex expressions:

# OR: fire if EITHER door is open
[[conditions]]
type = "script_expression"
script = '''
device_state("yolink_front_door")["open"] == true ||
device_state("yolink_back_door")["open"] == true
'''

# Complex AND/OR mix
[[conditions]]
type = "script_expression"
script = '''
let night = device_state("mode_night")["on"] == true;
let temp = device_state("thermostat")["temperature"];
night && (temp > 78 || temp < 65)
'''

Condition trace in fire history

Every condition evaluation is recorded in the rule's fire history. For debugging, check:

curl -s http://localhost:8080/api/v1/automations/RULE_ID/history \
-H "Authorization: Bearer $TOKEN" | jq '.[0].conditions'

Each condition entry includes passed, actual, expected, and a human-readable reason.