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

# Triggers

> Send events from devices to your agent and handle them with server-side logic

## Two Sides of a Trigger

A device trigger has two sides:

1. **Device side** -- the device fires the trigger using `client.trigger()` or `device.trigger()`
2. **Agent side** -- you define what happens when the trigger arrives using `defineDeviceTrigger()`

The device sends the event. The agent handles it.

<Card title="Think of it as:" icon="bell">
  A doorbell. The device presses it (fires the trigger). Your agent-side code decides what to do when it rings (the execute function).
</Card>

## Device Side: Firing Triggers

### Node.js

```typescript theme={null}
// Fire a trigger with a payload
await device.trigger('low_stock', {
  sku: 'WIDGET-001',
  currentQuantity: 3,
  threshold: 10,
  location: 'warehouse-A',
});
```

The `trigger()` method resolves when the server acknowledges receipt. It does not wait for the agent to finish processing.

### Python

```python theme={null}
# Fire a trigger with a payload
await client.trigger("low_stock", {
    "sku": "WIDGET-001",
    "currentQuantity": 3,
    "threshold": 10,
    "location": "warehouse-A",
})
```

### MicroPython

```python theme={null}
device.trigger("low_stock", {
    "sku": "WIDGET-001",
    "currentQuantity": 3,
    "threshold": 10,
    "location": "warehouse-A",
})
```

## Agent Side: Handling Triggers

On the agent side, create a device trigger primitive using `defineDeviceTrigger()`. This is a standalone file that gets compiled, pushed, and deployed like any other Lua primitive.

```typescript theme={null}
// src/triggers/low-stock.ts
import { defineDeviceTrigger } from 'lua-cli';
import { z } from 'zod';

export const lowStock = defineDeviceTrigger({
  name: 'low-stock',
  description: 'Fired when inventory for an item drops below its reorder threshold',
  payloadSchema: z.object({
    sku: z.string(),
    currentQuantity: z.number(),
    threshold: z.number(),
    location: z.string(),
  }),
  execute: async (payload, { agent, device }) => {
    await agent.chat(
      `Device ${device.name} reports low stock for SKU ${payload.sku}. ` +
      `Current quantity: ${payload.currentQuantity}, threshold: ${payload.threshold}. ` +
      `Location: ${payload.location}. Please create a reorder request.`
    );
  },
});
```

<Note>
  The trigger `name` on the agent side uses hyphens (e.g., `low-stock`). The trigger name on the device side uses underscores (e.g., `low_stock`). The gateway maps between the two conventions automatically.
</Note>

## Both Sides Together

