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

# Fleet Dashboard

> Fleet-wide recording management and device registry

Manage recordings, devices, and telemetry across robot fleets from a single dashboard. The fleet dashboard provides a centralized view of all connected devices, their recordings, and aggregate fleet health metrics -- so you can monitor what your robots are capturing without digging through individual recordings.

<Info>
  Fleet Management is in preview. APIs and features described on this page may change.
</Info>

## Device Registry

Every physical device in your fleet -- robot arms, autonomous vehicles, drones, mobile robots -- gets registered in the device registry. Each device receives a unique `dev_` prefixed ID that ties it to its recordings, events, and telemetry.

### Device Properties

| Property           | Type       | Description                                               |
| ------------------ | ---------- | --------------------------------------------------------- |
| `uid`              | `string`   | Unique device ID (`dev_` prefix, auto-generated)          |
| `name`             | `string`   | Human-readable device name                                |
| `type`             | `string`   | Device category (e.g., `manipulator`, `vehicle`, `drone`) |
| `status`           | `string`   | Current status: `online`, `offline`, or `maintenance`     |
| `firmware_version` | `string`   | Firmware or software version running on the device        |
| `metadata`         | `object`   | Arbitrary key-value pairs (location, serial number, etc.) |
| `last_seen_at`     | `datetime` | Timestamp of the last heartbeat or recording upload       |
| `created_at`       | `datetime` | When the device was registered                            |

### Register a Device

<CodeGroup>
  ```python Python theme={null}
  from avala import Client

  client = Client()

  device = client.fleet.devices.register(
      name="robot-arm-01",
      type="manipulator",
      firmware_version="2.4.1",
      metadata={"location": "warehouse-b", "serial": "RA-2024-0142"}
  )

  print(f"Registered: {device.name} ({device.uid})")
  ```

  ```typescript TypeScript theme={null}
  import Avala from "@avala-ai/sdk";

  const avala = new Avala();

  const device = await avala.fleet.devices.register({
    name: "robot-arm-01",
    type: "manipulator",
    firmwareVersion: "2.4.1",
    metadata: { location: "warehouse-b", serial: "RA-2024-0142" },
  });

  console.log(`Registered: ${device.name} (${device.uid})`);
  ```

  ```bash CLI theme={null}
  avala fleet devices register \
    --name "robot-arm-01" \
    --type manipulator \
    --firmware "2.4.1" \
    --metadata '{"location": "warehouse-b", "serial": "RA-2024-0142"}'
  ```
</CodeGroup>

### List Devices

Filter devices by status, type, or metadata tags to find exactly what you need.

<CodeGroup>
  ```python Python theme={null}
  # List all online devices
  devices = client.fleet.devices.list(status="online")
  for device in devices:
      print(f"{device.name} ({device.uid}) -- {device.status}")

  # Filter by type
  manipulators = client.fleet.devices.list(type="manipulator")

  # Filter by metadata
  warehouse_b = client.fleet.devices.list(metadata={"location": "warehouse-b"})
  ```

  ```typescript TypeScript theme={null}
  // List all online devices
  const devices = await avala.fleet.devices.list({ status: "online" });
  for (const device of devices.items) {
    console.log(`${device.name} (${device.uid}) -- ${device.status}`);
  }

  // Filter by type
  const manipulators = await avala.fleet.devices.list({ type: "manipulator" });

  // Filter by metadata
  const warehouseB = await avala.fleet.devices.list({
    metadata: { location: "warehouse-b" },
  });
  ```

  ```bash CLI theme={null}
  avala fleet devices list --status online
  avala fleet devices list --type manipulator
  avala fleet devices list --metadata '{"location": "warehouse-b"}'
  ```
</CodeGroup>

### Update Device Status

Set a device to `maintenance` before performing firmware updates or physical servicing. This suppresses alerts and pauses recording rules for the device.

<CodeGroup>
  ```python Python theme={null}
  client.fleet.devices.update(
      device_id="dev_abc123",
      status="maintenance",
      firmware_version="2.5.0"
  )
  ```

  ```typescript TypeScript theme={null}
  await avala.fleet.devices.update({
    deviceId: "dev_abc123",
    status: "maintenance",
    firmwareVersion: "2.5.0",
  });
  ```

  ```bash CLI theme={null}
  avala fleet devices update dev_abc123 --status maintenance --firmware "2.5.0"
  ```
</CodeGroup>

<Tip>
  When a device transitions from `maintenance` back to `online`, any recording rules assigned to it or its device group resume automatically.
</Tip>

### Deregister a Device

Remove a device from the registry. Existing recordings and events are preserved -- only the device entry is removed.

<CodeGroup>
  ```python Python theme={null}
  client.fleet.devices.deregister(device_id="dev_abc123")
  ```

  ```typescript TypeScript theme={null}
  await avala.fleet.devices.deregister({ deviceId: "dev_abc123" });
  ```

  ```bash CLI theme={null}
  avala fleet devices deregister dev_abc123
  ```
