Skip to main content
WearMetrics is the single shape every adapter writes into. Downstream layers (synheart-session, synheart-core, the runtime ingest queue) read this shape; they do not consume vendor-specific JSON.

WearMetrics

This is the Flutter class. Kotlin and Swift carry the same fields with a few differences: RR intervals are rrIntervals, meta holds strings only, and battery is a metric (BATTERY / .battery) instead of meta.battery. JSON shape (canonical, Flutter toJson()):

Top-level fields

metrics keys

Maps from the MetricType enum: HealthKit and Health Connect each report a single HRV figure: SDNN and RMSSD respectively. They are different statistics, so each is stored only under its own key (hrv_sdnn from HealthKit, hrv_rmssd from Health Connect) and the other key is absent. Flutter releases before 0.7.0 copied the one value into both keys. Adapters MAY emit additional vendor-specific metric keys but downstream consumers MUST tolerate unknown keys. Stable keys above are the normalised contract.

meta keys (stable)

Adapter-specific meta on Flutter: Garmin cloud adds garmin_data (the vendor record), body_battery, vo2_max, fitness_age and source_type; when Garmin reports no stress value, stress falls back to 100 - body_battery. WHOOP cloud adds recovery_score, strain_score, sleep_score, whoop_data and source_type.

rr_ms semantics

When present:
  • Each value is one inter-beat interval in milliseconds.
  • Order is the order observed by the device (chronological).
  • The list is per sample, not cumulative — multiple samples at the same timestamp can share an rr_ms payload via the meta field.
  • Range gates apply downstream: the runtime’s RR push rejects values outside [200, 6000].

Adapter source IDs

The top-level source field carries the adapter id — typically the bare vendor/source name. More specific flavor information (cloud vs BLE, etc.) lives in meta.source_type. Consumers MUST tolerate unknown source and meta.source_type values. When new adapters land, they take new stable strings. Existing strings do not change meaning.

Compatibility rules

The shape is unversioned. Compatibility rules:
  • Producers MAY add new metric keys, new meta keys, and new source values.
  • Producers MUST NOT remove or rename existing keys.
  • Consumers MUST tolerate unknown metric keys and unknown source values.
  • A future change to the top-level shape would be a breaking change; the SDK would expose both producers in parallel during migration.

Vendor → schema mapping (informal)

This table is informational only. Each adapter is the authoritative source for its mapping; this is a reading aid.

How downstream uses this

synheart-session turns wear readings into its own BiosignalSamples:
  • Swift: HealthKitBiosignalProvider(wear:) reads streamHR, and WearBiosignalProvider(bleHrmProvider:) bridges a BLE HRM, carrying its RR intervals as a list.
  • Kotlin: WearBiosignalProvider bridges a BLE HRM; HealthConnectBiosignalProvider polls Health Connect.
  • Flutter: the session SDK gets HR from the watch (WatchBiosignalProvider), not from synheart_wear.
synheart-core forwards the same shape into the runtime via pushHr / pushRr after consent gating.