Feature request
Use Apple Watch sleep stage data from HealthKit as labeled ground truth to train the on-device sleep stage classifier.
Background
The pod's piezo sensors produce raw vitals (HR, HRV, breathing rate) but we need labeled sleep stages to train a Core ML model. Apple Watch already classifies sleep into stages (wake/REM/core/deep) and writes them to HealthKit. By pairing Watch labels with pod sensor data, we get free training data from every user who wears a Watch to bed.
Implementation
Phase 1: Read HealthKit sleep stages
import HealthKit
let sleepType = HKCategoryType(.sleepAnalysis)
let store = HKHealthStore()
// Request read permission
store.requestAuthorization(toShare: nil, read: [sleepType]) { ... }
// Query sleep stages for a date range
let predicate = HKQuery.predicateForSamples(
withStart: bedtime, end: waketime, options: .strictStartDate
)
let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, ...) { _, samples, _ in
for sample in samples as? [HKCategorySample] ?? [] {
// sample.value maps to:
// HKCategoryValueSleepAnalysis.awake = 2
// HKCategoryValueSleepAnalysis.asleepREM = 5
// HKCategoryValueSleepAnalysis.asleepCore = 4
// HKCategoryValueSleepAnalysis.asleepDeep = 3
// HKCategoryValueSleepAnalysis.asleepUnspecified = 1
let stage = sample.value
let start = sample.startDate
let end = sample.endDate
}
}
Phase 2: Pair with pod data
For each HealthKit sleep stage epoch:
- Find pod vitals records within the same time window
- Create a training sample:
(hr, hrv, br, hr_delta, hrv_delta, br_delta) → stage_label
- Store paired data locally in a SQLite DB or JSON file
struct TrainingSample: Codable {
let timestamp: Date
let heartRate: Double
let hrv: Double?
let breathingRate: Double?
let hrDelta: Double // change from previous epoch
let hrvDelta: Double
let brDelta: Double
let label: Int // 0=wake, 1=light/core, 2=deep, 3=REM
let source: String // "apple_watch"
}
Phase 3: On-device training / fine-tuning
Once enough paired data exists (est. 10-20 nights):
- Export training samples as CSV or use Create ML directly
- Train a tabular classifier or small 1D-CNN
- Convert to .mlmodel → bundle in app or download OTA
- SleepAnalyzer switches from rule-based to model-based
Alternatively, use the paired data to fine-tune the rule-based thresholds in SleepAnalyzer.swift — no ML training needed, just adjust the HR ratio cutoffs based on real labeled data.
Phase 4: Pod-only classification
After training, the pod can classify sleep stages independently:
- Users without Apple Watch get sleep staging
- Users with Watch get validated staging (compare pod vs Watch)
- Show confidence scores: "Pod agrees with Watch 87% of the time"
Requirements
NSHealthShareUsageDescription in Info.plist
- HealthKit capability in Xcode signing
- User opt-in: "Allow Sleepypod to read sleep data from Apple Health?"
- Privacy: all training data stays on-device
Data quality notes
- Apple Watch sleep staging accuracy: ~80% agreement with polysomnography (Walch et al., PNAS 2019)
- Pod HR/HRV data needs calibration first (see core#172)
- Minimum viable training set: ~10 nights × ~480 epochs/night = ~4,800 samples
- Apple Watch sleep stages are available from watchOS 9+ / iOS 16+
References
- Walch et al., "Sleep stage prediction with raw acceleration and photoplethysmography," PNAS 2019
- Apple HealthKit documentation: HKCategoryValueSleepAnalysis
- Fonseca et al., "Cardiorespiratory sleep stage classification," Journal of Sleep Research, 2018
Feature request
Use Apple Watch sleep stage data from HealthKit as labeled ground truth to train the on-device sleep stage classifier.
Background
The pod's piezo sensors produce raw vitals (HR, HRV, breathing rate) but we need labeled sleep stages to train a Core ML model. Apple Watch already classifies sleep into stages (wake/REM/core/deep) and writes them to HealthKit. By pairing Watch labels with pod sensor data, we get free training data from every user who wears a Watch to bed.
Implementation
Phase 1: Read HealthKit sleep stages
Phase 2: Pair with pod data
For each HealthKit sleep stage epoch:
(hr, hrv, br, hr_delta, hrv_delta, br_delta) → stage_labelPhase 3: On-device training / fine-tuning
Once enough paired data exists (est. 10-20 nights):
Alternatively, use the paired data to fine-tune the rule-based thresholds in
SleepAnalyzer.swift— no ML training needed, just adjust the HR ratio cutoffs based on real labeled data.Phase 4: Pod-only classification
After training, the pod can classify sleep stages independently:
Requirements
NSHealthShareUsageDescriptionin Info.plistData quality notes
References