# PRD — Deployment + Sensors Integration (v2) ## Цель Обеспечить полную интеграцию: при создании beneficiary автоматически создаётся deployment на Legacy API, к которому затем привязываются BLE сенсоры. Использовать credentials `robster/rob2` (больше прав). Добавить Dropdown для выбора комнаты сенсора. ## Текущее состояние (уже работает!) ### ✅ Что УЖЕ реализовано: 1. **Deployment создаётся автоматически** при создании beneficiary (`beneficiaries.js:445-501`) 2. **Legacy API integration** полностью работает (`legacyAPI.js`) 3. **BLE сенсоры** подключаются и настраиваются (`PRD-SENSORS.md` — все задачи выполнены) 4. **Device Settings** есть поля location/description (TextInput) ### ❌ Что НЕ работает: 1. Credentials `anandk` имеют ограниченные права → нужен `robster/rob2` 2. Location вводится текстом → нужен Dropdown с комнатами 3. `updateDeviceMetadata` отправляет строку вместо числового кода --- ## User Flow ### Flow 1: Создание Beneficiary + Deployment (УЖЕ РАБОТАЕТ) | # | Актор | Действие | Система | Результат | |---|-------|----------|---------|-----------| | 1 | User | Заполняет форму "Add Loved One" | — | Вводит имя, адрес | | 2 | User | Нажимает "Continue" | POST `/me/beneficiaries` | — | | 3 | Backend | Создаёт beneficiary в PostgreSQL | INSERT `beneficiaries` | beneficiary_id | | 4 | Backend | Создаёт deployment в PostgreSQL | INSERT `beneficiary_deployments` | deployment.id | | 5 | Backend | Авторизуется на Legacy API | `legacyAPI.getLegacyToken()` | legacy_token | | 6 | Backend | Создаёт deployment на Legacy | `legacyAPI.createLegacyDeployment()` | legacy_deployment_id | | 7 | Backend | Сохраняет legacy_deployment_id | UPDATE `beneficiary_deployments` | Связь установлена | | 8 | User | Переходит к purchase/demo | — | Deployment готов | **Статус:** ✅ Полностью реализовано в `beneficiaries.js:419-501` ### Flow 2: Настройка устройства с Dropdown | # | Актор | Действие | Система | Результат | |---|-------|----------|---------|-----------| | 1 | User | Открывает Device Settings | GET devices | Текущие данные | | 2 | User | Видит Dropdown "Location" | — | Показывает текущую комнату | | 3 | User | Выбирает комнату из списка | — | "Bedroom", "Kitchen", etc. | | 4 | User | Вводит description (опционально) | — | Свободный текст | | 5 | User | Нажимает "Save" | POST `device_form` | location=102 | | 6 | System | Сохраняет на Legacy API | — | Обновлено | --- ## Задачи ### Backend (Простые) - [x] **1. Обновить Legacy API credentials** - Путь: `backend/.env` - Изменить: ``` LEGACY_API_USERNAME=robster LEGACY_API_PASSWORD=rob2 ``` - Задеплоить: `scp .env root@91.98.205.156:/var/www/wellnuo-api/.env` - Перезапустить: `ssh root@91.98.205.156 "pm2 restart wellnuo-api"` ### Frontend (Основная работа) - [x] **2. Добавить константы ROOM_LOCATIONS в api.ts** - Путь: `services/api.ts` - Добавить в начало файла: ```typescript // Room location codes for Legacy API export const ROOM_LOCATIONS: Record = { 'Bedroom': 102, 'Living Room': 103, 'Kitchen': 104, 'Bathroom': 105, 'Hallway': 106, 'Office': 107, 'Garage': 108, 'Dining Room': 109, 'Basement': 110, 'Other': 200 }; export const LOCATION_NAMES: Record = Object.fromEntries( Object.entries(ROOM_LOCATIONS).map(([k, v]) => [v, k]) ); ``` - [x] **3. Исправить updateDeviceMetadata для location codes** - Путь: `services/api.ts` (строка ~1783) - Изменить: ```typescript // БЫЛО: formData.append('location', updates.location); // СТАЛО: if (updates.location !== undefined) { // Convert room name to location code const locationCode = ROOM_LOCATIONS[updates.location] || ROOM_LOCATIONS['Other']; formData.append('location', locationCode.toString()); } ``` - [x] **4. Device Settings: заменить TextInput на Picker** - Путь: `app/(tabs)/beneficiaries/[id]/device-settings/[deviceId].tsx` - Импорт: `import { Picker } from '@react-native-picker/picker'` - Или использовать: `@react-native-community/picker` / кастомный ActionSheet **Заменить (строки 349-358):** ```tsx // БЫЛО: // СТАЛО: setLocation(value)} style={styles.picker} > ``` - [x] **5. Конвертировать location code → name при загрузке** - Путь: `app/(tabs)/beneficiaries/[id]/device-settings/[deviceId].tsx` - В `loadSensorInfo()` добавить конвертацию: ```typescript import { LOCATION_NAMES } from '@/services/api'; // При получении sensor: const locationName = sensor.location ? (LOCATION_NAMES[parseInt(sensor.location)] || sensor.location) : ''; setLocation(locationName); ``` - [x] **6. Добавить стили для Picker** - Путь: тот же файл - Добавить в StyleSheet: ```typescript pickerContainer: { backgroundColor: AppColors.background, borderRadius: BorderRadius.md, borderWidth: 1, borderColor: AppColors.border, overflow: 'hidden', }, picker: { height: 50, color: AppColors.textPrimary, }, ``` - [x] **7. Установить @react-native-picker/picker** - Команда: `npx expo install @react-native-picker/picker` --- ## Справочник: Location Codes (из legacyAPI.js) | Код | Название | Описание | |-----|----------|----------| | 102 | Bedroom | Спальня | | 103 | Living Room | Гостиная | | 104 | Kitchen | Кухня | | 105 | Bathroom | Ванная | | 106 | Hallway | Коридор | | 107 | Office | Кабинет | | 108 | Garage | Гараж | | 109 | Dining Room | Столовая | | 110 | Basement | Подвал | | 200 | Other | Другое | --- ## Вне scope - Синхронизация location с голосовым AI (Julia) — отдельная задача - WellNuo Lite интеграция — пока не трогаем - Редактирование deployment после создания — пока не нужно - Front Door (101) — нет в текущем mapping, не добавляем --- ## Чеклист верификации ### Backend - [x] Credentials обновлены на `robster/rob2` в .env - [x] PM2 перезапущен - [x] Тест: создать beneficiary → в логах видно "Created Legacy deployment: XXX" ### Frontend - [x] Device Settings показывает Picker/Dropdown вместо TextInput для location - [x] Picker содержит все 10 комнат - [x] При выборе комнаты — сохраняется location_code (число) на Legacy API - [x] При загрузке — location_code конвертируется в название - [x] Description остаётся TextInput - [x] Сохранение работает без ошибок ### End-to-End Flow - [x] Создать beneficiary → deployment создан на Legacy API - [x] Подключить BLE сенсор → привязан к deployment - [x] Открыть Device Settings → видно Dropdown - [x] Выбрать "Kitchen" → Save → проверить в Legacy API что location=104 - [x] Перезагрузить экран → показывает "Kitchen" --- ## Риски и Edge Cases 1. **Picker на Android vs iOS** — выглядит по-разному, возможно нужен ActionSheet 2. **Старые данные** — если location уже сохранён как текст "kitchen", не найдётся в LOCATION_NAMES 3. **Нет интернета** — Legacy API недоступен, нужен graceful error --- ## Порядок выполнения 1. ✅ Backend: обновить credentials (5 мин) 2. 🔨 Frontend: добавить константы ROOM_LOCATIONS (5 мин) 3. 🔨 Frontend: исправить updateDeviceMetadata (10 мин) 4. 🔨 Frontend: установить Picker пакет (2 мин) 5. 🔨 Frontend: заменить TextInput на Picker (20 мин) 6. 🔨 Frontend: конвертация code↔name (15 мин) 7. ✅ Тестирование E2E (15 мин) **Общее время: ~1.5 часа** --- **Минимальный проходной балл: 8/10**