Тест-план Entrixy/Dialer v6.66

Цель: пройти сценарии, отметить результат, скинуть отчёт. Прогресс автоматически сохраняется в браузере (localStorage). Если перешёл на другой телефон / закрыл страницу — открой снова, всё на месте.

Цветовая маркировка ролей в тестах: HOST — телефон-хозяин (привязан ESP, владелец ключей) GUEST — гостевой телефон (получил ключ от хозяина) 3RD — третий телефон (для cross-device проверок, Huawei если есть)
0 / 0 / всего 0 | prod-critical: 0/0
🔒 Прод-критичных тестов: не пройдено. До запуска нужно закрыть ВСЕ помеченные значком 🔒.

Подготовка перед стартом:

  1. На сервере: sudo supervisorctl restart dialer-ws (без этого rate-limit/dedup не работают).
  2. На обоих телефонах установлена 6.66 (или выше).
  3. ESP запитан и в зоне досягаемости.
  4. WiFi и моб.сеть на телефонах включены.

0. Установка и старт

S1Установка 6.66 на HOSTHOST
Скачать APK с entrixy.com и установить поверх любой предыдущей версии
Приложение запускается, не падает на старте. В about/настройках версия 6.66.
S2Установка 6.66 на GUESTGUEST
То же что S1, на гостевом телефоне
То же что S1
S3Установка на Huawei3RD
Скачать через браузер на Huawei, установить вручную
Установка проходит. Приложение запускается. (FCM не работает — это ОК на Huawei.)
S4Force-update диалогHOST
Откатиться на 6.55 (если есть APK) или 6.63 → запустить → дождаться versionCheck
Появляется блокирующий диалог «Доступна новая версия», кнопка «Скачать» открывает download.php → APK

1. Авто-старт после ребута

B1🔒 Авто-старт после reboot (HOST)HOST
Перезагрузить телефон. Подождать 5-10 мин. НЕ открывать приложение руками. Затем открыть и отправить логи.
В host.log есть строки BootReceiver: received ACTION_BOOT_COMPLETED + HostService.start returned ok
B2Гостевой ключ онлайн после rebootGUEST
Перезагрузить гостевой телефон. Подождать 5-10 мин. Хозяин в Гостях должен увидеть гостя «в сети».
В течение ~10 мин гость становится онлайн без открытия приложения
B3BootReceiver на Huawei3RD
Перезагрузить Huawei. Проверить как B1.
Срабатывает или не срабатывает (Huawei режет фоновые сервисы агрессивно) — зафиксировать факт.

2. Ключи и обмен

K1Регистрация хозяинаHOST
Установка → первый запуск → выбрать «Я хозяин»
Получает device_id, появляется главный экран с пустым списком
K2Создать гостевой ключHOST
Гости → «+» → ввести имя, выбрать объекты, лимиты → Сохранить
Ключ появляется в списке, доступен QR/ссылка для передачи
K3🔒 Принять ключ через QRGUEST
На госте: «+» → сканировать QR с хозяйского телефона
Welcome-диалог с приветствием от хозяина → подтвердить → ключ добавлен → карточки объектов появились
K4Принять ключ через deeplinkGUEST
Хозяин — скопировать ссылку. Послать гостю любым мессенджером. Гость тапает.
Открывается приложение → welcome → ключ принят
K5Несколько ключей у одного гостяGUEST
Хозяин создаёт второй ключ. Гость принимает (см. K3 или K4).
У гостя 2 секции карточек, каждая с label своего хозяина (если разные хосты — у нас один)
K6Редактирование гостевого ключаHOST
Гости → выбрать ключ → изменить label / поменять список объектов → Сохранить
На стороне гостя в течение нескольких секунд карточки обновляются (без перезапуска приложения)
K7🔒 Отзыв гостевого ключаHOST
Гости → выбрать ключ → Отозвать
На стороне гостя: «доступ отозван», карточки исчезают
K8Удаление гостевого ключа гостемGUEST
Гость → свайп карточки секции (или меню) → Удалить ключ
Карточки исчезают, ключ удалён

3. Объекты (номера, webhook, devices)

O1Добавить «телефонный номер»HOST
Настройки → + → Телефон → ввести номер + label
Карточка появляется. Тап → реальный звонок (только если разрешено разрешение CALL_PHONE)
O2Добавить webhook-объектHOST
+ → URL → ввести метод/URL/headers/body
Карточка появляется. Тап → HTTP-запрос уходит (проверить на сервере)
O3Удалить объектHOST
Длинный тап / меню → Удалить
Объект исчезает. Не падает.
O4Переименовать объектHOST
Тап → Редактировать → новое имя
Имя сохраняется и на хозяине, и у гостя (через bundle update)
O5Аватар объекта (preset)HOST
Тап на аватаре → выбрать пресет (цветной кружок)
Применяется. Реплицируется гостю.
O6Аватар объекта (фото)HOST
Тап на аватаре → загрузить фото
Применяется. Реплицируется гостю.

4. BLE pairing

