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

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

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

20 KiB
Raw Blame History

WellNuo Sensors - Implementation Plan

🎯 Цель

Заменить мок-данные в экране equipment.tsx на реальное управление WP сенсорами через Bluetooth Low Energy (BLE).


⚠️ КРИТИЧНО: Bluetooth в iOS Simulator

iOS Simulator НЕ ПОДДЕРЖИВАЕТ Bluetooth!

Решение:

  • На реальном устройстве: полный BLE функционал
  • В Simulator: показываем mock-данные + предупреждение

Проверка доступности BLE:

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 типы

Установка:

npx expo install react-native-ble-plx

Конфигурация app.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/)

// 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

Данные:

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 для получения списка:

// 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]               │
│                         │
└─────────────────────────┘

Алгоритм сканирования:

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:

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):

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:

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:

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

Переиспользуемая карточка устройства:

<DeviceCard
  device={sensor}
  onPress={() => router.push(`/device-settings/${sensor.deviceId}`)}
  onDetach={() => handleDetach(sensor)}
  showWiFi={true}
/>

2. WiFiNetworkItem Component

Элемент списка WiFi сетей:

<WiFiNetworkItem
  ssid="FrontierTower"
  rssi={-67}
  onSelect={() => handleSelectNetwork('FrontierTower')}
/>

3. BLEScanner Component

Компонент сканирования:

<BLEScanner
  isScanning={scanning}
  devices={foundDevices}
  onDeviceSelect={(device) => handleConnect(device)}
  onRescan={() => startScan()}
/>

4. SimulatorWarning Component

Предупреждение для Simulator:

{!Device.isDevice && (
  <SimulatorWarning
    message="Bluetooth is not available in iOS Simulator. Please test on a real device."
  />
)}

🔐 Permissions

iOS (Info.plist через app.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)

{
  "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

// 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 выключен

if (!isBluetoothEnabled) {
  Alert.alert(
    'Bluetooth Disabled',
    'Please enable Bluetooth to connect to sensors',
    [
      { text: 'Open Settings', onPress: () => Linking.openSettings() },
      { text: 'Cancel' }
    ]
  );
}

2. Нет permission

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