<CodeGroup>
  ```typescript Device Side (Node.js) theme={null}
  import { DeviceClient } from '@lua-ai-global/device-client';

  const device = new DeviceClient({
    agentId: process.env.LUA_AGENT_ID!,
    apiKey: process.env.LUA_API_KEY!,
    deviceName: 'temp-monitor',
    commands: [
      { name: 'read_temperature', description: 'Read current temperature' },
    ],
  });

  device.onCommand('read_temperature', async () => {
    const temp = readSensor();
    return { temperature: temp, unit: 'celsius' };
  });

  async function main() {
    await device.connect();

    // Check temperature every 30 seconds, trigger alert if too high
    setInterval(async () => {
      const temp = readSensor();
      if (temp > 40) {
        await device.trigger('temperature_alert', {
          temperature: temp,
          threshold: 40,
          sensor: 'main-hall',
        });
      }
    }, 30000);
  }

  function readSensor(): number {
    return 20 + Math.random() * 25; // simulated
  }

  main().catch(console.error);
  ```

  ```python Device Side (Python) theme={null}
  import asyncio
  import random
  from lua_device import DeviceClient, DeviceCommandDefinition

  client = DeviceClient(
      agent_id="your-agent-id",
      api_key="your-api-key",
      device_name="temp-monitor",
      commands=[
          DeviceCommandDefinition(name="read_temperature", description="Read current temperature"),
      ],
  )

  def read_sensor() -> float:
      return 20 + random.random() * 25  # simulated

  @client.on_command("read_temperature")
  async def handle_read_temperature(payload):
      temp = read_sensor()
      return {"temperature": temp, "unit": "celsius"}

  async def main():
      await client.connect()

      # Check temperature every 30 seconds, trigger alert if too high
      while True:
          temp = read_sensor()
          if temp > 40:
              await client.trigger("temperature_alert", {
                  "temperature": temp,
                  "threshold": 40,
                  "sensor": "main-hall",
              })
          await asyncio.sleep(30)

  asyncio.run(main())
  ```

  ```typescript Agent Side (src/triggers/temperature-alert.ts) theme={null}
  import { defineDeviceTrigger } from 'lua-cli';
  import { z } from 'zod';

  export const temperatureAlert = defineDeviceTrigger({
    name: 'temperature-alert',
    description: 'Fired when a temperature sensor exceeds its threshold',
    payloadSchema: z.object({
      temperature: z.number(),
      threshold: z.number(),
      sensor: z.string(),
    }),
    execute: async (payload, { agent, device }) => {
      const message =
        `ALERT: Device "${device.name}" sensor "${payload.sensor}" ` +
        `reads ${payload.temperature}C (threshold: ${payload.threshold}C). ` +
        `Please investigate and take appropriate action.`;

      await agent.chat(message);
    },
  });
  ```
</CodeGroup>

## Deploying Device Triggers

Device triggers are compiled and pushed like other Lua primitives:

```bash theme={null}
lua push device-trigger
```

Or push everything at once:

```bash theme={null}
lua push
```

Then deploy:

```bash theme={null}
lua deploy
```

## LuaDeviceTriggerConfig Reference

<ParamField body="name" type="string" required>
  Trigger name. Lowercase with hyphens (e.g., `paper-low`, `temperature-alert`).
</ParamField>

<ParamField body="description" type="string">
  Description of when this trigger fires. Helps with debugging and documentation.
</ParamField>

<ParamField body="payloadSchema" type="ZodType">
  Zod schema for validating the trigger payload. The payload is validated before `execute` runs.
</ParamField>

<ParamField body="execute" type="(payload, context) => Promise<any>" required>
  The function that runs when the trigger fires. Receives the validated payload and a context object.
</ParamField>

### Execute Context

The `execute` function receives a context object:

| Property | Type               | Description                                                          |
| -------- | ------------------ | -------------------------------------------------------------------- |
| `agent`  | object             | Agent context. Call `agent.chat()` to send messages or invoke tools. |
| `device` | `{ name: string }` | Information about the device that fired the trigger.                 |

## Trigger Result Listening (Optional)

On the device side, you can optionally listen for the result of trigger execution:

<CodeGroup>
  ```typescript TypeScript theme={null}
  device.onTriggerResult('temperature_alert', (result) => {
    console.log('Agent handled the alert:', result);
    // e.g., result might contain actions taken
  });
  ```

  ```python Python theme={null}
  @client.on_trigger_result("temperature_alert")
  async def handle_alert_result(result):
      print("Agent handled the alert:", result)
      # e.g., result might contain actions taken
  ```
</CodeGroup>

This is optional. Most triggers are fire-and-forget.

## Next Steps

<CardGroup cols={2}>
  <Card title="Agent Tools" icon="wrench" href="/devices/agent-tools">
    How the other direction works -- agent sending commands to devices
  </Card>

  <Card title="Self-Describing Commands" icon="list-check" href="/devices/self-describing-commands">
    How devices declare their command capabilities
  </Card>

  <Card title="Warehouse Example" icon="warehouse" href="/devices/examples/warehouse-inventory">
    Full example with triggers for low stock alerts
  </Card>

  <Card title="Industrial Sensor" icon="industry" href="/devices/examples/industrial-sensor">
    MicroPython trigger example for vibration anomalies
  </Card>
</CardGroup>