P1🔒 Pair с ESP (первый раз)HOST
Настройки → + → BLE → следовать инструкциям производителя (нажать на ESP кнопку pair / включить)
Через ~10-30с ESP виден, привязан. Появляется BLE-карточка.
P2Тап → fire (ручной)HOST
Тап по BLE-карточке
ESP получает FIRE, реле дёргается, статус «Сработало», в журнале запись
P3Поделиться BLE-объектом с гостемHOST
Гости → выбрать ключ → выбрать BLE-объект для шаринга → Сохранить
У гостя в карточках появляется этот BLE-объект (через bundle)
P4🔒 Тап → fire от гостяGUEST
У гостя тап по BLE-карточке
ESP получает FIRE, у хозяина в журнале запись «Гость: X открыл Y»
P5Удалить BLE-объектHOST
Тап BLE-карточка → Удалить
Удаляется. У гостя через bundle тоже исчезает.

5. BLE auto-fire (хозяин)

AH1Подход → оранжевыйHOST
Включить BLE-правило в автоматизации. Отойти >30 м. Постепенно подойти.
Карточка серая → оранжевая (когда ESP слышен, RSSI ниже порога)
AH2🔒 В зону → зелёный → fireHOST
Подойти близко (RSSI ≥ порог)
Мелькает зелёным → auto-fire → серая с зелёной обводкой + «Нажмите для вызова»
AH3🔒 Постоять — нет refireHOST
Постоять у ESP 60+ секунд (sleep_s=10 → 2+ advertise цикла)
Auto-fire не повторяется. Карточка остаётся серая+обводка непрерывно.
AH4Выход → серыйHOST
Отойти от ESP за пределы радио-захвата
Оранжевый → серый
AH5🔒 Возврат → fireHOST
Вернуться к ESP
Серый → оранжевый → зелёный → fire → серый+обводка
AH6ESP off → on (deliberate)HOST
Не двигаясь, выключить ESP. Подождать ~2 мин. Включить.
Карточка становится оранжевой → зелёной → fire. (Deliberate threshold отработал.)
AH7ESP короткий ребут (sleep-cycle)HOST
Не двигаясь, выключить ESP. Через 10 секунд включить.
Auto-fire НЕ повторяется (это короткий gap, считается sleep-циклом). Карточка остаётся серая+обводка.
AH8Cold-start в зонеHOST
Стоя рядом с ESP, через настройки убить HostService → перезапустить (или принудительно остановить и открыть приложение)
Сразу auto-fire НЕ происходит. Карточка серая+обводка ИЛИ ничего. Только после выхода-захода — fire.

6. BLE auto-fire (гость)

AG1Подход → оранжевый (гость)GUEST
Включить BLE-правило (если разрешает хозяин), отойти, вернуться
Аналогично AH1: серая → оранжевая
AG2🔒 В зону → fire (гость)GUEST
Подойти впритык
Fire успешен. В журнале гостя: «Я» + label. У хозяина в журнале: «Гость: X»
AG3Refire-блок (гость)GUEST
Постоять у ESP минуту
Не повторяется
AG4Гость: выход-возврат → fireGUEST
Отойти, вернуться
Fire срабатывает
AG5Гостевой radius-ограничение от хозяинаGUEST
Хозяин в шаблоне ставит BLE force, radius=1м. Гость не может поставить больше.
У гостя слайдер ограничен 1м, не разрешает 5м

7. Гостевой token renewal

R1🔒 Истёкший токен → offline UIGUEST
Сделать чтобы токен истёк (поменять время на телефоне +25ч, потом обратно — или дождаться). Глянуть карточку BLE.
Карточка серая, КРАСНЫЙ кружок-индикатор, тап → отбивка с error звуком, не пытается fire
R2Failed events НЕ копятсяGUEST
При R1 — попробовать тапать карточку 5-10 раз
В журнале гостя ничего не появляется. У хозяина в журнале ничего не приходит.
R3Renewal — 1 запрос на флапGUEST
Hосуществляй на госте Wi-Fi off/on быстро 3-5 раз подряд
В host.log хозяина — максимум 1 строка recv: ble_token_renew в минуту (throttle 60с/bleId)
R4🔒 Renewal восстанавливает токенHOST + GUEST
После истёкшего токена (R1) — оба онлайн, гость отправил renew, хозяин подписал.
Через несколько секунд карточка гостя снова работает, тап → fire успешен.
R5Server dedup проверкаHOST
Гость флапает WS. Хозяин offline. Через час хозяин online.
В host.log хозяина — НЕ 1300 строк, а одна (dedup отработал)

8. Гео-автоматизация

G1Настроить гео-точкуHOST
Автоматизация → Гео → выбрать на карте + radius (например 30 м)
Сохраняется. Точка видна на карте.
G2Подход (300м буфер) → оранжевыйHOST
Подойти к точке от 100-300м
Карточка оранжевая (anyClose активна)
G3В green-зону → зелёный → fireHOST
Подойти на радиус (30м)
Зелёный мелькает → fire → серый + обводка
G4Выход → перезарядHOST
Уйти >300м от точки
Серый. Armed=true (готов к новому fire при возврате)
G5Cold-start guard (гео)HOST
Находясь в green-зоне, перезапустить приложение
Не стреляет сразу. В логах: SKIP (cold-start in zone)

9. WiFi-автоматизация

