Files
overland-controller/docs/API.md
T

982 lines
19 KiB
Markdown

# HTTP API Reference
The Cargo ESP32 exposes a local JSON HTTP API for the embedded WebUI, the planned Waveshare ESP32-S3 dashboard, diagnostics, simulators, and future local integrations.
Default AP address:
```text
http://192.168.4.1
```
Current API base path:
```text
/api/v1
```
All documented endpoints are local-first and must continue to work without internet access. Consumers should ignore unknown response fields for forward compatibility.
Current firmware version:
```text
0.5.0
```
## Current Endpoints
```text
GET /
GET /api/v1/health
GET /api/v1/capabilities
GET /api/v1/status
GET /api/v1/config
GET /api/v1/config/export
POST /api/v1/relay/set
POST /api/v1/config/import
POST /api/v1/config/device
POST /api/v1/config/relay
POST /api/v1/config/temp
POST /api/v1/config/bms
POST /api/v1/config/save
POST /api/v1/config/factory-reset
GET /api/v1/config/wifi
POST /api/v1/config/wifi
POST /api/v1/wifi/connect
POST /api/v1/wifi/clear
POST /api/v1/temps/scan
POST /api/v1/temps/assign
POST /api/v1/temps/clear
POST /api/v1/bms/setup/enter
POST /api/v1/bms/setup/exit
POST /api/v1/bms/scan
POST /api/v1/bms/select
```
Pre-versioned root routes remain registered as compatibility aliases for existing local clients. New clients should use `/api/v1`.
Legacy GET relay routes remain available only as root compatibility aliases:
```text
GET /relay/relay_1/on
GET /relay/relay_1/off
GET /relay/relay_2/on
GET /relay/relay_2/off
```
## Dashboard Contract
The Waveshare ESP32-S3 dashboard MVP should use only:
```text
GET /api/v1/status
POST /api/v1/relay/set
```
The dashboard is a client only. It may cache last-known values for display, but the Cargo ESP32 remains the source of truth for relay state, BMS state, alarms, and configuration.
## Health
### GET /api/v1/health
Returns a lightweight liveness response for dashboards and service tools.
```json
{
"type": "health_response",
"ok": true,
"api_version": "v1",
"firmware_name": "overland-controller",
"firmware_version": "0.5.0",
"uptime_seconds": 123,
"network": {
"ap_enabled": true,
"ap_ip": "192.168.4.1",
"sta_enabled": true,
"sta_connected": false,
"sta_ssid": "",
"sta_ip": ""
},
"bms": {
"configured": true,
"connected": false
}
}
```
## Capabilities
### GET /api/v1/capabilities
Returns API and feature discovery data for clients.
```json
{
"type": "capabilities_response",
"ok": true,
"api_version": "v1",
"firmware_name": "overland-controller",
"firmware_version": "0.5.0",
"endpoints": [
"GET /api/v1/status",
"POST /api/v1/relay/set"
],
"limits": {
"relay_count": 2,
"temperature_sensor_count": 8,
"runtime_temperature_status_count": 4,
"wifi_network_count": 3
},
"features": {
"relay_control": true,
"temperature_scan": true,
"wifi_config": true,
"config_backup_restore": true,
"bms_setup": true,
"uart_json": true,
"root_compatibility_aliases": true
}
}
```
## Status
### GET /api/v1/status
Returns complete controller status.
Example requests:
GET /api/v1/status
GET /api/v1/status?fields=battery,relays,temps
Top-level full response:
```json
{
"type": "status_response",
"timestamp": 123456,
"battery": {},
"temps": [],
"relays": [],
"vehicle": {},
"network": {},
"alarms": {},
"system": {},
"config": {}
}
```
#### Optional `fields` query parameter
`GET /api/v1/status` accepts an optional comma-separated `fields` query parameter.
Valid fields:
battery, temps, relays, vehicle, network, alarms, system, config
If `fields` is omitted, the endpoint returns the complete status payload.
If an unknown field is requested, the Cargo ESP32 returns HTTP `400`:
{
"ok": false,
"error": "invalid_field",
"details": ["Unknown field 'xyz'"]
}
### battery
```json
{
"source": "jbd_bms",
"connected": true,
"soc": 70,
"voltage": 13.34,
"current": 0.0,
"remaining_ah": 104.8,
"capacity_ah": 150.0,
"runtime_hours": 0,
"temperature_f": 76.5,
"cycle_count": 3,
"cell_count": 4,
"ntc_count": 2,
"cell_voltages": [3.334, 3.333, 3.334, 3.333],
"cell_min_voltage": 3.333,
"cell_max_voltage": 3.334,
"cell_delta_mv": 1,
"cells_valid": true
}
```
`source` is `jbd_bms` when a configured BMS is expected, even if disconnected. It is `unconfigured` when BMS is not configured.
`runtime_hours` is discharge runtime estimated from remaining amp-hours and negative current. Charging time-to-full is derived by clients from `capacity_ah`, `remaining_ah`, and positive current.
### temps
Runtime temperature sensors are returned as an array.
```json
{
"id": "temp_1",
"name": "Cabin",
"enabled": true,
"weather": false,
"online": true,
"temperature_f": 72.4
}
```
Fields:
| Field | Description |
|---|---|
| `id` | Generic sensor ID |
| `name` | User-configured display name |
| `enabled` | Configured sensor enabled state |
| `weather` | Marks the outside/weather display sensor |
| `online` | Current sensor detection state |
| `temperature_f` | Current Fahrenheit value, or `null` when offline |
Config supports `temp_1` through `temp_8`. Current `/api/v1/status` runtime output is limited to the configured count capped at four sensors.
### relays
```json
{
"id": "relay_1",
"name": "Aux Power",
"pin": 16,
"enabled": true,
"state": false
}
```
Valid relay IDs:
```text
relay_1
relay_2
```
Relay names are user configuration. Firmware and clients should use generic IDs for commands.
### vehicle
```json
{
"ignition_on": false
}
```
### network
```json
{
"wifi_enabled": true,
"uart_connected": true,
"ap_enabled": true,
"ap_ip": "192.168.4.1",
"sta_enabled": true,
"sta_connected": false,
"sta_ssid": "",
"sta_ip": "",
"saved_network_count": 2,
"saved_networks": [
{
"index": 1,
"ssid": "Starlink",
"priority": 1,
"active": false
}
]
}
```
The AP remains enabled even when STA WiFi is configured or connected.
### alarms
```json
{
"low_soc": false,
"critical_soc": false,
"low_voltage": false,
"high_battery_temp": false,
"cell_imbalance": false,
"bms_disconnected": false
}
```
### system
```json
{
"firmware_name": "overland-controller",
"firmware_version": "0.5.0",
"build_date": "Jun 7 2026",
"build_time": "12:00:00",
"uptime_seconds": 123
}
```
### config
`/api/v1/status.config` embeds the same core configuration model used by `GET /api/v1/config`.
## Configuration
### GET /api/v1/config
Returns saved controller configuration.
```json
{
"device_name": "Overland Controller",
"relays": [
{
"id": "relay_1",
"name": "Aux Power",
"pin": 16,
"enabled": true
}
],
"bms": {
"enabled": true,
"name": "House Battery",
"address": "AA:BB:CC:DD:EE:FF",
"address_type": "public"
},
"temperature_sensor_count": 4,
"temperature_sensors": [
{
"id": "temp_1",
"name": "Cabin",
"address": "28:AA:BB:CC:DD:EE:FF:00",
"enabled": true,
"weather": false
}
]
}
```
### POST /api/v1/config/device
Updates the controller display name.
```json
{
"device_name": "Overland Controller"
}
```
Returns the updated config.
### POST /api/v1/config/relay
Updates relay configuration.
```json
{
"id": "relay_1",
"name": "Aux Power",
"enabled": true
}
```
Fields:
| Field | Required | Description |
|---|---:|---|
| `id` | Yes | `relay_1` or `relay_2` |
| `name` | No | User display name |
| `enabled` | No | Configured enabled state |
Returns the updated config.
### POST /api/v1/config/temp
Updates temperature sensor configuration.
```json
{
"id": "temp_1",
"name": "Cabin",
"address": "28:AA:BB:CC:DD:EE:FF:00",
"enabled": true,
"weather": false
}
```
Fields:
| Field | Required | Description |
|---|---:|---|
| `id` | Yes | `temp_1` through `temp_8` |
| `name` | No | User display name |
| `address` | No | DS18B20 address |
| `enabled` | No | Configured enabled state |
| `weather` | No | Outside/weather display flag |
Returns the updated config.
### POST /api/v1/config/bms
Updates BMS configuration.
```json
{
"enabled": true,
"name": "House Battery",
"address": "AA:BB:CC:DD:EE:FF",
"address_type": "public"
}
```
Address types:
```text
public
random
```
Returns the updated config.
### POST /api/v1/config/save
Persists the current active configuration.
Returns the current config.
### POST /api/v1/config/factory-reset
Clears saved configuration and restores firmware defaults.
Returns the reset config.
### GET /api/v1/config/export
Returns a backup envelope containing controller config and saved WiFi networks.
Important: export includes saved WiFi passwords so the backup can be restored. Treat the response as sensitive.
```json
{
"type": "config_export_response",
"ok": true,
"api_version": "v1",
"firmware_name": "overland-controller",
"firmware_version": "0.5.0",
"config": {
"device_name": "Overland Controller",
"temperature_sensor_count": 4,
"relays": [],
"bms": {},
"temperature_sensors": []
},
"wifi": {
"networks": [
{
"index": 1,
"ssid": "Starlink",
"priority": 1,
"password_set": true,
"password": "password_here"
}
]
}
}
```
### POST /api/v1/config/import
Restores a config backup. The preferred request shape is the same envelope returned by `GET /api/v1/config/export`.
```json
{
"config": {
"device_name": "Overland Controller",
"temperature_sensor_count": 4,
"relays": [],
"bms": {},
"temperature_sensors": []
},
"wifi": {
"networks": []
}
}
```
The endpoint also accepts the bare config object without an outer `config` field. When `wifi.networks` is included, saved STA WiFi networks are replaced and persisted. It does not initiate STA reconnect; use `POST /api/v1/wifi/connect` after import if needed.
Returns the updated config.
## Relay Control
### POST /api/v1/relay/set
Preferred relay command endpoint.
```json
{
"id": "relay_1",
"state": true
}
```
Response:
```json
{
"type": "relay_response",
"ok": true,
"id": "relay_1",
"state": true
}
```
### Legacy GET relay routes
These routes remain for compatibility but should not be used by new clients:
```text
GET /relay/relay_1/on
GET /relay/relay_1/off
GET /relay/relay_2/on
GET /relay/relay_2/off
```
Response:
```json
{
"ok": true,
"id": "relay_1",
"state": true
}
```
## WiFi
### GET /api/v1/config/wifi
Returns AP/STA WiFi configuration status. Passwords are not returned.
```json
{
"type": "wifi_config_response",
"ok": true,
"wifi": {
"ap_enabled": true,
"sta_enabled": true,
"network_count": 2,
"active_ssid": "Starlink",
"sta_connected": true,
"ap_ip": "192.168.4.1",
"sta_ip": "192.168.1.50",
"networks": [
{
"index": 1,
"ssid": "Starlink",
"priority": 1,
"password_set": true
}
]
}
}
```
### POST /api/v1/config/wifi
Preferred multi-network request:
```json
{
"networks": [
{
"ssid": "Starlink",
"password": "password_here",
"priority": 1
},
{
"ssid": "Home WiFi",
"password": "password_here",
"priority": 2
}
]
}
```
Legacy single-network shape is also accepted:
```json
{
"ssid": "Starlink",
"password": "password_here"
}
```
Returns WiFi config status.
Runtime behavior:
- AP remains enabled.
- Lower priority numbers are tried first.
- If STA disconnects, saved networks are retried by priority.
### POST /api/v1/wifi/connect
Attempts STA connection using saved networks by priority.
Returns WiFi config status.
### POST /api/v1/wifi/clear
Clears saved STA WiFi networks.
Returns WiFi config status.
## Temperature Probe Setup
### POST /api/v1/temps/scan
Scans the DS18B20 bus and returns unassigned probe addresses.
```json
{
"type": "temp_scan_response",
"ok": true,
"devices": [
{
"index": 1,
"address": "28:AA:BB:CC:DD:EE:FF:00"
}
]
}
```
Already-assigned probe addresses are hidden from scan results until cleared.
### POST /api/v1/temps/assign
Assigns a scanned probe to a logical temperature slot.
By ID:
```json
{
"id": "temp_1",
"index": 1
}
```
By slot number:
```json
{
"slot": 1,
"index": 1
}
```
Returns the updated config.
### POST /api/v1/temps/clear
Clears one or all temperature assignments.
By ID:
```json
{
"id": "temp_1"
}
```
By slot number:
```json
{
"slot": 1
}
```
Empty POST body clears all temperature assignments.
Clearing removes the stored DS18B20 address, disables the slot, clears the weather flag, resets cached temperature state, and persists config.
Returns the updated config.
## BMS Setup
These endpoints are for BMS discovery and selection. Do not change JBD/Xiaoxiang BLE behavior without explicit approval.
### POST /api/v1/bms/setup/enter
Enters BMS setup mode.
Response:
```json
{
"type": "bms_setup_response",
"ok": true,
"mode": "setup"
}
```
### POST /api/v1/bms/setup/exit
Exits BMS setup mode.
Response:
```json
{
"type": "bms_setup_response",
"ok": true,
"mode": "normal"
}
```
### POST /api/v1/bms/scan
Scans for BLE devices.
Response:
```json
{
"type": "ble_scan_response",
"devices": [
{
"index": 1,
"name": "House Battery",
"address": "aa:bb:cc:dd:ee:ff",
"rssi": -65
}
]
}
```
Some BMS devices advertise intermittently. Repeated scans may be needed.
### POST /api/v1/bms/select
Selects a BMS from the most recent scan result.
```json
{
"index": 1
}
```
Response:
```json
{
"type": "bms_setup_response",
"ok": true,
"name": "House Battery",
"address": "aa:bb:cc:dd:ee:ff"
}
```
Selection enables BMS config, saves the selected address, exits setup mode, and reconnects on the next update cycle.
## Embedded WebUI
### GET /
Serves the embedded WebUI.
The WebUI is a lightweight local setup and phone dashboard surface. It should remain small and self-contained:
- Inline HTML/CSS/JS
- No external libraries
- No large assets
- No heavy JavaScript framework
The WebUI is not a replacement for the planned Waveshare ESP32-S3 physical dashboard.
## Errors
Error responses use this shape:
```json
{
"ok": false,
"error": "invalid_json"
}
```
Known error codes:
```text
invalid_json
invalid_config
unknown_relay
unknown_temp_sensor
invalid_temp_selection
invalid_bms_selection
invalid_relay_route
missing_relay_action
invalid_relay_action
```
Some serial/UART errors use `message` instead of `error`; HTTP clients should rely on `error` for HTTP failures.
## Compatibility
Current stable API prefix is `/api/v1`, for example `GET /api/v1/status`.
Pre-versioned root routes remain registered as local compatibility aliases. Do not add new root-only HTTP API routes.
New fields may be added to existing JSON objects. Consumers should ignore unknown fields.
## Not Current API
These routes are not part of the current registered HTTP API:
```text
POST /api/v1/bms/reconnect
POST /api/v1/system/restart
```
If added later, update this document and add contract tests.
### AP configuration
The Cargo ESP32 access point uses WPA2 authentication.
On first boot, if no AP password exists, the controller generates and saves a unique password. Until the OLED setup display is added, that generated password is printed to Serial during boot.
#### GET /api/v1/config/ap
Returns AP metadata. The password is never returned.
Example response:
{
"ok": true,
"ssid": "OverlandController",
"password_set": true,
"password_min_length": 8,
"password_max_length": 63,
"auth": "wpa2"
}
#### POST /api/v1/config/ap
Updates the AP SSID/password and restarts the access point.
Example request:
{
"ssid": "Overland-Controller",
"password": "new-secure-password"
}
Rules:
- SSID must be 1-32 characters.
- Password must be 8-63 characters.
- Password is stored in controller preferences.
- Password is not returned by status or config APIs.
- Future dashboard pairing will use Cargo-led credential migration so the dashboard follows AP credential changes automatically.
#### POST /api/v1/config/ap/reset
Resets the Cargo ESP32 access point to default setup credentials.
Behavior:
- SSID is reset to `OverlandController`.
- A new random WPA2 password is generated.
- The new credentials are saved in Preferences.
- The AP is restarted.
- The password is not returned by the API response.
The regenerated password must be read from the OLED setup display or Serial Monitor.
Example response:
{
"ok": true,
"ssid": "OverlandController",
"password_set": true,
"auth": "wpa2",
"ap_ip": "192.168.4.1",
"message": "AP reset to default SSID with regenerated password."
}
## Temperature Telemetry
DS18B20 temperature support is already implemented in the Cargo ESP/WebUI/API path.
Architecture expectation:
- Cargo ESP32 owns DS18B20 sensor reads.
- Cargo ESP32 exposes temperature telemetry through the API.
- WebUI displays temperature telemetry from Cargo ESP state.
- Dashboard ESP32-S3 consumes temperature telemetry from the Cargo ESP API.
The dashboard should not directly read cargo/fridge DS18B20 sensors for v1.
### Temperature Sensor Grouping
Temperature sensors now support a `group` field in addition to the existing `weather` flag.
Supported group values:
- `cabin`
- `fridge`
- `outside`
- `battery`
- `other`
The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display.
### Temperature Sensor Priority
Temperature sensors support a numeric `priority` field. Lower numbers are displayed first within a grouped dashboard tile.
Example:
- Fridge Zone 1: `group=fridge`, `priority=1`
- Fridge Zone 2: `group=fridge`, `priority=2`
The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`.
### Temperature Slot and Group Display
The Cargo ESP currently supports 4 temperature sensor slots.
Temperature sensor `group` values are free-form text in the WebUI. Entering a new group name effectively creates that group. The dashboard overview shows the first two available non-weather groups by priority. Multiple sensors in the same group are combined into one tile, such as `34°/45°`.
Sensors marked `weather: true` are excluded from the temperature card and shown in the top status strip as `OUT ##°`.
### Temperature High Alerts
Each temperature sensor supports optional high-temperature alerting:
- `high_alert_enabled`
- `high_alert_f`
- `high_alert`
When enabled and the live sensor temperature exceeds the configured threshold, the Cargo ESP includes `high_alert: true` for that sensor in `/api/v1/status`. The ESP32-S3 dashboard colors the affected grouped temperature tile red if any sensor in that displayed group is in alert.
### Hidden Dashboard Diagnostics
The dashboard has a hidden diagnostics overlay. Tap the top status card five times within roughly three seconds to open it. Double-tap the diagnostics overlay to return to the main dashboard.
The diagnostics overlay shows dashboard version, uptime, heap, WiFi RSSI/IP, Cargo API status, Cargo firmware version, Cargo uptime, Cargo heap, Cargo AP client count, BMS status, relay count, and basic control-loop state.