# REST API

`go run . -port /dev/ttyUSB0 serve` starts two HTTP servers on the same UART link:

| Base URL | Flag | Used by |
|----------|------|---------|
| `http://localhost:8080` | `-addr` (default `:8080`) | Web dashboard + automation on the UI routes |
| `http://localhost:8081` | `-api-addr` (default `:8081`, `""` disables) | External programs; subset of routes + service info |

WebSocket streaming (accel/tap push): [`API_WEBSOCKET.md`](API_WEBSOCKET.md).

All JSON responses use `Content-Type: application/json`. On UART errors many routes return **503** with `"error"` in the body.

---

## External API (`:8081`)

### Service info

```http
GET /
GET /api/v1/
```

```json
{
  "name": "powerpod-external-api",
  "version": "1",
  "serial_port": "/dev/ttyUSB0",
  "websocket": "/ws",
  "default_interval_ms": 16,
  "min_interval_ms": 1,
  "max_interval_ms": 10000,
  "tap_display_min_ms": 2000,
  "description": "..."
}
```

### Battery

```http
GET /api/battery?all_clients=true
GET /api/battery?client_id=16
POST /api/battery
Content-Type: application/json
```

POST body:

```json
{"all_clients": true}
{"client_id": 0}
{"client_id": 16}
```

Response:

```json
{
  "success": true,
  "samples": [
    {
      "client_id": 16,
      "lipo1": {"valid": true, "voltage_mv": 3850, "percent": 71},
      "lipo2": {"valid": false},
      "age_ms": 1200
    }
  ]
}
```

Slaves push battery to the master every **30 s**; these routes read the master cache.

WebSocket equivalent: `get_battery` on `ws://localhost:8081/ws` (reply type `battery_status`).

### LED ring

```http
POST /api/led-ring
Content-Type: application/json
```

Body:

```json
{"mode":"color","client_id":16,"r":255,"g":0,"b":0,"intensity":128}
{"mode":"digit","client_id":0,"digit":3,"r":0,"g":255,"b":0}
{"mode":"find-me","all_clients":true,"slaves_only":true}
```

| `mode` | Notes |
|--------|--------|
| `clear` | Turn off |
| `color` | Full ring RGB + `intensity` |
| `progress` | `progress` 0–100 |
| `digit` | `digit` 0–10 |
| `blink` | `blink_ms`, `blink_count` |
| `find-me` | Locate pod |

Use `client_id` (`0` = master) or `all_clients` (+ optional `slaves_only`) for broadcast.

Response: `success`, `slaves_updated`, optional `error`.

WebSocket: `set_led_ring` with the same fields plus `"type":"set_led_ring"` → `led_ring_status`.

---

## Dashboard API (`:8080`)

Used by the web UI; safe for scripts that drive the same features.

### Live stream (host `CACHE_STATUS` poll ~16 ms)

```http
GET /api/live-stream
PUT /api/live-stream
Content-Type: application/json
{"enable": true}
```

```json
{"enabled": true, "success": true}
```

Enables fast UART polling for dashboard accel/tap display. Per-slave accel still requires accel-stream (below).

### Accel stream (firmware ESP-NOW, per slave)

```http
GET /api/clients/16/accel-stream
PUT /api/clients/16/accel-stream
Content-Type: application/json
{"enable": true}
```

```json
{"enabled": true, "client_id": 16, "success": true}
```

All slaves:

```http
POST /api/accel-stream
Content-Type: application/json
{"write": true, "enable": true, "all_clients": true}
```

Polling on the host runs only while at least one slave has streaming enabled (here or via external WebSocket / dashboard).

### Tap notify (firmware; does not start host tap polling)

```http
GET /api/clients/16/tap-notify
PUT /api/clients/16/tap-notify
Content-Type: application/json
{"single": true, "double_tap": false, "triple": false}
```

```json
{
  "client_id": 16,
  "success": true,
  "slaves_updated": 1,
  "single": true,
  "double_tap": false,
  "triple": false
}
```

All slaves:

```http
POST /api/tap-notify
Content-Type: application/json
{"single": true, "double_tap": false, "triple": false, "all_clients": true}
```

Host tap display / external `set_tap_stream` is separate.

### Tap snapshot (one-shot, via `CACHE_STATUS`)

```http
GET /api/tap-snapshot?client_id=16
```

Reads the combined cache (`CACHE_STATUS`); optional `client_id` filters pending tap events. Pending taps are consumed on read.

```json
{
  "events": [
    {"client_id": 16, "kind": "single", "age_ms": 4}
  ]
}
```

### Deadzone

```http
GET /api/deadzone?client_id=0
POST /api/deadzone
Content-Type: application/json
{"write": true, "deadzone": 128, "client_id": 0}
```

With `all_clients` + `slaves_only`: push to ESP-NOW slaves only (master BMA456 unchanged).

```json
{"deadzone": 128, "client_id": 0, "success": true, "slaves_updated": 2}
```

### Unicast test

```http
POST /api/unicast-test
Content-Type: application/json
{"client_id": 16, "seq": 42}
```

### Find me

```http
POST /api/find-me
Content-Type: application/json
{"client_id": 16}
```

`client_id` `0` = master LED ring.

### Restart

```http
POST /api/restart
Content-Type: application/json
{"client_id": 16}
```

### OTA (master UART upload)

```http
POST /api/ota
Content-Type: multipart/form-data
```

Form field **`firmware`**: binary image, max **2 MiB**.

```json
{"success": true, "bytes_written": 123456, "target_slot": 1}
```

Firmware distributes to slaves over ESP-NOW after `OTA_END`. Progress also appears on dashboard WebSocket as `ota_progress` messages.

CLI equivalent: `go run . -port /dev/ttyUSB0 ota build/powerpod.bin`

### LED ring and battery

Same as external API:

- `POST /api/led-ring`
- `GET` / `POST` `/api/battery`

---

## Dashboard vs external

| Feature | Dashboard `:8080` | External `:8081` |
|---------|-------------------|------------------|
| Client list | Via dashboard WebSocket state / CLI `clients` | WebSocket `list_clients` |
| Accel/tap **push stream** | WebSocket state when live-stream on | WebSocket `set_stream` / `set_tap_stream` |
| Accel stream enable | REST `PUT .../accel-stream` | WebSocket `set_accel_stream` |
| Tap notify | REST `PUT .../tap-notify` | WebSocket `set_tap_notify` |
| LED / battery | REST | REST + WebSocket on `:8081` |

---

## UI mapping

| UI action | REST / CLI |
|-----------|------------|
| Nur Master deadzone | `POST /api/deadzone` `client_id: 0` or CLI `deadzone -set -client 0` |
| Einzelner Slave | `client_id: <id>` |
| Alle Slaves deadzone | `all_clients` + `slaves_only` on POST |
| Unicast test | `POST /api/unicast-test` |
| Tap notify S/D/T | `PUT /api/clients/{id}/tap-notify` |
| Tap receive (UI) | Live stream + tap notify; see WebSocket doc for external API |