W1Добавить BSSIDHOST
Автоматизация → WiFi → Включить → «Добавить сеть» (выбрать из текущих)
Сеть в списке. Порог wifiMinMatch виден.
W2Match → fireHOST
Подключиться к этой WiFi-сети
Карточка зелёная → fire
W3Отключиться → rearmHOST
Отключиться от WiFi
Карточка серая, armed=true
W4WiFi force-share гостюGUEST
Хозяин в шаблоне ставит WiFi force. Гость видит политику.
У гостя WiFi-правило заблокировано на изменение, но видно

10. Time-window автоматизация

T1Окно текущим временемHOST
Создать окно с включением «сейчас» + 1 час
Карточка зелёная, fire срабатывает
T2Окно в будущемHOST
Создать окно которое начнётся через 1 минуту
Карточка серая → оранжевая (countdown 5 мин) → зелёная → fire
T3Day-of-week фильтрHOST
Окно только в будни. Сегодня будний?
Работает в выбранные дни, не работает в исключённые
T4Несколько окон в деньHOST
Два окна: утром и вечером
Оба работают, между ними карточка серая

11. Комбинированные правила

C1ИЛИ-режим: geo+wifiHOST
combineMode=any, оба правила активны. Выполнить только wifi (не быть в гео-зоне).
Fire срабатывает (любое правило)
C2И-режим: gео+wifi, не обаHOST
combineMode=all, оба правила. Только wifi выполнен.
Auto-fire НЕ срабатывает. Карточка серая (без обводки) или оранжевая если рядом.
C3И-режим: все правила выполненыHOST
combineMode=all, geo+wifi. Зайти в гео-зону + подключиться к WiFi.
Карточка серая + зелёная обводка + «Нажмите для вызова». Auto-fire НЕ срабатывает (И-режим требует тап). Ручной тап → fire.
C4BLE + geo в И-режимеHOST
BLE+гео combineMode=all. В гео-зоне с ESP далеко.
Серый. Подойти к ESP → обводка + «Нажмите». Тап → fire.

12. Auth/Confirm gating

AU1PIN setupHOST
Шаблон: Auth=PIN. Тап карточки.
Если PIN не задан → диалог setup. Установить → тап → диалог ввода PIN → fire после правильного.
AU2БиометрияHOST
Шаблон: Auth=biometric. Тап карточки.
Системный prompt отпечатка/Face. После успеха → fire.
AU3Confirm-диалогHOST
Шаблон: Confirm=true. Тап.
Диалог «Открыть?» — да → fire, отмена → нет fire.
AU4Auth от хозяина для гостяGUEST
Хозяин ставит PIN force. Гость видит политику.
У гостя тап требует PIN (но гость не знает PIN хозяина — должен ставить свой)

13. Звонки (phone-объекты)

CL1Гостевой тап → звонок у хозяинаGUEST
Гость → тап по карточке «телефон»
У хозяина срабатывает звонок (или notify попап если включён режим notify)
CL2Hangup timerHOST
Установить hangup=10. Гость зовёт.
Звонок прерывается через 10 секунд
CL3Phone busyHOST
Хозяин в разговоре. Гость зовёт.
Гость получает статус «занято», звонок не идёт (если forceWhenBusy=false)

14. Webhooks — режим «phone» (хозяин-хук)

Когда webhook_mode = "phone" — HTTP-запрос делает приложение на хозяйском телефоне. Сервер только триггерит. Требует хозяин в сети.

WHH1🔒 Phone-mode: тап хозяина → phone делает HTTPHOST
Создать webhook, режим «phone», URL = webhook.site. Тап на хозяине.
Запрос с IP телефона хозяина приходит. В журнале хозяина: запись об успехе.
WHH2🔒 Phone-mode: гость → server → host_self_webhook → host firesGUEST
Хозяин шарит phone-webhook гостю. Гость тапает на своей карточке.
Сервер шлёт хозяину host_self_webhook через WS. Хозяин делает HTTP. Webhook secret гостю не утекает. У хозяина в журнале «Гость: X».
WHH3Phone-mode: хозяин offline → гость не можетGUEST
Хозяин закрыл WS / выключил телефон. Гость тапает phone-webhook.
Статус «хозяин не в сети» или подобная ошибка. Webhook не уходит.
WHH4Phone-mode: custom headersHOST
Webhook с Authorization Bearer, X-API-Key, Content-Type. Тап.
Все headers пришли на целевой сервис
WHH5Phone-mode: GET / POST / PUTHOST
Проверить каждый HTTP-метод
Все три работают, body только для POST/PUT
WHH6Phone-mode: автозапуск по гео-правилуHOST
Phone-webhook + гео-правило → войти в зону
Авто-fire срабатывает, webhook уходит без тапа
WHH7Phone-mode: ошибка → статус не падаетHOST
URL = несуществующий сервер. Тап.
Статус «не удалось» в карточке. Приложение не крашится.
WHH8Phone-mode → server-mode переключениеHOST
В настройках webhook'а переключить mode с «phone» на «server»
Сохраняется, отправляется на сервер через number_update. У гостя через bundle тоже видно (если шарено).

14a. Webhooks — режим «server» (сервер-хук)

Когда webhook_mode = "server" — HTTP-запрос делает Workerman прямо с VPS. С HMAC-SHA256 подписью. Хозяин может быть offline.

