Skip to content

Health Connect export creates fragmented sleep sessions instead of one complete session #193

Description

@FlixiDoe

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

  1. Wear a WHOOP 4.0 overnight.
  2. Let OpenStrap Edge sync and derive the sleep data.
  3. Confirm that the complete night appears correctly in OpenStrap Edge.
  4. Enable Health Connect export.
  5. Open the exported sleep data in a Health Connect-compatible app.
  6. 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:

  1. Check the Boolean result of every Health Connect write.
  2. Delete previous sleep records using the actual sleep-session window, including periods before midnight.
  3. Re-export the full session idempotently.
  4. 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

Complete sleep session shown in OpenStrap Edge Fragmented sleep sessions shown after Health Connect export

Quick checks

  • The official WHOOP app is not also connected to this band
  • I've searched existing issues for this

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions