OpenRM is an unofficial iOS app that syncs your ResMed AirSense 11 CPAP with Apple Health. It pairs with the device over BLE, decodes its therapy spools, runs an on-device sleep-staging model, and writes each night's sessions into the Health app — so your CPAP data lives alongside the rest of your health data, no cloud round-trip required.
Not affiliated with, endorsed by, or supported by ResMed Inc. AirSense and myAir are trademarks of ResMed. This is independent research code; use at your own risk and do not expect parity with the official myAir app.
- Pairs and authenticates with an AirSense 11 over BLE using the device's FIG framing layer + SRP-6a key exchange + AES session crypto.
- Pulls therapy spools (per-day Summary, per-minute therapy data, diagnostic 10-minute periodic, GUI activity events, cellular activity, setting profiles).
- Decodes them into Swift structs whose field semantics are mapped against the open-source OSCAR ResMed loader. Wire-format details (protobuf containers, second-order predictive coding + zigzag + Rice-k=2 for the per-minute spool) were reverse-engineered from BLE captures.
- Stages sleep (Awake / Light / Deep / REM) via a TCN classifier
shipped as a CoreML
.mlpackage, with Viterbi smoothing over learned transition priors. Also keeps a fallback 3-class classifier for comparison. - Syncs sessions to HealthKit as per-session
sleepAnalysissamples, deduped by an external UUID so repeat syncs don't double-count. - Refreshes in the background via
BGTaskSchedulerso a freshly-woken user opens the app to today's data already populated.
┌─────────────────────────────────────────┐
AirSense 11 │ CoreBluetooth → FigFrame (framing) │
BLE ───►│ SRPClient (auth) → FigCrypto (AES) │
│ CPAPController (high-level API) │
└────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Spool decoders │
│ • SpoolDecoder (Summary) │
│ • PerMinuteSpoolDecoder │
│ • DiagnosticTenMinutePeriodic… │
│ • GUIActivityEvents… │
│ • CellularActivityEvents… │
│ • SettingProfilesCollection… │
└────────────┬────────────────────────────┘
│
▼
┌──────────────────────┐ ┌─────────────────┐
│ SleepFeatureBuilder │ │ HealthKitSync │
│ → SleepStageDetector│ │ (per-session) │
│ (CoreML + Viterbi)│ └─────────────────┘
└──────────────────────┘
- Xcode 26.0 or newer (project is on the iOS 26 / macOS 26.4 SDKs)
- An iPhone running iOS 26+ for full BLE + HealthKit functionality (Mac Catalyst and native macOS targets build, but HealthKit writes are iOS-only by Apple framework limitation)
- A real AirSense 11 to pair with — there is no simulator path
- Your own Apple Developer team for signing (the public source has
DEVELOPMENT_TEAM = ""— set yours in Signing & Capabilities)
The only third-party dependency is attaswift/BigInt, wired in via Swift Package Manager and resolved automatically by Xcode on first build.
git clone https://github.com/sparc5/OpenRM.git
cd OpenRM
open OpenRM.xcodeprojIn Xcode:
- Select the OpenRM target → Signing & Capabilities → set your Apple Developer team.
- Plug in an iPhone, select it as the run destination, and build.
- On first launch the app will scan for an AirSense 11. If the device has never been paired, OpenRM walks you through pairing with the 4-digit PIN shown on the CPAP screen. Subsequent sessions resume silently with the persisted SRP key.
OpenRM/
├── OpenRMApp.swift — @main + UIApplicationDelegate
├── ContentView.swift — root view
├── DashboardView.swift — main screen (LEDs, gauges, metrics)
├── HistoryView.swift — past nights
├── SessionDetailView.swift — drill-down for one session
│
├── BLEManager.swift — CoreBluetooth wrapper
├── FigFrame.swift — FIG framing layer
├── FigCrypto.swift — AES session crypto
├── SRPClient.swift — SRP-6a key exchange
├── CPAPController.swift — high-level pairing/session API
├── CredentialStore.swift — Keychain persistence
│
├── SpoolDecoder.swift — Summary
├── PerMinuteSpoolDecoder.swift — therapy minute-by-minute
├── DiagnosticTenMinutePeriodicSpoolDecoder.swift
├── GUIActivityEventsSpoolDecoder.swift
├── CellularActivityEventsSpoolDecoder.swift
├── SettingProfilesCollectionSpoolDecoder.swift
│
├── SleepFeatureBuilder.swift — feature pipeline
├── SleepStageDetector.swift — legacy 3-class
├── SleepStageDetectorV2.swift — TCN 4-class + Viterbi
├── BLE3ClassClassifier.swift — fallback gradient-boosted classifier
├── SleepViterbi.swift — transition-prior smoothing
│
├── HealthKitSync.swift — sleepAnalysis samples → Health
├── BackgroundSync.swift — BGTaskScheduler integration
├── CleaningReminder.swift — periodic cleaning notifications
├── LEDDisplay.swift, NeedleGauge.swift — dashboard widgets
│
├── Assets.xcassets/ — app icon, launch screen, colors
└── Resources/
├── SleepModel.mlpackage — TCN sleep-stage model
├── feature_spec.json — model input contract
└── transitions.json — Viterbi transition priors
- The OSCAR project, whose
resmed_loader.cppwas the Rosetta Stone for AirSense 11 field names. OpenRM does not include any OSCAR code; it only references field semantics that OSCAR documents from the SD-card EDF format. - The CoreBluetooth and HealthKit teams at Apple, for APIs that make this kind of side-project tractable.
Pre-release research code. Pairing, session resume, summary decoding, per-minute decoding, sleep staging, and HealthKit sync all work end-to-end on the author's device, but the protocol surface is large and edge cases absolutely exist. Issues and PRs welcome.
OpenRM is licensed under the GNU General Public License v3.0. See
LICENSE for the full text.
In short: you may use, study, modify, and redistribute this code, provided that any distributed derivative work is also licensed under GPL-3.0 and ships its source. This is the same license as the OSCAR project, so the two ecosystems remain interoperable.
Note: the license governs OpenRM's source code only. Do not submit PRs containing decompiled ResMed firmware, decoded proprietary protocol specifications, or other ResMed-copyrighted material.