# Требования к модулю: 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 показало, что на якорных часах он нереализуем для живого пульса: 1. **Поля данных не имеют `Toybox.Communications` до API 5.0.0**, а Fenix 6X навсегда остался на CIQ API ≤ 4.1.7 — поле данных «Силовая» (Ф-2, m13) не может ни передавать, ни принимать. 2. **CIQ-приложение** (Communications доступен) не может работать одновременно со штатной активностью «Силовая» — живой пульс во время тренировки из приложения не транслировать; companion SDK (`com.garmin.connectiq:ciq-companion-app-sdk`) для v1 неприменим. 3. **Штатная трансляция «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-железа; канал односторонний (часы → телефон). 4. Следствие для **Ф-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 %)» / «часы: нет связи» / «часы: не выбраны»); пульс — крупное текущее значение, окраска по зонам Танака (`ZoneCalculator` m10, границы из `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. Инварианты (🛡 — нарушать запрещено) 1. **Офлайн-first**: приём пульса — локальный BLE, сеть и облако не используются; сервер не участвует (БЦ-3, Ф-1.1). 2. **Деградация молча** (Ф-2.4, Ф-3.2): отсутствие часов, потери связи, отказ разрешения, выключенный Bluetooth — только прочерки и статус; тренировка, голос, ручной ввод и sync не блокируются. 3. **Канал не пишет в БД**: единственная точка записи — `WorkoutCore.finishWorkout(avgHr)` (единственный писатель LocalDb — инвариант m09); live-сэмплы никуда не сохраняются. 4. **Часы не зависят от телефона** (Ф-2.4 «не зависимость»): телефон ничего не «требует» от часов; трансляцию включает сам атлет на часах (инструкция в «Настройках»). 5. **Тайны**: BLE-адрес устройства хранится только в DataStore телефона; в лог/журнал команд адреса не пишутся (З-6). 6. **Платформо-независимость канала** (указание владельца 2026-10-19): любые часы, транслирующие стандартный Heart Rate Service, работают без изменений кода; Garmin/fenix-специфика в коде запрещена; новые транспорты подключаются реализацией `WatchTransport` без правок фасада, UI и БД; то же требование наследуется m13 — мультитаргет-сборка поля данных по моделям (vision 1.1, Ф-2.8). ## 4. Критерии приёмки модуля 1. `assembleDebug` собирается; unit-тесты (все, включая m09–m11) зелёные. 2. Приёмка на устройстве владельца (Fenix 6X, см. инструкцию в readme): включить «Broadcast Heart Rate» → «Настройки → Часы» → скан → выбрать часы → экран «Тренировка»: статус «связь есть», пульс обновляется ≈1 Гц при активном экране часов, линия последнего подхода рисуется; финиш → `avg_hr` в записи (История на телефоне, карточка в консоли m08 после sync); «Допуск» → «взять с часов» подставляет пульс. 3. Отрицательные исходы: часы не выбраны / трансляция выключена / Bluetooth выключен / отказ разрешения — прочерки и статус текстом, приложение работает (ручной смок владельца). 4. Прототип ОВ-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 в телах разделов, версии поднимутся на ближайшем ревью).