vibe coded website (plus captive portal)
Some checks failed
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Successful in 3m57s
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Successful in 3m48s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 3m18s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 3m14s
Some checks failed
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Successful in 3m57s
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Successful in 3m48s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 3m18s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 3m14s
needs missing ESP32 implementation Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
722
firmware/README-API.md
Normal file
722
firmware/README-API.md
Normal file
@@ -0,0 +1,722 @@
|
||||
# System Control - API Documentation
|
||||
|
||||
This document describes all REST API endpoints and WebSocket messages required for the ESP32 firmware implementation.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [REST API Endpoints](#rest-api-endpoints)
|
||||
- [WiFi](#wifi)
|
||||
- [Light Control](#light-control)
|
||||
- [Schema](#schema)
|
||||
- [Devices](#devices)
|
||||
- [Scenes](#scenes)
|
||||
- [WebSocket](#websocket)
|
||||
- [Connection](#connection)
|
||||
- [Client to Server Messages](#client-to-server-messages)
|
||||
- [Server to Client Messages](#server-to-client-messages)
|
||||
|
||||
---
|
||||
|
||||
## REST API Endpoints
|
||||
|
||||
### WiFi
|
||||
|
||||
#### Scan Networks
|
||||
|
||||
Scans for available WiFi networks.
|
||||
|
||||
- **URL:** `/api/wifi/scan`
|
||||
- **Method:** `GET`
|
||||
- **Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"ssid": "NetworkName",
|
||||
"rssi": -45
|
||||
},
|
||||
{
|
||||
"ssid": "AnotherNetwork",
|
||||
"rssi": -72
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|--------|------------------------------------|
|
||||
| ssid | string | Network name (SSID) |
|
||||
| rssi | number | Signal strength in dBm |
|
||||
|
||||
---
|
||||
|
||||
#### Save WiFi Configuration
|
||||
|
||||
Saves WiFi credentials and initiates connection.
|
||||
|
||||
- **URL:** `/api/wifi/config`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"ssid": "NetworkName",
|
||||
"password": "SecretPassword123"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|----------|--------|----------|-------------------|
|
||||
| ssid | string | Yes | Network name |
|
||||
| password | string | No | Network password |
|
||||
|
||||
- **Response:** `200 OK` on success, `400 Bad Request` on error
|
||||
|
||||
---
|
||||
|
||||
#### Get WiFi Status
|
||||
|
||||
Returns current WiFi connection status.
|
||||
|
||||
- **URL:** `/api/wifi/status`
|
||||
- **Method:** `GET`
|
||||
- **Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"connected": true,
|
||||
"ssid": "NetworkName",
|
||||
"ip": "192.168.1.100",
|
||||
"rssi": -45
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-----------|---------|------------------------------------------|
|
||||
| connected | boolean | Whether connected to a network |
|
||||
| ssid | string | Connected network name (if connected) |
|
||||
| ip | string | Assigned IP address (if connected) |
|
||||
| rssi | number | Signal strength in dBm (if connected) |
|
||||
|
||||
---
|
||||
|
||||
### Light Control
|
||||
|
||||
#### Set Light Power
|
||||
|
||||
Turns the main light on or off.
|
||||
|
||||
- **URL:** `/api/light/power`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"on": true
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|---------|----------|--------------------------|
|
||||
| on | boolean | Yes | `true` = on, `false` = off |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Set Light Mode
|
||||
|
||||
Sets the lighting mode.
|
||||
|
||||
- **URL:** `/api/light/mode`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "simulation"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|--------|----------|------------------------------------------------|
|
||||
| mode | string | Yes | One of: `day`, `night`, `simulation` |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Set Active Schema
|
||||
|
||||
Sets the active schema for simulation mode.
|
||||
|
||||
- **URL:** `/api/light/schema`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "schema_01.csv"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|--------|--------|----------|-------------------------------------------------------|
|
||||
| schema | string | Yes | Schema filename: `schema_01.csv`, `schema_02.csv`, etc. |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Get Light Status
|
||||
|
||||
Returns current light status (alternative to WebSocket).
|
||||
|
||||
- **URL:** `/api/light/status`
|
||||
- **Method:** `GET`
|
||||
- **Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"on": true,
|
||||
"mode": "simulation",
|
||||
"schema": "schema_01.csv",
|
||||
"color": {
|
||||
"r": 255,
|
||||
"g": 240,
|
||||
"b": 220
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|--------|---------|--------------------------------------|
|
||||
| on | boolean | Current power state |
|
||||
| mode | string | Current mode (day/night/simulation) |
|
||||
| schema | string | Active schema filename |
|
||||
| color | object | Current RGB color being displayed |
|
||||
|
||||
---
|
||||
|
||||
### Schema
|
||||
|
||||
#### Load Schema
|
||||
|
||||
Loads a schema file.
|
||||
|
||||
- **URL:** `/api/schema/{filename}`
|
||||
- **Method:** `GET`
|
||||
- **URL Parameters:**
|
||||
- `filename`: Schema file name (e.g., `schema_01.csv`)
|
||||
- **Response:** CSV text data
|
||||
|
||||
```
|
||||
255,240,220,0,100,250
|
||||
255,230,200,0,120,250
|
||||
...
|
||||
```
|
||||
|
||||
The CSV format has 48 rows (one per 30-minute interval) with 6 values per row:
|
||||
|
||||
| Column | Description | Range |
|
||||
|--------|--------------------------------|---------|
|
||||
| 1 | Red (R) | 0-255 |
|
||||
| 2 | Green (G) | 0-255 |
|
||||
| 3 | Blue (B) | 0-255 |
|
||||
| 4 | Value 1 (V1) - custom value | 0-255 |
|
||||
| 5 | Value 2 (V2) - custom value | 0-255 |
|
||||
| 6 | Value 3 (V3) - custom value | 0-255 |
|
||||
|
||||
---
|
||||
|
||||
#### Save Schema
|
||||
|
||||
Saves a schema file.
|
||||
|
||||
- **URL:** `/api/schema/{filename}`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `text/csv`
|
||||
- **URL Parameters:**
|
||||
- `filename`: Schema file name (e.g., `schema_01.csv`)
|
||||
- **Request Body:** CSV text data (same format as above)
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
### Devices
|
||||
|
||||
#### Scan for Devices
|
||||
|
||||
Scans for available Matter devices to pair.
|
||||
|
||||
- **URL:** `/api/devices/scan`
|
||||
- **Method:** `GET`
|
||||
- **Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "matter-001",
|
||||
"type": "light",
|
||||
"name": "Matter Lamp"
|
||||
},
|
||||
{
|
||||
"id": "matter-002",
|
||||
"type": "sensor",
|
||||
"name": "Temperature Sensor"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|--------|-----------------------------------------------|
|
||||
| id | string | Unique device identifier |
|
||||
| type | string | Device type: `light`, `sensor`, `unknown` |
|
||||
| name | string | Device name (can be empty) |
|
||||
|
||||
---
|
||||
|
||||
#### Pair Device
|
||||
|
||||
Pairs a discovered device.
|
||||
|
||||
- **URL:** `/api/devices/pair`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "matter-001",
|
||||
"name": "Living Room Lamp"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|--------|----------|------------------------------|
|
||||
| id | string | Yes | Device ID from scan |
|
||||
| name | string | Yes | User-defined device name |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Get Paired Devices
|
||||
|
||||
Returns list of all paired devices.
|
||||
|
||||
- **URL:** `/api/devices/paired`
|
||||
- **Method:** `GET`
|
||||
- **Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "matter-001",
|
||||
"type": "light",
|
||||
"name": "Living Room Lamp"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|--------|-------------------------------------------|
|
||||
| id | string | Unique device identifier |
|
||||
| type | string | Device type: `light`, `sensor`, `unknown` |
|
||||
| name | string | User-defined device name |
|
||||
|
||||
---
|
||||
|
||||
#### Update Device Name
|
||||
|
||||
Updates the name of a paired device.
|
||||
|
||||
- **URL:** `/api/devices/update`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "matter-001",
|
||||
"name": "New Device Name"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|--------|----------|------------------------|
|
||||
| id | string | Yes | Device ID |
|
||||
| name | string | Yes | New device name |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Unpair Device
|
||||
|
||||
Removes a paired device.
|
||||
|
||||
- **URL:** `/api/devices/unpair`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "matter-001"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|--------|----------|---------------|
|
||||
| id | string | Yes | Device ID |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Toggle Device
|
||||
|
||||
Toggles a device (e.g., light on/off).
|
||||
|
||||
- **URL:** `/api/devices/toggle`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "matter-001"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|--------|----------|---------------|
|
||||
| id | string | Yes | Device ID |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
### Scenes
|
||||
|
||||
#### Get All Scenes
|
||||
|
||||
Returns all configured scenes.
|
||||
|
||||
- **URL:** `/api/scenes`
|
||||
- **Method:** `GET`
|
||||
- **Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "scene-1",
|
||||
"name": "Evening Mood",
|
||||
"icon": "🌅",
|
||||
"actions": {
|
||||
"light": "on",
|
||||
"mode": "simulation",
|
||||
"schema": "schema_02.csv",
|
||||
"devices": [
|
||||
{
|
||||
"id": "matter-001",
|
||||
"state": "on"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "scene-2",
|
||||
"name": "Night Mode",
|
||||
"icon": "🌙",
|
||||
"actions": {
|
||||
"light": "on",
|
||||
"mode": "night"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Scene Object:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|---------|--------|------------------------------------|
|
||||
| id | string | Unique scene identifier |
|
||||
| name | string | Scene name |
|
||||
| icon | string | Emoji icon for the scene |
|
||||
| actions | object | Actions to execute (see below) |
|
||||
|
||||
**Actions Object:**
|
||||
|
||||
| Field | Type | Optional | Description |
|
||||
|---------|--------|----------|------------------------------------------|
|
||||
| light | string | Yes | `"on"` or `"off"` |
|
||||
| mode | string | Yes | `"day"`, `"night"`, or `"simulation"` |
|
||||
| schema | string | Yes | Schema filename (e.g., `schema_01.csv`) |
|
||||
| devices | array | Yes | Array of device actions (see below) |
|
||||
|
||||
**Device Action Object:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|--------|----------------------------------|
|
||||
| id | string | Device ID |
|
||||
| state | string | `"on"` or `"off"` |
|
||||
|
||||
---
|
||||
|
||||
#### Create/Update Scene
|
||||
|
||||
Creates a new scene or updates an existing one.
|
||||
|
||||
- **URL:** `/api/scenes`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "scene-1",
|
||||
"name": "Evening Mood",
|
||||
"icon": "🌅",
|
||||
"actions": {
|
||||
"light": "on",
|
||||
"mode": "simulation",
|
||||
"schema": "schema_02.csv",
|
||||
"devices": [
|
||||
{
|
||||
"id": "matter-001",
|
||||
"state": "on"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Delete Scene
|
||||
|
||||
Deletes a scene.
|
||||
|
||||
- **URL:** `/api/scenes`
|
||||
- **Method:** `DELETE`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "scene-1"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|--------|----------|---------------|
|
||||
| id | string | Yes | Scene ID |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
#### Activate Scene
|
||||
|
||||
Executes all actions of a scene.
|
||||
|
||||
- **URL:** `/api/scenes/activate`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "scene-1"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|--------|----------|---------------|
|
||||
| id | string | Yes | Scene ID |
|
||||
|
||||
- **Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
## WebSocket
|
||||
|
||||
### Connection
|
||||
|
||||
- **URL:** `ws://{host}/ws` or `wss://{host}/ws`
|
||||
- **Protocol:** JSON messages
|
||||
|
||||
The WebSocket connection is used for real-time status updates. The client should reconnect automatically if the connection is lost (recommended: 3 second delay).
|
||||
|
||||
---
|
||||
|
||||
### Client to Server Messages
|
||||
|
||||
#### Request Status
|
||||
|
||||
Requests the current system status.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "getStatus"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Server to Client Messages
|
||||
|
||||
#### Status Update
|
||||
|
||||
Sent in response to `getStatus` or when status changes.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "status",
|
||||
"on": true,
|
||||
"mode": "simulation",
|
||||
"schema": "schema_01.csv",
|
||||
"color": {
|
||||
"r": 255,
|
||||
"g": 240,
|
||||
"b": 220
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|--------|---------|--------------------------------------|
|
||||
| type | string | Always `"status"` |
|
||||
| on | boolean | Light power state |
|
||||
| mode | string | Current mode (day/night/simulation) |
|
||||
| schema | string | Active schema filename |
|
||||
| color | object | Current RGB color (optional) |
|
||||
|
||||
---
|
||||
|
||||
#### Color Update
|
||||
|
||||
Sent when the current color changes (during simulation).
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "color",
|
||||
"r": 255,
|
||||
"g": 200,
|
||||
"b": 150
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|--------|--------------------------|
|
||||
| type | string | Always `"color"` |
|
||||
| r | number | Red value (0-255) |
|
||||
| g | number | Green value (0-255) |
|
||||
| b | number | Blue value (0-255) |
|
||||
|
||||
---
|
||||
|
||||
#### WiFi Status Update
|
||||
|
||||
Sent when WiFi connection status changes.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "wifi",
|
||||
"connected": true,
|
||||
"ip": "192.168.1.100",
|
||||
"rssi": -45
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-----------|---------|---------------------------------------|
|
||||
| type | string | Always `"wifi"` |
|
||||
| connected | boolean | Whether connected to a network |
|
||||
| ip | string | Assigned IP address (if connected) |
|
||||
| rssi | number | Signal strength in dBm (if connected) |
|
||||
|
||||
---
|
||||
|
||||
## Static Files
|
||||
|
||||
The web interface files should be served from the `/storage/www/` directory:
|
||||
|
||||
| Path | Description |
|
||||
|----------------------|------------------------------------------------|
|
||||
| `/` | Serves `index.html` (or `captive.html` in AP mode) |
|
||||
| `/index.html` | Main HTML file (full interface) |
|
||||
| `/captive.html` | Captive portal (WiFi setup only) |
|
||||
| `/css/shared.css` | Shared styles for all pages |
|
||||
| `/css/index.css` | Styles for main interface |
|
||||
| `/css/captive.css` | Styles for captive portal |
|
||||
| `/js/wifi-shared.js` | Shared WiFi configuration logic |
|
||||
| `/js/*.js` | JavaScript modules |
|
||||
|
||||
---
|
||||
|
||||
## Captive Portal
|
||||
|
||||
When the ESP32 is in Access Point (AP) mode (no WiFi configured or connection failed), it should serve the captive portal:
|
||||
|
||||
### Behavior
|
||||
|
||||
1. **AP Mode Activation:**
|
||||
- ESP32 creates an access point (e.g., "marklin-setup")
|
||||
- DNS server redirects all requests to the ESP32's IP (captive portal detection)
|
||||
|
||||
2. **Captive Portal Detection:**
|
||||
- Respond to common captive portal detection URLs:
|
||||
- `/generate_204` (Android)
|
||||
- `/hotspot-detect.html` (Apple)
|
||||
- `/connecttest.txt` (Windows)
|
||||
- Return redirect or serve `captive.html`
|
||||
|
||||
3. **Serving Files in AP Mode:**
|
||||
- `/` → `captive.html`
|
||||
- `/captive.html` → Captive portal page
|
||||
- `/js/wifi-shared.js` → WiFi functions
|
||||
- API endpoints remain the same (`/api/wifi/*`)
|
||||
|
||||
4. **After Successful Configuration:**
|
||||
- ESP32 attempts to connect to the configured network
|
||||
- If successful, switch to Station mode and serve `index.html`
|
||||
- If failed, remain in AP mode
|
||||
|
||||
### Recommended AP Settings
|
||||
|
||||
| Setting | Value |
|
||||
|---------------|--------------------------|
|
||||
| SSID | `marklin-setup` |
|
||||
| Password | None (open) or `marklin` |
|
||||
| IP Address | `192.168.4.1` |
|
||||
| Gateway | `192.168.4.1` |
|
||||
| Subnet | `255.255.255.0` |
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
All endpoints should return appropriate HTTP status codes:
|
||||
|
||||
| Code | Description |
|
||||
|------|---------------------------------------|
|
||||
| 200 | Success |
|
||||
| 400 | Bad Request (invalid input) |
|
||||
| 404 | Not Found (resource doesn't exist) |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
Error responses should include a JSON body with an error message:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Description of what went wrong"
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user