Skip to main content

Sleep process

Breaking change in 2.2.0. Sleep is now computed on the bracelet ("Corsano sleep") and downloaded by the SDK. The former app-side Philips/FXC processing, and the bundled Philips library, are removed. Several APIs moved or changed — see Migrating from 2.1.x at the bottom of this page.

Introduction

The bracelet monitors and computes the sleep itself. The SDK downloads the finished sleep session — start / stop and sleep slots — like any other data, rather than processing a raw signal on the phone.

Requirements

Settings for sleep

Sleep explained

Sleep is a complex matter, as we don't all have the same behavior during our sleep. The sleep needs to be accurate for different situation:

  • No wake up during the night
  • Wakes up and get out of bed to go to the bathroom
  • User checks their phone as soon as they wake up, or later during the day

Monitoring period

Start of sleep monitoring on the bracelet

The sleep is monitored on the bracelet from bedtime - 2h.

End of sleep monitoring on the bracelet

In the morning, starting from risetime - 2h, the bracelet detect activity from the user, if the user is active, then the sleep is stopped. If no activity is detected, the sleep monitoring will be stopped at risetime + 3h.

It is possible to stop the sleep manually, by sending a stop sleep command through the SDK. Why a manual stop? The manual stop is useful for the last situation "Use checks their phone as soon as they wake up". If the user is still in bed, and check their phone, it is possible that no activity is detected as the person stays in bed. We recommend putting a "Stop sleep now" button in your app, if no activity is detected but the user wants to check their sleep, they can click on the button, sleep will be stopped and ready soon after that.

Manual stop technically

Sleep analysis

  1. Sleep Monitoring on the bracelet

  2. Continuous sync to the SDK during the night or in the morning

  3. Activity detected on the bracelet, sleep monitoring stopped on the bracelet

  4. The bracelet computes the sleep session; the SDK downloads it during the next sync

  5. Sleep is ready with start time, stop time, sleep stages: light sleep / deep sleep / awake period / REM sleep

Manual Stop sleep

Sending the stop-sleep command tells the bracelet to end the current sleep session; the finished session is available shortly after.

manager.sendStopSleep(
bleDevice,
onSuccess = { stopTime -> Log.d("Corsano-SDK", "Sleep stopped at $stopTime") },
onError = { error -> Log.e("Corsano-SDK", "Stop sleep failed: $error") }
)

The bracelet has to be connected for this to succeed.

Moved in 2.2.0: sendStopSleep is now on Manager (manager.sendStopSleep(...)). It was previously manager.sleepProcessUpdater.sendStopSleep(...).

Sleep processing on the app

From 2.2.0 the sleep session is computed on the bracelet and downloaded, so there is no on-app parsing to run. One call remains, to bring the app's sleep state up to date after a sync:

suspend fun processSleepIfNeeded()

processSleepIfNeeded() checks whether a sleep session finished on the bracelet and updates the stored sleep state accordingly. It replaces the former manager.sleepProcessUpdater.schedule(...).

Sleep session

The sleep monitoring provides a sleep session (start, stop, 30 seconds slots), then the sleep session is filtered to obtain the best user friendly result. Here are the filtering steps:

  • Remove awake blocks at the beginning and the end of the night.
    • The sleep will always start and end with a sleep stage (light / REM / deep)
  • Noise filtering: the graph is flattened, the slots have to last at least 2 min straight
    • Example: REM sleep during 20 min, then one 30 seconds awake slot, and after 10 more minutes of REM sleep: the 30s awake will be filtered and will not appear
  • 1 minute slots
    • Slots have 0 seconds (22:01:00, 22:02:00, etc)
    • Start and stop time have 0 seconds too

Sleep status

To check where the sleep is in the process for a given day, call:

suspend fun getSleepState(dayDate: Date): PhilipsSleepState

Changed in 2.2.0: getSleepState is now a suspend function on Manager taking a single dayDate, returning the state directly. It replaces the callback-based PhilipsSleepUtils.getSleepState(...), which took the repositories and a risetime and delivered the result through an onDone callback.

val state = manager.getSleepState(Date())

It returns:

enum class PhilipsSleepState {
NOT_AVAILABLE_WATCH_NOT_WORN, // watch not worn (verified with HRM data, no HRM during the night)
AVAILABLE, // ready and stored in DB
IS_TODAY_AND_IS_ONGOING, // sleep still being recorded on the bracelet
IS_TODAY_AND_IS_PROCESSING, // recording stopped on the bracelet, being finalised
IS_TODAY_AND_IS_PROCESSING_FROM_PROCESS_NOW, // recording stopped via "Stop Sleep", being finalised
NOT_AVAILABLE // no sleep could be identified
}

Note: The enum keeps the PhilipsSleepState name for source compatibility even though sleep is now Corsano-computed. Treat it as the sleep state, not a Philips-specific type.

Migrating from 2.1.x

The 2.2.0 sleep migration removes the app-side Philips processing. If you integrated sleep before 2.2.0, apply these changes.

Removed — delete any use of these:

RemovedReplace with
Manager.sleepProcessUpdater and the SleepProcessUpdater classNothing to schedule; sleep is computed on the bracelet. Use processSleepIfNeeded()
PhilipsSleepUtilsManager.getSleepState(dayDate)
SleepProcessResultRemoved — there is no per-parse result any more
The bundled Philips/FXC libraryNothing — remove any packaging step for it

Moved or changed:

Before (≤ 2.1.x)After (2.2.0)
manager.sleepProcessUpdater.sendStopSleep(bleDevice, …)manager.sendStopSleep(bleDevice, onSuccess, onError)
manager.sleepProcessUpdater.schedule(…)manager.processSleepIfNeeded() (suspend)
PhilipsSleepUtils.getSleepState(repos…, onDone)manager.getSleepState(dayDate) (suspend, returns the state)

Data model: MetricSleep gained movementLevel, activityCount, hrm and rmssd, and its quality field is deprecated — see MetricSleep.

Tips:

  • The removed symbols are compile errors, so the compiler points you at every call site — fix them one by one against the table above.
  • getSleepState and processSleepIfNeeded are suspend; call them from a coroutine. If you previously drove sleep from the onDone callback, move that logic after the suspend call returns.
  • Sleep processing no longer retries on the phone and there is no .wiff-parsing failure to handle, so any retry/SleepProcessResult handling you had can be deleted rather than ported.
  • Nothing changes in how you read a finished sleep: SleepSummaryModel via sleepSummaryRepository, and MetricSleep via metricSleepRepository, are unchanged apart from the new MetricSleep fields.