From 2b68b70584b268aa989db82993468f84b89cc82c Mon Sep 17 00:00:00 2001 From: Sergei Date: Wed, 14 Jan 2026 19:08:19 -0800 Subject: [PATCH] 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 --- docs/BLE_PROTOCOL.md | 599 ++++++++++++++++++++++++ docs/SENSORS_IMPLEMENTATION_PLAN.md | 693 ++++++++++++++++++++++++++++ 2 files changed, 1292 insertions(+) create mode 100644 docs/BLE_PROTOCOL.md create mode 100644 docs/SENSORS_IMPLEMENTATION_PLAN.md diff --git a/docs/BLE_PROTOCOL.md b/docs/BLE_PROTOCOL.md new file mode 100644 index 0000000..b274a53 --- /dev/null +++ b/docs/BLE_PROTOCOL.md @@ -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 diff --git a/docs/SENSORS_IMPLEMENTATION_PLAN.md b/docs/SENSORS_IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..63a83e3 --- /dev/null +++ b/docs/SENSORS_IMPLEMENTATION_PLAN.md @@ -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 { + // 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 + router.push(`/device-settings/${sensor.deviceId}`)} + onDetach={() => handleDetach(sensor)} + showWiFi={true} +/> +``` + +### 2. WiFiNetworkItem Component + +Элемент списка WiFi сетей: +```tsx + handleSelectNetwork('FrontierTower')} +/> +``` + +### 3. BLEScanner Component + +Компонент сканирования: +```tsx + handleConnect(device)} + onRescan={() => startScan()} +/> +``` + +### 4. SimulatorWarning Component + +Предупреждение для Simulator: +```tsx +{!Device.isDevice && ( + +)} +``` + +--- + +## 🔐 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 { + // Возвращаем фейковые 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 { + // Симулируем задержку + await delay(1000); + return true; + } + + async getWiFiList(): Promise { + return [ + { ssid: 'FrontierTower', rssi: -55 }, + { ssid: 'HomeNetwork', rssi: -67 }, + { ssid: 'TP-Link_5G', rssi: -75 } + ]; + } + + async setWiFi(ssid: string, password: string): Promise { + 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 данные)