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

# Smart Office

> Build an AI facilities concierge with meeting room sensors, thermostats, and desk occupancy

## Overview

**Industry:** Facilities Management

A smart office system where the AI agent acts as a facilities concierge. It checks meeting room availability, adjusts climate controls, monitors desk occupancy, and sends notifications to office displays.

**Commands:**

* `check_meeting_rooms` -- Query occupancy sensors in all meeting rooms
* `adjust_thermostat` -- Set target temperature for a zone
* `desk_occupancy` -- Get desk utilization for a floor
* `send_notification` -- Display a message on an office screen

## Device Client

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

  // Simulated office hardware
  const meetingRooms = [
    { name: 'Atlas', floor: 3, capacity: 8, occupied: true, bookedUntil: '14:30' },
    { name: 'Everest', floor: 3, capacity: 12, occupied: false, bookedUntil: null },
    { name: 'Summit', floor: 4, capacity: 6, occupied: true, bookedUntil: '15:00' },
    { name: 'Pinnacle', floor: 4, capacity: 20, occupied: false, bookedUntil: null },
  ];

  const zones: Record<string, { currentTemp: number; targetTemp: number; mode: string }> = {
    'floor-3': { currentTemp: 23.2, targetTemp: 22, mode: 'cooling' },
    'floor-4': { currentTemp: 21.8, targetTemp: 22, mode: 'idle' },
  };

  const desks: Record<string, { total: number; occupied: number }> = {
    'floor-3': { total: 48, occupied: 31 },
    'floor-4': { total: 52, occupied: 18 },
  };

  const device = new DeviceClient({
    agentId: process.env.LUA_AGENT_ID!,
    apiKey: process.env.LUA_API_KEY!,
    deviceName: 'office-controller',
    group: 'building-systems',
    commands: [
      {
        name: 'check_meeting_rooms',
        description: 'Check availability and occupancy of all meeting rooms. Returns room name, floor, capacity, and whether currently occupied.',
        inputSchema: {
          type: 'object',
          properties: {
            floor: { type: 'number', description: 'Filter by floor number (optional)' },
            minCapacity: { type: 'number', description: 'Minimum room capacity (optional)' },
          },
        },
      },
      {
        name: 'adjust_thermostat',
        description: 'Adjust the target temperature for a building zone. Zones are identified by floor (e.g., floor-3, floor-4).',
        inputSchema: {
          type: 'object',
          properties: {
            zone: { type: 'string', description: 'Zone identifier (e.g., floor-3)' },
            targetTemperature: { type: 'number', minimum: 18, maximum: 28, description: 'Target temperature in celsius' },
          },
          required: ['zone', 'targetTemperature'],
        },
      },
      {
        name: 'desk_occupancy',
        description: 'Get desk occupancy statistics for a given floor. Returns total desks, occupied desks, and utilization percentage.',
        inputSchema: {
          type: 'object',
          properties: {
            floor: { type: 'number', description: 'Floor number to check' },
          },
          required: ['floor'],
        },
      },
      {
        name: 'send_notification',
        description: 'Display a notification message on the office lobby or floor display screens',
        inputSchema: {
          type: 'object',
          properties: {
            message: { type: 'string', description: 'Message text to display' },
            screen: { type: 'string', enum: ['lobby', 'floor-3', 'floor-4', 'all'], description: 'Target screen' },
            priority: { type: 'string', enum: ['info', 'warning', 'urgent'], default: 'info' },
          },
          required: ['message', 'screen'],
        },
      },
    ],
  });

  device.onCommand('check_meeting_rooms', async (payload) => {
    let rooms = meetingRooms;
    if (payload?.floor) {
      rooms = rooms.filter(r => r.floor === payload.floor);
    }
    if (payload?.minCapacity) {
      rooms = rooms.filter(r => r.capacity >= payload.minCapacity);
    }
    return {
      rooms: rooms.map(r => ({
        ...r,
        available: !r.occupied,
      })),
      timestamp: new Date().toISOString(),
    };
  });

  device.onCommand('adjust_thermostat', async (payload) => {
    const zone = zones[payload.zone];
    if (!zone) {
      throw new Error(`Unknown zone: ${payload.zone}`);
    }
    zone.targetTemp = payload.targetTemperature;
    zone.mode = zone.currentTemp > payload.targetTemperature ? 'cooling' : 'heating';
    return {
      zone: payload.zone,
      currentTemperature: zone.currentTemp,
      targetTemperature: zone.targetTemp,
      mode: zone.mode,
    };
  });

  device.onCommand('desk_occupancy', async (payload) => {
    const floorKey = `floor-${payload.floor}`;
    const floor = desks[floorKey];
    if (!floor) {
      throw new Error(`No data for floor ${payload.floor}`);
    }
    return {
      floor: payload.floor,
      totalDesks: floor.total,
      occupiedDesks: floor.occupied,
      availableDesks: floor.total - floor.occupied,
      utilization: Math.round((floor.occupied / floor.total) * 100),
    };
  });

  device.onCommand('send_notification', async (payload) => {
    console.log(`[DISPLAY ${payload.screen}] ${payload.priority?.toUpperCase() || 'INFO'}: ${payload.message}`);
    return {
      sent: true,
      screen: payload.screen,
      message: payload.message,
      timestamp: new Date().toISOString(),
    };
  });

  async function main() {
    await device.connect();
    console.log('Office controller online');
  }

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

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

  # Simulated office hardware
  meeting_rooms = [
      {"name": "Atlas", "floor": 3, "capacity": 8, "occupied": True, "bookedUntil": "14:30"},
      {"name": "Everest", "floor": 3, "capacity": 12, "occupied": False, "bookedUntil": None},
      {"name": "Summit", "floor": 4, "capacity": 6, "occupied": True, "bookedUntil": "15:00"},
      {"name": "Pinnacle", "floor": 4, "capacity": 20, "occupied": False, "bookedUntil": None},
  ]

  zones = {
      "floor-3": {"currentTemp": 23.2, "targetTemp": 22, "mode": "cooling"},
      "floor-4": {"currentTemp": 21.8, "targetTemp": 22, "mode": "idle"},
  }

  desks = {
      "floor-3": {"total": 48, "occupied": 31},
      "floor-4": {"total": 52, "occupied": 18},
  }

  client = DeviceClient(
      agent_id=os.environ["LUA_AGENT_ID"],
      api_key=os.environ["LUA_API_KEY"],
      device_name="office-controller",
      group="building-systems",
      commands=[
          DeviceCommandDefinition(
              name="check_meeting_rooms",
              description="Check availability and occupancy of all meeting rooms. Returns room name, floor, capacity, and whether currently occupied.",
              input_schema={
                  "type": "object",
                  "properties": {
                      "floor": {"type": "number", "description": "Filter by floor number (optional)"},
                      "minCapacity": {"type": "number", "description": "Minimum room capacity (optional)"},
                  },
              },
          ),
          DeviceCommandDefinition(
              name="adjust_thermostat",
              description="Adjust the target temperature for a building zone. Zones are identified by floor (e.g., floor-3, floor-4).",
              input_schema={
                  "type": "object",
                  "properties": {
                      "zone": {"type": "string", "description": "Zone identifier (e.g., floor-3)"},
                      "targetTemperature": {"type": "number", "minimum": 18, "maximum": 28, "description": "Target temperature in celsius"},
                  },
                  "required": ["zone", "targetTemperature"],
              },
          ),
          DeviceCommandDefinition(
              name="desk_occupancy",
              description="Get desk occupancy statistics for a given floor. Returns total desks, occupied desks, and utilization percentage.",
              input_schema={
                  "type": "object",
                  "properties": {
                      "floor": {"type": "number", "description": "Floor number to check"},
                  },
                  "required": ["floor"],
              },
          ),
          DeviceCommandDefinition(
              name="send_notification",
              description="Display a notification message on the office lobby or floor display screens",
              input_schema={
                  "type": "object",
                  "properties": {
                      "message": {"type": "string", "description": "Message text to display"},
                      "screen": {"type": "string", "enum": ["lobby", "floor-3", "floor-4", "all"], "description": "Target screen"},
                      "priority": {"type": "string", "enum": ["info", "warning", "urgent"], "default": "info"},
                  },
                  "required": ["message", "screen"],
              },
          ),
      ],
  )

  @client.on_command("check_meeting_rooms")
  async def handle_check_meeting_rooms(payload):
      rooms = meeting_rooms
      if payload and payload.get("floor"):
          rooms = [r for r in rooms if r["floor"] == payload["floor"]]
      if payload and payload.get("minCapacity"):
          rooms = [r for r in rooms if r["capacity"] >= payload["minCapacity"]]
      return {
          "rooms": [{**r, "available": not r["occupied"]} for r in rooms],
          "timestamp": datetime.now(timezone.utc).isoformat(),
      }

  @client.on_command("adjust_thermostat")
  async def handle_adjust_thermostat(payload):
      zone = zones.get(payload["zone"])
      if not zone:
          raise Exception(f"Unknown zone: {payload['zone']}")
      zone["targetTemp"] = payload["targetTemperature"]
      zone["mode"] = "cooling" if zone["currentTemp"] > payload["targetTemperature"] else "heating"
      return {
          "zone": payload["zone"],
          "currentTemperature": zone["currentTemp"],
          "targetTemperature": zone["targetTemp"],
          "mode": zone["mode"],
      }

  @client.on_command("desk_occupancy")
  async def handle_desk_occupancy(payload):
      floor_key = f"floor-{payload['floor']}"
      floor = desks.get(floor_key)
      if not floor:
          raise Exception(f"No data for floor {payload['floor']}")
      return {
          "floor": payload["floor"],
          "totalDesks": floor["total"],
          "occupiedDesks": floor["occupied"],
          "availableDesks": floor["total"] - floor["occupied"],
          "utilization": round((floor["occupied"] / floor["total"]) * 100),
      }

  @client.on_command("send_notification")
  async def handle_send_notification(payload):
      priority = (payload.get("priority") or "info").upper()
      print(f"[DISPLAY {payload['screen']}] {priority}: {payload['message']}")
      return {
          "sent": True,
          "screen": payload["screen"],
          "message": payload["message"],
          "timestamp": datetime.now(timezone.utc).isoformat(),
      }

  async def main():
      await client.connect()
      print("Office controller online")

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

