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
This commit is contained in:
parent
5092678430
commit
2b68b70584
599
docs/BLE_PROTOCOL.md
Normal file
599
docs/BLE_PROTOCOL.md
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
# 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
|
||||||
693
docs/SENSORS_IMPLEMENTATION_PLAN.md
Normal file
693
docs/SENSORS_IMPLEMENTATION_PLAN.md
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
# WellNuo Sensors - Implementation Plan
|
||||||
|
|
||||||
|
## 🎯 Цель
|
||||||
|
Заменить мок-данные в экране `equipment.tsx` на реальное управление WP сенсорами через Bluetooth Low Energy (BLE).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ КРИТИЧНО: Bluetooth в iOS Simulator
|
||||||
|
|
||||||
|
**iOS Simulator НЕ ПОДДЕРЖИВАЕТ Bluetooth!**
|
||||||
|
|
||||||
|
### Решение:
|
||||||
|
- **На реальном устройстве:** полный BLE функционал
|
||||||
|
- **В Simulator:** показываем mock-данные + предупреждение
|
||||||
|
|
||||||
|
### Проверка доступности BLE:
|
||||||
|
```typescript
|
||||||
|
import * as Device from 'expo-device';
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
|
const isBLEAvailable = Platform.OS !== 'web' && Device.isDevice;
|
||||||
|
// Device.isDevice = false в Simulator, true на реальном устройстве
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Библиотека: react-native-ble-plx
|
||||||
|
|
||||||
|
**Рекомендуемая библиотека:** `react-native-ble-plx`
|
||||||
|
- ✅ Поддержка Expo (через config plugin)
|
||||||
|
- ✅ Кроссплатформенность (iOS + Android)
|
||||||
|
- ✅ Активная поддержка
|
||||||
|
- ✅ TypeScript типы
|
||||||
|
|
||||||
|
### Установка:
|
||||||
|
```bash
|
||||||
|
npx expo install react-native-ble-plx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Конфигурация `app.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"react-native-ble-plx",
|
||||||
|
{
|
||||||
|
"isBackgroundEnabled": true,
|
||||||
|
"modes": ["peripheral", "central"],
|
||||||
|
"bluetoothAlwaysPermission": "Allow $(PRODUCT_NAME) to connect to WellNuo sensors"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Архитектура
|
||||||
|
|
||||||
|
### 1. BLE Service (`services/ble/`)
|
||||||
|
|
||||||
|
```
|
||||||
|
services/ble/
|
||||||
|
├── BLEManager.ts # Центральный менеджер BLE
|
||||||
|
├── WellPlugDevice.ts # Класс для работы с WP устройствами
|
||||||
|
├── BLECommands.ts # Константы команд (pin|7856, w, W, a, s, D)
|
||||||
|
├── MockBLEManager.ts # Mock для Simulator
|
||||||
|
└── types.ts # TypeScript типы
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Context (`contexts/BLEContext.tsx`)
|
||||||
|
|
||||||
|
Глобальный контекст для управления BLE состоянием:
|
||||||
|
- Список найденных устройств
|
||||||
|
- Состояние сканирования
|
||||||
|
- Подключенные устройства
|
||||||
|
- Ошибки
|
||||||
|
|
||||||
|
### 3. Хуки (`hooks/`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// hooks/useBLE.ts
|
||||||
|
export function useBLE() {
|
||||||
|
// Доступ к BLEContext
|
||||||
|
// Scan, connect, disconnect
|
||||||
|
}
|
||||||
|
|
||||||
|
// hooks/useWellPlugDevice.ts
|
||||||
|
export function useWellPlugDevice(deviceId: string) {
|
||||||
|
// Управление конкретным WP устройством
|
||||||
|
// getWiFiList(), setWiFi(), checkCurrentWiFi()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Экраны и User Flow
|
||||||
|
|
||||||
|
### Экран 1: Equipment List (текущий `equipment.tsx`)
|
||||||
|
|
||||||
|
**Функционал:**
|
||||||
|
- ✅ Показать список привязанных WP сенсоров к beneficiary
|
||||||
|
- ✅ Статус онлайн/офлайн через API `request_devices` (fresh=true)
|
||||||
|
- ✅ Последний раз виден (Last seen)
|
||||||
|
- ✅ Текущая WiFi сеть (получаем через BLE команду `a`)
|
||||||
|
- ✅ Кнопка "Настроить" → переход на Device Settings
|
||||||
|
- ✅ Кнопка "Добавить сенсор" → Scan Screen
|
||||||
|
|
||||||
|
**Данные:**
|
||||||
|
```typescript
|
||||||
|
interface WPSensor {
|
||||||
|
deviceId: string; // device_id из PostgreSQL
|
||||||
|
wellId: number; // well_id (497, 523)
|
||||||
|
mac: string; // MAC адрес (142B2F81A14C)
|
||||||
|
name: string; // "WP_497_81a14c"
|
||||||
|
|
||||||
|
// Статус (из API)
|
||||||
|
status: 'online' | 'offline';
|
||||||
|
lastSeen: Date; // timestamp из device_list API
|
||||||
|
|
||||||
|
// WiFi (из BLE)
|
||||||
|
currentWiFi?: {
|
||||||
|
ssid: string; // "FrontierTower"
|
||||||
|
rssi: number; // -67 dBm
|
||||||
|
};
|
||||||
|
|
||||||
|
// Привязка
|
||||||
|
beneficiaryId: string;
|
||||||
|
deploymentId: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**API для получения списка:**
|
||||||
|
```typescript
|
||||||
|
// services/api.ts
|
||||||
|
async getDevicesForBeneficiary(beneficiaryId: string): Promise<WPSensor[]> {
|
||||||
|
// 1. Получить deployment_id для beneficiary из PostgreSQL
|
||||||
|
// 2. Вызвать Legacy API: device_list_by_deployment
|
||||||
|
// 3. Вызвать request_devices(fresh=true) для онлайн статуса
|
||||||
|
// 4. Объединить данные
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**BLE для текущего WiFi:**
|
||||||
|
- При открытии экрана → подключаемся к каждому устройству по BLE
|
||||||
|
- Отправляем команду `a`
|
||||||
|
- Парсим ответ: `mac,XXXXXX|a|SSID,RSSI`
|
||||||
|
- Обновляем UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Экран 2: Add Sensor (новый `add-sensor.tsx`)
|
||||||
|
|
||||||
|
**Путь:** `app/(tabs)/beneficiaries/[id]/add-sensor.tsx`
|
||||||
|
|
||||||
|
**Функционал:**
|
||||||
|
- ✅ Сканирование BLE устройств с именем `WP_*`
|
||||||
|
- ✅ Показать список найденных (с RSSI - сила сигнала)
|
||||||
|
- ✅ Сортировка по близости (RSSI)
|
||||||
|
- ✅ Выбор устройства → переход на Setup WiFi Screen
|
||||||
|
|
||||||
|
**UI:**
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ ← Scan for Sensors │
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 📡 Scanning... │
|
||||||
|
│ │
|
||||||
|
│ WP_497_81a14c │
|
||||||
|
│ Signal: ████░░ -55dBm │
|
||||||
|
│ [Connect] │
|
||||||
|
│ │
|
||||||
|
│ WP_523_81aad4 │
|
||||||
|
│ Signal: ███░░░ -67dBm │
|
||||||
|
│ [Connect] │
|
||||||
|
│ │
|
||||||
|
│ [Rescan] │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Алгоритм сканирования:**
|
||||||
|
```typescript
|
||||||
|
async function scanForWPDevices() {
|
||||||
|
const manager = new BleManager();
|
||||||
|
|
||||||
|
// Сканируем 10 секунд
|
||||||
|
manager.startDeviceScan(
|
||||||
|
null, // serviceUUIDs (можем фильтровать по service UUID)
|
||||||
|
null, // options
|
||||||
|
(error, device) => {
|
||||||
|
if (error) {
|
||||||
|
// Handle error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Фильтруем только WP_ устройства
|
||||||
|
if (device.name?.startsWith('WP_')) {
|
||||||
|
// Добавляем в список
|
||||||
|
// Сортируем по RSSI (ближе = выше значение)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Через 10 секунд останавливаем
|
||||||
|
setTimeout(() => manager.stopDeviceScan(), 10000);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Экран 3: Setup WiFi (новый `setup-wifi.tsx`)
|
||||||
|
|
||||||
|
**Путь:** `app/(tabs)/beneficiaries/[id]/setup-wifi.tsx`
|
||||||
|
|
||||||
|
**Параметры:** `?deviceName=WP_497_81a14c&beneficiaryId=123`
|
||||||
|
|
||||||
|
**Функционал:**
|
||||||
|
|
||||||
|
**Шаг 1: Подключение к устройству**
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ ← Setup WP_497 │
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 📱 Connecting to │
|
||||||
|
│ WP_497_81a14c... │
|
||||||
|
│ │
|
||||||
|
│ ⏳ Please wait │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Шаг 2: Unlock (автоматически)**
|
||||||
|
- Отправляем `pin|7856`
|
||||||
|
- Ждём ответ `pin|ok`
|
||||||
|
|
||||||
|
**Шаг 3: Получение списка WiFi**
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ ← Select WiFi Network │
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 📶 FrontierTower │
|
||||||
|
│ Signal: ████░ -55 │
|
||||||
|
│ [Select] │
|
||||||
|
│ │
|
||||||
|
│ 📶 HomeNetwork │
|
||||||
|
│ Signal: ███░░ -67 │
|
||||||
|
│ [Select] │
|
||||||
|
│ │
|
||||||
|
│ 📶 TP-Link_5G │
|
||||||
|
│ Signal: ██░░░ -75 │
|
||||||
|
│ [Select] │
|
||||||
|
│ │
|
||||||
|
│ [Rescan] │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Алгоритм:**
|
||||||
|
- Отправляем команду `w`
|
||||||
|
- Ждём ответ: `mac,XXXXXX|w|3|SSID1,RSSI1|SSID2,RSSI2|SSID3,RSSI3`
|
||||||
|
- Парсим список
|
||||||
|
- Сортируем по RSSI (сильный сигнал первым)
|
||||||
|
|
||||||
|
**Шаг 4: Ввод пароля**
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ ← WiFi Password │
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Network: FrontierTower │
|
||||||
|
│ │
|
||||||
|
│ Password: │
|
||||||
|
│ [**************] │
|
||||||
|
│ [ ] Show password │
|
||||||
|
│ │
|
||||||
|
│ [Connect] │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Шаг 5: Настройка WiFi**
|
||||||
|
- Отправляем `W|FrontierTower,password123`
|
||||||
|
- Ждём ответ `mac,XXXXXX|W|ok` или `mac,XXXXXX|W|fail`
|
||||||
|
- Если `ok` → ждём 5 секунд → проверяем командой `a`
|
||||||
|
|
||||||
|
**Шаг 6: Проверка подключения**
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ ✅ Connected! │
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Network: FrontierTower │
|
||||||
|
│ Signal: ████░ -67 dBm │
|
||||||
|
│ │
|
||||||
|
│ Device is now online │
|
||||||
|
│ and sending data │
|
||||||
|
│ │
|
||||||
|
│ [Continue] │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Экран 4: Device Settings (новый `device-settings.tsx`)
|
||||||
|
|
||||||
|
**Путь:** `app/(tabs)/beneficiaries/[id]/device-settings/[deviceId].tsx`
|
||||||
|
|
||||||
|
**Функционал:**
|
||||||
|
- ✅ Показать текущую WiFi (команда `a`)
|
||||||
|
- ✅ Изменить WiFi → Setup WiFi Screen
|
||||||
|
- ✅ Перезагрузить устройство (команда `s`)
|
||||||
|
- ✅ Отвязать от beneficiary (Detach)
|
||||||
|
- ✅ Проверить статус онлайн/офлайн
|
||||||
|
|
||||||
|
**UI:**
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ ← Device Settings │
|
||||||
|
│ WP_497_81a14c │
|
||||||
|
├─────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Status │
|
||||||
|
│ 🟢 Online │
|
||||||
|
│ Last seen: 2 min ago │
|
||||||
|
│ │
|
||||||
|
│ WiFi Network │
|
||||||
|
│ 📶 FrontierTower │
|
||||||
|
│ Signal: ████░ -67 dBm │
|
||||||
|
│ [Change WiFi] │
|
||||||
|
│ │
|
||||||
|
│ Device Info │
|
||||||
|
│ MAC: 142B2F81A14C │
|
||||||
|
│ Well ID: 497 │
|
||||||
|
│ │
|
||||||
|
│ Actions │
|
||||||
|
│ [🔄 Reboot Device] │
|
||||||
|
│ [🔗 Detach Device] │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 API Integration
|
||||||
|
|
||||||
|
### Legacy API Endpoints
|
||||||
|
|
||||||
|
**1. Получить список устройств deployment:**
|
||||||
|
```typescript
|
||||||
|
POST https://eluxnetworks.net/function/well-api/api
|
||||||
|
{
|
||||||
|
function: 'device_list_by_deployment',
|
||||||
|
user_name: 'USER',
|
||||||
|
token: 'TOKEN',
|
||||||
|
deployment_id: 70,
|
||||||
|
first: 0,
|
||||||
|
last: 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Проверить онлайн статус (batch):**
|
||||||
|
```typescript
|
||||||
|
POST https://eluxnetworks.net/function/well-api/api
|
||||||
|
{
|
||||||
|
function: 'request_devices',
|
||||||
|
user_name: 'USER',
|
||||||
|
token: 'TOKEN',
|
||||||
|
deployment_id: 70,
|
||||||
|
group_id: 'All',
|
||||||
|
location: 'All',
|
||||||
|
fresh: true // ← Только онлайн устройства
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Привязать устройство к deployment:**
|
||||||
|
```typescript
|
||||||
|
POST https://eluxnetworks.net/function/well-api/api
|
||||||
|
{
|
||||||
|
function: 'set_deployment',
|
||||||
|
// ... все параметры из BLE_PROTOCOL.md
|
||||||
|
devices: [497, 523], // well_ids
|
||||||
|
wifis: ["FrontierTower|password123"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### PostgreSQL
|
||||||
|
|
||||||
|
**Получить deployment_id для beneficiary:**
|
||||||
|
```sql
|
||||||
|
SELECT d.deployment_id, pd.access_to_deployments
|
||||||
|
FROM person_details pd
|
||||||
|
JOIN deployments d ON d.deployment_id = ANY(string_to_array(pd.access_to_deployments, ',')::int[])
|
||||||
|
WHERE pd.user_id = $1;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Компоненты
|
||||||
|
|
||||||
|
### 1. DeviceCard Component
|
||||||
|
|
||||||
|
Переиспользуемая карточка устройства:
|
||||||
|
```tsx
|
||||||
|
<DeviceCard
|
||||||
|
device={sensor}
|
||||||
|
onPress={() => router.push(`/device-settings/${sensor.deviceId}`)}
|
||||||
|
onDetach={() => handleDetach(sensor)}
|
||||||
|
showWiFi={true}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. WiFiNetworkItem Component
|
||||||
|
|
||||||
|
Элемент списка WiFi сетей:
|
||||||
|
```tsx
|
||||||
|
<WiFiNetworkItem
|
||||||
|
ssid="FrontierTower"
|
||||||
|
rssi={-67}
|
||||||
|
onSelect={() => handleSelectNetwork('FrontierTower')}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. BLEScanner Component
|
||||||
|
|
||||||
|
Компонент сканирования:
|
||||||
|
```tsx
|
||||||
|
<BLEScanner
|
||||||
|
isScanning={scanning}
|
||||||
|
devices={foundDevices}
|
||||||
|
onDeviceSelect={(device) => handleConnect(device)}
|
||||||
|
onRescan={() => startScan()}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. SimulatorWarning Component
|
||||||
|
|
||||||
|
Предупреждение для Simulator:
|
||||||
|
```tsx
|
||||||
|
{!Device.isDevice && (
|
||||||
|
<SimulatorWarning
|
||||||
|
message="Bluetooth is not available in iOS Simulator. Please test on a real device."
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Permissions
|
||||||
|
|
||||||
|
### iOS (Info.plist через app.json)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"ios": {
|
||||||
|
"infoPlist": {
|
||||||
|
"NSBluetoothAlwaysUsageDescription": "WellNuo needs Bluetooth to connect to your wellness sensors",
|
||||||
|
"NSBluetoothPeripheralUsageDescription": "WellNuo needs Bluetooth to manage your sensors"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Android (AndroidManifest.xml через app.json)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"android": {
|
||||||
|
"permissions": [
|
||||||
|
"android.permission.BLUETOOTH",
|
||||||
|
"android.permission.BLUETOOTH_ADMIN",
|
||||||
|
"android.permission.BLUETOOTH_CONNECT",
|
||||||
|
"android.permission.BLUETOOTH_SCAN",
|
||||||
|
"android.permission.ACCESS_FINE_LOCATION"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📱 Mock Data для Simulator
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// services/ble/MockBLEManager.ts
|
||||||
|
|
||||||
|
export class MockBLEManager implements IBLEManager {
|
||||||
|
async scanDevices(): Promise<WPDevice[]> {
|
||||||
|
// Возвращаем фейковые WP устройства
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'mock-1',
|
||||||
|
name: 'WP_497_81a14c',
|
||||||
|
rssi: -55,
|
||||||
|
mac: '142B2F81A14C'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'mock-2',
|
||||||
|
name: 'WP_523_81aad4',
|
||||||
|
rssi: -67,
|
||||||
|
mac: '142B2F81AAD4'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect(deviceId: string): Promise<boolean> {
|
||||||
|
// Симулируем задержку
|
||||||
|
await delay(1000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getWiFiList(): Promise<WiFiNetwork[]> {
|
||||||
|
return [
|
||||||
|
{ ssid: 'FrontierTower', rssi: -55 },
|
||||||
|
{ ssid: 'HomeNetwork', rssi: -67 },
|
||||||
|
{ ssid: 'TP-Link_5G', rssi: -75 }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async setWiFi(ssid: string, password: string): Promise<boolean> {
|
||||||
|
await delay(2000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCurrentWiFi(): Promise<{ ssid: string; rssi: number }> {
|
||||||
|
return { ssid: 'FrontierTower', rssi: -67 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Тестирование
|
||||||
|
|
||||||
|
### 1. На iOS Simulator
|
||||||
|
- ❌ BLE не работает
|
||||||
|
- ✅ Показываем mock данные
|
||||||
|
- ✅ Проверяем UI/UX флоу
|
||||||
|
|
||||||
|
### 2. На реальном iPhone
|
||||||
|
- ✅ Полный BLE функционал
|
||||||
|
- ✅ Подключение к WP устройствам
|
||||||
|
- ✅ Настройка WiFi
|
||||||
|
|
||||||
|
### 3. Android Emulator
|
||||||
|
- ⚠️ Может работать с пробросом Bluetooth
|
||||||
|
- Не рекомендуется для основной разработки
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 План Реализации (По Шагам)
|
||||||
|
|
||||||
|
### Фаза 1: Инфраструктура (2-3 часа)
|
||||||
|
1. ✅ Установить `react-native-ble-plx`
|
||||||
|
2. ✅ Настроить permissions (iOS + Android)
|
||||||
|
3. ✅ Создать `services/ble/BLEManager.ts`
|
||||||
|
4. ✅ Создать `services/ble/MockBLEManager.ts`
|
||||||
|
5. ✅ Создать `contexts/BLEContext.tsx`
|
||||||
|
6. ✅ Добавить проверку `Device.isDevice`
|
||||||
|
|
||||||
|
### Фаза 2: API Integration (1-2 часа)
|
||||||
|
1. ✅ Добавить методы в `services/api.ts`:
|
||||||
|
- `getDevicesForBeneficiary()`
|
||||||
|
- `getDeviceStatus()`
|
||||||
|
- `attachDeviceToDeployment()`
|
||||||
|
2. ✅ Интегрировать Legacy API endpoints
|
||||||
|
3. ✅ Добавить PostgreSQL queries
|
||||||
|
|
||||||
|
### Фаза 3: Equipment Screen (1-2 часа)
|
||||||
|
1. ✅ Заменить mock данные на реальные API calls
|
||||||
|
2. ✅ Добавить BLE функционал для getCurrentWiFi
|
||||||
|
3. ✅ Обновить UI для WP сенсоров
|
||||||
|
4. ✅ Добавить SimulatorWarning
|
||||||
|
|
||||||
|
### Фаза 4: Add Sensor Screen (2-3 часа)
|
||||||
|
1. ✅ Создать новый экран `add-sensor.tsx`
|
||||||
|
2. ✅ Реализовать BLE сканирование
|
||||||
|
3. ✅ Показать список устройств с RSSI
|
||||||
|
4. ✅ Добавить кнопку подключения
|
||||||
|
|
||||||
|
### Фаза 5: Setup WiFi Screen (3-4 часа)
|
||||||
|
1. ✅ Создать экран `setup-wifi.tsx`
|
||||||
|
2. ✅ Реализовать мульти-шаговый флоу:
|
||||||
|
- Connect → Unlock → Get WiFi List → Enter Password → Set WiFi → Verify
|
||||||
|
3. ✅ Обработка ошибок
|
||||||
|
4. ✅ Привязка к deployment через API
|
||||||
|
|
||||||
|
### Фаза 6: Device Settings Screen (1-2 часа)
|
||||||
|
1. ✅ Создать экран `device-settings.tsx`
|
||||||
|
2. ✅ Показать текущую WiFi
|
||||||
|
3. ✅ Кнопки: Change WiFi, Reboot, Detach
|
||||||
|
4. ✅ Статус онлайн/офлайн
|
||||||
|
|
||||||
|
### Фаза 7: Тестирование (2-3 часа)
|
||||||
|
1. ✅ Тест на Simulator (mock данные)
|
||||||
|
2. ✅ Тест на реальном iPhone с WP устройствами
|
||||||
|
3. ✅ Проверить все флоу
|
||||||
|
4. ✅ Обработка edge cases
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 Edge Cases
|
||||||
|
|
||||||
|
### 1. Bluetooth выключен
|
||||||
|
```tsx
|
||||||
|
if (!isBluetoothEnabled) {
|
||||||
|
Alert.alert(
|
||||||
|
'Bluetooth Disabled',
|
||||||
|
'Please enable Bluetooth to connect to sensors',
|
||||||
|
[
|
||||||
|
{ text: 'Open Settings', onPress: () => Linking.openSettings() },
|
||||||
|
{ text: 'Cancel' }
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Нет permission
|
||||||
|
```tsx
|
||||||
|
const status = await manager.requestPermissions();
|
||||||
|
if (status !== 'granted') {
|
||||||
|
// Показать инструкцию
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Устройство не найдено
|
||||||
|
- Показать "No devices found"
|
||||||
|
- Кнопка Rescan
|
||||||
|
- Проверить что устройство включено
|
||||||
|
|
||||||
|
### 4. Не подключается к WiFi
|
||||||
|
- Показать ошибку от устройства
|
||||||
|
- Предложить повторить
|
||||||
|
- Проверить правильность пароля
|
||||||
|
|
||||||
|
### 5. Устройство оффлайн
|
||||||
|
- Показать серым
|
||||||
|
- Не пытаться подключаться по BLE
|
||||||
|
- Показать Last seen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Итоговый User Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Юзер открывает Equipment Screen
|
||||||
|
→ Видит список WP сенсоров (если есть)
|
||||||
|
→ Статус: онлайн/оффлайн (из API)
|
||||||
|
→ Текущая WiFi (из BLE)
|
||||||
|
|
||||||
|
2. Юзер нажимает "Add Sensor"
|
||||||
|
→ Открывается Add Sensor Screen
|
||||||
|
→ Сканирование BLE (10 сек)
|
||||||
|
→ Список найденных WP_* устройств
|
||||||
|
→ Сортировка по близости (RSSI)
|
||||||
|
|
||||||
|
3. Юзер выбирает устройство
|
||||||
|
→ Подключение по BLE
|
||||||
|
→ Автоматический unlock (pin|7856)
|
||||||
|
→ Получение списка WiFi сетей
|
||||||
|
→ Показ списка с сигналом
|
||||||
|
|
||||||
|
4. Юзер выбирает WiFi сеть
|
||||||
|
→ Ввод пароля
|
||||||
|
→ Отправка credentials (W|SSID,PASS)
|
||||||
|
→ Ожидание подключения (5 сек)
|
||||||
|
→ Проверка (команда a)
|
||||||
|
|
||||||
|
5. Success!
|
||||||
|
→ Устройство подключено к WiFi
|
||||||
|
→ Привязка к beneficiary через API
|
||||||
|
→ Возврат на Equipment Screen
|
||||||
|
→ Устройство появляется в списке как "online"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Changelog
|
||||||
|
|
||||||
|
- **2026-01-14** - Создан план реализации функционала сенсоров
|
||||||
|
- Определена архитектура BLE управления
|
||||||
|
- Спроектированы все экраны
|
||||||
|
- Решена проблема с Simulator (mock данные)
|
||||||
Loading…
x
Reference in New Issue
Block a user