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
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.
Sleep analysis
-
Sleep Monitoring on the bracelet
-
Continuous sync to the SDK during the night or in the morning
-
Activity detected on the bracelet, sleep monitoring stopped on the bracelet
-
The bracelet computes the sleep session; the SDK downloads it during the next sync
-
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:
sendStopSleepis now onManager(manager.sendStopSleep(...)). It was previouslymanager.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:
getSleepStateis now asuspendfunction onManagertaking a singledayDate, returning the state directly. It replaces the callback-basedPhilipsSleepUtils.getSleepState(...), which took the repositories and a risetime and delivered the result through anonDonecallback.
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
PhilipsSleepStatename 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:
| Removed | Replace with |
|---|---|
Manager.sleepProcessUpdater and the SleepProcessUpdater class | Nothing to schedule; sleep is computed on the bracelet. Use processSleepIfNeeded() |
PhilipsSleepUtils | Manager.getSleepState(dayDate) |
SleepProcessResult | Removed — there is no per-parse result any more |
| The bundled Philips/FXC library | Nothing — 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.
getSleepStateandprocessSleepIfNeededaresuspend; call them from a coroutine. If you previously drove sleep from theonDonecallback, move that logic after thesuspendcall returns.- Sleep processing no longer retries on the phone and there is no
.wiff-parsing failure to handle, so any retry/SleepProcessResulthandling you had can be deleted rather than ported. - Nothing changes in how you read a finished sleep:
SleepSummaryModelviasleepSummaryRepository, andMetricSleepviametricSleepRepository, are unchanged apart from the newMetricSleepfields.