</CodeGroup>

<Warning>
  Deregistering a device does not delete its recordings or events. However, the device will no longer appear in the registry and cannot receive new recordings until re-registered.
</Warning>

## Recording Browser

The recording browser provides a filterable, sortable view of all recordings across your fleet. Recordings are automatically associated with the device that captured them based on the device ID in the upload metadata.

### Filter Recordings

<CodeGroup>
  ```python Python theme={null}
  # List recordings from a specific device
  recordings = client.fleet.recordings.list(
      device_id="dev_abc123",
      status="ready"
  )

  # Filter by date range
  from datetime import datetime, timedelta

  recordings = client.fleet.recordings.list(
      since=datetime(2026, 1, 1),
      until=datetime(2026, 2, 1),
      status="ready"
  )

  # Filter by tags
  recordings = client.fleet.recordings.list(
      tags=["highway", "night-driving"]
  )

  for rec in recordings:
      print(f"{rec.uid} | {rec.device_name} | {rec.duration_seconds}s | {rec.status}")
  ```

  ```typescript TypeScript theme={null}
  // List recordings from a specific device
  const recordings = await avala.fleet.recordings.list({
    deviceId: "dev_abc123",
    status: "ready",
  });

  // Filter by date range
  const filtered = await avala.fleet.recordings.list({
    since: "2026-01-01T00:00:00Z",
    until: "2026-02-01T00:00:00Z",
    status: "ready",
  });

  // Filter by tags
  const tagged = await avala.fleet.recordings.list({
    tags: ["highway", "night-driving"],
  });

  for (const rec of recordings.items) {
    console.log(`${rec.uid} | ${rec.deviceName} | ${rec.durationSeconds}s | ${rec.status}`);
  }
  ```

  ```bash CLI theme={null}
  avala fleet recordings list --device dev_abc123 --status ready
  avala fleet recordings list --since 2026-01-01 --until 2026-02-01
  avala fleet recordings list --tags highway,night-driving
  ```
</CodeGroup>

### Recording Statuses

| Status       | Description                                                             |
| ------------ | ----------------------------------------------------------------------- |
| `uploading`  | Recording is being uploaded to Avala                                    |
| `processing` | Upload complete, MCAP file is being indexed and validated               |
| `ready`      | Processing complete, recording is available for playback and annotation |
| `error`      | Processing failed (corrupt file, unsupported format, missing schemas)   |
| `archived`   | Recording has been archived and is no longer actively accessible        |

<Tip>
  Recordings transition from `uploading` to `processing` to `ready` automatically. If a recording stays in `processing` for more than 15 minutes, check the file format and size against the [recording best practices](/docs/visualization/guides/recording-best-practices).
</Tip>

## Fleet Metrics

Aggregate statistics give you a high-level view of fleet activity. Metrics update in real time on the dashboard and are available through the SDK for integration with external monitoring systems.

### Available Metrics

| Metric                   | Description                                 |
| ------------------------ | ------------------------------------------- |
| `active_devices`         | Devices with status `online`                |
| `total_devices`          | All registered devices (any status)         |
| `recordings_today`       | Recordings uploaded in the current UTC day  |
| `recordings_this_week`   | Recordings uploaded in the current UTC week |
| `total_recordings`       | All-time recording count                    |
| `total_storage_bytes`    | Total storage used across all recordings    |
| `avg_recording_duration` | Average recording duration in seconds       |
| `recordings_by_status`   | Breakdown of recordings by status           |

<CodeGroup>
  ```python Python theme={null}
  metrics = client.fleet.metrics.get()

  print(f"Active devices: {metrics.active_devices}/{metrics.total_devices}")
  print(f"Recordings today: {metrics.recordings_today}")
  print(f"Total storage: {metrics.total_storage_bytes / 1e9:.1f} GB")
  print(f"Avg duration: {metrics.avg_recording_duration:.0f}s")

  # Breakdown by status
  for status, count in metrics.recordings_by_status.items():
      print(f"  {status}: {count}")
  ```

  ```typescript TypeScript theme={null}
  const metrics = await avala.fleet.metrics.get();

  console.log(`Active devices: ${metrics.activeDevices}/${metrics.totalDevices}`);
  console.log(`Recordings today: ${metrics.recordingsToday}`);
  console.log(`Total storage: ${(metrics.totalStorageBytes / 1e9).toFixed(1)} GB`);
  console.log(`Avg duration: ${metrics.avgRecordingDuration.toFixed(0)}s`);

  for (const [status, count] of Object.entries(metrics.recordingsByStatus)) {
    console.log(`  ${status}: ${count}`);
  }
  ```

  ```bash CLI theme={null}
  avala fleet metrics
  ```
</CodeGroup>

### Time-Series Metrics

Query metrics over time for trend analysis and reporting.

