Skip to main content

Triggers

A trigger defines what event causes a rule to be evaluated. Every rule has exactly one trigger.

Trigger reference

DeviceStateChanged

Fires when any MQTT state publish arrives for a device.

[trigger]
type = "device_state_changed"
device = "entryway.front_door"

# Optional: only fire when this attribute changes
attribute = "open"

# Optional: only fire when the attribute changes TO this specific value
to = true
FieldRequiredDescription
deviceyesPreferred device reference: canonical name, unique display name, or raw device ID
device_idyesBackward-compatible alias for device
attributenoNarrow to one attribute (e.g. "on", "open", "temperature")
tonoOnly fire when the attribute's new value equals this

If you use a display name and more than one device matches it, HomeCore marks the rule invalid with an ambiguity error.

Examples:

# Fire on any state change for a device
[trigger]
type = "device_state_changed"
device = "hallway.dimmer"

# Fire only when the "on" attribute changes (either direction)
[trigger]
type = "device_state_changed"
device = "living_room.floor_lamp"
attribute = "on"

# Fire only when a door opens (open = true)
[trigger]
type = "device_state_changed"
device = "garage.main_door"
attribute = "open"
to = true

TimeOfDay

Fires at a specific time on specified days of the week. Computed in local wall-clock time.

[trigger]
type = "time_of_day"
time = "07:30" # HH:MM
days = ["Mon", "Tue", "Wed", "Thu", "Fri"]

Valid day names: Mon, Tue, Wed, Thu, Fri, Sat, Sun

Catch-up on restart: If HomeCore restarts after a TimeOfDay trigger's scheduled time, the scheduler fires it immediately if the missed time falls within catchup_window_minutes (default 15).


SunEvent

Fires at sunrise or sunset, computed locally from your latitude/longitude in config.

[trigger]
type = "sun_event"
event = "sunset" # "sunrise" | "sunset"
offset_minutes = -30 # minutes before (-) or after (+) the event

Examples:

# At sunrise
[trigger]
type = "sun_event"
event = "sunrise"

# 30 minutes before sunset
[trigger]
type = "sun_event"
event = "sunset"
offset_minutes = -30

# 15 minutes after sunrise
[trigger]
type = "sun_event"
event = "sunrise"
offset_minutes = 15

Cron

Fires on a repeating cron schedule. Uses 6-field expressions (second, minute, hour, day-of-month, month, day-of-week) evaluated in local wall-clock time.

[trigger]
type = "cron"
expression = "0 30 7 * * Mon-Fri"
# ^ ^ ^ ^ ^ ^
# s m h dom mo dow
ExpressionMeaning
0 0 7 * * *7:00 AM every day
0 30 7 * * Mon-Fri7:30 AM weekdays only
0 */15 * * * *Every 15 minutes
0 0 */2 * * *Every 2 hours
0 0 8 1 * *8:00 AM on the 1st of every month
0 0 20 * * Sat,Sun8:00 PM weekends only

Rules with invalid cron expressions are automatically disabled at startup with an error field set.

# Check for invalid cron rules
curl -s http://localhost:8080/api/v1/automations \
-H "Authorization: Bearer $TOKEN" \
| jq '[.[] | select(.error | strings | contains("cron")) | {name, error}]'

WebhookReceived

Fires when an HTTP POST arrives at /api/v1/webhooks/{path}. No authentication required. The path acts as the shared secret.

[trigger]
type = "webhook_received"
path = "front-door-bell-a3f9c2"

The webhook URL: POST http://homecore.local/api/v1/webhooks/front-door-bell-a3f9c2

The request body (if valid JSON) is forwarded as the body field in the event payload and accessible in ScriptExpression conditions:

# Condition using webhook body
[[conditions]]
type = "script_expression"
script = 'event.body["pin"] == "1234"'

Example — trigger from a cloud service:

# Fire the trigger from any HTTP client — a script, a scheduler, a physical button, etc.
curl -X POST http://homecore.local/api/v1/webhooks/front-door-bell-a3f9c2 \
-H "Content-Type: application/json" \
-d '{"source": "doorbell", "action": "pressed"}'

CustomEvent