WHS1🔒 Server-mode: тап хозяина → сервер делает HTTPHOST
Создать webhook режим «server», URL + webhook_secret. Тап.
Запрос приходит с IP VPS (не телефона). В private/wh.log запись. В журнале хозяина — успех.
WHS2🔒 Server-mode: хозяин offline, гость → сервер срабатываетGUEST
Хозяин закрыл приложение/WS. Гость тапает server-webhook.
Сервер сам идёт по URL, не дёргает хозяина. У целевого сервиса запрос принят. Гость видит «успех» (через device_status от сервера).
WHS3🔒 Server-mode: HMAC-подпись валиднаHOST
На целевом сервисе принять JSON, проверить hash_hmac('sha256', body_without_signature, secret) === signature
Подпись совпадает. Поле signature подмножество body. timestamp ± 60 сек от текущего.
WHS4Server-mode: 15с watchdog при висящем targetHOST
URL = сервис что не отвечает (например netcat без send). Тап.
Через 15 секунд сервер закрывает соединение. Гостю/хозяину — статус «webhook timeout».
WHS5Server-mode: HTTPS-цель (TLS)HOST
URL = https://api.somewhere.com/...
SSL handshake проходит, запрос идёт по TLS
WHS6Server-mode: автозапуск по гео-правилу хозяинаHOST
server-webhook + гео-правило хозяина → войти в зону
Срабатывает auto-fire, но HTTP делает сервер (тот же случай что и в WHH6 но без участия хозяина-Android в HTTP-вызове)
WHS7Server-mode: webhook_secret не утекает в bundle гостюGUEST
Поднять host_sync на гостевой стороне, посмотреть bundle для server-webhook'а
Полей webhook_url и webhook_secret у гостя НЕТ. Только обозначение «это server-webhook». Сервер сам знает где брать конфиг.
WHS8Server-mode: target вернул 5xxHOST
URL вернёт 500. Тап.
Сервер ловит, статус «webhook error 500» гостю/хозяину. Не зацикливание ретраев.

14b. Готовые интеграции

В /integrations/ есть готовые конфиги. Минимум по одному запросу на каждую.

