Google Health Data Types and Fitbit Google Health Fallback#625
Google Health Data Types and Fitbit Google Health Fallback#625chrisnowak wants to merge 30 commits into
Conversation
Documents the MyDataHelpsUI-side plan for the new Google Health back-end: adding Google Health to the Connect Devices menu, introducing Google Health daily data types and wiring them into the shared/combined types (steps, resting heart rate, sleep), and a Fitbit-preferred/Google-Health-fallback backwards-compatibility strategy for existing Fitbit-configured graphs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
…ping Confirms OAuth external-account connection model, and documents the exact V2 device-data type strings for the GoogleHealth namespace (steps-daily, dailyRestingHeartRate-list-beatsPerMinute, sleep-list-session-asleep) derived from the Consumers CfhrFramework/GoogleHealth back-end, including which metrics are pre-rolled daily vs. need a per-day sum aggregate (sleep). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
…nalysis Appendix A enumerates every queryable GoogleHealth V2 type string derived from the back-end FetchingPlan, tagged by cadence (daily rollup / daily list / intraday / sample / session / static). Appendix B buckets them into metrics that feed existing/combined types (steps, RHR, sleep, calories, floors, distance, HRV, SpO2, active/zone minutes, HR min/max/avg, sleep stages) versus net-new daily data types (VO2 max, weight, body fat, glucose, hydration, nutrition, body temperature, sleep sub-metrics, HR-zone calories/time). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
…Fitbit fallback, and menu Adds Google Health as a data source across the daily data system, behind a temporary type bridge until the MyDataHelps.js SDK ships the GoogleHealth V2 namespace and googleHealthEnabled flag: - Google Health V2 providers for steps, resting HR, sleep (total + light/rem/ deep), calories (active/total), floors, wear time, breathing rate, HRV, SpO2, min/max/avg HR, active minutes, and HR-zone minutes. - Per-source GoogleHealth* daily data types (google-health.tsx) and enum entries. - Google Health wired into the combined Steps, RestingHeartRate, SleepMinutes, and ActiveCaloriesBurned providers and availability sources. - Fitbit types with a Google Health analog now prefer Fitbit but fall back to Google Health per day (combineResultsUsingFirstValue), keeping existing Fitbit-configured graphs working as Fitbit is retired; enum keys unchanged. - Wear time added as a daily data type (Consumers PR #13629 wearTime-daily), including a generic wear-time label across locales. - Google Health added to the Connect Devices menu (OAuth external account, above Fitbit), gated on googleHealthEnabled. Deferred pending unit verification: distance (mm vs meters) and sedentary (seconds vs minutes). tsc clean; full Jest suite green (580 tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Google Health distance-daily is in millimeters and sedentaryPeriod-daily is in seconds; the providers convert to meters (/1000) and minutes (/60) to match the other sources. Adds a standalone GoogleHealthDistance type and wires FitbitSedentaryMinutes to fall back to Google Health. Divisors are derived from the back-end field units and should be confirmed against real data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
The plan is now implemented; dropping the working planning document. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
SDK 5.7.0 adds the GoogleHealth V2 namespace and the googleHealthEnabled flag natively, so the temporary bridge is no longer needed: - Remove the ambient DataCollectionSettings augmentation from custom.d.ts. - Delete google-health-namespace.ts; the daily-data helpers use the SDK's DeviceDataV2Namespace / SupportedDeviceDataV2DataType directly and the GoogleHealth casts are gone. - Google Health providers reference the namespace directly. tsc clean; full Jest suite green (582 tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
…into claude/google-health-integration-plan-p6kxla # Conflicts: # src/components/container/ConnectDevicesMenu/ConnectDevicesMenu.tsx # src/helpers/providerIDs.ts
…llback Maps the GoogleHealth namespace to its intraday heart-rate V2 type (heartRate-list) so it can be selected as a data source, and auto-appends Google Health as a fallback whenever Fitbit is a source: Google Health is queried right after Fitbit so Fitbit still wins each interval and Google Health only fills the intervals Fitbit is missing (matching the daily-data fallback as Fitbit is retired). Adds unit tests for the fallback ordering and the merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
npm@latest is now 12.x, which requires Node >=22, but the publish job runs on Node 20 — pinning to npm@11 keeps the Update npm step compatible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Wear time is a minutes metric; use minutesFormatter + minutesToHoursYAxisConverter (matching sleep time and Apple Watch wear time) so MicroTrend and charts show e.g. "1h 33m" instead of a raw "93". Applies to both Fitbit and Google Health wear time. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Google Health sleep sessions were dated by getStartDate (the session start), so a night's sleep landed on the day you fell asleep instead of the day you woke up. Date sleep by getSleepDate (observationDate + 6h) instead - Google Health sets a sleep point's observationDate to the session end time, so this attributes sleep to the wake-up day, matching Fitbit, Apple Health, Health Connect and Oura (all of which apply the same 6-hour shift / 6pm cutoff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Flip the two first-value mergers from Fitbit-preferred to Google-Health-preferred so a Fitbit-configured metric shows Google Health data whenever it is available, falling back to Fitbit only for intervals/days Google Health is missing. Google Health's calculations (e.g. sleep staging, sedentary time) track the device apps and avoid Fitbit artifacts like reporting a full 24h sedentary on non-wear days. - Daily: rename fitbitWithGoogleHealthFallback -> googleHealthPreferredOverFitbit (and the fitbit.tsx helper -> withGoogleHealthPreferred), ordering Google Health first in the first-value combine. - Intraday heart rate: insert Google Health before Fitbit in the source list so it wins each interval; rename withGoogleHealthFallback -> withGoogleHealthPreferred. - The combined (max/average) daily providers are unaffected since they blend rather than prefer a source. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
There was a problem hiding this comment.
Pull request overview
This PR expands the device-data “daily data” layer to support a broad set of Google Health V2 data types, and updates Fitbit-backed daily types (and intraday heart rate) to prefer Google Health when available to smooth the Fitbit retirement transition.
Changes:
- Add Google Health daily data providers + daily data type definitions (sleep, steps, heart rate, activity minutes/zones, calories, distance, etc.).
- Update Fitbit daily types (and intraday HR provider ordering) to prefer Google Health data when present, while preserving existing Fitbit-configured type keys.
- Update combined daily types/providers to include Google Health, add translations for the new “wear-time” label, and pin npm in the publish workflow for Node 20 compatibility.
Reviewed changes
Copilot reviewed 36 out of 37 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/helpers/heart-rate-data-providers/combined-intraday-heart-rate-providers.ts | Adds Google Health intraday HR type mapping + source ordering helper to prefer Google Health over Fitbit. |
| src/helpers/daily-data-types/google-health.tsx | Introduces Google Health daily data type definitions (labels/icons/formatters/availability). |
| src/helpers/daily-data-types/fitbit.tsx | Wraps Fitbit daily types to prefer Google Health providers while keeping existing Fitbit type keys. |
| src/helpers/daily-data-types/combined.tsx | Adds Google Health as an additional availability source for unified/combined types. |
| src/helpers/daily-data-types/all.ts | Registers Google Health type definitions into the global daily type list. |
| src/helpers/daily-data-types.tsx | Extends DailyDataType and DailyDataTypeDefinition.dataSource to include Google Health. |
| src/helpers/daily-data-providers/index.ts | Re-exports Google Health providers and the “prefer over Fitbit” wrapper. |
| src/helpers/daily-data-providers/google-health.ts | Implements Google Health V2 daily providers (rollups, totals, unit conversions). |
| src/helpers/daily-data-providers/google-health-preferred-over-fitbit.ts | Adds a wrapper that prefers Google Health per-day and falls back to Fitbit on missing days. |
| src/helpers/daily-data-providers/data-collection-helper.ts | Adds the googleHealthEnabled flag mapping for availability checks. |
| src/helpers/daily-data-providers/combined-steps.ts | Adds Google Health as an additional steps source in the combined steps provider. |
| src/helpers/daily-data-providers/combined-sleep.ts | Adds Google Health as an additional sleep source in the combined sleep provider. |
| src/helpers/daily-data-providers/combined-resting-heart-rate.ts | Adds Google Health as an additional resting HR source in the combined RHR provider. |
| src/helpers/daily-data-providers/combined-active-calories-burned.ts | Adds Google Health as an additional active-calories source in the combined provider. |
| package.json | Bumps package version for this Google Health iteration. |
| package-lock.json | Updates lockfile version metadata to match the package version bump. |
| locales/en.json | Adds translation for wear-time. |
| locales/es.json | Adds translation for wear-time. |
| locales/de.json | Adds translation for wear-time. |
| locales/fr.json | Adds translation for wear-time. |
| locales/fr-CA.json | Adds translation for wear-time. |
| locales/it.json | Adds translation for wear-time. |
| locales/nl.json | Adds translation for wear-time. |
| locales/pl.json | Adds translation for wear-time. |
| locales/pt.json | Adds translation for wear-time. |
| locales/pt-PT.json | Adds translation for wear-time. |
| locales/ro.json | Adds translation for wear-time. |
| locales/so.json | Adds translation for wear-time. |
| locales/sw.json | Adds translation for wear-time. |
| locales/tl.json | Adds translation for wear-time. |
| locales/fil.json | Adds translation for wear-time. |
| locales/vi.json | Adds translation for wear-time. |
| .github/workflows/cicd-publish.yml | Pins npm to v11 to avoid Node 22 requirement on the Node 20 publish runner. |
| tests/helpers/heart-rate-data-providers/combined-intraday-heart-rate-providers.test.ts | Adds tests asserting Google Health is preferred over Fitbit for intraday HR. |
| tests/helpers/daily-data-providers/google-health.test.ts | Adds unit tests for key Google Health daily providers and conversions. |
| tests/helpers/daily-data-providers/google-health-preferred-over-fitbit.test.ts | Adds unit tests for the per-day preference/fallback wrapper. |
| tests/helpers/daily-data-providers/combined-active-calories-burned.test.ts | Extends combined active calories tests to include Google Health. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "feeling-overall-previous": "Wie haben Sie sich insgesamt früher gefühlt?", | ||
| "feeling-overall-today": "Wie fühlen Sie sich insgesamt heute?", | ||
| "fitbit-wear-time": "Fitbit-Tragezeit", | ||
| "wear-time": "Tragezeit", |
There was a problem hiding this comment.
This needs to go through the localization hub or else everything will get out of sync. I can help if you like.
There was a problem hiding this comment.
Synced with locale hub. This hub PR needs to be merged at the same time as this one: https://github.com/CareEvolution/localization-hub-strings/pull/3
(It's stacked on the locale hub PR for the other google health PR to mirror the merge order of these two MDHUI PRs)
| * | ||
| * All data is served through the V2 device data API under the "GoogleHealth" namespace. | ||
| * The V2 type strings are the back-end realtimedb type names (see | ||
| * Consumers/Mogul/Code/CfhrFramework/GoogleHealth). Two shapes are used here: |
There was a problem hiding this comment.
Would external folks get these names from the alldatatypes endpoint?
There was a problem hiding this comment.
Ah yes, and this comment should be removed
MyDataHelpsUI is open source; drop the reference to the internal Consumers back-end file path from the google-health providers docstring. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
lynnfaraday
left a comment
There was a problem hiding this comment.
My comments have been addressed. Defer to Garrett on the rest.
Addresses review: the array is never reassigned. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: note that when the caller already lists Google Health explicitly, its ordering is respected as-is (the merge keeps the first value per interval), so the preference only applies when Google Health is inserted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review comment on the test description. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: add an optional valueFn to googleHealthDailyValue and use it for the distance (mm->m) and sedentary (s->min) conversions, dropping the separate scaleResult helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: drop the withGoogleHealthPreferred deconstruction helper and spell out dataProvider (googleHealthPreferredOverFitbit) and availabilityCheck (combinedAvailabilityCheck(sources(...))) on each Fitbit type so they read at a glance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: replace the single google-health.ts with one file per data type (grouping sub-type families like sleep, heart-rate min/max/avg, activity minutes and HR-zone minutes, matching the Fitbit/Garmin layout), backed by a shared google-health-common.ts query helper. Also removes the back-end type-name docstring per review. Barrel exports and all consumers are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: mock googleHealthStepsDataProvider, add a Google-Health-only case, and include it in the multi-source combined result test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: mock googleHealthTotalSleepMinutesDataProvider, add a Google-Health-only case, and include it in the multi-source combined result test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: mock googleHealthRestingHeartRateDataProvider, add a Google-Health-only case, and include it in the multi-source combined result test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
Addresses review: replace the single google-health.test.ts with one test file per provider (matching the split), covering every new provider - steps, resting HR, calories, active calories, floors, distance, wear time, breathing rate, HRV, SpO2, heart-rate min/max/avg, activity minutes (incl. the summed total) and HR-zone minutes (incl. the summed elevated), plus the sleep stage totals. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KpZw7nQjd2YL7YzUKFx7AU
greinard
left a comment
There was a problem hiding this comment.
Looks good to me.
I'll make that UT adjustment we spoke about offline, which once merged, will cause some test failures here. Once those are fixed up, should be good to 🚢 whenever you are ready.
Overview
Based on #624
Implements a number of google health daily data types. I was not super comprehensive; I scoped this mostly to things that were similar to things we already supported from other sources (in particular Fitbit) vs. trying to support every google fit type in here.
Additionally, all Fitbit types other than RestingCaloriesBurned
fall back to Google Health when Fitbit data is not availablewill prefer Google Health if it is available. So long as participants connect to Google Health by the time Fitbit api is discontinued their data should look uninterrupted and we should not need to adjust any existing views.Edit: Originally had it fall back to Google Health, but it appears that there are differences on things like sleep time and sedentary time. Google Health is consistent with the Google Health app now, so it should prefer Google Health when it's available.
e.g.
My Fitbit API sleep data says 7h 54m last night
Google Health API sleep data says 8h 18m
The Google Health app says 8h 18m
Security
Testing
Documentation
@CareEvolution/api-docs.Reviewers
Assign to the appropriate reviewer(s). Minimally, a second set of eyes is needed ensure no non-public information is published. Consider also including:
Consider "Squash and merge" as needed to keep the commit history reasonable on
main.