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

# Warehouse Scanner

> Build an AI-powered warehouse inventory system with barcode scanners and weight sensors

## Overview

**Industry:** Logistics / Warehousing

A warehouse device that connects barcode scanners, weight scales, stock-level sensors, and gate controllers to an AI agent. The agent manages inventory, verifies shipments, and controls dock access through natural language.

**Commands:**

* `scan_barcode` -- Activate scanner, return barcode value
* `read_weight` -- Read current weight on the scale
* `check_stock_level` -- Query stock for a given SKU
* `open_gate` -- Open a dock gate

**Triggers:**

* `low_stock_alert` -- Fires when stock drops below reorder threshold

## Device Client

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

  // Simulated hardware interfaces
  const scanner = { scan: async () => ({ value: 'SKU-2024-0847', format: 'CODE128' }) };
  const scale = { read: async () => ({ weight: 12.4, unit: 'kg' }) };
  const inventory: Record<string, { quantity: number; threshold: number; name: string }> = {
    'SKU-2024-0847': { quantity: 8, threshold: 10, name: 'Widget A' },
    'SKU-2024-0312': { quantity: 142, threshold: 20, name: 'Bracket B' },
    'SKU-2024-1100': { quantity: 3, threshold: 15, name: 'Sensor C' },
  };
  const gate = { open: async () => ({ opened: true }) };

  const device = new DeviceClient({
    agentId: process.env.LUA_AGENT_ID!,
    apiKey: process.env.LUA_API_KEY!,
    deviceName: 'warehouse-station-01',
    group: 'warehouse-floor',
    commands: [
      {
        name: 'scan_barcode',
        description: 'Activate the barcode scanner and return the scanned code. Takes 1-2 seconds. Returns barcode value and format.',
        timeoutMs: 10000,
      },
      {
        name: 'read_weight',
        description: 'Read the current weight on the shipping scale in kilograms',
      },
      {
        name: 'check_stock_level',
        description: 'Check current stock level for a given SKU',
        inputSchema: {
          type: 'object',
          properties: {
            sku: { type: 'string', description: 'Product SKU to check' },
          },
          required: ['sku'],
        },
      },
      {
        name: 'open_gate',
        description: 'Open the dock gate for incoming/outgoing shipments. Confirm with the user before opening.',
        inputSchema: {
          type: 'object',
          properties: {
            gate: { type: 'string', enum: ['dock-a', 'dock-b', 'dock-c'] },
          },
          required: ['gate'],
        },
        timeoutMs: 15000,
      },
    ],
  });

  device.onCommand('scan_barcode', async () => {
    const result = await scanner.scan();
    return { barcode: result.value, format: result.format, timestamp: new Date().toISOString() };
  });

  device.onCommand('read_weight', async () => {
    const result = await scale.read();
    return { weight: result.weight, unit: result.unit };
  });

  device.onCommand('check_stock_level', async (payload) => {
    const item = inventory[payload.sku];
    if (!item) {
      return { error: `Unknown SKU: ${payload.sku}`, found: false };
    }
    return {
      sku: payload.sku,
      name: item.name,
      quantity: item.quantity,
      threshold: item.threshold,
      status: item.quantity <= item.threshold ? 'low' : 'ok',
      found: true,
    };
  });

  device.onCommand('open_gate', async (payload) => {
    const result = await gate.open();
    return { gate: payload.gate, opened: result.opened, timestamp: new Date().toISOString() };
  });

  async function main() {
    await device.connect();
    console.log('Warehouse station online');

    // Monitor stock levels every 60 seconds
    setInterval(async () => {
      for (const [sku, item] of Object.entries(inventory)) {
        if (item.quantity <= item.threshold) {
          await device.trigger('low_stock_alert', {
            sku,
            name: item.name,
            currentQuantity: item.quantity,
            threshold: item.threshold,
          });
        }
      }
    }, 60000);
  }

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

  ```python Python theme={null}
  import asyncio
  import os
  from datetime import datetime, timezone
  from lua_device import DeviceClient, DeviceCommandDefinition

  # Simulated hardware interfaces
  async def scan_barcode():
      return {"value": "SKU-2024-0847", "format": "CODE128"}

  async def read_scale():
      return {"weight": 12.4, "unit": "kg"}

  inventory = {
      "SKU-2024-0847": {"quantity": 8, "threshold": 10, "name": "Widget A"},
      "SKU-2024-0312": {"quantity": 142, "threshold": 20, "name": "Bracket B"},
      "SKU-2024-1100": {"quantity": 3, "threshold": 15, "name": "Sensor C"},
  }

  async def open_gate_hw():
      return {"opened": True}

  client = DeviceClient(
      agent_id=os.environ["LUA_AGENT_ID"],
      api_key=os.environ["LUA_API_KEY"],
      device_name="warehouse-station-01",
      group="warehouse-floor",
      commands=[
          DeviceCommandDefinition(
              name="scan_barcode",
              description="Activate the barcode scanner and return the scanned code. Takes 1-2 seconds. Returns barcode value and format.",
              timeout_ms=10000,
          ),
          DeviceCommandDefinition(
              name="read_weight",
              description="Read the current weight on the shipping scale in kilograms",
          ),
          DeviceCommandDefinition(
              name="check_stock_level",
              description="Check current stock level for a given SKU",
              input_schema={
                  "type": "object",
                  "properties": {
                      "sku": {"type": "string", "description": "Product SKU to check"},
                  },
                  "required": ["sku"],
              },
          ),
          DeviceCommandDefinition(
              name="open_gate",
              description="Open the dock gate for incoming/outgoing shipments. Confirm with the user before opening.",
              input_schema={
                  "type": "object",
                  "properties": {
                      "gate": {"type": "string", "enum": ["dock-a", "dock-b", "dock-c"]},
                  },
                  "required": ["gate"],
              },
              timeout_ms=15000,
          ),
      ],
  )

  @client.on_command("scan_barcode")
  async def handle_scan_barcode(payload):
      result = await scan_barcode()
      return {"barcode": result["value"], "format": result["format"], "timestamp": datetime.now(timezone.utc).isoformat()}

  @client.on_command("read_weight")
  async def handle_read_weight(payload):
      result = await read_scale()
      return {"weight": result["weight"], "unit": result["unit"]}

  @client.on_command("check_stock_level")
  async def handle_check_stock_level(payload):
      item = inventory.get(payload["sku"])
      if not item:
          return {"error": f"Unknown SKU: {payload['sku']}", "found": False}
      return {
          "sku": payload["sku"],
          "name": item["name"],
          "quantity": item["quantity"],
          "threshold": item["threshold"],
          "status": "low" if item["quantity"] <= item["threshold"] else "ok",
          "found": True,
      }

  @client.on_command("open_gate")
  async def handle_open_gate(payload):
      result = await open_gate_hw()
      return {"gate": payload["gate"], "opened": result["opened"], "timestamp": datetime.now(timezone.utc).isoformat()}

  async def main():
      await client.connect()
      print("Warehouse station online")

      # Monitor stock levels every 60 seconds
      while True:
          for sku, item in inventory.items():
              if item["quantity"] <= item["threshold"]:
                  await client.trigger("low_stock_alert", {
                      "sku": sku,
                      "name": item["name"],
                      "currentQuantity": item["quantity"],
                      "threshold": item["threshold"],
                  })
          await asyncio.sleep(60)

  asyncio.run(main())
  ```
</CodeGroup>

## Agent-Side Trigger Handler

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

export const lowStockAlert = defineDeviceTrigger({
  name: 'low-stock-alert',
  description: 'Fired when warehouse stock for an item drops below its reorder threshold',
  payloadSchema: z.object({
    sku: z.string(),
    name: z.string(),
    currentQuantity: z.number(),
    threshold: z.number(),
  }),
  execute: async (payload, { agent, device }) => {
    await agent.chat(
      `LOW STOCK ALERT from ${device.name}: ` +
      `"${payload.name}" (${payload.sku}) is at ${payload.currentQuantity} units, ` +
      `below the reorder threshold of ${payload.threshold}. ` +
      `Please create a purchase order to restock.`
    );
  },
});
```

## Agent Configuration

```typescript theme={null}
// src/index.ts
import { LuaAgent, LuaSkill } from 'lua-cli';
import { lowStockAlert } from './triggers/low-stock-alert';

const warehouseSkill = new LuaSkill({
  name: 'warehouse-operations',
  description: 'Warehouse inventory and shipping tools',
  context: `
    This skill works with warehouse floor devices.

    Device tools (available when devices are connected):
    - scan_barcode: Use when a worker asks to scan or identify an item
    - read_weight: Use when verifying shipment weights
    - check_stock_level: Use when checking inventory for a SKU
    - open_gate: Use when a shipment needs dock access. ALWAYS confirm with the user first.

    Guidelines:
    - After scanning a barcode, automatically check its stock level
    - Compare shipment weights against expected values when available
    - Never open a gate without explicit user confirmation
  `,
  tools: [],
});

export const agent = new LuaAgent({
  name: 'warehouse-agent',
  persona: `You are a warehouse operations assistant. You help workers manage inventory,
    verify shipments, and control dock access. Be concise and action-oriented.
    If a device is offline, tell the worker and suggest they check the connection.`,
  skills: [warehouseSkill],
  deviceTriggers: [lowStockAlert],
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Smart Office" icon="building" href="/devices/examples/smart-office">
    Facilities management example
  </Card>

  <Card title="Industrial Sensor" icon="industry" href="/devices/examples/industrial-sensor">
    Factory monitoring on Pico W
  </Card>

  <Card title="Triggers" icon="bolt" href="/devices/triggers">
    Deep dive into trigger architecture
  </Card>

  <Card title="Node.js Client" icon="node-js" href="/devices/node-client">
    Full client reference
  </Card>
</CardGroup>