Fires when a FireEvent action emits the matching event_type. Enables clean rule chaining — one rule fires an event, other rules react to it in the same process with no MQTT round-trip.

[trigger]
type = "custom_event"
event_type = "morning_routine_started"

This is the primary mechanism for fan-out (one event → many parallel reactions) and pipeline patterns (chain of rules that each do one thing).

Example chain:

# Rule 1: motion sensor → start morning routine
[trigger]
type = "device_state_changed"
device_id = "motion_bedroom"
attribute = "motion"
to = true

[[actions]]
type = "fire_event"
event_type = "morning_routine_started"
payload = {}

# Rule 2: morning routine → turn on coffee
[trigger]
type = "custom_event"
event_type = "morning_routine_started"

[[actions]]
type = "set_device_state"
device_id = "smart_plug_coffee"
state = { on = true }

# Rule 3: morning routine → set thermostat
[trigger]
type = "custom_event"
event_type = "morning_routine_started"

[[actions]]
type = "set_device_state"
device_id = "thermostat_main"
state = { target_temp = 70 }

SystemStarted

Fires once, immediately after the rule engine finishes pre-populating its device cache on startup. Use to catch state that may have changed while HomeCore was offline.

[trigger]
type = "system_started"

Always pair with DeviceState conditions to guard the action:

# Alert if garage door was left open across a restart
[trigger]
type = "system_started"

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

[[actions]]
type = "notify"
channel = "telegram"
message = "Garage door is OPEN (detected on startup)"

DeviceAvailabilityChanged

Fires when a device comes online or goes offline.

[trigger]
type = "device_availability_changed"
device_id = "hue_001788fffe6841b3_1"

# Optional: only fire in one direction
to = false # only when device goes offline
toFires when
trueDevice comes online
falseDevice goes offline
(omitted)Either direction

MqttMessage

Fires when a raw MQTT message arrives on a matching topic. Supports MQTT wildcards.

[trigger]
type = "mqtt_message"
topic_pattern = "homecore/devices/+/state" # + = one level
# topic_pattern = "homecore/devices/#" # # = rest of path

Use this for non-standard integrations or when you need to react to any MQTT traffic, not just typed device events.


ManualTrigger

Never fires automatically. Only activated via the /test dry-run endpoint.

[trigger]
type = "manual_trigger"

Useful for rules that should only ever run when explicitly tested.


ModeChanged

Fires when a named mode transitions on or off.

[trigger]
type = "mode_changed"
mode_name = "mode_night"
to = true # fires when mode_night turns on

ButtonEvent

Fires when a physical button device (e.g. a Pico remote, keypad, or smart switch) reports a button press.

[trigger]
type = "button_event"
device_id = "lutron_pico_bedroom"
button = 2 # optional: specific button number
action = "press" # optional: "press" | "release"
FieldRequiredDescription
device_idyesButton device
buttonnoSpecific button number to match
actionno"press" or "release"

NumericThreshold

Fires when a numeric attribute crosses a threshold value in a specified direction.

[trigger]
type = "numeric_threshold"
device_id = "sensor_basement_temp"
attribute = "temperature"
threshold = 80
direction = "above" # "above" | "below"
FieldRequiredDescription
device_idyesDevice to monitor
attributeyesNumeric attribute name
thresholdyesThreshold value
directionyes"above" (fires when value crosses from below to above) or "below" (fires when value crosses from above to below)

Only fires on the crossing transition, not on every update while the value remains past the threshold.


HubVariableChanged

Fires when a hub variable is written to (via SetHubVariable action or API).

[trigger]
type = "hub_variable_changed"
name = "alarm_armed"

# Optional: only fire when variable changes to this value
to = true

Periodic

Fires at a fixed interval. Simpler than Cron for basic repeating tasks.

[trigger]
type = "periodic"
interval_secs = 300 # every 5 minutes

CalendarEvent

Fires based on .ics calendar events. See Advanced: Calendar triggers for full configuration.

[trigger]
type = "calendar_event"
calendar_id = "work"
event_match = "contains"
summary = "Team meeting"
offset_minutes = -15

Trigger context in conditions

For WebhookReceived triggers, the request body is available in ScriptExpression conditions as event.body. For DeviceStateChanged, event.device_id, event.attribute, and event.value are available.