Skip to main content

Overview

The Synheart Session Kotlin SDK provides SynheartSession, a timer-driven session engine that buffers heart-rate samples from a BiosignalProvider, emits typed SessionEvents on a SharedFlow, and delivers a SessionSummary when the session ends. An optional BehaviorProvider adds behavioral signals to each frame. The bundled wrappers for synheart-wear and synheart-behavior use reflection, so those libraries are only needed at runtime. WatchSessionRelay runs a session on a companion Wear OS watch instead and streams the watch’s events back.
The SDK computes mean HR locally. HRV (hr_sdnn_ms, rmssd_ms, pnn50) is supplied by the host through ingestHsiMetrics() and is 0.0 until then.

Installation

Maven Central

kotlinx-coroutines-core is an api dependency, so Flow and SharedFlow are available to your code without adding it yourself.

Local Module

Requirements

  • Android API 21+ (minSdk 21)
  • Java 17 bytecode
  • The library is built with Kotlin 2.3.0 and depends on kotlin-stdlib 2.3.0

Quick Start

Mock Mode (Development)

startSession returns a hot SharedFlow<SessionEvent> that replays up to 1024 events, so a collector that subscribes after the start still sees SessionStarted. The flow never completes on its own; end collection on SessionSummary or SessionErrorEvent (for example with transformWhile, as above).

Real BLE Heart Rate Data

If the BLE HRM is not connected when the session starts, the session emits a SessionErrorEvent with SENSOR_UNAVAILABLE and ends.

Health Connect Heart Rate Data

The provider polls the adapter’s readSnapshot() every 2 seconds by default (pollIntervalMs). Each sample’s rr_intervals_ms is derived from BPM (60000 / bpm), not measured.

Session Configuration

SessionConfig

ComputeProfile

Session Events

SessionEvent is a sealed class. Every subclass has sessionId and toMap(), which returns the snake_case wire map shared with the other SDKs.

SessionStarted

Emitted immediately by startSession.

SessionFrame

Emitted every emitIntervalSec while the window holds at least one sample (frames are skipped while it is empty).
metrics keys:

BiosignalFrame

Emitted every rawEmitIntervalSec when includeRawSamples = true and the window is not empty. seq is a separate counter from SessionFrame.seq.
Each sample map has timestamp_ms, bpm, source, and, when the provider supplies them, rr_intervals_ms and device_id.

SessionSummary

Emitted once when the duration elapses or stopSession is called.

SessionErrorEvent

Emitted instead of a summary when the session ends abnormally — for example when the provider throws from startStreaming, which ends the session with SENSOR_UNAVAILABLE. On the wire (toMap()), the code is under error_code. Error codes (SessionErrorCode): API misuse throws instead: startSession throws SessionError.InvalidState if any session is already running (whatever its id) or the instance is disposed, and stopSession throws it if no session is running or the id does not match. See Errors.

HRV Metrics

The latest map replaces the previous one and is used for every following frame and the summary. The call is a no-op after dispose() or when sessionId does not match the running session.

Behavioral Signals

SynheartSession accepts an optional BehaviorProvider. The engine pulls a snapshot at each frame and once more for the summary.

Mock Behavior (Development)

Production (via synheart-behavior)

BehaviorSdkProvider reports isAvailable from the SDK’s isInitialized() and reads getCurrentStats() at each frame. The behavior field is only present when a BehaviorProvider is configured, reports isAvailable, and returns a snapshot.

Companion Watch Sessions (Wear OS)

WatchSessionRelay drives a session on a paired Wear OS watch over the Wearable Data Layer. The watch’s companion app runs the session; the relay sends commands and decodes the watch’s events into SessionEvents. It is independent of SynheartSession. The Play Services libraries are compileOnly in the SDK, so a host that uses the relay must add them itself:
The relay uses await() from kotlinx-coroutines-play-services. Without play-services-wearable (or on a device without Google Play services), status() reports supported = false and startSession emits a SessionErrorEvent rather than crashing.
startSession emits a SessionErrorEvent with SENSOR_UNAVAILABLE instead of throwing when Play Services is unavailable, when no node accepts the command, or when the watch sends nothing within 10 seconds. A node can accept the message while no app answers it — the Data Layer delivers only between apps with the same package name and signing certificate. WatchStatus has supported, reachable, paired, installed, and canStartSession (supported && reachable). Wear OS gives no separate paired/installed signal, so paired and installed track reachable. The message format is described in Watch Protocol.

Architecture

Custom Provider

Any class implementing BiosignalProvider can drive the session:

API Reference

SynheartSession

Resources