Commit Graph
39 Commits
Author SHA1 Message Date
Gideon Zenz 67c71ef325 Garmin: Replace SLF4J 2.0 fluent API call with 1.x-compatible logging
LOG.atLevel(logLevel).log(...) in NativeFITMessage.getFieldDefinition
uses the SLF4J 2.0 fluent API, which logback-android 3.0.0 does not
implement. This throws AbstractMethodError at runtime, crashing the
FitAsyncProcessor thread and aborting the entire Garmin device sync
when a .FIT file triggers the type/size mismatch path.

Replace with a switch dispatching to LOG.debug/info/warn.
2026-05-29 21:07:16 +02:00
Gideon Zenz 6c70aa371d Health Connect: Drop out-of-range sample values to prevent sync abort
Health Connect enforces hard min/max bounds on each record-type field; a
single out-of-range value caused the record constructor to throw and
killed the entire batch insert. The trigger was a Garmin Venu 3
producing HRV > 200 ms, but several syncers shared the same gap (no
upper bound, only > 0 or no validation at all).

Add explicit per-syncer numeric guards matching the bounds enforced by
HC's record constructors, drop out-of-range samples, log them with the
existing [HC_SYNC] prefix, and continue the sync.

Bounds applied (per-record HC enforcement):
- HRV: 1..200 ms
- VO2Max: 0..100 ml/kg/min
- Weight: 0..1000 kg
- BloodGlucose: 0..900.91 mg/dL (= 50 mmol/L) -- previously unvalidated
- RespiratoryRate: 0..1000 breaths/min
- HeartRate / RestingHR: 1..300 / 0..300 bpm (widened from 20..250 to
  match HC's contract; downstream consumers can apply physiological
  filters)
- SpO2: 0..100 %
- Steps: 1..1000000 per record
- ActiveCalories: 0..1000000 kcal per record
- Distance: 0..1000000 m per record
- Skin temperature delta: -30..30 °C
- Body temperature: existing physiological clamp 25..45 °C kept (HC has
  no enforced bound)

Add Logger.skipOutOfRange extension in syncers/SyncerLogging.kt and
relax HC_SYNC_TAG visibility from private to internal so syncers can
share it.

HeartRateSync aggregates dropped-sample counts into one summary log
line per slice rather than per-sample, to avoid log spam from glitchy
firmware.

convertSample visibility relaxed from protected to internal in the two
abstract base syncers so the new boundary tests can call it directly.

30 new unit tests in SyncerRangeValidationTest cover boundary
conditions per syncer.
2026-05-27 11:44:35 +02:00
Gideon Zenz 7832f8a4a8 Health Connect: Sanitise GPS route to prevent ExerciseRoute crash
ExerciseRoute requires strictly increasing timestamps; duplicate-second
points from device GPS parsers (e.g. Xiaomi Smart Band 9 Pro outdoor
cycling) caused IllegalArgumentException at construction time, aborting
the workout sync entirely.

Extract route construction into buildSanitisedRoute, which:
- drops points outside the workout time window
- drops points with non-finite or out-of-range lat/lng (HC's Location
  constructor enforces lat in [-90, 90], lng in [-180, 180])
- drops points with non-finite or negative hdop/vdop accuracy (HC
  rejects negative accuracy)
- deduplicates points by Instant
- returns null if fewer than two usable points remain
- catches IllegalArgumentException as a safety net so future HC
  invariants degrade gracefully (workout saves without route rather
  than the whole sync aborting)

Logs dropped points with the [HC_SYNC] prefix so users can grep.
2026-05-27 10:20:07 +02:00
Gideon ZenzandJosé Rebelo 539031b4b1 SleepAnalysis: Fix awake duration overflow when gap is not bridged
Only commit gap time to awakeSleepDuration when sleep actually resumes
(bridge confirmed), instead of speculatively counting all non-sleep
time as awake. Fixes total duration exceeding session span.
2026-03-24 18:58:24 +01:00
Gideon ZenzandJosé Rebelo 4c966bf232 SleepAnalysis: Bridge non-sleep gaps with no activity as awake sleep
Fix dead code bug where MAX_WAKE_PHASE_LENGTH could never trigger
because Block A unconditionally nulled sleepStart before Block B
could check it. Non-sleep samples now only break a session if the
user has step activity or the gap exceeds 1 hour. Bridged gaps are
counted as awake sleep duration.

