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.
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).
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.
- HR (every second per Bluetooth profile).
- RR intervals when the device advertises the optional RR field.
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 aGarminHealth 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 avendor_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
OuraProviderwith mediated OAuth via the Synheart Wear API (samevendor_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 exportsexport.zip from the iOS Health app:
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: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 asstream(best-effort) and log.
Permission flow
SynheartWear.requestPermissions(permissions, reason):
- Maps Synheart
PermissionTypevalues to OS permission scopes. - Routes to the appropriate adapter (HealthKit on iOS, Health Connect on Android, OAuth flow for cloud adapters).
- Returns
Map<PermissionType, ConsentStatus>per type.
Related
- Wear Overview β full SDK surface.
- Data Schema β what each adapter writes into the unified
WearMetrics. - BLE Heart Rate Monitor β direct BLE HRM flow.