<CodeGroup>
  ```python Python theme={null}
  # Recordings per day over the last 30 days
  timeseries = client.fleet.metrics.timeseries(
      metric="recordings",
      interval="day",
      since="2026-01-01T00:00:00Z",
      until="2026-02-01T00:00:00Z"
  )

  for point in timeseries:
      print(f"{point.timestamp}: {point.value} recordings")
  ```

  ```typescript TypeScript theme={null}
  const timeseries = await avala.fleet.metrics.timeseries({
    metric: "recordings",
    interval: "day",
    since: "2026-01-01T00:00:00Z",
    until: "2026-02-01T00:00:00Z",
  });

  for (const point of timeseries.items) {
    console.log(`${point.timestamp}: ${point.value} recordings`);
  }
  ```
</CodeGroup>

## Device Groups

Organize devices into logical groups to manage them collectively. Groups let you apply recording rules, alert policies, and metadata at the group level instead of configuring each device individually.

### Create a Group

<CodeGroup>
  ```python Python theme={null}
  group = client.fleet.groups.create(
      name="Warehouse B - Pick & Place",
      description="Robot arms in warehouse B performing pick and place operations",
      device_ids=["dev_abc123", "dev_def456", "dev_ghi789"]
  )

  print(f"Group created: {group.name} ({group.uid}) with {len(group.device_ids)} devices")
  ```

  ```typescript TypeScript theme={null}
  const group = await avala.fleet.groups.create({
    name: "Warehouse B - Pick & Place",
    description: "Robot arms in warehouse B performing pick and place operations",
    deviceIds: ["dev_abc123", "dev_def456", "dev_ghi789"],
  });

  console.log(`Group created: ${group.name} (${group.uid}) with ${group.deviceIds.length} devices`);
  ```

  ```bash CLI theme={null}
  avala fleet groups create \
    --name "Warehouse B - Pick & Place" \
    --devices dev_abc123,dev_def456,dev_ghi789
  ```
</CodeGroup>

### Manage Group Membership

<CodeGroup>
  ```python Python theme={null}
  # Add devices to a group
  client.fleet.groups.add_devices(
      group_id="grp_abc123",
      device_ids=["dev_jkl012"]
  )

  # Remove devices from a group
  client.fleet.groups.remove_devices(
      group_id="grp_abc123",
      device_ids=["dev_ghi789"]
  )

  # List groups for a device
  groups = client.fleet.groups.list(device_id="dev_abc123")
  ```

  ```typescript TypeScript theme={null}
  // Add devices to a group
  await avala.fleet.groups.addDevices({
    groupId: "grp_abc123",
    deviceIds: ["dev_jkl012"],
  });

  // Remove devices from a group
  await avala.fleet.groups.removeDevices({
    groupId: "grp_abc123",
    deviceIds: ["dev_ghi789"],
  });

  // List groups for a device
  const groups = await avala.fleet.groups.list({ deviceId: "dev_abc123" });
  ```

  ```bash CLI theme={null}
  avala fleet groups add-devices grp_abc123 --devices dev_jkl012
  avala fleet groups remove-devices grp_abc123 --devices dev_ghi789
  avala fleet groups list --device dev_abc123
  ```
</CodeGroup>

<Note>
  A device can belong to multiple groups. Recording rules and alerts applied to any of a device's groups will apply to that device. If overlapping rules conflict, the most restrictive rule takes precedence.
</Note>

### Group-Level Metrics

Each group has its own metrics, aggregated from its member devices.

<CodeGroup>
  ```python Python theme={null}
  group_metrics = client.fleet.metrics.get(group_id="grp_abc123")

  print(f"Group: {group_metrics.group_name}")
  print(f"Active devices: {group_metrics.active_devices}/{group_metrics.total_devices}")
  print(f"Recordings this week: {group_metrics.recordings_this_week}")
  ```

  ```typescript TypeScript theme={null}
  const groupMetrics = await avala.fleet.metrics.get({ groupId: "grp_abc123" });

  console.log(`Group: ${groupMetrics.groupName}`);
  console.log(`Active devices: ${groupMetrics.activeDevices}/${groupMetrics.totalDevices}`);
  console.log(`Recordings this week: ${groupMetrics.recordingsThisWeek}`);
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="" icon="flag" href="/docs/visualization/fleet/events-and-markers">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Events & Markers</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Annotate recordings with timestamped events for errors, state changes, and anomalies.</p>
  </Card>

  <Card title="" icon="gears" href="/docs/visualization/fleet/recording-rules">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Recording Rules</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Automatically flag and tag recordings that match defined conditions.</p>
  </Card>

  <Card title="" icon="bell" href="/docs/visualization/fleet/alerts-and-notifications">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Alerts & Notifications</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Route alerts to Slack, email, and webhooks when fleet conditions change.</p>
  </Card>

  <Card title="" icon="clipboard-check" href="/docs/visualization/guides/recording-best-practices">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Recording Best Practices</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Tips for capturing data that uploads and visualizes reliably.</p>
  </Card>
</CardGroup>
