Estimate daily calories on-device when the wearable reports none - #98
Merged
Conversation
…i-c12e0d # Conflicts: # PulseLoop/Views/RootViews.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rings that don't report calories (TK18/LuckRing) — and history syncs on rings that do (Colmi sends calories only in live packets) — left days showing "—". This adds an on-device daily estimate: Mifflin-St Jeor BMR accrued over elapsed minutes + net active energy from workouts (existing Keytel/MET engine), all-day HR (Keytel gated at 60% HRmax so resting HR spikes never count), and cadence-tiered step energy from history buckets. Each interval is attributed to exactly one estimator, so nothing double-counts.
A device-reported value always wins at read time (
ActivityDaily.effectiveCalories), so this only fills days with no device calories; extending to all wearables later is a one-line gate change. The calorie goal ring keeps measuring the active-energy portion vs the 500 kcal goal; the displayed number is the total.Recompute hooks: history-bucket sync (batched, flushed at sync done), live packets from no-calorie devices (throttled), workout finish/edit/delete, late HR backfill, profile changes, launch backfill. New
estimatedActiveCaloriesfield onActivityDailyis an additive lightweight migration.Type of change
How was this tested?
PulseLoopTests) — 17 new tests inDailyCalorieEstimatorTests(formula values, FLEX gating, overlap exclusion, midnight proration, device-wins selection, re-sync idempotency)-seedDemo YES, no hardware) — new-demoEstimatedCalories YESlaunch arg reshapes seeded days into ring-history days; Today/Activity show the estimated total instead of "—"Privacy & data
Checklist