> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Action Triggers

> Define when actions execute using time-based schedules (ON EVERY, ON TIMESTAMP), topic-based events, or manual invocation

## Overview

Every Action needs a trigger—the event that starts its execution. Choosing the right trigger type determines how your automation responds to real-world conditions.

<Tip>
  **Think of triggers like different ways to start your car.** A time-based trigger is like an auto-start that warms up your car every morning at 7 AM. A topic-based trigger is like pushing the start button when you get in. A callable action is like asking someone else to start it for you.
</Tip>

## When to Use Each Type

| Trigger Type                                | Use When                                                  | Example                                               |
| ------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------- |
| **Time-Based** (`ON EVERY`, `ON TIMESTAMP`) | You need a clock-driven schedule (interval or wall-clock) | Heartbeats, polling, shift windows, one-shot cutovers |
| **Topic-Based** (`ON TOPIC`)                | You need to react when data arrives                       | Sensor processing, command handling                   |
| **Initialization** (`ON START`)             | You need to run logic once when the broker starts         | Bootstrap UNS structure, set default config values    |
| **Callable** (no trigger)                   | You need reusable logic invoked by other actions          | Utility functions, shared calculations                |

***

## In This Page

* **Time-Based Triggers** — Intervals, weekday/weekend rules, and `ON TIMESTAMP` (daily or one-shot)
* **Topic-Based Triggers** — React to MQTT messages with `ON TOPIC`
* **Initialization Triggers** — Run once at broker startup with `ON START`
* **Callable Actions** — Invoke manually or from other actions

***

## Time-Based Triggers

LoT supports several time-based trigger forms. All of them use the `ON` keyword and run the action's `DO` block automatically—no MQTT message is required to fire them. Together they cover recurring intervals, calendar rules (a specific weekday, Monday–Friday, or Saturday–Sunday), daily wall-clock times, and single one-shot executions.

### Quick reference

| Syntax                                 | Pattern                                      | Recurs?             |
| -------------------------------------- | -------------------------------------------- | ------------------- |
| `ON EVERY` *N* *UNIT*                  | Every *N* seconds, minutes, hours, and so on | Yes — indefinitely  |
| `ON EVERY` *DAY* `AT` `"HH:mm"`        | Named weekday at a fixed local time          | Yes — every week    |
| `ON EVERY WEEKDAY AT` `"HH:mm"`        | Monday–Friday at a fixed local time          | Yes — every weekday |
| `ON EVERY WEEKEND AT` `"HH:mm"`        | Saturday–Sunday at a fixed local time        | Yes — every weekend |
| `ON TIMESTAMP` `"HH:mm:ss"`            | Every day at a fixed local time              | Yes — every day     |
| `ON TIMESTAMP` `"dd-MM-yyyy HH:mm:ss"` | Exact date and time                          | No — one-shot       |

<Note>
  **Time zone:** For `HH:mm` and `HH:mm:ss` in `ON EVERY … AT` and in daily `ON TIMESTAMP`, values are interpreted in the **local time of the machine running the broker**, then converted to UTC internally. The action is scheduled for the correct wall-clock time across daylight saving changes. The one-shot form `dd-MM-yyyy HH:mm:ss` is interpreted as **UTC**, as described below.
</Note>

<Note>
  **Structured payloads:** `PUBLISH TOPIC` does not support inline JSON object literals. For multi-field records, define a **COLLAPSED** model and use `PUBLISH MODEL … TO … WITH` (see [Publishing Models](/lot-language/models/publish-model)). The timed-action examples below use that pattern.
</Note>

### 1. `ON EVERY` *N* *UNIT* — fixed interval

Fires repeatedly every *N* units of time. Common for monitoring, polling, and heartbeats.

```lot theme={null}
DEFINE ACTION <Name>
ON EVERY <N> <UNIT> DO
    -- body
```

#### Supported units

| Keyword(s)           | Example               |
| -------------------- | --------------------- |
| `MILLISECOND` / `MS` | `ON EVERY 500 MS`     |
| `SECOND` / `SECONDS` | `ON EVERY 30 SECONDS` |
| `MINUTE` / `MINUTES` | `ON EVERY 5 MINUTES`  |
| `HOUR` / `HOURS`     | `ON EVERY 1 HOUR`     |
| `DAY` / `DAYS`       | `ON EVERY 1 DAY`      |
| `WEEK` / `WEEKS`     | `ON EVERY 2 WEEKS`    |
| `MONTH` / `MONTHS`   | `ON EVERY 1 MONTH`    |
| `YEAR` / `YEARS`     | `ON EVERY 1 YEAR`     |

