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

# Devices

> Hardware and local machines that connect to the agent, expose commands as tools, and send it events

A device is a client (a sensor, a printer, a kiosk, a desktop) that holds a connection to the platform with a device credential, declares the commands it accepts, and can send events. Each command becomes a [tool](/concepts/skills-and-tools) the agent can call while the device is connected; each event is a device trigger the agent handles in code on the server. Devices exist so an agent can act on the physical world without an intermediary service between the hardware and the model.

## How a device connects

A device connects over one of two transports: Socket.IO over WebSocket, the default for the Node client, or MQTT, used by the Node, Python, and MicroPython clients. The MQTT clients default to MQTT over WebSocket with TLS on port 443 (`wss://mqtt.heylua.ai/mqtt`), which passes most firewalls; the clients also accept a raw `mqtts://` URL.  At connect the client presents a device credential: a key of the form `api_<uuid>.<secret>` issued for one agent, one device name, and a set of operations (`commands`, `triggers`, `assets.upload`). The platform re-checks the credential while the device stays connected, so revoking it takes the device offline.

With the connection the device declares its commands: a name, a description, and an optional JSON Schema for the input. The platform turns each into a tool named `device__<device>__<command>` (hyphens in the device name become underscores), described as `[Device: <name>] <description>. If the device is offline, this will return an error.`, and adds `device__<device>__is_online`, which answers `{ status: 'online' }` or, for a device that isn't connected, its stored `offline`, `registered`, or `disabled`. The model calls them like any other tool; the platform forwards the call, waits up to the command's timeout (30 seconds by default), and returns the device's reply. When a device that declared its own commands (a self-describing device) disconnects, its command tools are gone on the agent's next turn; a device declared with `defineDevice` keeps its tools while offline, and calls to it return `DEVICE_OFFLINE`. A self-describing device's stored command list also expires 24 hours after it connected, and heartbeats don't extend it, so a device connected for longer than a day loses its command tools until it reconnects. Either way the prompt tells the model to check `is_online` before acting on a device.

Events travel the other way. The device sends a trigger with a name and a payload, and the platform runs the matching handler on the server: the `execute` of a `defineDeviceTrigger` you pushed, or of a trigger declared inside `defineDevice`. The handler runs with the platform APIs in scope (`Channels`, `User`, `Data`, `Agents`), so a "temperature exceeded" event can become a message to an operator or a stored alert; it receives no environment variables, so `env()` is empty there. Its second argument carries only the device and trigger names; the `agent` field in its type is not populated at runtime, so don't call it. Events are delivered at least once from a durable queue: a handler that throws or times out runs again, up to 3 attempts 60 seconds apart with the same `triggerId`, so handlers must be idempotent, and each attempt has up to 10 minutes, the platform's default cap for event handlers. An event with no pushed handler is acknowledged and skipped.

```ts src/devices/label-printer.device.ts theme={null}
import { Data, defineDevice } from 'lua-cli';
import { z } from 'zod';

export const labelPrinter = defineDevice({
  name: 'label-printer',
  description: 'Thermal label printer at the packing bench',
  commands: {
    print: {
      description: 'Print a shipping label',
      inputSchema: z.object({ text: z.string(), copies: z.number().default(1) }),
      timeoutMs: 30000,
    },
  },
  triggers: {
    paper_low: {
      description: 'Fires when the paper level drops below the threshold',
      payloadSchema: z.object({ level: z.number() }),
      async execute(payload, { device }) {
        await Data.create('printer-alerts', { device: device.name, level: payload.level, at: Date.now() });
      },
    },
  },
});
```

`defineDevice` and `defineDeviceTrigger` are the agent-side declarations, registered on `LuaAgent.devices` and `deviceTriggers`. A self-describing device needs no `defineDevice` to get tools, but the declaration is where command timeouts and retries, a `group` for fan-out (`device__<group>__<command>__all` sends to every registered device in the group, online or not; an offline member fails with a 404 and counts in `failed`; membership is the `group` each device reports when it connects), and trigger handlers live. A command is not retried unless you set `retry`, and then only after a timeout or a server-side failure; an offline or rate-limited device fails at once.

Devices and device triggers are not part of an [agent version](/concepts/releases-and-versions). A pushed `defineDevice` declaration reaches the runtime on the agent's next turn whether or not you publish it; `--auto-deploy` only records it as the active version. A standalone `defineDeviceTrigger` runs only after its pushed version is published, with `--auto-deploy` or the push prompt. `lua deploy` has no device types. `lua devices list|status|enable|disable|remove|test|test-trigger --device-name <name>` manage registered devices from the CLI: `enable` and `disable` admit or refuse the device's connection, and `test-trigger` relays a test event to the device without running a handler. The client libraries are `@lua-ai-global/device-client` for Node (Socket.IO and MQTT, with CDN uploads), `lua-device-client` on PyPI for Python (MQTT), and a single `lua_device.py` file for MicroPython that runs on a Raspberry Pi Pico W.

## Devices and webhooks

A [webhook](/concepts/webhooks) is a request your code handles once; the sender needs nothing beyond the signature and gets an HTTP response. A device is a standing, authenticated connection in both directions: the agent can call the device, the device can wake the agent, and liveness is known at every moment. Use a device when the agent must act on the hardware or the hardware sends more than an occasional event; use a webhook when a system posts you data.

## When to use it

* The agent should control hardware or a local machine from a conversation: expose commands.
* Sensors or a desktop should wake the agent: send device triggers and push a handler.
* The hardware is a microcontroller: use the MicroPython client over MQTT.
* Don't use a device for a service that already has an HTTP API; call it from a tool with `fetch`.

## Limits

| Item                                            | Value                                                 |
| ----------------------------------------------- | ----------------------------------------------------- |
| Device tools per agent                          | 128                                                   |
| Command timeout                                 | 30,000 ms default; no retries unless `retry` is set   |
| Trigger and response payload                    | 1 MB                                                  |
| Triggers per agent                              | 10 per second                                         |
| Trigger handler time limit                      | 10 min, the platform's default cap for event handlers |
| Stored command list of a self-describing device | 24 h after connect; heartbeats don't extend it        |
| `is_online` values                              | `online`, `offline`, `registered`, `disabled`         |
| Client heartbeat                                | Every 30 s                                            |

## Next steps

<Columns cols={2}>
  <Card title="Devices quickstart" href="/devices/quickstart">Connect a first device and run a command from chat.</Card>
  <Card title="How devices work" href="/devices/how-it-works">The command and trigger path end to end.</Card>
  <Card title="defineDevice reference" href="/reference/sdk/device-definition">Commands, triggers, groups, and handlers.</Card>
  <Card title="Reliability and limits" href="/devices/reliability-and-limits">Reconnection, heartbeats, and payload limits.</Card>
</Columns>