See: #5757
2026-03-24 18:58:24 +01:00
Gideon ZenzandJosé Rebelo d7c1022926 Xiaomi: Map sleep stage 5 (awake) to AWAKE_SLEEP
getActivityKindForSample was written before AWAKE_SLEEP existed in
ActivityKind (April 2024 vs August 2024), so decoded stage 5 (awake
during sleep) fell through to UNKNOWN. This caused the sleep overlay
to skip awake periods, which may fragment sleep sessions in
SleepAnalysis and result in missing or truncated Health Connect
sleep records.

See: https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/5920
2026-03-23 00:35:32 +01:00
Gideon Zenz 7096e11dcf Fix: Hide only all-day HR interval and activity monitoring for devices without display 2026-03-12 23:22:15 +01:00
Gideon Zenz b5e6fa82a6 Remove dead supportsActivityTracking guards and unused methods from RecordedWorkoutSyncer
RecordedWorkoutSyncer is only invoked for devices where supportsActivityTracks() is true,
and all such devices also support activity tracking — so the !supportsActivityTracking
guards were always false, making addHeartRateRecord and addStepsRecord unreachable.
HR and steps are already synced by HeartRateSyncer and StepsSyncer at activity level.
2026-03-12 21:46:41 +01:00
Gideon Zenz f9d1f3874c Health Connect: Fix duplicate and truncated sleep records across slice boundaries
SleepSyncer was inserting the same sleep session in multiple slices when
the session spanned a slice boundary. The 1-day look-back caused the
session to be re-discovered in adjacent slices, and the overlap check
allowed it to be synced in each one.
Assign each session to the slice where its start time falls
([sliceStart, sliceEnd)). This ownership rule ensures each session is
synced exactly once regardless of how many slices it overlaps with.
Add a 12-hour look-forward to the DB query for SLEEP so that sessions
starting near the slice end are fetched with their full data, preventing
truncation at the slice boundary.
Fixes: https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/5873
2026-03-12 18:30:52 +01:00
Gideon Zenz f07779f249 Health Connect: Fix duplicate records from workout syncer overlapping ACTIVITY sync
RecordedWorkoutSyncer was writing heart rate, steps, and calorie records
that overlap with the per-minute data already synced by HeartRateSyncer,
StepsSyncer, and ActiveCaloriesSyncer in the ACTIVITY data type sync.
The supportsActiveCalories guard from 8c575535b only covered
processDetailedWorkout but not processAggregateWorkout, which is the
code path hit when no track file is available. No guards existed for
heart rate or steps in either path.

Skip workout-level aggregate HR and steps when supportsActivityTracking
(covered by ACTIVITY sync), and skip workout-level calories when
supportsActiveCalories (covered by ActiveCaloriesSyncer). Per-second HR
from detailed workout track data is always synced as it is more granular
than the per-minute activity HR.
2026-03-12 18:30:52 +01:00
Gideon ZenzandMerow 92f0a7d802 Add data export for HC sync verification tooling as debug function 2026-03-02 20:24:53 +01:00
Gideon Zenz b5b0b585cc Fix #5797: Advance HC sync cursor by last-written record timestamp
The sync state cursor was advancing to the slice end timestamp
unconditionally, even when no records were written. Late-arriving data
(sleep, workouts, VO2Max, etc.) that arrives after the cursor has
already advanced past it would be permanently skipped.
Each syncer now reports the latest timestamp of records actually
inserted into Health Connect. The sync cursor advances to
max(currentPersisted, latestRecordTimestamp) instead of
sliceEndTimestamp. If no records were written for a slice, the cursor
stays put. This handles all late-arriving data generically without
needing per-data-type special-casing.
2026-03-02 20:19:02 +01:00
Gideon Zenz 3f59815f5f Hide heart rate monitoring config for Helio Strap (always-on by design) 2026-02-14 18:27:17 +01:00
Gideon Zenz 3c8a9b5821 Enable Sony WF1000XM4 Headphones speak-to-chat support 2026-02-14 07:50:33 +01:00
Gideon ZenzandJosé Rebelo 14aadfe0a0 Implement the mac address changing feature in the debug settings 2026-02-08 19:16:28 +01:00
Gideon ZenzandJosé Rebelo b42db53109 Add "Today" and "distant past" quick filter option to workout list with persistent selection
- Add "Today" and "distant past" option to time period filter dropdown
- Store selected filter in SharedPreferences for persistence across sessions
- Auto-restore last selected filter when opening workout filter settings
- Today filter shows workouts from midnight to current time, distant pass shows all workouts
2026-01-25 16:58:00 +01:00
Gideon ZenzandJosé Rebelo a486de73c5 Parse workout summaries upfront to ensure correct activityKind before processing 2026-01-16 22:12:23 +01:00
Gideon ZenzandJosé Rebelo 3bebe6563d Fix cadence syncing to only sync for appropriate activity types
Use ActivityKind.getCycleUnit() to determine if cadence should be synced.
Only sync StepsCadenceRecord for step-based activities (walking, running, hiking) and CyclingPedalingCadenceRecord for cycling activities.
Skip cadence syncing for activities with other cycle units (strokes, jumps, reps, swings) as Health Connect lacks appropriate cadence record types.