<Note>
  Singular and plural unit names are interchangeable (for example, `1 SECOND` and `1 SECONDS`). `ON EVERY 1 DAY` fires every 24 hours from the last run; for the same clock time every calendar day, use daily `ON TIMESTAMP` instead.
</Note>

```lot theme={null}
DEFINE ACTION SystemHeartbeat
ON EVERY 5 SECONDS DO
    PUBLISH TOPIC "system/heartbeat" WITH "alive"
    PUBLISH TOPIC "system/heartbeat/timestamp" WITH TIMESTAMP "UTC"
```

```lot theme={null}
DEFINE ACTION PollMachineTemperature
ON EVERY 30 SECONDS DO
    SET "temp" WITH GET TOPIC "machine/sensor/temperature"
    IF {temp} > 85 THEN
        PUBLISH TOPIC "alerts/temperature/high" WITH {temp}
    PUBLISH TOPIC "machine/temperature/last_check" WITH TIMESTAMP "UTC"
```

```lot theme={null}
DEFINE ACTION FastSensorSample
ON EVERY 500 MS DO
    PUBLISH TOPIC "sensors/vibration/sample" WITH GET TOPIC "sensors/vibration/raw"
```

### 2. `ON EVERY` *DAY* `AT` — weekly, specific day

Fires once per week on the named weekday at the given **local** time. Use `"HH:mm"` or `"HH:mm:ss"`; seconds are optional.

| Keyword             | Fires on               |
| ------------------- | ---------------------- |
| `MONDAY` … `SUNDAY` | That weekday each week |

```lot theme={null}
DEFINE ACTION <Name>
ON EVERY MONDAY AT "08:00:00" DO
    -- body
```

```lot theme={null}
DEFINE MODEL ProductionWeeklyReport COLLAPSED
    ADD STRING "week"
    ADD INT "total_parts"
    ADD INT "total_rejects"

DEFINE ACTION WeeklyProductionReport
ON EVERY MONDAY AT "08:00:00" DO
    SET "week_total" WITH GET TOPIC "factory/line1/parts_produced"
    SET "week_rejects" WITH GET TOPIC "factory/line1/rejects"
    PUBLISH MODEL ProductionWeeklyReport TO "reports/weekly/production" WITH
        week = TIMESTAMP "UTC"
        total_parts = {week_total}
        total_rejects = {week_rejects}
    PUBLISH TOPIC "factory/line1/parts_produced" WITH 0
    PUBLISH TOPIC "factory/line1/rejects" WITH 0
```

### 3. `ON EVERY WEEKDAY AT` — Monday–Friday

Fires Monday through Friday at the same **local** time—one action instead of five separate daily schedules.

```lot theme={null}
DEFINE MODEL ShiftMorningBriefing COLLAPSED
    ADD STRING "line_status"
    ADD STRING "pending_orders"
    ADD STRING "shift_start"

DEFINE ACTION ShiftStartBriefing
ON EVERY WEEKDAY AT "07:30" DO
    SET "line_status" WITH GET TOPIC "factory/line1/status"
    SET "pending_orders" WITH GET TOPIC "erp/orders/pending"
    PUBLISH MODEL ShiftMorningBriefing TO "shift/briefing/morning" WITH
        line_status = {line_status}
        pending_orders = {pending_orders}
        shift_start = TIMESTAMP "UTC"
```

### 4. `ON EVERY WEEKEND AT` — Saturday and Sunday

Fires on both weekend days at the specified **local** time—useful for maintenance windows or weekend-only reporting.

```lot theme={null}
DEFINE ACTION WeekendMaintenanceWindow
ON EVERY WEEKEND AT "06:00:00" DO
    PUBLISH TOPIC "maintenance/window/open" WITH "true"
    PUBLISH TOPIC "maintenance/window/start" WITH TIMESTAMP "UTC"
    PUBLISH TOPIC "notifications/maintenance" WITH "Weekend maintenance window is now open"
```

### 5. `ON TIMESTAMP` — daily fixed time

Fires **every day** at the given **local** wall-clock time. Unlike `ON EVERY 1 DAY`, this tracks clock time, not elapsed time since the broker or last run started.

