# Android SDK Source: https://sdk.sleepcycle.com/en/android Advanced sleep analysis capabilities for Android applications # Sleep Cycle SDK - Android Documentation ## Overview The Sleep Cycle SDK for Android enables developers to integrate advanced sleep analysis capabilities into their applications. The SDK provides real-time sleep tracking using audio and motion sensors, delivering detailed sleep insights and events throughout the night. ## System Requirements Minimum Android API Level: * Min SDK: API level 28 (Android 9.0 Pie) * Compile SDK: API level 35 Kotlin: * Kotlin Version: 1.9+ (JVM target 11) * The SDK is written in Kotlin and provides a Kotlin-first API ## Installation Find the [latest version on Maven Central](https://central.sonatype.com/artifact/com.sleepcycle.sdk/sdk-android). ### Groovy DSL Add the Sleep Cycle SDK dependency to your `build.gradle`: ```gradle theme={null} dependencies { implementation "com.sleepcycle.sdk:sdk-android:" } ``` ### Kotlin DSL Add the Sleep Cycle SDK dependency to your `build.gradle.kts`: ```kotlin theme={null} dependencies { implementation("com.sleepcycle.sdk:sdk-android:") } ``` ## Prerequisites ### Permissions The SDK requires microphone access for audio-based sleep analysis: ```xml theme={null} ``` The SDK automatically includes the WAKE\_LOCK permission in its manifest to keep the device awake during analysis: ```xml theme={null} ``` ### Keeping the analysis active using a foreground service To ensure continuous sleep analysis throughout the night, you must implement a foreground service. This prevents Android from terminating the analysis process during extended periods. It is up to the host app to start the foreground service correctly. The service must declare appropriate foreground service types in its manifest to specify what system resources it needs access to. For sleep analysis, you'll typically need the `health` or `microphone` service types, which grant access to health sensors and microphone respectively. ## General The SDK is thread safe and can be called from any thread. ## Initialize the SDK The SDK requires authentication before use. The initialization process validates your credentials and determines available features. ```kotlin theme={null} import com.sleepcycle.sdk.SleepCycleSdk try { val features = SleepCycleSdk.initialize( context = applicationContext, apiKey = "your-api-key-here" ) Log.d("SDK", "Authorized with features: $features") } catch (e: Exception) { Log.e("SDK", "Authorization failed: ${e.message}") } ``` The returned `SleepAnalysisFeatures` indicates which capabilities are available for your API key:
sleepStaging Boolean - Sleep staging analysis
smartAlarm Boolean - Smart alarm functionality
audioEvents Boolean - Audio event detection
snoringDetection Boolean - Snoring detection
realTimeSleepStaging Boolean - Real-time sleep staging
multiChannelAnalysis Boolean - Multi-channel analysis (stereo, two channels)
extendedAudioEvents Boolean - Extended audio event types beyond the standard set
## Get the SDK state Monitor SDK state changes using the StateFlow: ```kotlin theme={null} import com.sleepcycle.sdk.SdkState val sdkStateFlow: StateFlow = SleepCycleSdk.sdkStateFlow ``` Get the current state: ```kotlin theme={null} val currentState: SdkState = SleepCycleSdk.getState() ``` ## Start a sleep analysis session Once initialized, you can start a sleep analysis session. The method returns a `UUID` that identifies the session. ```kotlin theme={null} import com.sleepcycle.sdk.SleepAnalysisConfig try { val sessionId: UUID = SleepCycleSdk.startAnalysis( config = SleepAnalysisConfig( useAudio = true, useAccelerometer = true ) ) Log.d("SDK", "Analysis started with session ID: $sessionId") } catch (e: Exception) { Log.e("SDK", "Failed to start analysis: ${e.message}") } ``` Parameters:
config SleepAnalysisConfig - Configuration object specifying which sensors to use
startMillisUtc Long - Analysis start time in UTC milliseconds (defaults to current time)
dataSource DataSource? - Optional custom data source. When null, the SDK uses live device sensors
audioEventListeners List\ - Optional list of listeners that receive callbacks during audio analysis
## Resume a session The SDK supports resuming a previously started analysis session. This is useful when your app restarts or the foreground service is terminated by the system. ```kotlin theme={null} try { if (SleepCycleSdk.isResumePossible()) { SleepCycleSdk.resumeAnalysis() } } catch (e: Exception) { Log.e("SDK", "Failed to resume analysis: ${e.message}") } ``` ## Stop a session To stop an active analysis session and retrieve the results: ```kotlin theme={null} try { val result: AnalysisResult? = SleepCycleSdk.stopAnalysis() result?.let { analysisResult -> val events = analysisResult.events val breathingRates = analysisResult.breathingRates val sleepStageIntervals = analysisResult.sleepStageIntervals analysisResult.statistics?.let { statistics -> Log.d("SDK", "Sleep duration: ${statistics.totalSleepDurationSeconds}") } events.forEach { event -> Log.d("SDK", "${event.type} from ${event.startTime} to ${event.endTime}") } breathingRates.forEach { breathingRate -> Log.d("SDK", "Breathing rate: ${breathingRate.bpm} bpm at ${breathingRate.timestampSecondsUtc}") } analysisResult.audioStatistics?.let { audioStats -> audioStats.healthIntervals.forEach { interval -> Log.d("SDK", "Audio ${interval.status}: ${interval.interval}") } } } } catch (e: Exception) { Log.e("SDK", "Failed to stop analysis: ${e.message}") } ``` ## Analysis result The `AnalysisResult` contains the complete output of a sleep analysis session:
sessionId UUID - Unique session identifier
startSecondsUtc Double - Session start time in UTC seconds
endSecondsUtc Double - Session end time in UTC seconds
timeZoneId String - IANA time zone (e.g. "Europe/Stockholm") captured at session start
events List\ - Detected sleep events
breathingRates List\ - Breathing rate measurements
sleepStageIntervals List\ - Sleep stage data
realTimeSleepStageIntervals List\ - Sleep stages emitted live during the session
statistics SleepStatistics? - Aggregated sleep statistics (nullable)
audioStatistics AudioStatistics? - Audio health statistics (nullable)
### SleepStatistics When available, `statistics` contains aggregated metrics about the sleep session:
totalSleepDurationSeconds Double - Total time spent sleeping
sleepOnsetLatencySeconds Double? - Time to fall asleep
sleepEfficiency Double - Ratio of sleep to time in bed (0.0 to 1.0)
finalWakeTimeSecondsUtc Double? - Time of final awakening (UTC seconds)
numberOfAwakenings Int - Number of awakenings during the night
snoreTimeSeconds Double - Total time spent snoring
snoreSessions List\ - Individual snoring sessions
sleepStageDurationsSeconds Map\ - Duration per sleep stage
### AudioStatistics When available, `audioStatistics` contains information about audio input health throughout the session. ## Sleep score After a session completes, you can compute a sleep score for the night with `SleepScoring.compute()`. The score combines the night's result with a short history of previous nights. ```kotlin theme={null} import com.sleepcycle.sdk.score.SleepScore import com.sleepcycle.sdk.score.SleepScoring import com.sleepcycle.sdk.score.toSleepScoreHistoryEntry // Build a history entry from each completed result and persist it for future nights val historyEntry = analysisResult.toSleepScoreHistoryEntry() val score: SleepScore = SleepScoring.compute( result = analysisResult, history = recentHistoryEntries, dateOfBirthSecondsUtc = userDateOfBirthSecondsUtc, // optional, age-adjusts the quality subscore chronoType = userChronoType // optional, selects the timing subscore window ) Log.d("SDK", "Sleep score: ${score.total} (duration=${score.duration}, quality=${score.quality}, routine=${score.routine})") ``` Each `SleepScore` field is in the range 0.0–1.0, where higher is better:
total Float - Overall sleep score for the night
duration Float - How much the user slept
quality Float - How well the user slept
routine Float - The user's sleep schedule
`SleepScoring.identifyChronoType(history)` derives the user's `ChronoType` (`EXTREME_MORNING`, `MORNING`, `INTERMEDIATE`, `EVENING`, `EXTREME_EVENING`) from recent history, or returns `null` when there isn't enough data. The result can be passed back into `compute()` as the `chronoType` argument. ## Real-time events The SDK provides real-time event updates during analysis through a Flow API: ```kotlin theme={null} import com.sleepcycle.sdk.Event import com.sleepcycle.sdk.EventType lifecycleScope.launch { SleepCycleSdk.eventFlow.collect { events: List -> events.forEach { event -> when (event.type) { EventType.MOVEMENT -> handleMovement(event) EventType.SNORING -> handleSnoring(event) EventType.TALKING -> handleTalking(event) EventType.COUGHING -> handleCoughing(event) else -> handleOtherEvent(event) } } } } ``` When the `extendedAudioEvents` feature is enabled for your API key, additional `EventType` values are reported alongside the standard ones: `BIRD`, `CAT`, `DIGESTIVE`, `DOG`, `FART`, `MUSIC`, `SNEEZE`, `THROAT_CLEARING`, `TRAFFIC`, `WATER`, and `WIND`. Each `Event` contains:
type EventType - The type of event
startTime Double - Start timestamp in UTC seconds
endTime Double - End timestamp in UTC seconds
probability Float - Confidence score (0.0 to 1.0)
source EventSource - Source of detection
sessionId UUID - The session this event belongs to
signature FloatArray? - Optional feature vector (for snoring events)
## Real-time breathing rate The SDK provides real-time breathing rate measurements during analysis: ```kotlin theme={null} import com.sleepcycle.sdk.BreathingRate lifecycleScope.launch { SleepCycleSdk.breathingRateFlow.collect { breathingRate: BreathingRate -> Log.d("SDK", "Breathing rate: ${breathingRate.bpm} bpm (confidence: ${breathingRate.confidence})") } } ``` Each `BreathingRate` contains:
timestampSecondsUtc Double - Time of measurement in seconds since Unix epoch
bpm Float - Breathing rate in breaths per minute
confidence Float - Confidence level of the measurement (0.0 to 1.0)
sessionId UUID - The session this measurement belongs to
### Real-time sleep staging (Experimental) This feature is experimental and may change in future releases. The API and behavior are subject to modification without notice. The SDK can provide real-time sleep stage predictions during analysis. This feature requires the `realTimeSleepStaging` capability to be enabled for your API key. ```kotlin theme={null} import com.sleepcycle.sdk.SleepStage import com.sleepcycle.sdk.SleepStageInterval lifecycleScope.launch { SleepCycleSdk.sleepStageFlow.collect { stageInterval: SleepStageInterval -> when (stageInterval.stage) { SleepStage.AWAKE -> Log.d("SDK", "Awake: ${stageInterval.interval}") SleepStage.LIGHT -> Log.d("SDK", "Light sleep: ${stageInterval.interval}") SleepStage.DEEP -> Log.d("SDK", "Deep sleep: ${stageInterval.interval}") SleepStage.REM -> Log.d("SDK", "REM sleep: ${stageInterval.interval}") } } } ``` The flow emits `SleepStageInterval` objects approximately every 30 seconds during analysis, providing near real-time feedback on sleep state transitions. ### Real-time audio health The SDK monitors the health of the audio input during analysis and emits status updates when the audio state changes: ```kotlin theme={null} import com.sleepcycle.sdk.AudioHealthUpdate import com.sleepcycle.sdk.AudioHealthStatus lifecycleScope.launch { SleepCycleSdk.audioHealthFlow.collect { update: AudioHealthUpdate -> when (update.status) { AudioHealthStatus.HEALTHY -> Log.d("SDK", "Audio input healthy") AudioHealthStatus.FLATLINE -> Log.w("SDK", "Audio flatline detected") AudioHealthStatus.MISSING_INPUT -> Log.w("SDK", "Audio input missing") } } } ``` `AudioHealthStatus` values:
HEALTHY - Audio input contains a varying signal
FLATLINE - Constant value detected (non-functional microphone or muted input)
MISSING\_INPUT - No audio input received for an extended period
## Smart alarm The smart alarm monitors movement during a wake-up window and emits events when the user is in a light sleep phase, allowing you to wake them at an optimal moment. This requires the `smartAlarm` feature to be enabled for your API key and is only supported on the primary channel. Configure a wake-up window with `SmartAlarmConfig` and pass it when starting analysis: ```kotlin theme={null} import com.sleepcycle.sdk.SmartAlarmConfig SleepCycleSdk.startAnalysis( config = SleepAnalysisConfig(useAudio = true, useAccelerometer = true), smartAlarmConfig = SmartAlarmConfig( wakeupWindowStartSecondsUtc = windowStartSecondsUtc, wakeupWindowEndSecondsUtc = windowEndSecondsUtc ) ) ``` Observe alarm lifecycle events through `smartAlarmFlow`: ```kotlin theme={null} import com.sleepcycle.sdk.SmartAlarmEvent lifecycleScope.launch { SleepCycleSdk.smartAlarmFlow.collect { event: SmartAlarmEvent -> when (val type = event.type) { is SmartAlarmEvent.Type.Armed -> Log.d("SDK", "Smart alarm armed") is SmartAlarmEvent.Type.Triggered -> when (type.reason) { SmartAlarmEvent.Reason.OPTIMAL_WAKE_UP -> ringAlarm() SmartAlarmEvent.Reason.USER_INTERACTION -> ringAlarm() SmartAlarmEvent.Reason.WINDOW_END -> ringAlarm() } } } } ``` The `smartAlarmConfig` parameter is also available on `startMultiChannelAnalysis()`. ## Event signatures For snoring events, the `Event.signature` property contains a 16-dimensional feature vector that represents unique characteristics of the detected snore. Snore events from the same person are grouped close to each other in the signature space, allowing clustering of events by person. ## Audio event listener The `AudioEventListener` interface allows you to receive real-time audio analysis updates during a session. Implement this interface to access raw audio samples, event detection, and volume information as analysis progresses. ```kotlin theme={null} val audioEventListener = object : AudioEventListener { override fun onAudioAnalysisBatchCompleted( audioSamples: FloatArray, audioSampleRate: Int, audioStartTime: Double, audioEndTime: Double, audioProbability: Map, eventsStarted: List, eventsEnded: List, dbSpl: FloatArray, sessionId: UUID ) { // Process audio samples and events } } try { SleepCycleSdk.startAnalysis( config = SleepAnalysisConfig(useAudio = true), audioEventListeners = listOf(audioEventListener) ) } catch (e: Exception) { Log.e("SDK", "Failed to start analysis: ${e.message}") } ``` The `audioSamples` parameter contains all processed audio data in sequence, without any gaps or overlap between batches. Each batch continues exactly where the previous batch ended, ensuring complete coverage of all analyzed audio. The `audioProbability` parameter provides the per-batch probability for each `EventType` in the analyzed time window. The `dbSpl` parameter provides the A-weighted sound volume in dB SPL for each time frame in the batch. ## Audio clips The SDK can capture short audio recordings when specific sleep events are detected, such as snoring, sleep talking, or coughing. To use audio clips, create an audio clips producer and pass it to `startAnalysis`: ```kotlin theme={null} import com.sleepcycle.sdk.* // Configure which events trigger audio clips val audioClipsConfig = AudioClipsConfig( activeTypes = hashMapOf( EventType.SNORING to EventTypeConfig(minDuration = 0.5), EventType.TALKING to EventTypeConfig(minDuration = 0.5) ), clipLength = 10.0 // Clip duration in seconds ) // Implement receiver to handle captured clips val audioClipsReceiver = object : AudioClipsReceiver { override fun onAudioClipReceived(audioClip: AudioClip) { // Process or store the audio clip } } try { // Create audio clips producer val audioClipsProducer = SleepCycleSdk.createAudioClipsProducer( config = audioClipsConfig, receiver = audioClipsReceiver ) // Pass to startAnalysis SleepCycleSdk.startAnalysis( config = SleepAnalysisConfig(useAudio = true), audioEventListeners = listOf(audioClipsProducer) ) } catch (e: Exception) { Log.e("SDK", "Failed to start analysis with audio clips: ${e.message}") } ``` Each `AudioClip` contains:
startTime Double - Start timestamp in seconds
type EventType - The event type that triggered the capture
samples FloatArray - Raw audio samples
sampleRate Int - Sample rate in Hz
sessionId UUID - The session this clip belongs to
## Multi-channel analysis The SDK supports analyzing two channels simultaneously using a stereo audio source. Multi-channel analysis separates the data source lifecycle from individual session lifecycles, allowing you to start and stop sessions on each channel independently. The stereo stream is expected to come from two separate mono microphones combined into a single stereo stream, with one microphone per channel. This requires the `multiChannelAnalysis` feature to be enabled for your API key. ### Channel separation When using stereo input, `ChannelSeparationConfig` controls how audio events are assigned to channels. Built-in presets: * `BED_SIDE_MICS` — bedside microphones placed apart (default) * `CENTERED_MIC_ARRAY` — closely spaced microphone array * `DETECTION_STRENGTH_ONLY` — no spatial filtering, uses only detection confidence All parameters (mic distance, ambiguous zone, confidence threshold, per-event-type settings) can be tuned individually to fit your specific hardware setup and use case. ### Data source lifecycle Start the data source with a stereo audio configuration before starting individual sessions: ```kotlin theme={null} try { val dataSource = SleepCycleSdk.createLiveDataSource( audioFormat = DataSource.AudioFormat.STEREO ) SleepCycleSdk.startDataSource( dataSource = dataSource, channelSeparationConfig = ChannelSeparationConfig.BED_SIDE_MICS ) } catch (e: Exception) { Log.e("SDK", "Failed to start data source: ${e.message}") } ``` ### Starting sessions on each channel Once the data source is running, start a session on each channel: ```kotlin theme={null} import com.sleepcycle.sdk.AnalysisChannel try { val primarySessionId: UUID = SleepCycleSdk.startMultiChannelAnalysis( channel = AnalysisChannel.PRIMARY, config = SleepAnalysisConfig(useAudio = true, useAccelerometer = true) ) val secondarySessionId: UUID = SleepCycleSdk.startMultiChannelAnalysis( channel = AnalysisChannel.SECONDARY, config = SleepAnalysisConfig(useAudio = true, useAccelerometer = false) ) } catch (e: Exception) { Log.e("SDK", "Failed to start multi-channel analysis: ${e.message}") } ``` `AnalysisChannel` values:
PRIMARY - First audio channel (or mono)
SECONDARY - Second audio channel in stereo
### Stopping sessions independently Each session can be stopped independently to retrieve its result: ```kotlin theme={null} try { val primaryResult: AnalysisResult? = SleepCycleSdk.stopAnalysis( sessionId = primarySessionId ) val secondaryResult: AnalysisResult? = SleepCycleSdk.stopAnalysis( sessionId = secondarySessionId ) } catch (e: Exception) { Log.e("SDK", "Failed to stop analysis: ${e.message}") } ``` ### Stopping the data source After all sessions have been stopped, stop the data source: ```kotlin theme={null} try { SleepCycleSdk.stopDataSource() } catch (e: Exception) { Log.e("SDK", "Failed to stop data source: ${e.message}") } ``` Calling `stopDataSource()` while sessions are still active will force-stop them and discard their results. To retrieve results, stop each session first. # Android Release Notes Source: https://sdk.sleepcycle.com/en/android-release-notes ## Version 1.2.0 ### New Features * **Sleep score**: Compute a per-night sleep score with `SleepScoring.compute(result, history, dateOfBirthSecondsUtc, chronoType)`, which returns a `SleepScore` with `total`, `duration`, `quality`, and `routine` subscores (each 0.0–1.0). Build history entries from a completed result via `AnalysisResult.toSleepScoreHistoryEntry()` and derive the user's `ChronoType` with `SleepScoring.identifyChronoType(history)`. * **Extended audio events**: New `extendedAudioEvents` feature flag exposes additional `EventType` values beyond the standard set. * **Real-time sleep staging in results**: `AnalysisResult` now includes `realTimeSleepStageIntervals`, the sleep stages emitted live during the session (in addition to the post-session `sleepStageIntervals`). ### API Changes * `AudioEventListener.onAudioAnalysisBatchCompleted()` now receives `audioProbability: Map`, the per-batch probability for each event type. The `rms: FloatArray` parameter has been replaced by `dbSpl: FloatArray`, providing A-weighted sound volume in dB SPL. * `EventEndedInfo` now includes an `eventType: EventType` property. * `AnalysisResult` now includes `timeZoneId: String` (the IANA time zone captured at session start) and `realTimeSleepStageIntervals: List`. ## Version 1.1.1 ### New Features * **Smart alarm**: Configure a wake-up window via `SmartAlarmConfig` and observe alarm lifecycle events (`Armed`, `Triggered`) through the new `smartAlarmFlow: SharedFlow`. Trigger reasons include `OPTIMAL_WAKE_UP`, `USER_INTERACTION`, and `WINDOW_END`. Smart alarm is gated behind `SleepAnalysisFeatures.smartAlarm` and only supported on the primary channel. * **Stereo channel separation**: New `ChannelSeparationConfig` controls how stereo audio channels are assigned to analysis channels, with built-in presets `BED_SIDE_MICS`, `CENTERED_MIC_ARRAY`, and `DETECTION_STRENGTH_ONLY`. ### API Changes * Added `smartAlarmConfig: SmartAlarmConfig?` parameter to `startAnalysis()` and `startMultiChannelAnalysis()`. * Added `channelSeparationConfig: ChannelSeparationConfig` parameter to `startAnalysis()`, `startDataSource()`, and `resumeAnalysis()`. * Added `forceTokenRefresh: Boolean` parameter to `initialize()` to force a fresh token fetch even if a cached token exists. * `resumeAnalysis()` now returns `List` instead of `Boolean`, providing session ID, channel, and smart alarm config for each resumed session. * `createLiveDataSource()` now enables the gyroscope when the accelerometer is enabled. * `stopDataSource()` and `stopAnalysis()` no longer throw when the SDK is not initialized; they return gracefully instead. ## Version 1.1.0 ### New Features * **Multi-channel analysis**: Analyze two channels simultaneously using stereo audio. New `startDataSource()` / `stopDataSource()` methods separate the data source lifecycle from session lifecycles, with per-channel start via `startMultiChannelAnalysis(channel:)` and per-session stop via `stopAnalysis(sessionId:)`. * **Audio health monitoring**: New `audioHealthFlow: SharedFlow` emits real-time audio input health status changes (`HEALTHY`, `FLATLINE`, `MISSING_INPUT`). `AudioStatistics` and `AudioHealthInterval` are included in the analysis result. * **Session IDs on all public types**: `Event`, `BreathingRate`, `SleepStageInterval`, `AudioClip`, and `AudioHealthUpdate` now include a `sessionId: UUID` property to identify which session they belong to. ### API Changes * `startAnalysis()` now returns a `UUID` identifying the session. * Added `tag: String?` parameter to `startAnalysis()` for labeling sessions. * `AudioEventListener.onAudioAnalysisBatchCompleted()` now includes a `sessionId: UUID` parameter. * `AnalysisResult` now includes `sleepStageIntervals`, `audioStatistics`, and `sessionId` properties. * Added `multiChannelAnalysis` to `SleepAnalysisFeatures`. ## Version 1.0.10 **Released:** January 26, 2026 ### New Features * Disk space checking: The SDK now checks for sufficient disk space before starting analysis and throws `SdkNotEnoughDiskSpaceException` if there is insufficient space available. ### API Changes * Added `SdkNotInitializedException` exception thrown when SDK methods are called before initialization. * Added `@throws` documentation for `SdkNotInitializedException` and `SdkNotEnoughDiskSpaceException` on `startAnalysis()`. ### Bug Fixes * Improved error handling when writing to the database to prevent crashes on low disk space. ## Version 1.0.9 **Released:** January 15, 2026 ### New Features * Preferred audio device: Added ability to specify a preferred audio input device via the `preferredDevice` parameter in `createLiveDataSource()`. ### API Changes * Added support for stereo audio input. * The `dataSource` parameter in `startAnalysis()` and `resumeAnalysis()` is now mandatory (non-nullable) with a default value of `createLiveDataSource()`. * Added `createLiveDataSource()` factory method for creating a live data source that captures from device sensors. * Made `LiveDataSource` class public, allowing direct instantiation for custom configurations. * Extended `DataSource` interface with new methods: * `audioOutputEnabled()` - Check if audio output is enabled * `accelerometerOutputEnabled()` - Check if accelerometer output is enabled * `getAudioFormat()` - Returns the audio channel configuration (MONO or STEREO) * Published artifact now includes source files (excluding internal package) for easier debugging. # Cough Radar API Source: https://sdk.sleepcycle.com/en/cough-radar Geospatial coughing analytics API for health monitoring ## Overview The Cough Radar REST API provides geospatial coughing analytics, enabling health monitoring applications to display coughing heatmaps and retrieve classification levels (normal, elevated, high) for specific geographic locations. A typical integration displays an interactive map with cough-intensity heatmap overlays, accompanied by a panel showing the current classification and historical trends for the selected region. To build this: 1. Use [`/v1/tiles`](#post-%2Fv1%2Ftiles) to get tile URLs for rendering heatmap overlays on your map. The response is compatible with standard map libraries like Google Maps, Apple Maps, and Mapbox. 2. Use [`/v1/classifications`](#post-%2Fv1%2Fclassifications) to fetch the cough activity level (normal, elevated, high) and trend data for the user's current view or location. Display the classification status and render the time series data in a graph. ## Authentication All API requests require authentication via the `API-Key` header. Your API key must have the `coughRadar` capability enabled. ``` API-Key: your-api-key ``` ## Base URL ``` https://cough-radar-api.sdk.sleepcycle.com ``` ## Endpoints ### POST /v1/tiles Fetch available heatmap tile data for a date range. Returns tile metadata including URLs for rendering map overlays. **Request** ```bash theme={null} curl -X POST https://cough-radar-api.sdk.sleepcycle.com/v1/tiles \ -H "Content-Type: application/json" \ -H "API-Key: your-api-key" \ -d '{ "date_interval_start": "2026-01-01T00:00:00+00:00", "date_interval_end": "2026-02-01T00:00:00+00:00" }' ``` **Request Body** | Field | Type | Required | Description | | --------------------- | -------- | -------- | ----------------------------------- | | `date_interval_start` | datetime | Yes | Start date (ISO 8601 with timezone) | | `date_interval_end` | datetime | Yes | End date (ISO 8601 with timezone) | **Response (200 OK)** ```json theme={null} { "name": "aabbbccc-1234-5678-9101-abcdefabcdef", "tile_data": [ {"date": "2026-02-01T00:00:00+00:00", "date_string": "2026-02-01"} ], "base_template_url": "https://{base-url}/aabbbccc-1234-5678-9101-abcdefabcdef", "relative_tile_template_path": "{date_string}/tile_{x}_{y}_{z}.png", "max_zoom": 10 } ``` | Field | Type | Description | | ----------------------------- | ------- | --------------------------------- | | `name` | string | Tile set identifier | | `tile_data` | array | Available dates with tile data | | `base_template_url` | string | Base URL for tile images | | `relative_tile_template_path` | string | URL template for individual tiles | | `max_zoom` | integer | Maximum zoom level available | *** ### POST /v1/classifications Get coughing classification (normal, elevated, high) for a geographic location or bounding box on a specific date. You can specify the location using either: * **coordinate** - A single point. The API returns aggregated data for the surrounding area (approximately 200 km radius). * **bounding\_box** - A geographic rectangle. The API returns aggregated data for the specified region. **Request (with coordinate)** ```bash theme={null} curl -X POST https://cough-radar-api.sdk.sleepcycle.com/v1/classifications \ -H "Content-Type: application/json" \ -H "API-Key: your-api-key" \ -d '{ "date": "2026-02-01T00:00:00+00:00", "coordinate": {"latitude": 59.33, "longitude": 18.07} }' ``` **Request (with bounding box)** ```bash theme={null} curl -X POST https://cough-radar-api.sdk.sleepcycle.com/v1/classifications \ -H "Content-Type: application/json" \ -H "API-Key: your-api-key" \ -d '{ "date": "2026-02-01T00:00:00+00:00", "bounding_box": { "latitude_min": 59.0, "latitude_max": 60.0, "longitude_min": 17.0, "longitude_max": 18.0 } }' ``` **Request Body** | Field | Type | Required | Description | | ---------------------- | -------- | -------- | ------------------------------------------------ | | `date` | datetime | Yes | Date for classification (ISO 8601 with timezone) | | `trend_number_of_days` | integer | No | Days for trend calculation (default: 30) | | `coordinate` | object | No\* | Single point location | | `bounding_box` | object | No\* | Geographic bounding box | \*Either `coordinate` or `bounding_box` must be provided. **Response (200 OK)** ```json theme={null} { "classification": "normal", "coughing_per_hour_average": 0.8, "coughing_data_time_series": { "date": ["2026-01-31T00:00:00+00:00", "2026-02-01T00:00:00+00:00"], "cough_per_hour_average": [0.25, 0.45] }, "elevated_threshold": 0.8, "high_threshold": 1.2 } ``` | Field | Type | Description | | --------------------------- | ------ | ------------------------------------- | | `classification` | string | Classification level (see Data Types) | | `coughing_per_hour_average` | float | Average coughs per hour | | `coughing_data_time_series` | object | Historical trend data | | `elevated_threshold` | float | Threshold for elevated classification | | `high_threshold` | float | Threshold for high classification | *** ## Data Types ### Classification Values Classifications compare current cough levels against historical data from the same location over the past year: | Value | Description | | ----------------- | ------------------------------------------------------------------------------------------------ | | `normal` | Cough levels are within the typical range for this location | | `elevated` | Cough levels are higher than usual, ranking in the top 25% of recorded levels over the past year | | `high` | Cough levels are very high, ranking in the top 10% of recorded levels over the past year | | `not_enough_data` | Insufficient data to accurately classify cough levels | ### Coughing Data Time Series Object Historical trend data for the requested location. | Field | Type | Description | | ------------------------ | ----- | --------------------------------------------------- | | `date` | array | List of dates (ISO 8601 with timezone) | | `cough_per_hour_average` | array | Average coughs per hour for each corresponding date | ### Coordinate Object | Field | Type | Range | Description | | ----------- | ----- | ----------- | ---------------------------- | | `latitude` | float | -90 to 90 | Latitude in decimal degrees | | `longitude` | float | -180 to 180 | Longitude in decimal degrees | ### Bounding Box Object | Field | Type | Range | Description | | --------------- | ----- | ----------- | ----------------- | | `latitude_min` | float | -90 to 90 | Southern boundary | | `latitude_max` | float | -90 to 90 | Northern boundary | | `longitude_min` | float | -180 to 180 | Western boundary | | `longitude_max` | float | -180 to 180 | Eastern boundary | *** ## Error Codes | Status Code | Description | | ----------- | --------------------------------------- | | 200 | Success | | 401 | Missing or invalid API key | | 403 | API key lacks `coughRadar` capability | | 422 | Invalid request body (validation error) | | 500 | Internal server error | # Overview Source: https://sdk.sleepcycle.com/en/index Sleep Cycle SDK Documentation # Sleep Cycle SDK The Sleep Cycle SDK enables developers to integrate advanced sleep analysis capabilities into their mobile applications. The SDK provides real-time sleep tracking using audio and motion sensors, delivering detailed sleep insights and events throughout the night. ## Platform Documentation Integration guide for Android applications Integration guide for iOS applications ## REST APIs Geospatial coughing analytics API # iOS SDK Source: https://sdk.sleepcycle.com/en/ios Advanced sleep analysis capabilities for iOS applications # Sleep Cycle SDK - iOS Documentation ## Overview The Sleep Cycle SDK for iOS enables developers to integrate advanced sleep analysis capabilities into their applications. The SDK provides real-time sleep tracking using audio and motion sensors, delivering detailed sleep insights, stage transitions, and detected events throughout the night. ## System Requirements Minimum iOS Version: * iOS: 16.0+ * macOS: 13.0+ Swift: * Swift Version: 5.9+ * The SDK is written in Swift and provides a Swift-native API with async/await support ## Installation ### Swift Package Manager Add the Sleep Cycle SDK to your project using Swift Package Manager: 1. In Xcode, select **File** → **Add Package Dependencies** 2. Enter the package URL: `https://github.com/MDLabs/sleepcycle-sdk-swift` 3. Select the version you want to use (Semantic Versioning) 4. Add the package to your target Alternatively, add it to your `Package.swift`: ```swift theme={null} dependencies: [ .package(url: "https://github.com/MDLabs/sleepcycle-sdk-swift", from: "1.2.0") ] ``` > The SDK requires an API key for authorization. Contact Sleep Cycle to obtain credentials. ## Prerequisites ### Permissions The SDK requires microphone access for audio-based sleep analysis. Add the following to your `Info.plist`: ```xml theme={null} NSMicrophoneUsageDescription We need access to the microphone to analyze your sleep patterns and detect snoring. ``` For motion-based analysis, you may also need: ```xml theme={null} NSMotionUsageDescription We use motion data to track your sleep movements. ``` ### Background modes To ensure continuous sleep analysis throughout the night, enable the appropriate background modes in your app's capabilities: 1. Open your project in Xcode 2. Select your app target 3. Go to "Signing & Capabilities" 4. Add "Background Modes" capability 5. Enable "Audio" Alternatively, add this to your `Info.plist`: ```xml theme={null} UIBackgroundModes audio ``` The SDK uses the audio background mode to maintain continuous audio processing during sleep analysis. Your app should also implement proper session management to prevent iOS from suspending the analysis process. ## General The SDK is thread-safe and uses Swift concurrency (async/await) for all asynchronous operations. ## Initialize the SDK The SDK requires authentication before use. The initialization process validates your credentials and determines available features. ```swift theme={null} import SleepCycleSDK Task { do { let features = try await SleepCycleSdk.initialize( logLevel: .info, apiKey: "your-api-key-here" ) print("Authorized with features: \(features)") } catch { print("Initialization error: \(error)") } } ``` Parameters:
logLevel LogLevel - SDK log verbosity (.debug, .info, .warning, .error, .noLog)
logger Logger? - Optional custom logger (defaults to the system logger)
apiKey String - Your Sleep Cycle API key
forceTokenRefresh Bool - Force a fresh token fetch even if a cached token exists (defaults to false)
The returned `SleepAnalysisFeatures` indicates which capabilities are available for your API key:
sleepStaging Bool - Sleep staging analysis
smartAlarm Bool - Smart alarm functionality
audioEvents Bool - Audio event detection
snoringDetection Bool - Snoring detection
realTimeSleepStaging Bool - Real-time sleep staging
multiChannelAnalysis Bool - Multi-channel analysis (stereo, two channels)
extendedAudioEvents Bool - Extended audio event types beyond the standard set
Access feature flags at runtime: ```swift theme={null} if SleepCycleSdk.isFeatureEnabled(\.audioEvents) { // Present snore/talk event UI } ``` ## Get the SDK state Monitor SDK state changes using the AsyncStream: ```swift theme={null} import SleepCycleSDK Task { for await state in await SleepCycleSdk.stateStream { switch state { case .uninitialized: print("SDK not initialized") case .initialized: print("SDK ready") case .running: print("Analysis in progress") } } } ``` Get the current state: ```swift theme={null} let currentState = await SleepCycleSdk.currentState ``` ## Start a sleep analysis session Once initialized, you can start a sleep analysis session. The method returns a `UUID` that identifies the session. ```swift theme={null} import SleepCycleSDK Task { do { let sessionId: UUID = try await SleepCycleSdk.startAnalysis( config: SleepAnalysisConfig( useAudio: true, useAccelerometer: true ) ) print("Analysis started with session ID: \(sessionId)") } catch { print("Failed to start analysis: \(error)") } } ``` Parameters:
config SleepAnalysisConfig - Configuration object specifying which sensors to use
at Date - The start time for the analysis (defaults to current time)
using DataSource? - Optional data source (e.g., live or file replay)
eventListeners \[AudioEventListener] - Optional array of listeners that receive callbacks during audio analysis. Use this to capture audio samples and events in real-time
## Resume a session The SDK supports resuming a previously started analysis session. This is useful when your app restarts or the background task is terminated by the system. ```swift theme={null} if await SleepCycleSdk.isResumePossible() { let resumedSessions: [ResumedSession] = try await SleepCycleSdk.resumeAnalysis() for session in resumedSessions { print("Resumed \(session.sessionId) on channel \(session.channel)") } } ``` `resumeAnalysis()` returns an array of `ResumedSession`, each containing the `sessionId`, `channel`, and any persisted `smartAlarmConfig` for the resumed session. ## Stop a session To stop an active analysis session and retrieve the results: ```swift theme={null} Task { do { let result = try await SleepCycleSdk.stopAnalysis() print("Session ID: \(result.sessionId)") if let statistics = result.statistics { print("Total sleep duration: \(statistics.totalSleepDuration ?? 0)") print("Sleep efficiency: \(statistics.sleepEfficiency ?? 0)") if let snoreSessions = statistics.snoreSessions { for session in snoreSessions { print("Snoring session: \(session.interval)") } } } for event in result.events { print("\(event.type) detected: \(event.interval), p=\(event.probability)") } for breathingRate in result.breathingRates { print("Breathing rate: \(breathingRate.bpm) bpm at \(breathingRate.timestamp)") } for stageInterval in result.sleepStageIntervals { print("\(stageInterval.stage): \(stageInterval.interval)") } if let audioStats = result.audioStatistics { for interval in audioStats.healthIntervals { print("Audio \(interval.status): \(interval.interval)") } } } catch { print("Failed to stop analysis: \(error)") } } ``` ## Analysis result The `AnalysisResult` contains the complete output of a sleep analysis session:
sessionId UUID - Unique session identifier
startTime Date - Session start time
endTime Date - Session end time
timeZone TimeZone - Time zone captured at session start
events \[Event] - Detected sleep events
breathingRates \[BreathingRate] - Breathing rate measurements
sleepStageIntervals \[SleepStageInterval] - Sleep stage data
realTimeSleepStageIntervals \[SleepStageInterval] - Sleep stages emitted live during the session
statistics SleepStatistics? - Aggregated sleep statistics (optional)
audioStatistics AudioStatistics? - Audio health statistics (optional)
### SleepStatistics When available, `statistics` contains aggregated metrics about the sleep session:
totalSleepDuration Double? - Total time spent sleeping
sleepOnsetLatency Double? - Time to fall asleep
sleepEfficiency Double? - Ratio of sleep to time in bed (0.0 to 1.0)
finalWakeTime Date? - Time of final awakening
numberOfAwakenings Int? - Number of awakenings during the night
snoreTime Double? - Total time spent snoring
snoreSessions \[SnoreSession]? - Individual snoring sessions
sleepStageDurations \[SleepStage: Double]? - Duration per sleep stage
### AudioStatistics When available, `audioStatistics` contains information about audio input health throughout the session. ## Sleep score After a session completes, you can compute a sleep score for the night with `SleepScoring.compute(result:history:dateOfBirth:chronoType:)`. The score combines the night's result with a short history of previous nights. ```swift theme={null} import SleepCycleSDK // Build a history entry from each completed result and persist it for future nights let historyEntry = result.toSleepScoreHistoryEntry() let score = SleepScoring.compute( result: result, history: recentHistoryEntries, dateOfBirth: userDateOfBirth, // optional, age-adjusts the quality subscore chronoType: userChronoType // optional, selects the timing subscore window ) print("Sleep score: \(score.total) (duration=\(score.duration), quality=\(score.quality), routine=\(score.routine))") ``` Each `SleepScore` field is in the range 0.0–1.0, where higher is better:
total Float - Overall sleep score for the night
duration Float - How much the user slept
quality Float - How well the user slept
routine Float - The user's sleep schedule
`SleepScoring.identifyChronoType(history:)` derives the user's `ChronoType` (`.extremeMorning`, `.morning`, `.intermediate`, `.evening`, `.extremeEvening`) from recent history, or returns `nil` when there isn't enough data. The result can be passed back into `compute(...)` as the `chronoType` argument. ## Real-time events The SDK provides real-time event updates during analysis through an AsyncStream API: ```swift theme={null} Task { for await events in await SleepCycleSdk.eventStream { for event in events { switch event.type { case .movement: handleMovement(event) case .snoring: handleSnoring(event) case .talking: handleTalking(event) case .coughing: handleCoughing(event) default: handleOtherEvent(event) } } } } ``` When the `extendedAudioEvents` feature is enabled for your API key, additional `EventType` values are reported alongside the standard ones: `.bird`, `.cat`, `.digestive`, `.dog`, `.fart`, `.music`, `.sneeze`, `.throatClearing`, `.traffic`, `.water`, and `.wind`. Use `EventType.standardTypes` and `EventType.extendedTypes` to distinguish the two sets. Each `Event` contains:
type EventType - The type of event
interval DateInterval - Time interval of the event
probability Double - Confidence score (0.0 to 1.0)
source EventSource - Source of detection
sessionId UUID - The session this event belongs to
signature \[Float]? - Optional feature vector (for snoring events)
## Real-time breathing rate The SDK provides real-time breathing rate measurements during analysis: ```swift theme={null} Task { for await breathingRate in await SleepCycleSdk.breathingRateStream { print("Breathing rate: \(breathingRate.bpm) bpm (confidence: \(breathingRate.confidence))") } } ``` Each `BreathingRate` contains:
timestamp Date - The time when the measurement was recorded
bpm Double - Breathing rate in breaths per minute
confidence Double - Confidence score of the measurement (0.0 to 1.0)
sessionId UUID - The session this measurement belongs to
### Real-time sleep staging (Experimental) This feature is experimental and may change in future releases. The API and behavior are subject to modification without notice. The SDK can provide real-time sleep stage predictions during analysis. This feature requires the `realTimeSleepStaging` capability to be enabled for your API key. ```swift theme={null} Task { for await stageInterval in await SleepCycleSdk.sleepStageStream { switch stageInterval.stage { case .awake: print("Awake: \(stageInterval.interval)") case .light: print("Light sleep: \(stageInterval.interval)") case .deep: print("Deep sleep: \(stageInterval.interval)") case .rem: print("REM sleep: \(stageInterval.interval)") } } } ``` The stream emits `SleepStageInterval` objects approximately every 30 seconds during analysis, providing near real-time feedback on sleep state transitions. ### Real-time audio health The SDK monitors the health of the audio input during analysis and emits status updates when the audio state changes: ```swift theme={null} Task { for await update in await SleepCycleSdk.audioHealthStream { switch update.status { case .healthy: print("Audio input healthy") case .flatline: print("Audio flatline detected") case .missingInput: print("Audio input missing") } } } ``` `AudioHealthStatus` values:
.healthy - Audio input contains a varying signal
.flatline - Constant value detected (non-functional microphone or muted input)
.missingInput - No audio input received for an extended period
## Smart alarm The smart alarm monitors movement during a wake-up window and emits events when the user is in a light sleep phase, allowing you to wake them at an optimal moment. This requires the `smartAlarm` feature to be enabled for your API key and is only supported on the primary channel. Configure a wake-up window with `SmartAlarmConfig` and pass it when starting analysis: ```swift theme={null} import SleepCycleSDK let wakeupWindow = DateInterval(start: windowStart, end: windowEnd) try await SleepCycleSdk.startAnalysis( config: SleepAnalysisConfig(useAudio: true, useAccelerometer: true), smartAlarmConfig: SmartAlarmConfig(wakeupWindow: wakeupWindow) ) ``` Observe alarm lifecycle events through `smartAlarmStream`: ```swift theme={null} Task { for await event in await SleepCycleSdk.smartAlarmStream { switch event.type { case .armed: print("Smart alarm armed") case .triggered(let reason): switch reason { case .optimalWakeUp, .userInteraction, .windowEnd: ringAlarm() } } } } ``` The `smartAlarmConfig` parameter is also available on `startMultiChannelAnalysis(...)`. ## Event signatures For snoring events, the `Event.signature` property contains a 16-dimensional feature vector that represents unique characteristics of the detected snore. Snore events from the same person are grouped close to each other in the signature space, allowing clustering of events by person. ## Audio event listener The `AudioEventListener` protocol allows you to receive real-time audio analysis updates during a session. Implement this protocol to access raw audio samples, event detection, and volume information as analysis progresses. ```swift theme={null} class MyAudioListener: AudioEventListener { func onAudioAnalysisBatchCompleted( sessionId: UUID, audioSamples: [Float], audioSampleRate: Int, audioStartTime: Date, audioEndTime: Date, audioProbability: [EventType: Float], eventsStarted: [EventStartedInfo], eventsEnded: [EventEndedInfo], dbSpl: [Float] ) { // Process audio samples and events } } let listener = MyAudioListener() try await SleepCycleSdk.startAnalysis( config: SleepAnalysisConfig(useAudio: true), eventListeners: [listener] ) ``` The `audioSamples` parameter contains all processed audio data in sequence, without any gaps or overlap between batches. Each batch continues exactly where the previous batch ended, ensuring complete coverage of all analyzed audio. The `audioProbability` parameter provides the per-batch probability for each `EventType` in the analyzed time window. The `dbSpl` parameter provides the A-weighted sound volume in dB SPL for each time frame in the batch. ## Audio clips The SDK can capture short audio recordings when specific sleep events are detected, such as snoring, sleep talking, or coughing. Create an `AudioEventListener` using `AudioClipsConfig` and `AudioClipsReceiver`: ```swift theme={null} import SleepCycleSDK let listener = SleepCycleSdk.createAudioClipsProducer( audioClipsConfig: AudioClipsConfig( activeTypes: [ .snoring: EventTypeConfig(minDuration: 0.5), .talking: EventTypeConfig(minDuration: 0.5) ], clipLength: 5.0 ), receiver: MyAudioHandler() ) try await SleepCycleSdk.startAnalysis( config: SleepAnalysisConfig(useAudio: true), eventListeners: [listener] ) ``` Each `AudioClip` contains:
startTime Date - Start timestamp
type EventType - The event type that triggered the capture
samples \[Float] - Raw audio samples
sampleRate Int - Sample rate in Hz
sessionId UUID - The session this clip belongs to
## Multi-channel analysis The SDK supports analyzing two channels simultaneously using a stereo audio source. Multi-channel analysis separates the data source lifecycle from individual session lifecycles, allowing you to start and stop sessions on each channel independently. The stereo stream is expected to come from two separate mono microphones combined into a single stereo stream, with one microphone per channel. This requires the `multiChannelAnalysis` feature to be enabled for your API key. ### Channel separation When using stereo input, `ChannelSeparationConfig` controls how audio events are assigned to channels. Built-in presets: * `.bedSideMics` — bedside microphones placed apart (default) * `.centeredMicArray` — closely spaced microphone array * `.detectionStrengthOnly()` — no spatial filtering, uses only detection confidence All parameters (mic distance, ambiguous zone, confidence threshold, per-event-type settings) can be tuned individually to fit your specific hardware setup and use case. ### Data source lifecycle Start the data source with a stereo audio configuration before starting individual sessions: ```swift theme={null} import SleepCycleSDK let dataSource = SleepCycleSdk.createLiveDataSource() try await SleepCycleSdk.startDataSource( using: dataSource, channelSeparationConfig: .bedSideMics ) ``` ### Starting sessions on each channel Once the data source is running, start a session on each channel: ```swift theme={null} let primarySessionId: UUID = try await SleepCycleSdk.startMultiChannelAnalysis( channel: .primary, config: SleepAnalysisConfig(useAudio: true, useAccelerometer: true) ) let secondarySessionId: UUID = try await SleepCycleSdk.startMultiChannelAnalysis( channel: .secondary, config: SleepAnalysisConfig(useAudio: true, useAccelerometer: false) ) ``` `AnalysisChannel` values:
.primary - First audio channel (or mono)
.secondary - Second audio channel in stereo
### Stopping sessions independently Each session can be stopped independently to retrieve its result: ```swift theme={null} let primaryResult = try await SleepCycleSdk.stopAnalysis(sessionId: primarySessionId) let secondaryResult = try await SleepCycleSdk.stopAnalysis(sessionId: secondarySessionId) ``` ### Stopping the data source After all sessions have been stopped, stop the data source: ```swift theme={null} await SleepCycleSdk.stopDataSource() ``` Calling `stopDataSource()` while sessions are still active will force-stop them and discard their results. To retrieve results, stop each session first. # iOS Release Notes Source: https://sdk.sleepcycle.com/en/ios-release-notes ## Version 1.2.0 ### New Features * **Smart alarm**: Configure a wake-up window via `SmartAlarmConfig` and observe alarm lifecycle events through the new `smartAlarmStream: AsyncStream`. Event types are `.armed` and `.triggered(reason)`, with reasons `.optimalWakeUp`, `.userInteraction`, and `.windowEnd`. Smart alarm is gated behind the `smartAlarm` feature and only supported on the primary channel. * **Stereo channel separation**: New `ChannelSeparationConfig` controls how stereo audio channels are assigned to analysis channels, with built-in presets `.bedSideMics`, `.centeredMicArray`, and `.detectionStrengthOnly`. * **Configurable audio input**: New `AudioInputConfig` selects the audio format (mono/stereo) and a preferred input device when creating a live data source via `createLiveDataSource(audioConfig:)`. * **Sleep score**: Compute a per-night sleep score with `SleepScoring.compute(result:history:dateOfBirth:chronoType:)`, which returns a `SleepScore` with `total`, `duration`, `quality`, and `routine` subscores (each 0.0–1.0). Build history entries from a completed result via `AnalysisResult.toSleepScoreHistoryEntry()` and derive the user's `ChronoType` with `SleepScoring.identifyChronoType(history:)`. * **Extended audio events**: New `extendedAudioEvents` feature flag exposes additional `EventType` values beyond the standard set. `EventType.standardTypes` and `EventType.extendedTypes` group the available sets. * **Real-time sleep staging in results**: `AnalysisResult` now includes `realTimeSleepStageIntervals`, the sleep stages emitted live during the session (in addition to the post-session `sleepStageIntervals`). ### API Changes * The channel-aware start method is now `startMultiChannelAnalysis(channel:config:at:smartAlarmConfig:tag:)` and accepts a `smartAlarmConfig:` parameter. * `stopAnalysis(channel:)` is now `stopAnalysis(sessionId:)`, stopping a session by its `UUID`. * `resumeAnalysis()` now returns `[ResumedSession]` (session ID, channel, and smart alarm config per resumed session) instead of `[AnalysisChannel: UUID]`. * Added `forceTokenRefresh: Bool` parameter to `initialize()` to force a fresh token fetch even if a cached token exists. * Added `channelSeparationConfig: ChannelSeparationConfig` parameter to `startAnalysis(...)`, `startDataSource(...)`, and `resumeAnalysis(...)`. * `AudioEventListener.onAudioAnalysisBatchCompleted(...)` now receives `audioProbability: [EventType: Float]`, the per-batch probability for each event type. The `rms: [Float]` parameter has been replaced by `dbSpl: [Float]`, providing A-weighted sound volume in dB SPL. * `EventEndedInfo` now includes an `eventType: EventType` property. * `AnalysisResult` now includes `timeZone: TimeZone` (captured at session start) and `realTimeSleepStageIntervals: [SleepStageInterval]`. * The stream accessors (`eventStream`, `breathingRateStream`, `errorStream`, `sleepStageStream`, `audioHealthStream`, `stateStream`) and `currentState` are now `async` (`get async`). * Renamed `SleepCycleSdkError.sessionAlreadyRunning` to `.channelAlreadyRunning`, and added `.featureNotEnabled(_:)`. * Added `SleepCycleSdk.version` returning the SDK version string. ## Version 1.1.0 ### New Features * **Multi-channel analysis**: Analyze two channels simultaneously using stereo audio. New `startDataSource(using:eventListeners:)` / `stopDataSource()` methods separate the data source lifecycle from session lifecycles, with per-channel start via `startAnalysis(channel:config:at:tag:)` and per-session stop via `stopAnalysis(channel:at:)`. * **Audio health monitoring**: New `audioHealthStream: AsyncStream` emits real-time audio input health status changes (`.healthy`, `.flatline`, `.missingInput`). `AudioStatistics` and `AudioHealthInterval` are included in the analysis result. * **Session IDs on all public types**: `Event`, `BreathingRate`, `SleepStageInterval`, `AudioClip`, and `AudioHealthUpdate` now include a `sessionId: UUID` property to identify which session they belong to. ### API Changes * `startAnalysis()` now returns a `UUID` identifying the session. * Added `tag: String?` parameter to `startAnalysis()` for labeling sessions. * `AudioEventListener.onAudioAnalysisBatchCompleted()` now includes a `sessionId: UUID` parameter. * `AnalysisResult` now includes `sleepStageIntervals`, `audioStatistics`, and `sessionId` properties. * Added `multiChannelAnalysis` to `SleepAnalysisFeatures`. # License Source: https://sdk.sleepcycle.com/en/license Sleep Cycle SDK License Terms Copyright (c) 2025 Sleep Cycle AB All rights reserved. * Use of source code, binaries, and documentation contained within the Sleep Cycle SDK is permitted solely for integration with the Sleep Cycle platform by partners and customers who have received explicit authorization or license from Sleep Cycle AB. Any other use is strictly prohibited. * Neither the name "Sleep Cycle" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission from Sleep Cycle. * Redistribution, reverse engineering, decompilation, modification, or creation of derivative works based on this SDK, in whole or in part, is strictly prohibited unless expressly authorized in writing by Sleep Cycle AB. Any approved redistribution must retain this copyright notice, these license conditions, and the following disclaimer. THIS SOFTWARE AND RELATED MATERIALS ARE PROVIDED BY SLEEP CYCLE AB WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.