Note: Swimming workouts with stroke data (freestyle, backstroke, etc.) could be synced as ExerciseSegment records in a future enhancement.
2026-01-16 22:12:23 +01:00
Gideon ZenzandJosé Rebelo 46925c2dde Add missing workout data sync: active calories, steps, and cadence
- Fix calorie mapping: caloriesBurnt represents active calories, not total calories
- Parse RAW_SUMMARY_DATA when SUMMARY_DATA JSON is empty (fixes Amazfit/ZeppOS devices)
- Use device-specific ActivitySummaryParser to extract workout metrics from binary protobuf data
- Sync ActiveCaloriesBurnedRecord separately from total calories
- Add StepsRecord for step count during workouts
- Add intelligent cadence syncing (StepsCadenceRecord for running/walking, CyclingPedalingCadenceRecord for cycling)
- Add required Health Connect permissions to manifest
- Update HealthConnectPermissionManager with all workout permissions
2026-01-16 22:12:23 +01:00
Gideon Zenz 9c85d5995c Zepp OS: Enable workout detection sensitivity for Helio Strap
- Add supportsWorkoutActivityTypesConfiguration() to enable workout settings for devices without display
- Add supportsWorkoutDetectionCategories() to control visibility of workout categories
- Update ZeppOsSettingsCustomizer to hide workout categories preference for devices that don't support it
- Enable workout detection sensitivity configuration for Amazfit Helio Strap
- Workout categories selector is hidden for Helio Strap, only alert and sensitivity settings are shown
This allows the Helio Strap to configure automatic workout detection sensitivity despite having no display.
2026-01-04 17:14:51 +01:00
Gideon Zenz 3fe147247e Fix last sample sync issue by making slice boundaries inclusive
Changed boundary checks from exclusive [start, end) to inclusive [start, end] to ensure samples occurring exactly at slice boundaries are synced correctly. This fixes the issue where the last sample in the database would never get synced because it was filtered out by the exclusive end boundary check while the sync state advanced to its timestamp.

