From afd95908f1c7223e86bd29cdc90cc87f0bdfc8a2 Mon Sep 17 00:00:00 2001 From: Kristjan Komlosi Date: Sun, 8 Feb 2026 00:24:24 +0100 Subject: [PATCH] docs: clean up docs structure --- docs/API.md | 1048 +++++++++++++++++ .../DEVICE_DISCOVERY_SPEC.md | 0 readme.md | 4 +- state_test.db | Bin 8192 -> 0 bytes test/README.md | 24 - 5 files changed, 1050 insertions(+), 26 deletions(-) create mode 100644 docs/API.md rename DEVICE_DISCOVERY_SPEC.md => docs/DEVICE_DISCOVERY_SPEC.md (100%) delete mode 100644 state_test.db delete mode 100644 test/README.md diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..3b09579 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,1048 @@ +# Lambda IoT Core API Documentation + +## Overview + +The Lambda IoT Core API provides a RESTful interface for managing IoT devices, sensors, actors, and sensor readings. The API uses JWT authentication for protected endpoints and integrates with MQTT for real-time device communication. + +**Base URL**: `http://localhost:8080` (default) + +## Authentication + +Most endpoints require JWT authentication. Include the token in the `Authorization` header: + +``` +Authorization: Bearer +``` + +Tokens are obtained through the `/login` endpoint and are valid for 24 hours. + +--- + +## Public Endpoints + +### Health Check + +Check the service health status. + +**Endpoint**: `GET /health` + +**Authentication**: None required + +**Response**: +```json +{ + "status": "ok" +} +``` + +--- + +### Hello + +Simple greeting endpoint for testing connectivity. + +**Endpoint**: `GET /hello` + +**Authentication**: None required + +**Response**: +```json +{ + "message": "hello from lambdaiot" +} +``` + +--- + +### Register + +Create a new user account. + +**Endpoint**: `POST /register` + +**Authentication**: None required + +**Request Body**: +```json +{ + "username": "string (required, min: 3, max: 50)", + "password": "string (required, min: 6)", + "email": "string (optional)" +} +``` + +**Response** (201 Created): +```json +{ + "id": "uuid", + "username": "string" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid request format +- `409 Conflict`: Username already exists +- `500 Internal Server Error`: Database or server error + +--- + +### Login + +Authenticate and receive a JWT token. + +**Endpoint**: `POST /login` + +**Authentication**: None required + +**Request Body**: +```json +{ + "username": "string", + "password": "string" +} +``` + +**Response** (200 OK): +```json +{ + "token": "jwt-token-string" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid JSON +- `401 Unauthorized`: Invalid credentials +- `500 Internal Server Error`: Database or token generation error + +--- + +### List All Devices + +Retrieve all devices in the system (public read access). + +**Endpoint**: `GET /devices` + +**Authentication**: None required + +**Response** (200 OK): +```json +[ + { + "id": "uuid", + "name": "string", + "description": "string", + "location": "string", + "status_id": 1, + "created_at": "2026-01-14T10:00:00Z", + "updated_at": "2026-01-14T10:00:00Z" + } +] +``` + +**Status IDs**: +- `1`: ok +- `2`: pending +- `3`: lost +- `4`: disabled + +--- + +## Protected Endpoints + +All endpoints below require JWT authentication. + +### Protected Test + +Test authentication and view token claims. + +**Endpoint**: `GET /protected` + +**Authentication**: Required + +**Response** (200 OK): +```json +{ + "claims": { + "sub": "username", + "user_id": "uuid", + "exp": 1705324800 + } +} +``` + +--- + +## Device Endpoints + +### Create Device + +Create a new device. + +**Endpoint**: `POST /devices` + +**Authentication**: Required + +**Request Body**: +```json +{ + "name": "string (required)", + "description": "string (required)", + "location": "string (required)", + "status_id": 1 (required, range: 1-4) +} +``` + +**Response** (201 Created): +```json +{ + "id": "uuid" +} +``` + +--- + +### Get Device + +Retrieve a specific device by ID. + +**Endpoint**: `GET /devices/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Device UUID + +**Response** (200 OK): +```json +{ + "id": "uuid", + "name": "string", + "description": "string", + "location": "string", + "status_id": 1, + "created_at": "2026-01-14T10:00:00Z", + "updated_at": "2026-01-14T10:00:00Z" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid device ID +- `404 Not Found`: Device not found + +--- + +### Update Device + +Update an existing device. + +**Endpoint**: `PUT /devices/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Device UUID + +**Request Body** (all fields optional): +```json +{ + "name": "string", + "description": "string", + "location": "string", + "status_id": 1 +} +``` + +**Response** (200 OK): +```json +{ + "message": "device updated" +} +``` + +--- + +### Delete Device + +Delete a device. + +**Endpoint**: `DELETE /devices/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Device UUID + +**Response** (200 OK): +```json +{ + "message": "device deleted" +} +``` + +--- + +## Sensor Endpoints + +### Create Sensor + +Create a new sensor for a device. + +**Endpoint**: `POST /sensors` + +**Authentication**: Required + +**Request Body**: +```json +{ + "device_id": "uuid (required)", + "name": "string (required)", + "type": "string (required)", + "data_type_id": 1 (required, min: 1) +} +``` + +**Response** (201 Created): +```json +{ + "id": "uuid" +} +``` + +--- + +### List All Sensors + +Retrieve all sensors. + +**Endpoint**: `GET /sensors` + +**Authentication**: Required + +**Response** (200 OK): +```json +[ + { + "id": "uuid", + "device_id": "uuid", + "name": "string", + "type": "string", + "data_type_id": 1, + "created_at": "2026-01-14T10:00:00Z", + "updated_at": "2026-01-14T10:00:00Z" + } +] +``` + +--- + +### Get Sensor + +Retrieve a specific sensor by ID. + +**Endpoint**: `GET /sensors/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Sensor UUID + +**Response** (200 OK): +```json +{ + "id": "uuid", + "device_id": "uuid", + "name": "string", + "type": "string", + "data_type_id": 1, + "created_at": "2026-01-14T10:00:00Z", + "updated_at": "2026-01-14T10:00:00Z" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid sensor ID +- `404 Not Found`: Sensor not found + +--- + +### Update Sensor + +Update an existing sensor. + +**Endpoint**: `PUT /sensors/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Sensor UUID + +**Request Body** (all fields optional): +```json +{ + "device_id": "uuid", + "name": "string", + "type": "string", + "data_type_id": 1 +} +``` + +**Response** (200 OK): +```json +{ + "message": "sensor updated" +} +``` + +--- + +### Delete Sensor + +Delete a sensor. + +**Endpoint**: `DELETE /sensors/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Sensor UUID + +**Response** (200 OK): +```json +{ + "message": "sensor deleted" +} +``` + +--- + +### Trigger Sensor + +Request a new reading from a sensor via MQTT. + +**Endpoint**: `POST /sensors/:id/trigger` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Sensor UUID + +**Request Body** (optional): +```json +{ + "action": "string (default: 'read')" +} +``` + +**Response** (202 Accepted): +```json +{ + "status": "published", + "topic": "lambdaiot" +} +``` + +**MQTT Message Published**: +```json +{ + "type": "sensor_trigger", + "sensor_id": "uuid", + "action": "read", + "requested_at": "2026-01-14T10:00:00Z" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid sensor ID +- `404 Not Found`: Sensor not found +- `500 Internal Server Error`: Failed to publish to MQTT + +--- + +## Actor Endpoints + +### Create Actor + +Create a new actor for a device. + +**Endpoint**: `POST /actors` + +**Authentication**: Required + +**Request Body**: +```json +{ + "device_id": "uuid (required)", + "name": "string (required)", + "type": "string (required)", + "data_type_id": 1 (required, min: 1) +} +``` + +**Response** (201 Created): +```json +{ + "id": "uuid" +} +``` + +--- + +### List All Actors + +Retrieve all actors. + +**Endpoint**: `GET /actors` + +**Authentication**: Required + +**Response** (200 OK): +```json +[ + { + "id": "uuid", + "device_id": "uuid", + "name": "string", + "type": "string", + "data_type_id": 1, + "created_at": "2026-01-14T10:00:00Z", + "updated_at": "2026-01-14T10:00:00Z" + } +] +``` + +--- + +### Get Actor + +Retrieve a specific actor by ID. + +**Endpoint**: `GET /actors/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Actor UUID + +**Response** (200 OK): +```json +{ + "id": "uuid", + "device_id": "uuid", + "name": "string", + "type": "string", + "data_type_id": 1, + "created_at": "2026-01-14T10:00:00Z", + "updated_at": "2026-01-14T10:00:00Z" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid actor ID +- `404 Not Found`: Actor not found + +--- + +### Update Actor + +Update an existing actor. + +**Endpoint**: `PUT /actors/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Actor UUID + +**Request Body** (all fields optional): +```json +{ + "device_id": "uuid", + "name": "string", + "type": "string", + "data_type_id": 1 +} +``` + +**Response** (200 OK): +```json +{ + "message": "actor updated" +} +``` + +--- + +### Delete Actor + +Delete an actor. + +**Endpoint**: `DELETE /actors/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Actor UUID + +**Response** (200 OK): +```json +{ + "message": "actor deleted" +} +``` + +--- + +### Write to Actor + +Send a command to an actor via MQTT. + +**Endpoint**: `POST /actors/:id/write` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Actor UUID + +**Request Body**: +```json +{ + "action": "string (required)", + "value": "any JSON value (optional)" +} +``` + +**Response** (202 Accepted): +```json +{ + "status": "published", + "topic": "lambdaiot" +} +``` + +**MQTT Message Published**: +```json +{ + "type": "actor_command", + "actor_id": "uuid", + "action": "string", + "value": "any", + "requested_at": "2026-01-14T10:00:00Z" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid actor ID or missing action +- `404 Not Found`: Actor not found +- `500 Internal Server Error`: Failed to publish to MQTT + +--- + +## Sensor Reading Endpoints + +### Create Sensor Reading + +Create a new sensor reading. + +**Endpoint**: `POST /sensor-readings` + +**Authentication**: Required + +**Request Body**: +```json +{ + "sensor_id": "uuid (required)", + "value": 123.45 (required), + "value_at": "2026-01-14T10:00:00Z (optional, defaults to now)" +} +``` + +**Response** (201 Created): +```json +{ + "id": 123 +} +``` + +--- + +### List Sensor Readings + +Retrieve sensor readings with optional filtering and pagination. + +**Endpoint**: `GET /sensor-readings` + +**Authentication**: Required + +**Query Parameters**: +- `sensor_id`: Filter by sensor UUID (optional) +- `limit`: Number of results (default: 100, max: 1000) +- `page`: Page number for pagination (default: 0) + +**Response** (200 OK): +```json +[ + { + "id": 123, + "sensor_id": "uuid", + "value": 123.45, + "value_at": "2026-01-14T10:00:00Z" + } +] +``` + +--- + +### Get Sensor Reading + +Retrieve a specific sensor reading by ID. + +**Endpoint**: `GET /sensor-readings/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Reading ID (integer) + +**Response** (200 OK): +```json +{ + "id": 123, + "sensor_id": "uuid", + "value": 123.45, + "value_at": "2026-01-14T10:00:00Z" +} +``` + +**Error Responses**: +- `400 Bad Request`: Invalid reading ID +- `404 Not Found`: Reading not found + +--- + +### Update Sensor Reading + +Update an existing sensor reading. + +**Endpoint**: `PUT /sensor-readings/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Reading ID (integer) + +**Request Body** (all fields optional): +```json +{ + "value": 123.45, + "value_at": "2026-01-14T10:00:00Z" +} +``` + +**Response** (200 OK): +```json +{ + "message": "sensor reading updated" +} +``` + +--- + +### Delete Sensor Reading + +Delete a sensor reading. + +**Endpoint**: `DELETE /sensor-readings/:id` + +**Authentication**: Required + +**Path Parameters**: +- `id`: Reading ID (integer) + +**Response** (200 OK): +```json +{ + "message": "sensor reading deleted" +} +``` + +--- + +## MQTT & System Endpoints + +### MQTT Ping + +Publish a ping message to the MQTT broker for testing. + +**Endpoint**: `GET /mqttping` + +**Authentication**: Required + +**Response** (200 OK): +```json +{ + "timestamp": "2026-01-14T10:00:00Z", + "topic": "lambdaiot" +} +``` + +**MQTT Message Published**: +```json +{ + "type": "ping", + "timestamp": "2026-01-14T10:00:00Z" +} +``` + +--- + +## MQTT Integration + +### Device Discovery + +Devices can announce themselves by publishing to the discovery topic: + +**Topic**: `{configured_mqtt_topic}/discovery` + +**Message Format**: +```json +{ + "mac_address": "00:11:22:33:44:55", + "device": { + "id": "uuid", + "name": "Device Name", + "description": "Device Description", + "location": "Device Location", + "status_id": 1 + }, + "sensors": [ + { + "id": "uuid", + "name": "Temperature Sensor", + "type": "temperature", + "data_type_id": 1 + } + ], + "actors": [ + { + "id": "uuid", + "name": "LED Actuator", + "type": "led", + "data_type_id": 2 + } + ] +} +``` + +**Behavior**: +- New devices are automatically created with all sensors and actors +- Existing devices are updated, and sensors/actors are synced +- Device status is set to OK (1) upon discovery (payload `device.status_id` is ignored) + +--- + +### Device Check (State Polling) + +The server periodically publishes device check requests to the main MQTT topic and +expects devices to respond on the same topic. + +**Topic**: `{configured_mqtt_topic}` + +**Request Message Published**: +```json +{ + "type": "device_check_request", + "device_id": "uuid", + "requested_at": "2026-01-14T10:00:00Z" +} +``` + +**Expected Device Response**: +```json +{ + "type": "device_check_response", + "device_id": "uuid", + "status": "ok" | "lost" | "pending" +} +``` + +--- + +### Sensor Readings via MQTT + +Devices can publish sensor readings directly to the main MQTT topic. The server +stores readings only if the `sensor_id` exists in the database. + +**Topic**: `{configured_mqtt_topic}` + +**Message Format**: +```json +{ + "type": "sensor_reading", + "sensor_id": "uuid", + "value": 23.7, + "value_at": "2026-01-14T10:00:00Z" +} +``` + +**Notes**: +- `type` may also be `sensor_value`. +- `value` may be a number, boolean, or numeric string (e.g., "1", "0", "23.7"). +- `value_at` is optional; if omitted, the server uses the current time. + +--- + +## Error Responses + +All endpoints may return the following standard error responses: + +### 400 Bad Request +```json +{ + "error": "descriptive error message" +} +``` + +### 401 Unauthorized +```json +{ + "error": "invalid credentials" // or "no claims" +} +``` + +### 404 Not Found +```json +{ + "error": "resource not found" +} +``` + +### 409 Conflict +```json +{ + "error": "resource already exists" +} +``` + +### 500 Internal Server Error +```json +{ + "error": "descriptive error message" +} +``` + +--- + +## Data Types + +### Device Status IDs + +| ID | Status | Description | +|----|----------|--------------------------------------| +| 1 | OK | Device is online and functioning | +| 2 | Pending | Device is initializing or awaiting | +| 3 | Lost | Device has not checked in recently | +| 4 | Disabled | Device is administratively disabled | + +### Date/Time Format + +All timestamps use RFC3339 format: `2026-01-14T10:00:00Z` + +### UUIDs + +UUIDs follow the standard UUID format (any version), e.g. `550e8400-e29b-41d4-a716-446655440000`. + +--- + +## Configuration + +The server uses a configuration file (typically `config.toml`) with the following structure: + +```toml +[server] +address = ":8080" +port = 8080 +jwt_secret = "your-secret-key" + +[database] +host = "localhost" +port = 3306 +user = "root" +password = "password" +name = "lambdaiot" + +[mqtt] +broker = "tcp://localhost:1883" +topic = "lambdaiot" +client_id = "lambdaiot-core" +username = "" +password = "" +``` + +--- + +## Rate Limiting & Best Practices + +1. **Pagination**: Use `limit` and `page` parameters for large result sets +2. **MQTT Topics**: All MQTT messages are published to the configured topic +3. **Token Expiry**: JWT tokens expire after 24 hours - refresh as needed +4. **UUID Format**: Always use valid UUID v4 format for device, sensor, and actor IDs +5. **Date Format**: Use RFC3339 format for all timestamp fields +6. **Device Discovery**: Devices can self-register via MQTT discovery messages + +--- + +## Example Workflows + +### Complete Device Setup Workflow + +1. Register a user: + ```bash + POST /register + {"username": "admin", "password": "secret123"} + ``` + +2. Login and get token: + ```bash + POST /login + {"username": "admin", "password": "secret123"} + ``` + +3. Create a device: + ```bash + POST /devices (with JWT) + {"name": "Room Sensor", "description": "Living room", "location": "Floor 1", "status_id": 1} + ``` + +4. Create a sensor: + ```bash + POST /sensors (with JWT) + {"device_id": "device-uuid", "name": "Temperature", "type": "temp", "data_type_id": 1} + ``` + +5. Trigger a reading: + ```bash + POST /sensors/{sensor-id}/trigger (with JWT) + {"action": "read"} + ``` + +6. Create a reading: + ```bash + POST /sensor-readings (with JWT) + {"sensor_id": "sensor-uuid", "value": 22.5} + ``` + +### Query Readings for a Specific Sensor + +```bash +GET /sensor-readings?sensor_id={uuid}&limit=50&page=0 (with JWT) +``` + +--- + +## Support & Documentation + +For more information about device discovery specifications, see [DEVICE_DISCOVERY_SPEC.md](DEVICE_DISCOVERY_SPEC.md). + +--- + +*Generated: January 14, 2026* diff --git a/DEVICE_DISCOVERY_SPEC.md b/docs/DEVICE_DISCOVERY_SPEC.md similarity index 100% rename from DEVICE_DISCOVERY_SPEC.md rename to docs/DEVICE_DISCOVERY_SPEC.md diff --git a/readme.md b/readme.md index 61b6b16..3534331 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ -# Lambda-IoT Core +# Lambda IoT Core -This repository contains a minimal Go REST service used as the backend for the Lambda-IoT project. +This repository contains a minimal Go REST service used as the backend for the Lambda IoT project. Repository: https://git.piskot.si/SeminarM2/lambdaiot-core diff --git a/state_test.db b/state_test.db deleted file mode 100644 index 3f27e14b54354940fa9752dedaa0685db3bfdfdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeI#y$ZrG6b0a$AP9o!=2W<&g1GnsR>>9=HG*q|C?Mh~D{ps4m%E~{2UpZp&Q>ZlicLDzitAhMw>TzVu~A#Y y^F^2&t%vVtpM?f*^Ydpz^*$*7=Hb(iKmY;|fB*y_009U<00Izz00bcL!vZgxXe#Rf diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 56881fe..0000000 --- a/test/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Test compose - -This folder contains a simple Docker Compose stack to run the Go server together with a MySQL database, an MQTT broker, and a Python subscriber client. - -Quickstart: - -```bash -cd test -docker compose up --build -``` - -- The Go server is built from the repository `Dockerfile` and is configured via environment variables in `docker-compose.yml`. -- The MySQL database is initialized with the schema from `../ai-improved.sql`. -- The MQTT broker is `eclipse-mosquitto` and is exposed on port `1883`. -- The `subscriber` service runs `subscribe.py` and prints any messages published to the `lambdaiot` topic. - -To run the Python subscriber locally instead of in Docker: - -```bash -python3 -m venv .venv -source .venv/bin/activate -pip install -r requirements.txt -MQTT_BROKER=localhost:1883 MQTT_TOPIC=lambdaiot python subscribe.py -```