What happened?
Summary
OpenStrap Edge exports every sleep-stage segment to Health Connect as a separate SleepSessionRecord instead of exporting one complete sleep session containing all stages.
As a result, apps reading the Health Connect data display multiple short sleep sessions and an incorrect total sleep duration.
Actual behavior
OpenStrap Edge detected the full night correctly:
- Sleep window: 23:55–07:46
- Awake: 32 min
- REM: 1 h 35 min
- Light: 5 h 18 min
- Deep: 16 min
- Total detected sleep: approximately 7 h 9 min
After exporting to Health Connect, the receiving app displayed only 49 minutes split into separate sessions:
- 23:59–00:17
- 01:37–01:56
- 02:38–02:51
The full sleep session was not available.
Expected behavior
Health Connect should contain exactly one SleepSessionRecord covering the complete sleep window:
23:55–07:46
├── Awake stages
├── REM stages
├── Light stages
└── Deep stages
All sleep stages should be contained in the stages list of that single session.
Steps to reproduce
- Wear a WHOOP 4.0 overnight.
- Let OpenStrap Edge sync and derive the sleep data.
- Confirm that the complete night appears correctly in OpenStrap Edge.
- Enable Health Connect export.
- Open the exported sleep data in a Health Connect-compatible app.
- Observe that the sleep is split into multiple short sessions and the total duration is incorrect.
Suspected root cause
Based on the current implementation in lib/health/health_export.dart, each hypnogram segment appears to be written independently using writeHealthData() with types such as:
SLEEP_LIGHT
SLEEP_DEEP
SLEEP_REM
SLEEP_AWAKE
The Android implementation of the Flutter health package converts each of these writes into a separate SleepSessionRecord containing only one stage.
Although SLEEP_SESSION is included in the requested/exported data types, the complete sleep window does not appear to be written as one session containing all stage records.
This produces a structure similar to:
SleepSessionRecord(stage 1)
SleepSessionRecord(stage 2)
SleepSessionRecord(stage 3)
...
Instead of:
SleepSessionRecord(
startTime: sleep onset,
endTime: sleep offset,
stages: all hypnogram stages
)
Additional issue
The return value of writeHealthData() should also be checked.
The method returns Future<bool>, but the exporter currently appears to treat the operation as successful unless an exception is thrown. Health Connect or plugin failures returning false may therefore be silently ignored.
Proposed fix
Add a dedicated sleep-session export method that accepts:
- Full sleep-session start time
- Full sleep-session end time
- All sleep stages
- Stage start and end times
- Stage type
On Android, create exactly one SleepSessionRecord:
SleepSessionRecord(
startTime = onset,
endTime = offset,
stages = stages.map {
SleepSessionRecord.Stage(
startTime = it.start,
endTime = it.end,
stage = it.type
)
},
metadata = metadata
)
The current per-stage writeHealthData() loop should not be used for Android sleep export.
The exporter should also:
- Check the Boolean result of every Health Connect write.
- Delete previous sleep records using the actual sleep-session window, including periods before midnight.
- Re-export the full session idempotently.
- Ensure stages do not overlap and remain inside the session bounds.
Suggested regression test
Use a session crossing midnight:
Session: 23:55–07:46
Awake: 32 min
REM: 95 min
Light: 318 min
Deep: 16 min
Verify that:
- Exactly one sleep session is exported.
- The session covers 23:55–07:46.
- Every stage is inside the parent session.
- Stages do not overlap.
- A repeated export still leaves exactly one session.
- A failed or
false Health Connect write marks the export as unsuccessful.
App version
0.9.21+52
Platform
Android (APK)
Phone model and OS version
Google Pixel 9 Pro XL, Android 17
Which band?
WHOOP 4.0
Screenshots
Quick checks
What happened?
Summary
OpenStrap Edge exports every sleep-stage segment to Health Connect as a separate
SleepSessionRecordinstead of exporting one complete sleep session containing all stages.As a result, apps reading the Health Connect data display multiple short sleep sessions and an incorrect total sleep duration.
Actual behavior
OpenStrap Edge detected the full night correctly:
After exporting to Health Connect, the receiving app displayed only 49 minutes split into separate sessions:
The full sleep session was not available.
Expected behavior
Health Connect should contain exactly one
SleepSessionRecordcovering the complete sleep window:All sleep stages should be contained in the
stageslist of that single session.Steps to reproduce
Suspected root cause
Based on the current implementation in
lib/health/health_export.dart, each hypnogram segment appears to be written independently usingwriteHealthData()with types such as:SLEEP_LIGHTSLEEP_DEEPSLEEP_REMSLEEP_AWAKEThe Android implementation of the Flutter
healthpackage converts each of these writes into a separateSleepSessionRecordcontaining only one stage.Although
SLEEP_SESSIONis included in the requested/exported data types, the complete sleep window does not appear to be written as one session containing all stage records.This produces a structure similar to:
Instead of:
Additional issue
The return value of
writeHealthData()should also be checked.The method returns
Future<bool>, but the exporter currently appears to treat the operation as successful unless an exception is thrown. Health Connect or plugin failures returningfalsemay therefore be silently ignored.Proposed fix
Add a dedicated sleep-session export method that accepts:
On Android, create exactly one
SleepSessionRecord:The current per-stage
writeHealthData()loop should not be used for Android sleep export.The exporter should also:
Suggested regression test
Use a session crossing midnight:
Verify that:
falseHealth Connect write marks the export as unsuccessful.App version
0.9.21+52
Platform
Android (APK)
Phone model and OS version
Google Pixel 9 Pro XL, Android 17
Which band?
WHOOP 4.0
Screenshots
Quick checks