Affected syncers:
- AbstractTimeSampleSyncer (HRV, SpO2, Weight, etc.)
- StepsSyncer
- HeartRateSyncer
- SleepSyncer
2026-01-04 14:50:47 +01:00
Gideon Zenz 687b812ed4 Fix workout sync to track end time instead of start time for last synced position
Changed getLastSampleTimestamp() for WORKOUTS to use workout EndTime instead of
StartTime. Previously, using StartTime caused the sync state to advance only to
the start of the last workout, resulting in that workout being re-synced on every
sync run and any new workouts started after it being missed until the next sync.
Now correctly advances to workout end time, ensuring proper incremental sync.
2026-01-04 13:54:25 +01:00
Gideon ZenzandJosé Rebelo 2e582f48c6 Health Connect: Refactor HrvSyncer, Vo2MaxSyncer, and WeightSyncer to use AbstractTimeSampleSyncer 2026-01-03 14:37:37 +00:00
Gideon ZenzandJosé Rebelo 1bdf0bd130 fix timestamp issues 2026-01-03 15:03:21 +01:00
Gideon ZenzandJosé Rebelo fd29be7b4a - Filter WORKOUTS from sync iteration since it's only for permission grouping
- Move RecordedWorkoutSyncer outside activityBasedSamples null check
2026-01-03 15:03:21 +01:00
Gideon ZenzandJosé Rebelo 2e5a77e541 Add missing Health Connect permissions and WORKOUTS data type
- Add PERMISSION_WRITE_EXERCISE_ROUTE import to HealthConnectPermissionManager
- Include ElevationGainedRecord, PowerRecord, SpeedRecord, and ExerciseRoute permissions in requiredHealthConnectPermissions
- Add WORKOUTS enum to HealthConnectDataType with comprehensive permission mapping
- Handle WORKOUTS branches in HealthConnectUtils when expressions for exhaustiveness
2026-01-03 15:03:21 +01:00
Gideon ZenzandJosé Rebelo aea638d4be Adding HealthConnect sync for:
Body Temperature
    Skin Temperature
    Distance
    Exercise
    Heart rate
    Heart rate variability
    Oxygen saturation
    Sleep
    Steps
    Total calories burned
    VO2 max
    Weight

The feature is configurable via Settings->External Integrations->Health Connect

Based on https://codeberg.org/LLan/Gadgetbridge

Kudos to LLan for the preliminary work!
2026-01-03 15:03:21 +01:00
Gideon ZenzandJosé Rebelo 40137ef9e7 Ignore devices that don't support HRV measurements 2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo 3b4b7292c3 Invalidate HRV cache based on latest sample timestamp
Query latest HRV sample to invalidate also yesterday if needed
2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo 68f12ea92e Invalidate HRV summary cache when new data arrives
Add HrvCacheInvalidationReceiver to listen for ACTION_NEW_DATA broadcasts and invalidate the computed HRV summary cache for the current day.
2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo 9ef4942102 Add HRV summary computation for Huawei devices
Uses ComputedHrvSummarySampleProvider to generate HRV summary statistics
from per-minute HRV values stored by Huawei devices.
2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo ca532a6350 Implemented a static, thread-safe LRU cache that persists across provider instances: 2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo 290379a462 - Use SleepAnalysis for last night HRV calculation
- remove ColmiHrvSummarySampleProvider
2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo 0c7a2c3960 Adding Yawell Rings 2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo 8c0b8934ee Require minimum 7 days of overnight HRV data before calculating baseline status to prevent unreliable POOR status in first few days 2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo 9df32ad1ac Generic provider creation
Garmin algorithm implementation
POOR status addition
Caching for performance
Application to both Huami and Ultrahuman devices
2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo c0f5da073e Add HRV summary computation for Huami/Zepp OS devices
Implements HuamiHrvSummarySampleProvider to compute HRV summary statistics
on-demand from per-minute HRV value samples. Calculates weekly averages,
last night values, baselines, and HRV status (LOW/UNBALANCED/BALANCED).

This enables full HRV dashboard and status screen functionality for devices
that only provide raw HRV measurements without pre-computed summaries.

- Created HuamiHrvSummarySampleProvider with computed summary logic
- Added getHrvSummarySampleProvider() override in HuamiCoordinator
- Computes 7-day rolling average, last-night stats, and 14-day baselines
- Works retroactively with existing historical HRV data
2025-12-20 19:07:34 +01:00
Gideon ZenzandJosé Rebelo d10ad8b650 Finalizing device temperature and adding device location implementation 2025-10-20 00:38:34 +02:00
Gideon ZenzandJosé Rebelo d869eae55c Add DeviceKind (#5382)
Adding DeviceKinds to DeviceCoordinators as introduced by https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/5346

Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/5382
Co-authored-by: Gideon Zenz <gzenz@gmx.de>
Co-committed-by: Gideon Zenz <gzenz@gmx.de>
2025-09-23 23:23:20 +02:00