```lot theme={null}
DEFINE ACTION <Name>
ON TIMESTAMP "08:00:00" DO
    -- body
```

`"HH:mm"` without seconds is accepted.

```lot theme={null}
DEFINE ACTION DailyCounterReset
ON TIMESTAMP "00:00:00" DO
    PUBLISH TOPIC "factory/line1/parts_produced" WITH 0
    PUBLISH TOPIC "factory/line1/rejects" WITH 0
    PUBLISH TOPIC "factory/line1/day_start" WITH TIMESTAMP "UTC"
```

### 6. `ON TIMESTAMP` — one-shot, exact date and time

Fires **once** at the exact instant given, then never again (the scheduler moves the next run far into the future). Format is strictly **`dd-MM-yyyy HH:mm:ss`** (day–month–year). The timestamp is treated as **UTC**.

```lot theme={null}
DEFINE ACTION SystemCutoverNotice
ON TIMESTAMP "01-04-2026 06:00:00" DO
    PUBLISH TOPIC "system/cutover/notice" WITH "New firmware version going live in 1 hour"
    PUBLISH TOPIC "notifications/all" WITH "Scheduled maintenance begins at 07:00 UTC"
```

### Combination patterns

Real systems often split schedules across several actions—one trigger per concern.

<AccordionGroup>
  <Accordion title="Fast poll + daily summary">
    ```lot theme={null}
    DEFINE MODEL DailyTemperatureReport COLLAPSED
        ADD DOUBLE "max"
        ADD DOUBLE "min"
        ADD STRING "date"

    DEFINE ACTION SensorPoll
    ON EVERY 10 SECONDS DO
        SET "value" WITH GET TOPIC "sensors/temperature/raw"
        PUBLISH TOPIC "sensors/temperature/current" WITH {value}

    DEFINE ACTION DailySensorSummary
    ON TIMESTAMP "00:00:00" DO
        SET "max" WITH GET TOPIC "sensors/temperature/max"
        SET "min" WITH GET TOPIC "sensors/temperature/min"
        PUBLISH MODEL DailyTemperatureReport TO "reports/daily/temperature" WITH
            max = {max}
            min = {min}
            date = TIMESTAMP "UTC"
        PUBLISH TOPIC "sensors/temperature/max" WITH 0
        PUBLISH TOPIC "sensors/temperature/min" WITH 0
    ```
  </Accordion>

  <Accordion title="Weekday operations + weekend maintenance">
    ```lot theme={null}
    DEFINE ACTION WeekdayProductionStart
    ON EVERY WEEKDAY AT "06:00" DO
        PUBLISH TOPIC "factory/line1/mode" WITH "production"
        PUBLISH TOPIC "factory/line1/shift_start" WITH TIMESTAMP "UTC"

    DEFINE ACTION WeekendMaintenance
    ON EVERY WEEKEND AT "07:00" DO
        PUBLISH TOPIC "factory/line1/mode" WITH "maintenance"
        PUBLISH TOPIC "notifications/maintenance/start" WITH TIMESTAMP "UTC"
    ```
  </Accordion>

  <Accordion title="Weekly reset + daily snapshot + live throughput">
    ```lot theme={null}
    DEFINE MODEL DashboardThroughput COLLAPSED
        ADD INT "parts"
        ADD DOUBLE "rate_per_min"
        ADD INT "updated"

    DEFINE ACTION WeeklyKpiReset
    ON EVERY MONDAY AT "00:00:00" DO
        PUBLISH TOPIC "kpi/weekly/parts_produced" WITH 0
        PUBLISH TOPIC "kpi/weekly/reset_time" WITH TIMESTAMP "UTC"

    DEFINE ACTION DailyKpiSnapshot
    ON TIMESTAMP "23:59:00" DO
        SET "daily_parts" WITH GET TOPIC "factory/line1/parts_produced"
        PUBLISH TOPIC "kpi/daily/snapshot" WITH {daily_parts}
        PUBLISH TOPIC "kpi/daily/snapshot_time" WITH TIMESTAMP "UTC"

    DEFINE ACTION LiveThroughput
    ON EVERY 1 MINUTE DO
        SET "parts" WITH GET TOPIC "factory/line1/parts_produced"
        SET "rate" WITH GET TOPIC "factory/line1/production_rate"
        PUBLISH MODEL DashboardThroughput TO "dashboard/live/throughput" WITH
            parts = {parts}
            rate_per_min = {rate}
            updated = TIMESTAMP "UNIX"
    ```
  </Accordion>

  <Accordion title="One-shot cutover + ongoing heartbeat">
    ```lot theme={null}
    DEFINE ACTION CutoverActivation
    ON TIMESTAMP "01-07-2026 06:00:00" DO
        PUBLISH TOPIC "system/version" WITH "2.0"
        PUBLISH TOPIC "system/cutover/complete" WITH TIMESTAMP "UTC"

    DEFINE ACTION V2Heartbeat
    ON EVERY 30 SECONDS DO
        SET "ver" WITH GET TOPIC "system/version"
        IF {ver} EQUALS "2.0" THEN
            PUBLISH TOPIC "system/v2/heartbeat" WITH "running"
    ```
  </Accordion>
