Documentation Index
Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Synheart Session Kotlin SDK provides a native SessionEngine that captures heart rate data, computes session metrics on-device, and optionally fuses behavioral signals alongside HR data. The engine is timer-driven, emitting session frames at configurable intervals and delivering a session summary when complete. The SDK supports pluggable BiosignalProvider (for HR sources) and BehaviorProvider (for behavioral signals) via reflection-based wrappers.
Installation
Maven Central
dependencies {
implementation 'ai.synheart:synheart-session:0.2.0'
}
Local Module
// settings.gradle
include ':synheart-session'
project(':synheart-session').projectDir = new File('../synheart-session-kotlin')
// build.gradle
dependencies {
implementation project(':synheart-session')
}
Requirements
- Android API 21+
- Kotlin 1.9+
Quick Start
Mock Mode (Development)
import ai.synheart.session.*
val config = SessionConfig(
sessionId = UUID.randomUUID().toString(),
mode = SessionMode.FOCUS,
durationSec = 300,
profile = ComputeProfile(windowSec = 60, emitIntervalSec = 5)
)
val engine = SessionEngine()
engine.start(config) { event ->
when (event["type"] as? String) {
"session_started" -> Log.d("Session", "Started")
"session_frame" -> {
val metrics = event["metrics"] as? Map<*, *>
Log.d("Session", "HR: ${metrics?.get("hr_mean_bpm")} bpm")
}
"session_summary" -> Log.d("Session", "Complete")
"session_error" -> Log.e("Session", "Error: ${event["message"]}")
}
}
// Stop early (optional)
engine.stop(config.sessionId)
Real BLE Heart Rate Data
import ai.synheart.session.*
// Wrap any BLE HRM client as a BiosignalProvider (reflection-based, no compile dep needed)
val provider = WearBiosignalProvider(bleHrmClient)
val engine = SessionEngine(provider)
engine.start(config) { event ->
// Same event handling — metrics are now computed from real HR data
}
Health Connect Heart Rate Data
import ai.synheart.session.*
val provider = HealthConnectBiosignalProvider(healthConnectAdapter, context)
if (provider.isAvailable) {
val engine = SessionEngine(provider)
engine.start(config) { event ->
// Metrics are computed from Health Connect HR data
// (Wear OS watches, Samsung Health, Fitbit, etc.)
}
}
Session Configuration
val config = SessionConfig(
sessionId = UUID.randomUUID().toString(),
mode = SessionMode.FOCUS, // or SessionMode.BREATHING
durationSec = 60, // session duration in seconds
profile = ComputeProfile(
windowSec = 10, // sliding window size
emitIntervalSec = 3 // session frame interval
)
)
SessionConfig
| Parameter | Type | Required | Default | Description |
|---|
sessionId | String | No | UUID | Unique session identifier |
mode | SessionMode | Yes | - | Session mode (FOCUS or BREATHING) |
durationSec | Int | Yes | - | Session duration in seconds |
profile | ComputeProfile | No | 60s/5s | Window and emit configuration |
includeRawSamples | Boolean | No | false | Enable raw biosignal frame streaming |
windowLabel | String? | No | null | Optional label for the session window |
ComputeProfile
| Parameter | Type | Default | Description |
|---|
windowSec | Int | 60 | Sliding window size in seconds |
emitIntervalSec | Int | 5 | Session frame emission interval in seconds |
rawEmitIntervalSec | Int? | null | Raw biosignal frame interval (defaults to emitIntervalSec) |
Session Events
The engine callback receives Map<String, Any> dictionaries with a "type" key.
session_started
// event["type"] == "session_started"
// event["session_id"] == "..."
// event["started_at_ms"] == 1700000000000
session_frame
Contains flat metrics computed over the sliding window:
// event["type"] == "session_frame"
val metrics = event["metrics"] as? Map<String, Any>
val hr = metrics?.get("hr_mean_bpm") as? Double // Mean HR in BPM
val sdnn = metrics?.get("hr_sdnn_ms") as? Double // SDNN of RR intervals
val rmssd = metrics?.get("rmssd_ms") as? Double // RMSSD approximation
val motionRms = metrics?.get("motion_rms_g") as? Double // RMS acceleration (optional)
| Field | Type | Description |
|---|
hr_mean_bpm | Double | Mean heart rate (BPM) |
hr_sdnn_ms | Double | SDNN of RR intervals (ms) |
rmssd_ms | Double | RMSSD approximation (ms) |
sample_count | Int | Number of HR samples in window |
start_ms | Long | Window start timestamp (ms) |
end_ms | Long | Window end timestamp (ms) |
motion_rms_g | Double | RMS acceleration in g-force (optional, when accelerometer available) |
motion_sample_count | Int | Number of accelerometer samples in interval (optional) |
biosignal_frame
Emitted when includeRawSamples = true. Contains raw biosignal samples for live display:
val samples = event["samples"] as? List<Map<String, Any>>
samples?.forEach { sample ->
val bpm = sample["bpm"] as? Double
val rr = sample["rr_intervals_ms"] as? List<Double> // plural, array of ms
}
session_summary
// event["type"] == "session_summary"
val duration = event["duration_actual_sec"] as? Int
val metrics = event["metrics"] as? Map<String, Any>
session_error
// event["type"] == "session_error"
val code = event["error_code"] as? String // e.g. "sensor_unavailable"
val message = event["message"] as? String
Error codes:
| Code | Description |
|---|
permission_denied | Health data permissions not granted |
sensor_unavailable | Heart rate sensor not available |
low_battery | Device battery too low |
os_terminated | Operating system terminated the session |
invalid_state | Invalid session state (e.g., duplicate start) |
Behavioral Signals
The SessionEngine accepts an optional BehaviorProvider that fuses behavioral data into session frames.
Mock Behavior (Development)
val engine = SessionEngine(behaviorProvider = MockBehaviorProvider())
engine.start(config) { event ->
val behavior = event["behavior"] as? Map<*, *>
Log.d("Session", "Stability: ${behavior?.get("stability_index")}")
}
Production (via synheart-behavior)
// Wrap any behavior SDK object as a BehaviorProvider (reflection-based)
val behaviorProvider = BehaviorSdkProvider(behaviorSdkInstance)
val engine = SessionEngine(behaviorProvider = behaviorProvider)
The "behavior" key is only present in frames when a BehaviorProvider is configured and returns data.
Architecture
SessionEngine(provider: BiosignalProvider, behaviorProvider: BehaviorProvider?)
|
+-- BiosignalProvider (interface)
| +-- MockBiosignalProvider (sinusoidal mock, 1 Hz)
| +-- WearBiosignalProvider (wraps BLE HRM, reflection-based)
| +-- HealthConnectBiosignalProvider (wraps Health Connect, reflection-based)
|
+-- BehaviorProvider (interface, pull-based)
| +-- MockBehaviorProvider (stable mid-range values)
| +-- BehaviorSdkProvider (wraps behavior SDK, reflection-based)
|
+-- SampleRingBuffer (thread-safe sliding window)
+-- computeMetrics() (pure HR -> HRV metrics)
+-- SessionError (sealed class with 5 subclasses)
Custom Provider
Any class implementing BiosignalProvider can drive the session engine:
class MyProvider : BiosignalProvider {
override val isAvailable = true
override val name = "my_source"
override fun startStreaming(onSample: (BiosignalSample) -> Unit) {
// Push BiosignalSample values into the callback
}
override fun stopStreaming() { }
}
val engine = SessionEngine(MyProvider())
API Reference
SessionEngine
| Constructor | Description |
|---|
SessionEngine() | Default mock biosignal provider, no behavior |
SessionEngine(provider) | Custom biosignal provider |
SessionEngine(provider, behaviorProvider) | Custom biosignal + behavioral providers |
SessionEngine(behaviorProvider = bp) | Default mock biosignal + behavioral provider |
| Method | Description |
|---|
start(config, callback) | Start a session. Throws if already running. |
stop(sessionId) | Stop a running session. |
getStatus() | Returns status map or null if no active session. |
Resources