Skip to main content
synheart-wear ships a stable adapter layer per vendor. This page is the reference for what each adapter does, what it produces, and how it interacts with cloud streaming.

Adapter matrix

The Dart DeviceAdapter enum lists platformHealth, fitbit, garmin, whoop, samsungHealth. The Swift and Kotlin enums additionally include bleHrm / BLE_HRM. Across all three platforms, Oura has no enum entry and is reached via OuraProvider; BLE HRM on Dart is reached via BleHrmBridge.

Apple HealthKit

Capabilities:
  • HR (instantaneous + samples).
  • HRV (RMSSD, SDNN; SDNN derived on-device when only RR is exposed).
  • Steps, calories, distance.
  • RR intervals via the HealthKit RR channel (workout-mode only on most Apple Watch models).
  • Health profile snapshot (DOB, sex, height, weight) for personalization context.
  • Workout events.
Permissions: standard HealthKit HKObjectType set, requested once via requestPermissions. source field on WearMetrics: "apple_healthkit".

Health Connect (Android)

Capabilities:
  • HR, HRV (when the recording app provides it; otherwise SDNN/RMSSD are unavailable).
  • Steps, calories, distance.
  • Sleep records (sleep stages from compatible apps).
Health Connect retention is capped per record type by the platform (typically ~30 days for HR, longer for sleep). Hosts that need older data must use the Apple XML backfill path (Apple users) or an OAuth cloud adapter. source field: "health_connect".

BLE HRM

Direct connection over BLE GATT Heart Rate Profile (0x180D). No vendor cloud, no OAuth. Compatible devices:
  • WHOOP (Broadcast HR mode).
  • Polar chest straps (H10, OH1).
  • Wahoo TICKR family.
  • Garmin HRM-Pro / HRM-Dual.
  • Any HRM advertising 0x180D Heart Rate Service.
Capabilities:
  • HR (every second per Bluetooth profile).
  • RR intervals when the device advertises the optional RR field.
The bridge runs its own permission/scan/connect/disconnect lifecycle independent of requestPermissions. See BLE Heart Rate Monitor for the detailed flow. source field: "ble_hrm".

WHOOP

Two modes: WhoopProvider exposes:
  • fetchRawDataForFlux(start, end) β€” pulls raw vendor data for HSI 1.3 transformation.
  • Real-time HR via webhooks (mode A) or BLE (mode B).
  • Workout events.
source field: "whoop" for the OAuth path. The cloud-vs-BLE distinction lives in meta.source_type ("whoop_cloud"); WHOOP via BLE Broadcast HR uses the shared BLE HRM adapter (source = "ble_hrm").

Garmin (Cloud)

Path: OAuth β†’ Garmin Connect API webhooks β†’ cloud β†’ RAMEN. Delivery hint: ping β€” vendor only sends a notification; client must pull the full record via REST when needed. GarminProvider exposes:
  • fetchRawDataForFlux(start, end) β€” REST pull for Flux input.
  • Wellness, sleep, activity data classes (GarminWellnessData, GarminSleepData, GarminActivityData).
  • Connection state (GarminConnectionState) for UI.
  • Realtime data shape (GarminRealtimeData) when a session is active.
source field: "garmin" for the OAuth Connect API path; meta.source_type carries the more specific "garmin_cloud".

Garmin Health SDK (Real-Time Streaming)

Native RTS path β€” direct device pairing without going through Garmin Connect cloud. Requires a separate Garmin license and is bundled on demand for licensed integrations only. The underlying Garmin Health SDK code is proprietary; this SDK exposes a GarminHealth provider faΓ§ade that wraps it. source field: "garmin_sdk" (the adapter id from GarminSdkAdapter).

Fitbit

OAuth flow mediated by the Synheart Wear API β€” the device never sees Fitbit access tokens. Cloud delivers a vendor_user_id to the app via deep-link callback (?vendor=fitbit&user_id=...&status=success); pass that as the code argument to connectWithCode(...). FitbitProvider exposes:
  • fetchHrv, fetchSleep, fetchActivity β€” date-range record queries.
  • fetchUserProfile β€” Fitbit profile snapshot.
source field: "fitbit".

Oura

Two paths depending on platform:
  • Flutter: data flows through HealthKit (iOS) and Health Connect (Android) when an Oura companion app syncs there.
  • Kotlin / Swift: dedicated OuraProvider with mediated OAuth via the Synheart Wear API (same vendor_user_id-via-deep-link contract as Fitbit).
OuraProvider exposes fetchReadiness, fetchSleep, fetchHrv, fetchActivity. source field: "oura".

Streaming model

Today, cloud providers (Whoop, Garmin, Fitbit, Oura) are polled: streamHR / streamHRV run a timer (Combine Timer.publish on Swift, kotlinx.coroutines.flow.flow { delay(...) } on Kotlin) that calls readMetrics(isRealTime: true) on each tick. There is no push channel for these vendors yet. BleHrmProvider is push-based β€” the BLE characteristic notification feeds a SharedFlow / AsyncStream and streamHR picks up the latest sample. Future work: a RamenEventDispatcher on Kotlin / Swift (Flutter already has one) will materialize cloud-side ping events into REST pull-backs, replacing the timer-based polling for cloud providers.

Apple Health XML backfill

When a user exports export.zip from the iOS Health app:
Idempotency keys ensure repeated imports don’t duplicate samples in the runtime’s SQLite store. The high-level path through synheart-core aggregates samples per day and replays them via Synheart.srmPushWearableDaily(...) so the imported data populates the user’s runtime baselines.

RAMEN 🍜 integration

Each cloud adapter participates in the RAMEN stream when Synheart’s cloud ingestion publishes events:
Each adapter handler decides how to consume RamenEvent.payload_json:
  • DeliveryHint.stream: parse inline, emit immediately.
  • DeliveryHint.ping: schedule a REST pull (via the adapter’s provider) to fetch the body.
  • DeliveryHint.unknown: treat as stream (best-effort) and log.

Permission flow

SynheartWear.requestPermissions(permissions, reason):
  1. Maps Synheart PermissionType values to OS permission scopes.
  2. Routes to the appropriate adapter (HealthKit on iOS, Health Connect on Android, OAuth flow for cloud adapters).
  3. Returns Map<PermissionType, ConsentStatus> per type.
Adapters that don’t have OS-level permissions (BLE HRM relies on Bluetooth Manager prompts; cloud OAuth requires app navigation) handle their own UX outside this method.