WellNuo/docs/BLE_PROTOCOL.md
Sergei 2b68b70584 Add sensor system documentation
BLE_PROTOCOL.md:
- ESP32 BLE provisioning protocol spec
- Characteristics UUIDs and data formats
- WiFi credential exchange flow
- Security considerations
- Error handling

SENSORS_IMPLEMENTATION_PLAN.md:
- Complete implementation roadmap
- Phase 1: BLE scanning and connection
- Phase 2: WiFi provisioning
- Phase 3: Device management
- Phase 4: Status monitoring
- API endpoints and data models
- Testing checklist

Technical reference for:
- Backend developers
- Mobile developers
- QA team
2026-01-14 19:08:19 -08:00

600 lines
14 KiB
Markdown

# WellPlug BLE Protocol Documentation
## Overview
WellPlug devices (WP_XXX) use Bluetooth Low Energy (BLE) for configuration. This document describes the communication protocol.
## Device Information
| Parameter | Value |
|-----------|-------|
| Service UUID | `4fafc201-1fb5-459e-8fcc-c5c9c331914b` |
| Characteristic UUID | `beb5483e-36e1-4688-b7f5-ea07361b26a8` |
| Properties | read, write, notify |
| Platform | ESP32 |
## Connection Flow
1. **Scan** for devices with name starting with `WP_`
2. **Connect** to the device
3. **Subscribe** to notifications on the characteristic
4. **Unlock** device with PIN command
5. **Send commands** and receive responses
## Commands
### 1. Unlock Device (Required First!)
Must be sent before any other command.
| | |
|---|---|
| **Command** | `pin\|7856` |
| **Response** | `pin\|ok` |
| **Note** | PIN `7856` is universal for all devices |
**Example:**
```
Send: pin|7856
Recv: pin|ok
```
---
### 2. Get WiFi Networks List
Scans and returns available WiFi networks.
| | |
|---|---|
| **Command** | `w` |
| **Response** | `mac,XXXXXX\|w\|COUNT\|SSID1,RSSI1\|SSID2,RSSI2\|...` |
**Response Format:**
- `mac,XXXXXX` - Device MAC address
- `w` - Command echo
- `COUNT` - Number of networks found (-1 = scanning, -2 = no networks, 0+ = count)
- `SSID,RSSI` - Network name and signal strength (dBm)
**Example:**
```
Send: w
Recv: mac,142b2f81a14c|w|19|FrontierTower,-55|MyNetwork,-67|...
```
---
### 3. Set WiFi Credentials
Configures the device to connect to a WiFi network.
| | |
|---|---|
| **Command** | `W\|SSID,PASSWORD` |
| **Response** | `mac,XXXXXX\|W\|ok` or `mac,XXXXXX\|W\|fail` |
**Example:**
```
Send: W|FrontierTower,mypassword123
Recv: mac,142b2f81a14c|W|ok
```
**For devices with long MAC (>12 chars):**
Use JSON format instead:
```json
{"FUNC":"W","SSID":"NetworkName","PSW":"password123"}
```
---
### 4. Get WiFi Connection Status
Returns current WiFi connection status.
| | |
|---|---|
| **Command** | `a` |
| **Response** | `mac,XXXXXX\|a\|SSID,STATUS` |
**Status Values:**
- `0` - Not connected
- Other - Connected
**Example:**
```
Send: a
Recv: mac,142b2f81a14c|a|FrontierTower,1
```
---
### 5. Reboot Device
Restarts the device. BLE connection will be lost.
| | |
|---|---|
| **Command** | `s` |
| **Response** | (device disconnects) |
**Example:**
```
Send: s
(device reboots and disconnects)
```
---
### 6. Disconnect BLE
Disconnects BLE connection (device side).
| | |
|---|---|
| **Command** | `D` |
| **Response** | (connection closed) |
---
## Response Format
All responses follow this format:
```
mac,XXXXXX|COMMAND|DATA
```
Where:
- `mac,XXXXXX` - Device MAC address (last 12 hex chars)
- `COMMAND` - Echo of the command sent
- `DATA` - Response data (varies by command)
**Error Response:**
```
error
```
---
## Python Example
```python
import asyncio
from bleak import BleakClient, BleakScanner
SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
CHAR_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
async def configure_wellplug(device_name, wifi_ssid, wifi_password):
# Find device
device = await BleakScanner.find_device_by_name(device_name, timeout=10)
if not device:
print("Device not found")
return
def on_notify(sender, data):
print(f"Response: {data.decode('utf-8')}")
async with BleakClient(device) as client:
# Subscribe to notifications
await client.start_notify(CHAR_UUID, on_notify)
# 1. Unlock device
await client.write_gatt_char(CHAR_UUID, b"pin|7856", response=True)
await asyncio.sleep(1)
# 2. Get WiFi list
await client.write_gatt_char(CHAR_UUID, b"w", response=True)
await asyncio.sleep(2)
# 3. Set WiFi credentials
cmd = f"W|{wifi_ssid},{wifi_password}".encode()
await client.write_gatt_char(CHAR_UUID, cmd, response=True)
await asyncio.sleep(2)
# 4. Check status
await client.write_gatt_char(CHAR_UUID, b"a", response=True)
await asyncio.sleep(1)
# Usage
asyncio.run(configure_wellplug("WP_497_81a14c", "MyWiFi", "password123"))
```
---
## Important Notes
1. **Always unlock first** - Device won't respond to commands without PIN
2. **Use `response=True`** - When writing with bleak, use `response=True` for reliable communication
3. **Subscribe before writing** - Subscribe to notifications before sending commands
4. **Wait for responses** - Allow 1-2 seconds between commands for device to respond
5. **Reboot disconnects** - Command `s` will disconnect BLE, need to reconnect after
---
## Known Devices
| Device Name | MAC Address | Notes |
|-------------|-------------|-------|
| WP_497_81a14c | 142b2f81a14c | Test device 1 |
| WP_523_81aad4 | 142b2f81aad4 | Test device 2 |
---
---
# WellNuo Legacy API Documentation
## Overview
The WellNuo Legacy API (eluxnetworks.net) is used for device management, deployments, and sensor data. This is a REST-like API using POST requests with form-urlencoded parameters.
## Base URL
```
https://eluxnetworks.net/function/well-api/api
```
## Authentication
### Login (Get Token)
| | |
|---|---|
| **Function** | `credentials` |
| **Method** | POST |
**Parameters:**
| Parameter | Value |
|-----------|-------|
| `function` | `credentials` |
| `user_name` | username |
| `ps` | password |
| `clientId` | `001` |
| `nonce` | any value |
**Response:**
```json
{
"access_token": "eyJhbGci...",
"privileges": "-1",
"user_id": 32,
"max_role": -1,
"status": "200 OK"
}
```
**Example:**
```bash
curl -X POST "https://eluxnetworks.net/function/well-api/api" \
-d "function=credentials&user_name=USERNAME&ps=PASSWORD&clientId=001&nonce=111"
```
---
## Deployments
### List Deployments
| | |
|---|---|
| **Function** | `deployments_list` |
**Parameters:**
| Parameter | Required | Description |
|-----------|----------|-------------|
| `function` | Yes | `deployments_list` |
| `user_name` | Yes | Username |
| `token` | Yes | Access token |
| `first` | Yes | Start index (0) |
| `last` | Yes | End index (50) |
**Response:**
```json
{
"result_list": [
{"deployment_id": 21, "email": "user@example.com", "first_name": "John", "last_name": "Doe"},
...
],
"status": "200 OK"
}
```
---
## Devices
### List All Devices
| | |
|---|---|
| **Function** | `device_list` |
**Parameters:**
| Parameter | Required | Description |
|-----------|----------|-------------|
| `function` | Yes | `device_list` |
| `user_name` | Yes | Username |
| `token` | Yes | Access token |
| `first` | Yes | Start index |
| `last` | Yes | End index |
**Response:**
```json
{
"result_list": [
[device_id, well_id, "MAC_ADDRESS", timestamp, "location", "description", deployment_id],
...
],
"status": "200 OK"
}
```
**Device Array Format:**
- `[0]` device_id - Internal device ID
- `[1]` well_id - Device well ID (from device name WP_XXX)
- `[2]` MAC address (uppercase, no colons)
- `[3]` Last seen timestamp (Unix)
- `[4]` Location name
- `[5]` Description
- `[6]` deployment_id (0 = not assigned)
---
### List Devices by Deployment
| | |
|---|---|
| **Function** | `device_list_by_deployment` |
**Parameters:**
| Parameter | Required | Description |
|-----------|----------|-------------|
| `function` | Yes | `device_list_by_deployment` |
| `user_name` | Yes | Username |
| `token` | Yes | Access token |
| `deployment_id` | Yes | Target deployment ID |
| `first` | Yes | Start index |
| `last` | Yes | End index |
---
### Assign Device to Deployment
| | |
|---|---|
| **Function** | `device_set_well_id` |
**Parameters:**
| Parameter | Required | Description |
|-----------|----------|-------------|
| `function` | Yes | `device_set_well_id` |
| `user_name` | Yes | Username |
| `token` | Yes | Access token |
| `device_id` | Yes | Internal device ID |
| `well_id` | Yes | New well_id to assign |
| `mac` | Yes | Device MAC address |
**Example - Reassign device:**
```bash
curl -X POST "https://eluxnetworks.net/function/well-api/api" \
-d "function=device_set_well_id&user_name=USER&token=TOKEN&device_id=743&well_id=500&mac=142B2F81A14C"
```
---
### Update Device Settings
| | |
|---|---|
| **Function** | `device_form` |
**Parameters:**
| Parameter | Required | Description |
|-----------|----------|-------------|
| `function` | Yes | `device_form` |
| `user_name` | Yes | Username |
| `token` | Yes | Access token |
| `well_id` | Yes | Device well_id |
| `device_mac` | Yes | MAC address |
| `description` | No | Description text |
| `location` | No | Location code |
| `close_to` | No | Position description |
| `radar_threshold` | No | Radar sensitivity (0-100) |
| `group` | No | Group ID |
---
### Reboot Device
| | |
|---|---|
| **Function** | `device_reboot` |
**Parameters:**
| Parameter | Required | Description |
|-----------|----------|-------------|
| `function` | Yes | `device_reboot` |
| `user_name` | Yes | Username |
| `token` | Yes | Access token |
| `device_id` | Yes | Device ID to reboot |
---
## Device Online Status Checking
### Method 1: `device_list` API (Individual Check)
Use `device_list` to check last seen timestamp for specific devices.
**How to determine online/offline:**
- Field `[3]` contains Unix timestamp of last update
- Calculate hours since last update
- If < 1 hour ONLINE
- If > 1 hour → OFFLINE
**Example:**
```bash
curl -X POST "https://eluxnetworks.net/function/well-api/api" \
-d "function=device_list&user_name=USER&token=TOKEN&first=0&last=100"
```
**Response:**
```json
{
"result_list": [
[743, 497, "142B2F81A14C", 1736859744, "Location", "Description", 70],
[769, 523, "142B2F81AAD4", 1736859630, "Location", "Description", 70]
],
"status": "200 OK"
}
```
**Status calculation:**
```
Device 743 (WP_497):
Last seen: 1736859744 → 2026-01-14 10:02:24
Hours ago: 0.0 → ONLINE
Device 769 (WP_523):
Last seen: 1736859630 → 2026-01-14 10:00:30
Hours ago: 0.03 → ONLINE
```
---
### Method 2: `request_devices` API (Batch Check by Deployment) ⭐
**IMPORTANT: This is the endpoint Robert uses to show "green" (online) devices in the app!**
**Quote from Robert:** *"there is API call to find out if set of devices are recently seen by server"*
This endpoint returns only devices that are **recently active** (sent data to server in the last ~1 hour).
| | |
|---|---|
| **Function** | `request_devices` |
**Parameters:**
| Parameter | Required | Description |
|-----------|----------|-------------|
| `function` | Yes | `request_devices` |
| `user_name` | Yes | Username |
| `token` | Yes | Access token |
| `deployment_id` | Yes | Deployment to check |
| `group_id` | Yes | Group filter ("All" for all groups) |
| `location` | Yes | Location filter ("All" for all locations) |
| `fresh` | Yes | **`true`** ← KEY PARAMETER! Filters only recently seen devices |
**Example:**
```bash
curl -X POST "https://eluxnetworks.net/function/well-api/api" \
-d "function=request_devices" \
-d "user_name=USER" \
-d "token=TOKEN" \
-d "deployment_id=38" \
-d "group_id=All" \
-d "location=All" \
-d "fresh=true"
```
**How it works (Robert's app logic):**
1. Call `request_devices` with `fresh=true`
2. Response contains ONLY online devices (recently seen by server)
3. If device is in response → Show **GREEN** (online) ✅
4. If device is NOT in response → Show **GRAY** (offline) ❌
**Response example:**
```json
{
"result_list": [
[device_id_1, well_id_1, "MAC1", timestamp_1, ...],
[device_id_2, well_id_2, "MAC2", timestamp_2, ...]
],
"status": "200 OK"
}
```
Only devices that sent data recently appear in the list!
**Alternative without `fresh=true`:**
- Returns ALL devices in deployment (regardless of status)
- You need to manually check timestamp field [3] to determine online/offline
**Note:** This endpoint requires devices to be properly linked to the deployment in the Legacy API system. Use Method 1 (`device_list`) if deployment linkage is not set up.
---
## Creating and Activating Deployments
### Create/Update Deployment with Devices
**Function:** `set_deployment`
This endpoint is used to create a new deployment or update existing one with devices, beneficiary info, and WiFi credentials.
**Key Parameters:**
| Parameter | Description |
|-----------|-------------|
| `function` | `set_deployment` |
| `user_name` | Installer username |
| `token` | Access token |
| `deployment` | Deployment name or "NEW" for new deployment |
| `beneficiary_name` | Beneficiary full name |
| `beneficiary_email` | Beneficiary email |
| `beneficiary_user_name` | Beneficiary login username |
| `beneficiary_password` | Beneficiary password |
| `beneficiary_address` | Beneficiary address |
| `caretaker_username` | Caretaker username (can be same as installer) |
| `caretaker_email` | Caretaker email |
| `persons` | Number of persons in household |
| `pets` | Number of pets |
| `gender` | Gender |
| `race` | Race index |
| `born` | Year born |
| `lat` | GPS latitude |
| `lng` | GPS longitude |
| `wifis` | JSON array of WiFi credentials: `["SSID1\|password1", "SSID2\|password2"]` |
| `devices` | JSON array of device well_ids: `[497, 523]` |
| `beneficiary_photo` | Photo filename |
| `beneficiary_photo_data` | Base64 encoded JPEG photo |
| `reuse_existing_devices` | `1` to reuse, `0` to create new |
**Example WiFi credentials format:**
```json
["FrontierTower|frontiertower995", "HomeNetwork|password123"]
```
**Example devices list:**
```json
[497, 523]
```
**After creating deployment:**
1. Devices are linked to the deployment
2. WiFi credentials are stored
3. Beneficiary account is created
4. Devices will start reporting data to server
5. Devices become "live" and visible in `request_devices` with `fresh=true`
---
## Known Test Devices
| Device Name | device_id | well_id | MAC Address | Status |
|-------------|-----------|---------|-------------|--------|
| WP_497_81a14c | 743 | 497 | 142B2F81A14C | Configured |
| WP_523_81aad4 | 769 | 523 | 142B2F81AAD4 | Configured |
Both devices are assigned to:
- **Deployment ID**: 70
- **Beneficiary**: Sergei Terekhov (ID: 76)
- **Owner**: serter2069@gmail.com (User ID: 63)
- **WiFi Network**: FrontierTower
---
## Changelog
- **2026-01-14** - Added Legacy API documentation (deployments, devices)
- **2026-01-13** - Initial BLE protocol documentation created