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

# Triggers

> The Triggers tab in full — how a widget event sends a command to a device, step by step, with examples.

The **Triggers** tab is where a widget *does something* — when the user interacts
with it, it sends a command to a device. If you've struggled with Triggers, it's
almost always because the four-step model wasn't clear. Here it is in full.

## The mental model

Hold three ideas in your head and Triggers stop being confusing:

<CardGroup cols={3}>
  <Card title="1 · A widget is a remote" icon="gamepad">
    Interacting with it fires an **event** (a press, a toggle, a slide). That's the
    signal.
  </Card>

  <Card title="2 · A trigger is the wiring" icon="diagram-project">
    You decide **what** that signal sends and **where**: a command, to a device.
    That's the whole job of the Triggers tab.
  </Card>

  <Card title="3 · Firmware is the receiver" icon="microchip">
    Your device matches the command/action and acts on it — reading any values
    that rode along.
  </Card>
</CardGroup>

In one line:

> **The dashboard is the sender; your firmware is the receiver. A trigger is the
> wire between them.** Everything on this page is just filling in that wire.

And the key thing people miss: **value widgets carry a payload.** A slider, color
picker, or form automatically sends its current value along the wire (under a known
key) — you don't route it by hand. More on that
[below](#widgets-that-emit-a-value-auto-injected-params).

## The model: WHEN → SEND TO → EXECUTE → Action

Every trigger reads as one sentence:

> **WHEN** this event fires → **SEND TO** a device → **EXECUTE** a command → run an
> **Action** (with parameters).

```mermaid theme={null}
flowchart LR
  E["WHEN<br/>(event)"] --> T["SEND TO<br/>(device)"]
  T --> C["EXECUTE<br/>(command)"]
  C --> A["Action<br/>(+ params)"]
```

Those four pills are exactly what you see in the editor. You build a trigger by
filling them in, top to bottom.

## Step by step

<Steps>
  <Step title="Add Event — the WHEN">
    Click **Add Event** and pick *when* this trigger fires — e.g. `click` for a
    Button, `toggle` for a Switch, `threshold` for a Gauge. Each widget offers
    only the events it can actually fire (see the [table below](#which-events-each-widget-fires)).
  </Step>

  <Step title="Add Target — the SEND TO">
    Click **Add Target** and choose which device receives the command:

    * **Current Device** — the device this dashboard is bound to (the common case)
    * **Manual Device ID** — type a specific device/target ID to send elsewhere

    You can add multiple targets to send the same action to several devices.
  </Step>

  <Step title="Add Command — the EXECUTE">
    Click **Add Command** and pick one of your product's commands (**Select
    command…**), or type a command name. These are the commands you defined when
    you [created the product](/studio/create-product#step-3--commands--actions).
  </Step>

  <Step title="Add Action — with parameters">
    Under the command, **Add Action** and pick the action (e.g. `ON`, `OFF`,
    `setSpeed`). Give it **parameters** as JSON if the action needs input:

    ```json theme={null}
    { "speed": 80, "unit": "rpm" }
    ```

    Leave params empty (`{}`) for actions that take none.
  </Step>
</Steps>

That's one complete trigger. A widget can have several — e.g. a Switch with a
`on` trigger *and* an `off` trigger.

## What actually gets sent

Under the hood, a trigger builds this message to each target and sends it when the
event fires:

```json theme={null}
{
  "targetId": "<device>",
  "payload": {
    "commands": [
      { "command": "Operate", "actions": [ { "action": "ON", "params": {} } ] }
    ]
  }
}
```

Your firmware receives it and acts on it via
[`setUserCommandHandler`](/firmware/arduino/api-reference#receiving-commands) —
that's the "down" half of the [core loop](/learn/how-it-works).

## Which events each widget fires

Every widget supports the **common lifecycle events** — `load`, `ready`,
`destroy`, `update`, `visible`, `hidden` — plus its own interaction events:

| Widget                         | Events (beyond the common ones)                                                       |
| ------------------------------ | ------------------------------------------------------------------------------------- |
| **Button**                     | `click`, `doubleclick`, `longpress`, `push`, `release`, `hover`                       |
| **Switch**                     | `toggle`, `on`, `off`, `change`, `click`                                              |
| **Slider**                     | `slideStart`, `slide`, `slideEnd`, `change`, `min`, `max`                             |
| **Gauge**                      | `threshold`, `min`, `max`, `change`                                                   |
| **Form / Database Form**       | `submit`, `input`, `change`, `focus`, `blur`                                          |
| **Text Input**                 | `input`, `change`, `submit`, `clear`, `focus`, `blur`, `keydown`, `keyup`, `keypress` |
| **Color Picker**               | `change`, `select`                                                                    |
| **Chart / Table**              | `dataload`, `datafail`, `refresh`, `select`                                           |
| **Joystick**                   | `change`, `touchstart`, `touchend`                                                    |
| **Image / SVG**                | `click`, `hover`, `loaded`, `error`                                                   |
| **Label**                      | `click`, `hover`                                                                      |
| **Payment Widget**             | `paymentSuccess`, `paymentFailed`                                                     |
| **Countdown Timer**            | `complete`, `start`, `pause`, `reset`                                                 |
| **Voice to Text**              | `speechStart`, `speechEnd`, `speechResult`                                            |
| **Text to Speech**             | `speechStart`, `speechEnd`, `complete`                                                |
| **Navigate / URL Button**      | `click`                                                                               |
| **WebRTC (viewer/camera)**     | `loaded`, `error`                                                                     |
| **Map / Mission Planning Map** | `click`, `change`, `select`                                                           |

## Widgets that emit a value (auto-injected params)

Some widgets don't just fire an event — they **emit a value** (a slider's
position, a picked color, a form's fields). For these, the platform
**automatically injects that value into every action's params** when the event
fires. You do **not** type the live value by hand.

| Widget                   | On these events               | Injected into params as                                                         |
| ------------------------ | ----------------------------- | ------------------------------------------------------------------------------- |
| **Slider**               | `slide`, `slideEnd`, `change` | `{ "value": <number> }`                                                         |
| **Color Picker**         | `change`, `select`            | `{ "color", "hex", "rgb", "r", "g", "b", "hsl", "h", "s", "l", "hsv", "cmyk" }` |
| **Form / Database Form** | `submit`                      | the field values, keyed by field ID — `{ "<fieldId>": <value>, … }`             |

### How the merge works

The injected value is **merged into** the static params you configured, and wins on
a key clash:

```
your static params   +   injected value   =   what the device receives
{ "unit": "rpm" }        { "value": 80 }       { "unit": "rpm", "value": 80 }
```

So for a slider, leave the action's params as `{}` (or add extra static keys) — the
**`value`** key arrives automatically. In firmware, read it as `params["value"]`.

<Tip>
  This means your firmware just reads a stable key: `params["value"]` for a slider,
  `params["color"]` (or `params["hex"]`) for a color picker, and `params["<fieldId>"]`
  for each form field. You never wire the live value through the editor by hand.
</Tip>

### Reading emitted values in firmware (Arduino)

In your command handler, use `findParams(msg, command, action)` to get the action's
params, then read the injected key. These match the dashboard triggers above.

**Slider → `params["value"]`**

```cpp theme={null}
device.setUserCommandHandler([](JsonObject &msg) {
  // Trigger: WHEN change → Motor → setSpeed
  JsonObject p = device.findParams(msg, "Motor", "setSpeed");
  if (!p.isNull()) {
    int speed = p["value"];              // the slider's live position
    analogWrite(MOTOR_PIN, speed);
  }
});
```

**Color Picker → `params["color"]` / `r` `g` `b`**

```cpp theme={null}
JsonObject p = device.findParams(msg, "Light", "setColor");
if (!p.isNull()) {
  const char* hex = p["color"];          // e.g. "#ff8800"
  int r = p["r"] | 0;                    // per-channel components
  int g = p["g"] | 0;
  int b = p["b"] | 0;
  setRGB(r, g, b);
}
```

**Form → `params["<fieldId>"]`**

```cpp theme={null}
JsonObject p = device.findParams(msg, "Config", "save");
if (!p.isNull()) {
  const char* name  = p["deviceName"];   // form field IDs
  int         limit = p["maxTemp"];
  // apply the submitted settings
}
```

<Note>
  `p["r"] | 0` uses ArduinoJson's default-value operator — it yields `0` if the key
  is missing, so a partial payload won't crash. The command and action names in
  `findParams(...)` must match the trigger's **EXECUTE** command and **Action**.
</Note>

## Conditional events (the "when a value crosses…" case)

For value widgets, some events *are* the condition:

* **Gauge → `threshold` / `min` / `max`** — fires when the bound value crosses that
  point. Example: a Gauge with a `threshold` trigger that sends `Operate → OFF`
  when temperature exceeds the limit.
* **Slider → `min` / `max`** — fires at the ends of the range.

For richer conditional logic ("if X and Y, then…"), use the user-facing
[Rules](/app/rules) engine instead — Triggers are the direct
event-to-command wiring on a single widget.

## Worked examples

### A button that starts a device

```
WHEN     click
SEND TO  Current Device
EXECUTE  Operate
Action   START   params: {}
```

### A switch that toggles a relay

```
Trigger 1:  WHEN on  → Current Device → Operate → ON   { "relay": 1 }
Trigger 2:  WHEN off → Current Device → Operate → OFF  { "relay": 1 }
```

### A slider that sets speed live

The slider's position is injected automatically as `value` — leave params empty.

```
WHEN     change
SEND TO  Current Device
EXECUTE  Motor
Action   setSpeed   params: {}      ← the device receives { "value": <position> }
```

Firmware reads it with `params["value"]`. Add extra static keys if you want them
alongside — e.g. `{ "unit": "rpm" }` → device gets `{ "unit": "rpm", "value": 80 }`.

### A gauge that shuts things off past a threshold

```
WHEN     threshold
SEND TO  Current Device
EXECUTE  Operate
Action   OFF   params: {}
```

## Tips & gotchas

<Warning>
  The command and action names in a trigger must **match the commands you defined on
  the product**. If a dropdown is empty, define the command first in
  [API Commands](/studio/api-commands) or the
  [product wizard](/studio/create-product#step-3--commands--actions).
</Warning>

* **Params must be valid JSON** — the editor flags `Invalid JSON`. Use `{}` for no
  parameters.
* **"Current Device" vs. "Manual Device ID"** — use Current Device unless you're
  intentionally commanding a *different* device from this widget.
* **Multiple triggers per widget** — a Switch typically needs two (one for `on`,
  one for `off`).
* **No hardware to test?** Fire the trigger and watch it arrive in the
  [LiveLink Simulator](/studio/livelink-simulator)'s Device side.

<CardGroup cols={2}>
  <Card title="Define commands first" icon="terminal" href="/studio/api-commands">
    The commands your triggers execute.
  </Card>

  <Card title="The core loop" icon="diagram-project" href="/learn/how-it-works">
    How commands reach the device.
  </Card>
</CardGroup>