</AccordionGroup>

### Error handling

If a time string does not match an expected format, the broker logs an error similar to:

`Invalid DATE_TIME format: <value>. Expected formats:`

* `dd-MM-yyyy HH:mm:ss` (one-time)
* `HH:mm:ss` / `HH:mm` (daily)
* Weekly forms using a day name with `AT` and a time string

Errors are also published (retained) to:

`$SYS/Coreflux/Actions/<ActionName>/Error`

Subscribe to `$SYS/Coreflux/Actions/+/Error` to observe failures from any action.

### At a glance — valid time triggers

```lot theme={null}
ON EVERY 500 MS DO
ON EVERY 10 SECONDS DO
ON EVERY 5 MINUTES DO
ON EVERY 1 HOUR DO
ON EVERY 1 DAY DO
ON EVERY 1 WEEK DO
ON EVERY 1 MONTH DO
ON EVERY MONDAY AT "08:00:00" DO
ON EVERY WEEKDAY AT "07:30" DO
ON EVERY WEEKEND AT "06:00" DO
ON TIMESTAMP "07:00" DO
ON TIMESTAMP "00:00:00" DO
ON TIMESTAMP "25-12-2026 09:00:00" DO
```

### More examples

<AccordionGroup>
  <Accordion title="System heartbeat">
    ```lot theme={null}
    DEFINE ACTION SystemHeartbeat
    ON EVERY 5 SECONDS DO
        PUBLISH TOPIC "system/status" WITH "online"
        PUBLISH TOPIC "system/timestamp" WITH TIMESTAMP "UTC"
    ```
  </Accordion>

  <Accordion title="Counter with state">
    ```lot theme={null}
    DEFINE ACTION IncrementCounter
    ON EVERY 10 SECONDS DO
        IF GET TOPIC "counter/value" == EMPTY THEN
            PUBLISH TOPIC "counter/value" WITH 0
        ELSE
            PUBLISH TOPIC "counter/value" WITH (GET TOPIC "counter/value" + 1)
    ```
  </Accordion>
</AccordionGroup>

***

## Topic-Based Triggers

Use `ON TOPIC` to execute actions when MQTT messages arrive:

```lot theme={null}
DEFINE ACTION ProcessMessage
ON TOPIC "input/data" DO
    PUBLISH TOPIC "output/data" WITH PAYLOAD
```

### Wildcard Patterns

LoT supports MQTT wildcards for flexible topic matching:

<Tabs>
  <Tab title="Single-Level (+)">
    The `+` wildcard matches exactly one topic level:

    ```lot theme={null}
    DEFINE ACTION SensorRouter
    ON TOPIC "sensors/+/temperature" DO
        // POSITION 1="sensors", POSITION 2=wildcard (sensor_id), POSITION 3="temperature"
        SET "sensor_id" WITH TOPIC POSITION 2
        PUBLISH TOPIC "processed/" + {sensor_id} + "/temp" WITH PAYLOAD
    ```

    <Check>
      **Matches:** `sensors/room1/temperature`, `sensors/room2/temperature`
    </Check>

    <Warning>
      **Does NOT match:** `sensors/building1/floor2/temperature` (too many levels)
    </Warning>
  </Tab>

  <Tab title="Multi-Level (+/+)">
    Use multiple single-level wildcards for deeper hierarchies:

    ```lot theme={null}
    DEFINE ACTION FactoryMonitor
    ON TOPIC "factory/+/+/status" DO
        // POSITION 1="factory", POSITION 2=line_id, POSITION 3=machine_id, POSITION 4="status"
        SET "line_id" WITH TOPIC POSITION 2
        SET "machine_id" WITH TOPIC POSITION 3
        
        PUBLISH TOPIC "monitoring/" + {line_id} + "/" + {machine_id} WITH PAYLOAD
    ```

    <Check>
      **Matches:** `factory/line1/machine1/status`, `factory/line2/press3/status`
    </Check>

    <Warning>
      **Does NOT match:** `factory/line1/status` (missing one level), `factory/status` (missing two levels)
    </Warning>
  </Tab>
