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:
Sergei 2026-01-14 19:08:19 -08:00
parent 5092678430
commit 2b68b70584
2 changed files with 1292 additions and 0 deletions

599
docs/BLE_PROTOCOL.md Normal file
View 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

View 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 данные)