Skip to main content

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.

Think of it as:

A doorbell. The device presses it (fires the trigger). Your agent-side code decides what to do when it rings (the execute function).

Device Side: Firing Triggers

Node.js

// 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.

MicroPython

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.
// 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.`
    );
  },
});
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.

Both Sides Together

import { DeviceClient } from '@lua-ai/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);

Deploying Device Triggers

Device triggers are compiled and pushed like other Lua primitives:
lua push device-trigger
Or push everything at once:
lua push
Then deploy:
lua deploy

LuaDeviceTriggerConfig Reference

name
string
required
Trigger name. Lowercase with hyphens (e.g., paper-low, temperature-alert).
description
string
Description of when this trigger fires. Helps with debugging and documentation.
payloadSchema
ZodType
Zod schema for validating the trigger payload. The payload is validated before execute runs.
execute
(payload, context) => Promise<any>
required
The function that runs when the trigger fires. Receives the validated payload and a context object.

Execute Context

The execute function receives a context object:
PropertyTypeDescription
agentobjectAgent 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:
device.onTriggerResult('temperature_alert', (result) => {
  console.log('Agent handled the alert:', result);
  // e.g., result might contain actions taken
});
This is optional. Most triggers are fire-and-forget.

Next Steps

Agent Tools

How the other direction works — agent sending commands to devices

Self-Describing Commands

How devices declare their command capabilities

Warehouse Example

Full example with triggers for low stock alerts

Industrial Sensor

MicroPython trigger example for vibration anomalies