## Agent Configuration

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

const facilitiesSkill = new LuaSkill({
  name: 'facilities-concierge',
  description: 'Smart office management',
  context: `
    You manage office facilities through connected devices.

    Device tools:
    - check_meeting_rooms: Use when someone needs a meeting room. Filter by floor or capacity.
    - adjust_thermostat: Use when someone reports temperature issues. Range: 18-28C.
    - desk_occupancy: Use to report floor utilization.
    - send_notification: Use to broadcast messages to office screens.

    Guidelines:
    - When someone needs a room, check availability and suggest the best match
    - For temperature complaints, check current temp first, then adjust
    - Only send urgent notifications for actual emergencies
    - Be friendly and helpful -- you are the office concierge
  `,
  tools: [],
});

export const agent = new LuaAgent({
  name: 'office-concierge',
  persona: `You are a friendly office concierge. You help employees find meeting rooms,
    manage comfort settings, and stay informed about office status. Be warm and helpful.`,
  skills: [facilitiesSkill],
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Warehouse Scanner" icon="warehouse" href="/devices/examples/warehouse-inventory">
    Logistics and inventory management
  </Card>

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

  <Card title="Retail Kiosk" icon="store" href="/devices/examples/retail-kiosk">
    Customer-facing kiosk with NFC and receipts
  </Card>

  <Card title="Self-Describing Commands" icon="list-check" href="/devices/self-describing-commands">
    How to write effective command definitions
  </Card>
</CardGroup>
