16 KiB
Требования к модулю: m12-watch-channel
Модуль — WatchChannel телефона
Phone.WatchBody(архитектура разд. 1.2): приём live-пульса с часов Fenix 6X по штатной трансляции BLE (Heart Rate Service), статус связи, заряд (если передаётся), подача в экран «Тренировка» (m10), «Допуск» и запись среднего пульса тренировки (avg_hr, цепочка m09). Самодостаточная копия требований (d-dev-6). Источники: Ф-3.1–Ф-3.3 (статусная строка, пульс, таймер), Ф-13.2 (пульс сидя «с часов, если связаны»), Ф-2.4 (вибрация отдыха — errata, см. разд. 1.1), ОВ-3 (tech-req 3 п. 3, прототип 7 п. 1), ui-ux разд. 2.
1. Назначение и границы
Live-пульс на телефоне во время тренировки: часы Fenix 6X транслируют пульс штатной функцией «Broadcast Heart Rate», телефон принимает и показывает; отсутствие связи ничего не ломает (🛡 Ф-2.4, Ф-3.2 — деградация молча, прочерки).
1.1. Транспорт: обоснование (errata ОВ-3)
ОВ-3 (tech-req 3 п. 3) предполагал companion-канал Connect IQ Communications. Исследование 2026-10-19 показало, что на якорных часах он нереализуем для живого пульса:
- Поля данных не имеют
Toybox.Communicationsдо API 5.0.0, а Fenix 6X навсегда остался на CIQ API ≤ 4.1.7 — поле данных «Силовая» (Ф-2, m13) не может ни передавать, ни принимать. - CIQ-приложение (Communications доступен) не может работать
одновременно со штатной активностью «Силовая» — живой пульс
во время тренировки из приложения не транслировать; companion
SDK (
com.garmin.connectiq:ciq-companion-app-sdk) для v1 неприменим. - Штатная трансляция «Broadcast Heart Rate» на fenix 6 серии с прошивки июня 2020 идёт одновременно по ANT+ и BLE (стандартный Heart Rate Service 0x180D; подтверждено на Pro-линии — 6/6S/6X Pro; у базовой «шестёрки» 2022 г. сообщали только ANT+). BLE-приём работает на любом Android без Garmin Connect Mobile и без ANT-железа; канал односторонний (часы → телефон).
- Следствие для Ф-2.4 (вибрация конца отдыха «по таймеру,
переданному с телефона»): передать секунды отдыха на часы
в v1 нечем — таймер отдыха на часах делает m13 автономно
(длительность — настройка поля данных в Garmin Connect Mobile,
умолчание =
Rest.DefaultSecтелефона). Таймер телефона (Ф-3.3) работает независимо, как сейчас. Errata НД — разд. 5.
Критерий прототипа ОВ-3 (tech-req 7 п. 1 «пульс и таймер доходят при экране часов активным») исполняется в части «пульс доходит» (проверка на устройстве владельца); часть про таймер переносится в m13 как автономная вибрация (errata 7 п. 1).
В границах модуля:
- транспорт-интерфейс
WatchTransport+ первая реализация — BLE-клиентHrBleClient(требование владельца при утверждении гейта 1.3, 2026-10-19: инфраструктура должна поддерживать и другие умные часы): скан по UUID Heart Rate Service (0x180D), подключение, подписка на изменения характеристики Measurement (0x2A37, indications), разбор пакета (флаги: разрядность 8/16 бит, поля Energy/RR пропускаются), автопереподключение при потере, чтение Battery Service (0x180F) при его наличии — иначе заряд не показывается (Ф-3.1 «если передаётся»); интерфейс — для unit-тестов на фейках и для будущих транспортов (CIQ 5+, ANT+) без правок фасада и UI; только стандартные профили GATT, платформо-специфичных UUID/имён/логики в коде нет — любые часы, транслирующие стандартный Heart Rate Service, работают без изменений кода; платформо-специчное — только тексты подсказок включения трансляции в «Настройках» (расширяемый справочник по производителям, v1 — Garmin); - буфер пульса
HrSampleStore(чистый, в памяти): сэмплы (bpm,t_offset_sот старта тренировки), текущее значение, среднее за тренировку, серия «последний подход» (сэмплы от последнего записанного подхода до текущего момента — Ф-3.2 «линия за последний подход»), устаревание (> 10 с без сэмплов — «нет данных», прочерк); - фасад
WatchChannel: состояние канала —StateFlowсо статусом (не выбраны / скан / связь есть / потеряна / Bluetooth выключен / нет разрешения 🛡 текстом), текущим пульсом, зарядом, серией последнего подхода и средним; жизненный цикл: канал активен, пока открыты экраны «Тренировка» или «Допуск» (v1 — без foreground-сервиса; пульс считается за время открытого экрана — ограничение решения 5 гейта 1.3); - выбор устройства: в «Настройках» блок «Часы» — скан, список
найденных пульс-датчиков (имя + адрес), выбор сохраняется
(
Watch.HrDeviceAddressв DataStore); не задано — статус «часы не выбраны», канал не сканирует; сброс выбора; - экран «Тренировка» (интеграция m10, замена заглушек
WorkoutScreen: статус-строка): связь часов — зелёный/красный/
серый, дублируется текстом («часы: связь есть (заряд 80 %)» /
«часы: нет связи» / «часы: не выбраны»); пульс — крупное текущее
значение, окраска по зонам Танака (
ZoneCalculatorm10, границы изHr.MaxBpm), под значением — линия последнего подхода (Canvas, серия буфера); при отсутствии/устаревании — прочерк, голос и ручной ввод работают (Ф-1.1, Ф-3.2); - финиш тренировки:
WorkoutCore.finishWorkout(avgHr = среднее буфера)— готовая цепочка m09 (workout.avg_hr→ sync); канал сам в БД не пишет; - экран «Допуск» (Ф-13.2): если канал активен и есть свежий сэмпл (≤ 30 с) — кнопка «взять с часов» подставляет пульс сидя в поле допуска; иначе — ручной ввод пикером (как в m10);
- разрешения Bluetooth: Android 12+ —
BLUETOOTH_SCAN(neverForLocation) +BLUETOOTH_CONNECT(runtime); Android 8–11 —ACCESS_FINE_LOCATION(runtime) +BLUETOOTH/BLUETOOTH_ADMIN(манифест); отказ в разрешении — статус «нет разрешения», экран не блокируется (🛡); - unit-тесты (pure JVM): разбор 0x2A37 (фикстуры байтов: 8/16 бит, с полями Energy/RR), буфер (среднее, серия последнего подхода, устаревание), статусные переходы фасада на фейках клиента.
Вне границ модуля: m13 поле данных (Monkey C — отображение,
зоны, автономный таймер отдыха с настройкой длительности;
мультитаргет-сборка по моделям — там же), дополнительные
реализации WatchTransport — ANT+-приём (плагин Garmin, нужен
ANT-транспорт в телефоне) и CIQ companion (часы с CIQ 5+) —
кандидаты v2 по мере появления устройств, foreground-фон пульса
при погашенном экране (v2),
запись live-сэмплов в LocalDb/sync (история пульса — из .fit,
m07/m08), пульсограмма всей тренировки на телефоне (Ф-5 —
карточка факт из .fit на сервере m08), wake-слова, смена устройства
голосом.
2. Решения интерфейса (ui-ux разд. 2, tech-req 6)
- Статусная строка часов не конкурирует с пульсом: статус — мелкой строкой сверху, пульс — крупным значением (≥ 40 sp) в зоне пульса;
- статус и доступность связи дублируются текстом (🛡 цвет — только дублирование, не единственный носитель);
- линия последнего подхода — без сеток и подписей, минимум чернил; значение пульса — цифрой всегда;
- «взять с часов» на допуске — вторичная кнопка рядом с полем «Пульс сидя», после нажатия значение видно в поле (правка пикером сохраняется);
- блок «Часы» в «Настройках» — по образцу блока «Распознавание речи» (m11): статус, действие, инструкция текстом («включите Broadcast Heart Rate на часах»).
3. Инварианты (🛡 — нарушать запрещено)
- Офлайн-first: приём пульса — локальный BLE, сеть и облако не используются; сервер не участвует (БЦ-3, Ф-1.1).
- Деградация молча (Ф-2.4, Ф-3.2): отсутствие часов, потери связи, отказ разрешения, выключенный Bluetooth — только прочерки и статус; тренировка, голос, ручной ввод и sync не блокируются.
- Канал не пишет в БД: единственная точка записи —
WorkoutCore.finishWorkout(avgHr)(единственный писатель LocalDb — инвариант m09); live-сэмплы никуда не сохраняются. - Часы не зависят от телефона (Ф-2.4 «не зависимость»): телефон ничего не «требует» от часов; трансляцию включает сам атлет на часах (инструкция в «Настройках»).
- Тайны: BLE-адрес устройства хранится только в DataStore телефона; в лог/журнал команд адреса не пишутся (З-6).
- Платформо-независимость канала (указание владельца
2026-10-19): любые часы, транслирующие стандартный Heart Rate
Service, работают без изменений кода; Garmin/fenix-специфика
в коде запрещена; новые транспорты подключаются реализацией
WatchTransportбез правок фасада, UI и БД; то же требование наследуется m13 — мультитаргет-сборка поля данных по моделям (vision 1.1, Ф-2.8).
4. Критерии приёмки модуля
assembleDebugсобирается; unit-тесты (все, включая m09–m11) зелёные.- Приёмка на устройстве владельца (Fenix 6X, см. инструкцию
в readme): включить «Broadcast Heart Rate» → «Настройки → Часы»
→ скан → выбрать часы → экран «Тренировка»: статус «связь есть»,
пульс обновляется ≈1 Гц при активном экране часов, линия
последнего подхода рисуется; финиш →
avg_hrв записи (История на телефоне, карточка в консоли m08 после sync); «Допуск» → «взять с часов» подставляет пульс. - Отрицательные исходы: часы не выбраны / трансляция выключена / Bluetooth выключен / отказ разрешения — прочерки и статус текстом, приложение работает (ручной смок владельца).
- Прототип ОВ-3 (tech-req 7 п. 1) — в части «пульс доходит при экране часов активным»: подтверждается пунктом 2; фиксируется в журнале приёмки владельца; часть «таймер» — errata, m13.
5. Errata НД (по утверждению гейта 1.3, решение 2; ВНЕСЕНА 2026-10-19)
- requirements Ф-2.4: «вибрация — конец таймера отдыха,
переданного с телефона» → «вибрация — конец автономного таймера
отдыха поля; длительность — настройка поля в Garmin Connect
(умолчание
Rest.DefaultSec); передача с телефона — v2 (кандидат при смене часов на CIQ 5+)»; - requirements Ф-3.2: источник пульса — «штатная трансляция пульса часов (BLE Heart Rate Service; ОВ-3 закрыт: companion- канал Connect IQ на fenix 6X недоступен для полей данных)»;
- tech-requirements 3 п. 3 (ОВ-3): описание канала фактически = односторонняя штатная трансляция «Broadcast Heart Rate» (BLE/ANT+), Garmin Connect Mobile для пульса не нужен;
- tech-requirements 7 п. 1: критерий прототипа — «пульс доходит на телефон при активном экране часов» (таймер — автономный, проверяется в m13);
- architecture 1.1/1.2/3 (телефон↔часы): канал = BLE-приём штатной трансляции, односторонний.
Волна правок НД вносится после «утверждаю» по гейту 1.3 одним пакетом (без подъёма версий документов — errata в телах разделов, версии поднимутся на ближайшем ревью).