IN1Home Assistant (server-mode webhook)HOST
Поднять HA, скопировать integrations/home-assistant/configuration.yaml. Создать webhook со ссылкой на свой HA. Тап.
В HA автоматизация триггерится, скрипт выполняется
IN2Shelly релеHOST
Setup из integrations/shelly/README.md. URL = http://shelly-ip/relay/0?turn=on
Shelly щёлкает
IN3TasmotaHOST
Setup из integrations/tasmota/README.md. URL = http://tasmota/cm?cmnd=Power%20Toggle
Реле Tasmota переключается
IN4ESPHome nativeHOST
Прошить ESP с integrations/esphome/entrixy-esphome.yaml
Альтернативный путь через ESPHome работает
IN5OpenHABHOST
Setup из integrations/openhab/. Триггер сценария.
Срабатывает
IN6curl test-scriptHOST
Запустить integrations/curl/test-webhook.sh
Скрипт принимает payload, проверяет signature, выводит OK
IN7Flask-test endpointHOST
Поднять integrations/flask-test/entrixy.py. URL → ваш Flask.
Flask принимает, валидирует HMAC, отдаёт 200
IN8Документация интеграций понятнаHOST
Прочитать каждый README в /integrations/*/README.md глазами незнакомого с проектом юзера
Понятно как настроить за 10 минут без вопросов

14c. Socket / WebSocket клиент

SK1🔒 WS поднимается на старте сервисаHOST
Свежий запуск HostService → host_hello → host_ok в логе
WS установился в течение 2-5 секунд. В host.log: ws onOpen status=101
SK2WS гостя (отдельное соединение)GUEST
У гостя на каждый ключ — своё WS. После принятия ключа должно подняться.
В host.log гостя: guest[N] ws onOpen ... + guest_ok
SK3WS ping/pong (heartbeat)HOST
Оставить приложение свёрнутым на 5+ минут
WS остаётся живым (сервер шлёт ping каждые 10с, клиент отвечает pong). Не реконнектится без необходимости.
SK4WS auto-reconnect после сетевого разрываHOST
Включить «авиарежим» 5с → выключить
WS автоматически реконнектится в течение 1-3с (если стабильная сессия > 60с)
SK5WS под нагрузкой клиентаHOST
Создать 10 BLE-объектов или 10 host-номеров. Перезапустить.
host_hello проходит, hostSync приносит все 10 объектов, не тормозит UI
SK6🔒 WS под Doze (фон)HOST
Свернуть приложение, экран выключить, 15+ минут не трогать. Включить.
WS должен подняться в течение 1-2 минут после выхода Doze. Не должен застрять offline.
SK7Switch WiFi → mobileHOST
Выключить WiFi на телефоне (включена моб.сеть)
WS падает на старом интерфейсе, переподнимается на новом в течение 5-15с
SK8WSS TLSHOST
Просто работа — WSS на entrixy.com по 443. Проверить в логе.
В логе: proto=h2 или http/1.1, TLS handshake без ошибок

14d. ESP32 совместимость (через конфигуратор)

E0Конфигуратор открываетсяHOST
Страница загружается. Доступны пресеты, форма параметров, кнопки «Скомпилировать» + «Прошить через браузер».
E1🔒 Pair с ESP32-S3 (свежий config)HOST
Конфигуратор → пресет «ESP32-S3 DevKit» → скомпилировать → прошить через WebSerial. Затем pair flow в приложении.
Сборка занимает ~30с (первый раз) или мгновенно (если кешировано). Pair проходит, advertise виден, fire работает.
E2Pair с обычным ESP32 (vanilla WROOM)HOST
Конфигуратор → пресет «ESP32 WROOM» → скомпилировать → прошить. Pair.
Та же логика как с S3 — конфигуратор компилит под нужный чип, BLE-протокол одинаковый.
E3Advertise format на обычной ESP32HOST
Проверить что advertise в эфире виден через BLE-сканер (например nRF Connect)
21-байтный mfg_data с company_id 0x00E0, формат как на S3
E4GATT FIRE на обычной ESP32HOST
Дёрнуть fire с привязанного телефона
FIRE проходит, реле срабатывает, RESULT notify приходит. Те же характеристики (UUID 656e7472).
E5Sleep_s байт на обычной ESP32HOST
Установить sleep_s=10 или 20 на обычной ESP32. Проверить что Android правильно интерпретирует.
В host.log на стороне Android: AppState.bleSleepByDid[did] = N, окно online window рассчитывается
E6Two ESP в эфире одновременно (S3 + обычный)HOST
Иметь два разных ESP. Подойти к обоим.
Каждый виден как своя карточка, не путаются. Auto-fire срабатывает по правильному.
E7Конфигуратор: кастомные параметрыHOST
Изменить pin реле, длительность импульса, polarity → скомпилировать → прошить → проверить что реле срабатывает по новому пину.
Реле дёргается по правильному GPIO. Импульс длится новое значение мс. Polarity соблюдается (логический уровень).
E8Конфигуратор: кеш-hitHOST
Скомпилировать тот же конфиг второй раз
Ответ мгновенный (cached: true), не уходит в очередь
E9🔒 Конфигуратор: Web Flasher через WebSerialHOST
Подключить ESP по USB → нажать «Прошить через браузер» → выбрать порт в диалоге Chrome.
Прошивка заливается. Progress-bar идёт до 100%. После reset ESP появляется в эфире.

15. Виджет

WD1Добавить виджетHOST
Home → длинный тап → виджеты → Entrixy
Появляется. Открывается список объектов для привязки.
WD2Тап виджета → fireHOST
Тап по виджету на хом-скрине
Срабатывает соответствующее действие
WD3Виджет показывает live-статусHOST
Цвет/иконка виджета меняется при изменении armed/online
Видно зелёный/красный/серый в зависимости от состояния

16. Snapshot/Stream

SP1Конфиг snapshot URLHOST
Объект → Snapshot → ввести URL JPG камеры + auth если надо
Сохраняется, при раскрытии карточки видна картинка
SP2Авто-обновление по интервалуHOST
Интервал = 5с
Картинка обновляется каждые 5с пока карточка раскрыта
SP3showWhen=nearbyHOST
Установить showWhen=nearby. Снапшот включается когда в гео-зоне.
При входе в зону карточка авто-раскрывает поток
SP4Snapshot у гостя (whitelist)GUEST
Хозяин разрешает snapshot для объекта X гостю. Гость смотрит.
Гость видит поток. Для не-whitelisted объекта — нет потока.

17. Журнал

J1Запись о тапеHOST
Тап любого объекта
В журнале строка с timestamp, who=Я, what=label
J2Гостевой fire → у хозяинаHOST
Гость дёрнул BLE-замок. У хозяина журнал.
Строка «Гость: X открыл Y» (плюс ts от события у гостя)
J3Auto-fire записьHOST
Auto-fire по геозоне или BLE
Запись с who=«Авто» (или «Я» если фронт открыт)

18. Серверные фиксы

SV1🔒 WS restartHOST
Выполнить sudo supervisorctl restart dialer-ws
Воркер поднимается. Клиенты переподключаются. В private/wh.log нет ошибок.
SV2Rate-limit helloHOST
Принудительно перезапустить приложение 3 раза подряд в течение 5 секунд
Сервер закрывает второй и третий hello молча. Клиент через backoff восстановится через 1-2с.
SV3Server dedup в БДHOST
SQL: SELECT COUNT(*) FROM pending_host_msgs;
Не растёт неконтролируемо. После dedup на renew — максимум 1 строка на (host_id, ble_id, user_key).
SV4CDN-config работаетHOST
Поменять $GLOBALS['apk_url'] в _config.php на http://example.org/test.apk. Открыть /download.php в браузере.
302 редирект ведёт на новый URL. Вернуть обратно.

19. WS backoff и стабильность

WS1Backoff растёт на флапеHOST
Включить-выключить «авиарежим» 3 раза с интервалом 3 сек
В host.log видно ws reconnect scheduled in N ms — N растёт: 1000 → 2000 → 4000 → 8000.
WS2Backoff сбрасывается на стабильной сессииHOST
После WS1 — оставить сеть на 90 секунд. Потом снова разорвать.
В логе: sess=90000ms и delay снова 1000
WS3HTTP retry backoffHOST
Создать pending host-ключ (любой). Запретить сеть. Дождаться нескольких retry'ев.
Интервал растёт 30с → 60с → 2м → 5м. Не каждые 60с подряд.

19a. Серверная нагрузка (наблюдение)

LD1Cron load_log пишет данныеHOST
Открыть /private/loadlog/YYYY-MM-DD.tsv на сервере
Появляются новые строки каждую минуту с ws_conn, pending_msgs, db_threads, load_1m
LD2Dashboard работаетHOST
Открыть https://entrixy.com/admin/load.php в браузере
Графики WS, pending, DB threads, CPU load. Снапшот с текущими значениями. Чем больше данных накопится — тем интереснее графики.
LD3WS-коннект учитываетсяHOST
До запуска приложения WS=0. Запустить приложение на 2 устройствах.
На следующей минуте dashboard покажет WS=2-3 (host + guest + main host)
LD4pending_msgs циклHOST
Хозяин offline 1 минута, гость шлёт fire-event. Хозяин online — drain.
В графике pending_msgs кратковременный пик, потом 0

20. Huawei-специфика

HW1Установка3RD
Скачать APK на Huawei через браузер. Подтвердить установку из неизвестных источников.
Устанавливается, запускается, не падает
HW2BLE-фон без Google services3RD
Запустить как хозяина BLE. Свернуть. Дёрнуть с расстояния.
Auto-fire срабатывает (BLE-protocol не зависит от GMS)
HW3Гео без GMS3RD
Включить гео-правило. Подойти к точке.
FusedLocationProvider может не работать на Huawei. Fallback на GPS provider должен сработать ИЛИ зафиксировать что не работает.
HW4Авто-старт на Huawei3RD
Перезагрузить Huawei. Не открывать. Ждать.
HostService поднимается / не поднимается — зафиксировать. Huawei режет фоновые сервисы агрессивно, может не запуститься без ручного открытия.
HW5FCM не нужен на Huawei3RD
Запустить как гость на Huawei. Получить ключ.
Работает без FCM (push не приходит, но звонки/BLE работают через WS)

21. Безопасность / подпись APK

SC1🔒 Release APK подписан PROD-keystore (не debug)HOST
Проверить подпись: apksigner verify --print-certs entrixy.apk
Subject НЕ CN=Android Debug, а реальный CN=Entrixy или подобный. Сейчас он debug — это блокирует прод-релиз.
SC2🔒 R8/ProGuard включёнHOST
В build.gradle.kts: isMinifyEnabled = true + proguard rules
APK меньше (~12 МБ вместо 22), метод-имена обфусцированы (apk → dex2jar → jd проверить)
SC3🔒 Нет hardcoded secrets в APKHOST
grep по строкам в APK на наличие jwt_secret, db_pass, API keys
Ничего из server-side секретов не утекло в APK
SC4🔒 Все сетевые соединения через TLS (https/wss)HOST
В коде поискать «http://» или «ws://» — должны быть только https/wss
Только TLS. android:usesCleartextTraffic="false" в манифесте уже стоит — проверить что не отменено.
SC5BLE replay-protectionHOST
Захватить fire-write через snoop. Воспроизвести через BLE-инжектор (или nRF Connect).
ESP отвергает (RES_FIRE_NONCE_STALE). Replay невозможен.
SC6BLE HMAC mismatch отбитHOST
Отредактировать fire-payload (изменить 1 байт) и отправить
ESP отвергает (RES_FIRE_OWNER_SIG_BAD)
SC7Сервер JWT secret не утекаетHOST
Запрос с битым JWT → 401, без leak'а secret в ответе
Просто {"error":"unauthorized"}, никаких debug-данных
SC8Admin-панель закрыта паролёмHOST
Открыть /admin/ без авторизации
Login-форма, не пускает к данным. /admin/load.php тоже должен быть за паролём (сейчас открыт — это блокер).

22. Операционка — backup, monitoring

OP1🔒 Cron mysqldump БД работаетHOST
Настроить ежедневный mysqldump folder_dialer | gzip > /backup/db-$(date).sql.gz. Проверить что вчерашний бэкап есть.
Файлы накапливаются, ротация 30 дней. На отдельном диске или offsite (S3/B2).
OP2🔒 Восстановление БД из бэкапаHOST
Скопировать БД в test-БД, восстановить из бэкапа, проверить hosts/user_keys/ble_objects на корректность
Восстановление проходит без ошибок, данные совпадают
OP3Дашборд load.php обновляетсяHOST
Открыть /admin/load.php, подождать минуту, обновить
Новая точка в графиках появилась
OP4🔒 Alert при превышении порогаHOST
Настроить cron что проверяет последнюю строку load_log и шлёт email/telegram если CPU >2, pending_msgs >1000, или disk <1GB
Получено уведомление при симуляции (намерянно создать pending записи или нагрузить CPU)
OP5🔒 Supervisor автоматически перезапускает WSHOST
Убить процесс kill -9 $(pgrep -f workerman)
Через 5-10 сек supervisor поднял заново. Клиенты переподключились без вмешательства.
OP6Logrotate для host.logHOST
Проверить что private/wh.log, private/downloads.log не растут вечно
Logrotate настроен или есть ручной cron-cleanup
OP7SSL-сертификат автообновляетсяHOST
Проверить срок сертификата entrixy.com (openssl s_client). Должен быть LetsEncrypt с certbot-renew cron.
Сертификат действителен >30 дней. Renewal cron активен.

23. Производительность и стабильность

PF1🔒 24 часа без сбоевHOST
Оставить приложение на 2 телефонах + сервер на 24 часа без рестартов
WS остаётся подключённым (с реконнектами на флапах). Никаких краш-логов. RAM на сервере не растёт.
PF2100 BLE-объектов у хозяинаHOST
Создать в Prefs 100 фейковых BLE-объектов (через ADB или test-сборку)
Главный экран открывается за разумное время, не падает. UI отзывчивый.
PF350 гостевых ключей на хозяинеHOST
Создать 50 ключей через API. Каждый = отдельный WS на гостевой стороне.
Хозяин видит всех в списке. Производительность не падает.
PF4Память APK не растётHOST
Android Studio → Profiler → Memory. Открыть приложение, потыкать, оставить. Watch heap.
Heap не растёт линейно. После GC возвращается к baseline.
PF5Батарея ESP >1 месяц на AAHOST
Замерить ток в режиме deep-sleep + измерить amp-час батареи. Расчёт.
~100 мкА средне → CR123A (1.5 А·ч) живёт ~1.7 года. AA-пара (2.5 А·ч) ~2.8 года теоретически. Реально с самосрядом — 6-12 месяцев.
PF6Сервер: 100 одновременных WS-симуляцияHOST
Написать тест-скрипт что открывает 100 WS параллельно, шлёт host_hello, держит. Смотрим /admin/load.php.
Workerman справляется. CPU не уходит в 100%. RAM в пределах VPS.

24. Onboarding и UX

UX1🔒 Первый запуск без объясненийHOST
Свежая установка → дать незнакомому человеку без инструкций
Понятно что делать. Кнопки очевидны. Без падений до этапа «у меня нет ESP что дальше».
UX2Permissions: BLE / location / camera запрашиваются в нужный моментHOST
Свежая установка → пройти pair flow + сканировать QR
BLE permission на pair, camera на QR, location на BLE scan на Android 12-. Не все сразу при запуске.
UX3Сообщения об ошибках понятныHOST
Сценарии ошибок: BT off, нет сети, ESP вне зоны, истёкший токен
Текст для пользователя, не технический жаргон. «Включите Bluetooth», «Устройство не в зоне доступа».
UX4Пустые состояния не пугаютHOST
Нет объектов / нет гостей / нет журнала. Открыть соответствующие экраны.
Дружелюбное «здесь будут ваши X, добавьте первое». Не пустой экран.
UX5Foreground notification читаетсяHOST
Свернуть приложение, посмотреть на уведомление в шторке
Понятно зачем висит. Кнопка «Закрыть» или «Открыть приложение» если есть.
UX6Multi-language (если поддерживается)HOST
Сменить язык системы на английский → перезапустить приложение
UI на английском. Strings.xml has en/de/pt и т.д.

25. Распространение / установка

DI1🔒 APK скачивается с entrixy.comHOST
Открыть entrixy.com на телефоне → кнопка «Скачать»
APK загружается. Установка предлагает разрешить установку из неизвестных источников. После разрешения — устанавливается.
DI2Инструкция по sideload понятнаHOST
Дать незнакомому человеку ссылку entrixy.com и попросить установить
Получается без подсказок. На странице есть пошаговая инструкция.
DI3🔒 BLE Web Flasher на реальном ESP32-S3HOST
/esp/ble/ → пресет ESP32-S3 DevKit → подключить ESP по USB → «Прошить через браузер»
Прошивка заливается полностью. ESP перезагружается, advertise виден. Pair проходит с приложения.
DI4🔒 BLE Web Flasher на vanilla ESP32HOST
/esp/ble/ → пресет ESP32 WROOM + реле → прошить
То же что DI3, на классическом ESP32
DI5BLE-сборка под C3 / C6 / H2HOST
В /esp/ble/ попробовать каждый чип → скомпилировать
Успешная сборка для всех. C3/C6 без проблем, H2 — может потребовать NimBLE-fix.
DI6🔒 WS Web Flasher на ESP32HOST
/esp/socket/ → ESP32 + реле → ввести SSID, пароль, сгенерировать device_key/secret (зарегистрировать в приложении) → прошить
Прошивка заливается. ESP перезагружается, подключается к WiFi, видит сервер, в журнале [WS] auth OK. Команда «Открыть» из приложения замыкает реле.
DI7🔒 WS Web Flasher на ESP8266HOST
/esp/socket/ → ESP8266 + Wemos D1 → пин 5 → ввести WiFi-креды и device_key → прошить
Компиляция за 10–20 секунд, .bin ~395 КБ. Чип подключается к WSS, проходит auth, реле срабатывает на команду «Открыть».
DI8WS-сборка через пресеты Sonoff / ShellyHOST
/esp/socket/ → пресет Sonoff Mini R2 / Shelly Plus 1 → компилировать
Компиляция проходит. Пины подставляются корректно (R2: реле GPIO12, LED GPIO13 active-low; Shelly Plus 1: реле GPIO26, LED GPIO0, кнопка GPIO4).

26. Recovery / отказоустойчивость

RC1🔒 VPS hard-reboot → всё поднимаетсяHOST
Через панель VPS дать hard-reboot
После загрузки: MariaDB, Apache/nginx, PHP-FPM, Workerman через supervisor — всё стартует автоматически. /admin/load.php снова доступен через 1-2 минуты.
RC2БД connection lost → graceHOST
Остановить MariaDB на 30 секунд. Запустить.
Workerman не падает. После reconnect — продолжает работу. Клиенты переподключаются на следующем hello.
RC3Диск 95% → дегradation gracefullyHOST
Создать большой файл что заполняет диск до 99%. Попробовать pending_host_msgs INSERT.
PHP падает с ошибкой логирования, но не вешает весь стек. Alert OP4 сработал.
RC4Workerman OOM → restartHOST
Симулировать OOM (если возможно). Или просто kill -9.
Supervisor поднимает заново через несколько секунд
RC5ESP теряет питание во время fireHOST
Выдернуть питание ESP в момент когда телефон шлёт fire
При следующем включении ESP — состояние согласовано. Pair не нужен заново. Hostsync не падает.
RC6Телефон смена SIM/IMEIHOST
Поменять SIM на телефоне → перезапустить приложение
device_id остаётся (хранится в Prefs), не перерегистрация. Hosts/ключи не теряются.

27a. Прочие фичи (не покрыты ранее)

MS1Notify-mode call (попап вместо звонка)HOST
Phone-объект режим «notify». Гость звонит.
У хозяина показывается уведомление с кнопками accept/decline, реальный звонок не идёт
MS2FCM push когда HostService убитHOST
Force-stop приложения. Гость звонит.
FCM push поднимает HostService → call приходит. (На Huawei не сработает — нет GMS.)
MS3PWA-гостевое приложение работаетHOST
Открыть entrixy.com/app/ в Chrome на мобилке. Зарегистрироваться, добавить ключ.
Web-вариант гостя работает: видит карточки, может звонить, нажимать webhook'и
MS4PWA install как приложениеHOST
Chrome → меню → «Установить приложение»
PWA встаёт как нативное приложение в шторку
MS5Promo-баннер показываетсяHOST
В админке создать промо. Открыть приложение.
Промо-баннер показан. Тап → переход по URL. Закрытие → не показывается до cache_ttl.
MS6Время ESP синхронизировано через WiFi/NTPHOST
ESP с включённым NTP. Прописать WiFi через CHAR_WIFI. Подождать sync.
В логе ESP видно SNTP sync. Часы корректные без BLE-sync от хозяина.
MS7ESP factory-reset через long-pressHOST
Long-press на пин-кнопке ESP > 5 секунд
ESP уходит в pair-режим, прошлые owner-данные стёрты. Можно перепарить с другим хозяином.
MS8Multi-host на одном гостеGUEST
Принять 2 ключа от 2 разных хозяев
Видно обе секции с разными labels хозяев. Не путаются.
MS9Объект расшарен нескольким гостям одновременноHOST
Один phone-объект расшарен 3 разным гостям. Все три тапают в течение 5 секунд.
Три звонка в очередь (или forceWhenBusy=true — каждый своим путём). У хозяина в журнале три записи с правильными именами гостей.
MS10Объект расшарен с разными правилами разным гостямGUEST
Хозяин дал гостю A — force-гео + own-wifi. Гостю B — suggest-гео без wifi.
У A правила заблокированы на изменение (force), wifi свой. У B правила можно менять, wifi не показывается.
MS11Crash report отправляетсяHOST
Симулировать crash (если есть test-кнопка или через ADB)
Файл создаётся в Android, при следующем запуске отправляется. На сервере в private/crashes/ появляется (с дедупом).
MS12HelpScreen открываетсяHOST
Меню → Помощь / O приложении
Версия отображается. Ссылки на документацию. Не падает.

27. Юридическое и compliance

LG1🔒 Privacy Policy опубликованаHOST
Открыть entrixy.com/privacy
Страница есть. Описано: какие данные собираем (device_id, fcm_token, crash logs), как храним, GDPR-данные удаления по запросу.
LG2🔒 Terms of Service опубликованыHOST
Открыть entrixy.com/terms
Условия пользования. Ограничение ответственности (для случая когда BLE-замок не открыл когда нужно). Запрет использования для взлома чужих ESP.
LG3Согласие на сбор данных при первом запускеHOST
Свежая установка → первый запуск
Просьба согласиться с ToS и Privacy. Без согласия не пускает дальше.
LG4Удаление аккаунта работаетHOST
Settings → «Удалить аккаунт» (если есть, или API endpoint)
Все данные пользователя удалены с сервера (host_id, user_keys, fcm_token, history). По GDPR.
LG5Crash logs анонимныHOST
Открыть приватный crash файл
Нет PII: телефонных номеров, имён, email. Уже есть нормализация в normalizeCrashForHash.

Отчёт о тестировании

Скопируй ниже и отправь Claude в чат, либо нажми «Отправить на сервер» — отчёт сохранится в private/test-reports/