diff --git a/docs/session-logs/2026-10-17_hr-workout-mode.md b/docs/session-logs/2026-10-17_hr-workout-mode.md index 324b4a9..cdc2f22 100644 --- a/docs/session-logs/2026-10-17_hr-workout-mode.md +++ b/docs/session-logs/2026-10-17_hr-workout-mode.md @@ -44,6 +44,22 @@ project-hr-sensor. - Заметка: аудио в WebView может требовать user-gesture; старт записи - это жест, должно играть. Проверить на устройстве. +## Правка после теста: авто-ЧСС на старте + тоггл ВКЛ/ВЫКЛ вместо "Ветер" +Я перестарался: по просьбе "не искать, если браслета нет" убрал автоконнект ВООБЩЕ. Нужно было +оставить авто с проверкой наличия. Исправлено: +- `GPS.setHrOn(on)` (gps.js): ON - проверяет BT (off -> запрос включения), коннектит привязанный + датчик, запускает поллинг; OFF - стоп поллинг + disconnect. Безопасно: BleHrService при + отсутствии браслета коротко сканит и `stopSelf` (драйна нет). +- Старт записи (и GPS, и зарядки): если датчик привязан - авто `setHrOn(true)`. Так "надел + датчик -> начал -> подключился сам", без ручного коннекта каждый раз. Нет привязанного - ЧСС off. +- Экран записи: кнопка **"Ветер" убрана** (неудобна), на её месте **тоггл ЧСС** (квадрат с + сердечком+линией пульса; серый = выкл, красный = вкл), дёргает `GPS.setHrOn`. +- Ручная кнопка "Подключить" в настройках остаётся. + +Факт-чек (я ошибся ранее): на телефоне Google-сервисы ЕСТЬ (`com.google.android.gms`, Play +Store) - значит системный диалог включения гео через Play Services доступен. Авто-вкл BT/гео по +запросу (попап + 2 настройки "не спрашивать") - отдельная задача, теперь подтверждено выполнимо. + ## Проверка Синтаксис JS ок. На телефоне: выбрать "Зарядка", Старт (без браслета покажет "Подключите пульсометр"), подключить датчик -> пульс крупно + ккал растут; Стоп -> деталь с графиком. diff --git a/frontend/css/styles.css b/frontend/css/styles.css index 1a69380..28d8214 100644 --- a/frontend/css/styles.css +++ b/frontend/css/styles.css @@ -725,6 +725,11 @@ select { .wk-status { color: var(--muted); font-size: 13px; text-align: center; min-height: 18px; margin-bottom: 6px; } .workout-screen .btn-primary, .workout-screen .btn-secondary { margin-top: 8px; width: 100%; } +/* HR on/off toggle on the record screen (replaces the wind button): grey = off, coloured = on. */ +.rec-hr-toggle { color: #888; display: inline-flex; align-items: center; justify-content: center; min-width: 46px; padding: 6px 10px; } +.rec-hr-toggle svg { display: block; } +.rec-hr-toggle.active { color: #ef5350; background: rgba(239, 83, 80, 0.18); } + /* Progress fill for long-running action buttons: the background fills left-to-right by --pct (0-100%, set from JS). The label stays on top and readable; the unfilled part keeps the button's own background. Used by recompute / elevation-refine / roads-rebuild. */ diff --git a/frontend/js/gps.js b/frontend/js/gps.js index 599270a..81641f6 100644 --- a/frontend/js/gps.js +++ b/frontend/js/gps.js @@ -868,6 +868,28 @@ GPS._stopHrMonitor = function() { try { window.__TAURI__.core.invoke("ble_clear_hr"); } catch {} }; +// Turn HR on/off during a recording (the toggle on the record screen). ON connects to the +// paired strap (Kotlin scans ~5s and stops cleanly if it's absent — so no constant scanning / +// drain when the strap isn't there) and polls; OFF stops the poll and disconnects. +GPS.setHrOn = async function(on) { + GPS.recording.hrOn = !!on; + if (!on) { + GPS._stopHrMonitor(); + try { await window.__TAURI__.core.invoke("ble_disconnect"); } catch {} + return false; + } + const addr = await DB.getProfileValue("ble_device_addr"); + const type = await DB.getProfileValue("ble_device_type"); + if (!addr) { GPS.recording.hrOn = false; return false; } + try { + const bt = await window.__TAURI__.core.invoke("ble_check_bluetooth"); + if (bt === "off") { try { await window.__TAURI__.core.invoke("ble_request_enable"); } catch {} } + else if (bt !== "no_adapter") { try { await window.__TAURI__.core.invoke("ble_connect", { address: addr, deviceType: type || "hr_standard" }); } catch {} } + } catch {} + try { await GPS._startHrMonitor(); } catch {} + return true; +}; + // Low-battery warning for the HR strap: announce (voice) and beep ONCE at 10% and 5%. // Resets when the strap reads healthy again (recharged / battery replaced). GPS._batAlerted = {}; @@ -1386,7 +1408,11 @@ GPS.startRecording = async function(activityType, weightKg, profile) { GPS.recording.sessionId = await DB.createGpsSession( new Date().toISOString().slice(0, 10), new Date().toTimeString().slice(0, 5), "workout"); } catch (e) { console.error("[workout] createGpsSession failed:", e); } - try { await GPS._startHrMonitor(); } catch (e) { console.error("[HR] start error:", e); } + // Auto-connect the paired strap on start (workout = you want HR). Absent strap -> stops clean. + try { + const hrAddr = await DB.getProfileValue("ble_device_addr"); + if (hrAddr) await GPS.setHrOn(true); else GPS.recording.hrOn = false; + } catch (e) { console.error("[HR] start error:", e); } return; } @@ -1409,8 +1435,13 @@ GPS.startRecording = async function(activityType, weightKg, profile) { // Start native foreground service (Android) await GPS._startNativeService(); - // Start BLE HR monitor (auto-connect to paired device) - try { await GPS._startHrMonitor(); } catch (e) { console.error("[HR] start error:", e); } + // HR: default ON if a strap is paired — auto-connect + poll at start (the record screen has an + // on/off toggle). Safe: BleHrService scans briefly and stops if the strap isn't present, so no + // constant scanning/drain when it's absent. No paired strap -> HR stays off. + try { + const hrAddr = await DB.getProfileValue("ble_device_addr"); + if (hrAddr) await GPS.setHrOn(true); else GPS.recording.hrOn = false; + } catch (e) { console.error("[HR] start error:", e); } // Poll native service output file if (GPS.recording.nativeService) { diff --git a/frontend/js/main.js b/frontend/js/main.js index 3c8a407..caca390 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -4578,7 +4578,7 @@ async function renderRecordingInline(container) {