Skip to content

Google Health Data Types and Fitbit Google Health Fallback#625

Open
chrisnowak wants to merge 30 commits into
GoogleHealthConnectDevicesMenufrom
claude/google-health-integration-plan-p6kxla
Open

Google Health Data Types and Fitbit Google Health Fallback#625
chrisnowak wants to merge 30 commits into
GoogleHealthConnectDevicesMenufrom
claude/google-health-integration-plan-p6kxla

Conversation

@chrisnowak

@chrisnowak chrisnowak commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

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 available will 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

Consider potential security impacts and complete the following checklist.
REMINDER: All file contents are public.

  • I have ensured no secure credentials or sensitive information remain in code, metadata, comments, etc.
    • Please verify that you double checked that .storybook/preview.js does not contain your participant access key details.
    • There are no temporary testing changes committed such as API base URLs, access tokens, print/log statements, etc.
  • These changes do not introduce any security risks, or any such risks have been properly mitigated.

Testing

Consider whether the changes might have device-specific behaviors (screen padding, new APIs, etc.) and check one of the following boxes:

  • This change can be adequately tested using the MDH Storybook.
  • This change requires additional testing in the MDH iOS/Android/Web apps. (Create a pre-release tag/build and test in a ViewBuilder PR.)

Documentation

Consider whether there are any documentation impacts and check one of the following boxes:

  • I have added relevant Storybook updates to this PR.
  • If this feature requires a developer doc update, I have tagged @CareEvolution/api-docs.
  • This change does not impact documentation or Storybook.

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:

  • Subject-matter experts
  • Style/editing reviewers
  • Others requested by the content owner

Consider "Squash and merge" as needed to keep the commit history reasonable on main.

claude and others added 17 commits July 1, 2026 16:13
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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/helpers/heart-rate-data-providers/combined-intraday-heart-rate-providers.ts Outdated
Comment thread locales/de.json Outdated
"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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to go through the localization hub or else everything will get out of sync. I can help if you like.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would external folks get these names from the alldatatypes endpoint?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Comment thread src/helpers/daily-data-providers/combined-resting-heart-rate.ts
Comment thread src/helpers/daily-data-providers/combined-sleep.ts
Comment thread src/helpers/daily-data-providers/combined-steps.ts
Comment thread src/helpers/daily-data-types/google-health.tsx Outdated
Comment thread src/helpers/heart-rate-data-providers/combined-intraday-heart-rate-providers.ts Outdated
Comment thread src/helpers/daily-data-providers/google-health.ts Outdated
Comment thread src/helpers/daily-data-providers/google-health.ts Outdated
Comment thread __tests__/helpers/daily-data-providers/google-health.test.ts Outdated
Comment thread src/helpers/daily-data-types/fitbit.tsx Outdated
@lynnfaraday
lynnfaraday self-requested a review July 14, 2026 12:14

@lynnfaraday lynnfaraday left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comments have been addressed. Defer to Garrett on the rest.

@lynnfaraday
lynnfaraday self-requested a review July 14, 2026 12:20
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
claude added 9 commits July 14, 2026 15:32
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 greinard left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants