From 85e9f219160df196e4652cffc05cc6fd8cd60bfd Mon Sep 17 00:00:00 2001 From: Doug Marttila Date: Tue, 14 Jul 2026 16:58:18 -0400 Subject: [PATCH] Fix: scope Health Connect aggregate null-bucket skip to HEART_RATE only PR #18 changed getAggregateHCData so a missing aggregate bucket is skipped (`?: continue`) instead of falling back to 0, to avoid fabricating a 0 BPM heart-rate reading. That skip was applied unconditionally to every metric MapToHCAggregateMetric handles (STEPS, WEIGHT, HEIGHT, DISTANCE_DELTA, WATER, the SLEEP_* durations, ACTIVE_ENERGY_BURNED, TOTAL_CALORIES_BURNED), not just HEART_RATE. For every one of those other types, zero is a legitimate value for a bucket (e.g. 0 steps taken in an interval), so skipping the bucket silently drops real data instead of reporting it as zero. Scope the skip-on-null behavior to dataType == HEART_RATE and restore the zero fallback for every other aggregate metric type. Co-Authored-By: Claude Sonnet 5 --- .../kotlin/cachet/plugins/health/HealthPlugin.kt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index d67ccb6c8..71d030180 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -3277,11 +3277,19 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val response = healthConnectClient.aggregateGroupByDuration(request) for (durationResult in response) { - // The result is null if no data is available in the - // bucket — skip it rather than fabricating a zero - // value (a 0 BPM heart rate is not a real data point). var totalValue = durationResult.result[metricClassType] - ?: continue + if (totalValue == null) { + if (dataType == HEART_RATE) { + // A missing heart-rate bucket means no samples were + // recorded in it — skip it rather than fabricating a + // 0 BPM value, which would not be a real data point. + continue + } + // Every other aggregate type (steps, distance, calories, ...) + // legitimately can be zero for a bucket; report it as such + // rather than dropping the bucket. + totalValue = 0 + } if (totalValue is Length) { totalValue = totalValue.inMeters } else if (totalValue is Energy) {