</Tabs>

***

## Initialization Triggers

Use `ON START` to execute logic exactly once when the broker starts or when the action is first deployed. This is the right place to bootstrap a Unified Namespace (UNS) structure, publish retained default configurations, and log startup events.

```lot theme={null}
DEFINE ACTION InitUNSRoot
ON START DO
    KEEP TOPIC "config/uns_root" WITH "factory/lisbon"
    KEEP TOPIC "factory/info/name" WITH "Lisbon Plant"
    KEEP TOPIC "factory/info/timezone" WITH "Europe/Lisbon"
    PUBLISH TOPIC "system/log" WITH "UNS root initialised"
```

**Key characteristics:**

* Runs exactly once per broker start — not on a schedule, not on a message
* `KEEP TOPIC` publishes with the retain flag so the value is delivered to any future subscriber immediately on connection
* Use it to set default configuration values and retained state that must survive restarts
* There is no `ON STOP` equivalent — `ON START` is the correct place for setup logic

***

## Callable Actions

Actions without an event trigger are callable—they execute only when explicitly invoked by other actions or via system commands.

### Basic Callable Action

```lot theme={null}
DEFINE ACTION CacheUpdater
DO
    KEEP TOPIC "cache/latest" WITH (GET TOPIC "data/current")
    PUBLISH TOPIC "cache/updated" WITH TIMESTAMP "UTC"
```

### Callable Action with Parameters

Callable actions can accept input parameters and return values:

```lot theme={null}
DEFINE ACTION CalculateAverage
INPUT value1 AS DOUBLE
INPUT value2 AS DOUBLE
DO
    SET "sum" WITH ({value1} + {value2})
    SET "avg" WITH ({sum} / 2)
RETURN
    OUTPUT avg
```

### Invoking Callable Actions

<Tabs>
  <Tab title="From Another Action">
    Use `CALL ACTION` to invoke from within another action. This is the primary way to reuse logic across your automation:

    ```lot theme={null}
    DEFINE ACTION ProcessSensorPair
    ON TOPIC "sensors/pair/data" DO
        SET "temp1" WITH (GET JSON "sensor1" IN PAYLOAD AS DOUBLE)
        SET "temp2" WITH (GET JSON "sensor2" IN PAYLOAD AS DOUBLE)
        
        CALL ACTION CalculateAverage
            WITH value1 = {temp1}, value2 = {temp2}
            RETURN result
        
        PUBLISH TOPIC "sensors/average" WITH {result}
    ```
  </Tab>

  <Tab title="Via Command Topic">
    Publish a message to the system command topic to invoke an action externally:

    **Topic:** `$SYS/Coreflux/Command`

    **Payload:** `-runAction CacheUpdater`

    Use any MQTT client to publish this message. The action executes immediately upon receiving the command.
  </Tab>
</Tabs>

***

## Comparing Trigger Types

| Aspect             | Time-Based                       | Topic-Based     | ON START       | Callable           |
| ------------------ | -------------------------------- | --------------- | -------------- | ------------------ |
| **Trigger**        | Clock interval or schedule       | MQTT message    | Broker startup | Manual command     |
| **Frequency**      | Interval, daily, weekly, or once | Event-driven    | Once           | On-demand          |
| **Payload Access** | No                               | Yes (`PAYLOAD`) | No             | Via parameters     |
| **Best For**       | Monitoring, reporting            | Data processing | Initialization | Reusable utilities |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Operations" icon="gear" href="./operations">
    Learn about SET, GET, PUBLISH, and conditional logic.
  </Card>

  <Card title="Python Integration" icon="python" href="./python-integration">
    Extend Actions with Python for complex calculations.
  </Card>
</CardGroup>
