Skip to content

tanarchytan/noop

 
 

Repository files navigation

NOOP

NOOP — Android

Your strap. Your data. Your machine. Offline, on-device, no cloud.

The Android-only line of NOOP — the Liquid Metal look: living liquid scores and a sky that moves with your day.

Features · Build · Protocol

Today on Android    A metric's own trend on Android

The Liquid Metal look: living liquid scores, a sky that moves with your day — Today, and a metric’s own trend.

This is the Android-only fork (tanarchytan/noop, branch noop-tan) of the cross-platform ryanbr/noop. The iOS and macOS apps have been removed here; only the Android app is built and shipped. Fixes that help everyone still go upstream as PRs. See CLAUDE.md.


Download

The Android app you can run right now:

Build Notes
NOOP-full.apk (see Releases) The full app. minSdk 26 (Android 8+). Sideload — enable "install unknown apps". Blocked by Play Protect? See Installing on Android below.

Installing on Android (Play Protect blocked it?). NOOP isn't on the Play Store — it's an unsigned, source-available APK you sideload, because the project is anonymous and has no paid Play identity to publish or sign under. So Android treats it as an "unknown app" and Google Play Protect may warn or block on install (most stubbornly on stock Pixel / recent Android). Nothing is wrong with the file — it's just missing a Play signature. To get it on:

  • Tap "Install anyway." When the warning appears, choose More details → Install anyway.
  • No "Install anyway" button? It can vanish after a first install + uninstall. Grant the source directly: Settings → Apps → Special app access → Install unknown apps, pick the browser or file manager you're installing from, turn on "Allow from this source", then open the APK again.
  • Still blocked by Play Protect? It's your call to make for an unsigned app you trust: open the Play Store → your profile icon → Play Protect → ⚙ Settings, toggle "Scan apps with Play Protect" off, install NOOP, then switch it back on.
  • Reinstalling is safe. Uninstalling and installing again won't hurt anything — NOOP keeps all data on-device with allowBackup=false, so a reinstall simply starts fresh. There's no cloud copy to lose either way.

Prefer to build it yourself? See Quickstart (Android).

Everything runs offline. The only feature that ever uses the network is the optional AI Coach, and only with your own API key.


NOOP is a standalone, fully offline companion app for WHOOP straps (4.0 and 5.0). It pairs directly with the strap over Bluetooth, stores everything on your own device in SQLite, imports your existing WHOOP and Apple Health history, and computes recovery, strain, HRV, and sleep locally, with no WHOOP account and no WHOOP cloud.

It is built on prior community interoperability work and exists for one reason: to let someone who owns a WHOOP strap read their own biometric data from their own device, on a machine they control.

Not affiliated with WHOOP. NOOP is an independent, unofficial interoperability project. It is not affiliated with, endorsed by, or connected to WHOOP, Inc. "WHOOP" is used only to identify the hardware NOOP talks to. Use it only with a device you own, and not in breach of any agreement that applies to you. NOOP is not a medical device; every derived metric is an approximation, not clinical data. See DISCLAIMER.md.


Contents


Why NOOP

You bought the strap. The biometric stream it produces is yours. NOOP is built on that premise:

  • Own your data. NOOP reads heart rate, R-R intervals, SpO₂, skin temperature, respiration, accelerometer/gravity, battery, and event data straight off the strap over Bluetooth and writes it to a local SQLite database. Nothing is uploaded anywhere.
  • Account-free and local. NOOP never logs into a WHOOP account and never hits a WHOOP server. It does not bypass any login, paywall, or DRM; it simply talks to a device you own and reads data you generated.
  • Bring your history. Already have years of data in the official app or in Apple Health? Import the WHOOP CSV export and/or your Apple Health export.xml once, and it's permanently on your machine.
  • Transparent math. Recovery, strain, HRV, and sleep are recomputed on-device from documented, citable methods (Task Force 1996 HRV, Karvonen %HRR, Edwards / Banister TRIMP, Tanaka HRmax, and so on). The algorithms are approximations of — not reproductions of — any proprietary model, and every analyzer file documents exactly what it does.

Features

Everything below is a real screen in the Android app (Jetpack Compose, android/app/src/main/java/com/noop/ui/):

Screen What it does
Today (Control Center) Home dashboard: recovery ring, a "today's synthesis" insight, a grid of stat tiles (recovery, strain, sleep, HRV, RHR, SpO₂, respiratory, steps, weight, calories) each with a 14-day sparkline, live strap battery % and HR trend, recent workouts, and a data-sources footer.
Readiness An on-device "should you push today?" read that synthesizes established sports-science signals from your own history — HRV vs your baseline (Plews/Buchheit), resting-HR drift (Lamberts), sleeping respiratory-rate drift, training-load balance (acute:chronic workload ratio, Gabbett) and training monotony (Foster) — into a single headline (Primed / Balanced / Strained / Run down) with the drivers behind it. Pure local math, not medical advice.
Live Real-time view of the connected strap — heart rate and frame stream as they arrive (~1 Hz).
Breathe HRV haptic breathing biofeedback. The strap both measures HRV (R-R intervals) and buzzes its haptic motor, so NOOP paces your breath with felt cues (one buzz inhale, two exhale) and shows live HR + rolling RMSSD responding as the session deepens. Presets: Relax 4-6, Coherence 5.5, Box 4-4. Each session reports a pre/post HRV outcome so you can see how much you settled.
Intervals Silent haptic HIIT timer. The strap buzzes every transition (triple-buzz into WORK, single into REST, 3-2-1 tick at phase ends, long buzz on finish) so you train hands-free. Falls back to a glanceable visual timer with no strap.
Explore (Metric Explorer) Interrogate any single metric over time, built from the metric catalog.
Compare Plot two metrics together / against each other over a shared timeline.
Insights Behavioral and correlational insights derived from your own series — including Activity Cost, which learns what each activity type typically costs your next-morning recovery (and how long you take to bounce back) from your own history.
Sleep Sleep sessions with a hypnogram, stage breakdown, efficiency, resting HR, and HRV — computed by the on-device sleep stager. Browse back through past nights, not just last night.
Trends Long-range trends across recovery, strain, sleep, and biometrics — and a shareable one-page PDF report (recovery / sleep / HRV / resting HR / strain over a range you choose), rendered entirely on-device for a doctor, coach, or your own records.
Workouts Detected and manual exercise sessions with strain and heart-rate detail. Tap any session for a full detail view — its HR curve over the workout, time in each HR zone, duration, avg/max HR, and the Effort it added.
Health Biometric overview (HR, HRV, SpO₂, skin temperature, respiratory rate, etc.).
Stress Day-level stress / autonomic load visualization.
Mind A quick daily mood check-in that correlates how you feel against your own recovery, sleep and HRV over time — so you can see what actually moves your mood. On-device and non-clinical: a self-reflection log, not a mental-health assessment.
Apple Health Browse and reconcile data imported from your Apple Health export.
Data Sources One-tap import of a WHOOP CSV export, an Apple Health export, or a nutrition CSV (Cronometer / MacroFactor), plus live-strap status. "Bring your history in once, then it's yours."
Notifications Configure local notifications and thresholds.
Coach An optional AI Coach you can ask about your data in plain language. It's the one feature that can ever use the network: off until you add your own key — Anthropic, OpenAI, or any OpenAI-compatible endpoint including a local/self-hosted model (Ollama, LM Studio) — and it sends only a short text summary of recent metrics plus your question, never raw streams or identifiers. With a local model the conversation never leaves your machine. See docs/PRIVACY_SECURITY.md.
Settings Profile, preferences, step calibration (tune the stride/step estimate to your own walking), unit choices, the in-app What's new changelog, and an opt-in Experimental section (WHOOP 5/MG protocol probes).

There is also a first-run onboarding wizard that sets expectations (independent/experimental, WHOOP 4.0 vs 5/MG, on-device only), and an in-app "What's new" changelog shown after each update.


Strap support

NOOP is an independent project. What follows separates what has been verified against a real strap from what is implemented but not yet exercised on hardware, because the difference matters.

Strap Status
WHOOP 5.0 / MG Hardware-verified. Live HR, recovery, Strain, Rest, sleep staging, skin temperature, sleep SpO₂, HRV, respiratory rate, battery, firmware info, history offload and R22 deep IMU buffers, all exercised against a real strap over months of wear. Pick "WHOOP 5.0 / MG" before connecting.
WHOOP 4.0 ⚠️ Implemented, not hardware-verified. The v5/v9/v12/v24/v25 record decoders are written and unit-tested, but have never been run against a real 4.0 offload — the bond path has only ever been exercised on 5.0/MG. Expect it to work; treat any 4.0 number as unconfirmed until someone runs it.

Which health metrics are verified, and how

Sorted by how strongly each is confirmed — which is a different question from whether it works. Only health metrics are listed; the decode, transport and UI layers live in the architecture notes.

✅ Verified against your own strap

Metric Evidence
Heart rate, HRV (RMSSD/SDNN/pNN50) decoded from real 5.0/MG streams over months of wear
Respiratory rate recovered from R-R via respiratory sinus arrhythmia
Skin temperature cross-checked against the band's own readings
Blood oxygen (5.0/MG) the strap's own computed value, read off the wire
Steps, activity features wrap-aware counter and the 100 Hz motion buffer
Sleep regularity computes over a real week and refuses honestly when you haven't worn it enough

✅ Scored against sleep-lab data

Metric Evidence
Sleep staging Cohen's κ 0.311 against DREAMT (100 polysomnography subjects), 0.412 AAUWSS, 0.537 killa5, 0.379 sleep-accel. We tried re-tuning it and threw the result away — it scored better on the set it was fitted to and materially worse on every set it hadn't seen

✅ Checked against the previous implementation

Recovery (Charge) · Effort (Strain) · Rest · workout detection · calories · HR zones · personal baselines · Fitness Age · sleep debt · daily and daytime stress · nap detection · HRV frequency bands. Each still reproduces the numbers its predecessor produced.

⚠️ Honest about being weaker

Metric What to know
Resting HR Reads about 10 bpm below a reference band. The gap is consistent, and it is not a sensor or wrist difference — two bands agree to within 2 bpm on the same statistic. Left unchanged because the evidence says something is off, not what the right answer is
Body Age Every input cites a published mortality study, but only cardio fitness is published as a per-unit figure; the others are our interpolations of grouped results, and the sleep-duration weight is ours. A wellness comparison, not a clinical age
Blood oxygen (4.0) Not reported, and the sample rate is why. The red/IR pair arrives once a second, so the fastest signal it can carry is 0.5 Hz — while a pulse runs 0.83 to 3 Hz. The heartbeat a blood-oxygen reading depends on is gone before the app sees the data. Measured on two straps and 2.1 million samples: 89% and 98% of windows held no pulsation, and the few that flickered produced about 80%, which would be severe hypoxaemia in two healthy people. The app declines instead. The 5.0/MG reading is the strap's own and is unaffected
Rhythm Age Has never yet produced a value — it needs 7 days of continuous on-chip motion, and its scale factor is unconfirmed
Everything on WHOOP 4.0 The decoders are written and unit-tested but have never run against a real 4.0 band

The one gap: ECG — it requires the MG electrode (absent on the 5.0), and even on MG the firmware gate is unreachable in software. Everything else the strap measures is decoded and fed into the on-device scoring pipeline.

The metrics are computed locally from the strap's own sensor streams — no WHOOP account, no cloud, no proprietary algorithms. Each score is an honest, documented approximation grounded in published sports science, never a reproduction of WHOOP's private models.

Pairing a WHOOP 5.0 / MG — read this first

A WHOOP strap holds an encrypted Bluetooth bond with only one device at a time, and yours is normally bonded to the official WHOOP app on your phone. You can't just scan for it in NOOP — if the strap is still bonded to the WHOOP app, NOOP's pairing is refused and the strap log shows "Encryption is insufficient" / "bond refused." (Live heart rate is the exception — it rides the standard Bluetooth heart-rate profile, so it streams without a bond. But pairing — needed for the deeper features — does not.)

To pair properly:

  1. Close the official WHOOP app on your phone (fully quit it, or turn that phone's Bluetooth off) so it isn't holding the bond.
  2. Put the strap in pairing mode — on a 5.0/MG, tap the band repeatedly (firm taps on the sensor) until the LEDs flash blue.
  3. In NOOP: Live → choose "WHOOP 5.0 / MG" → Scan & Connect. Success looks like "CLIENT_HELLO acked — link established" in the strap log (not "bond refused"). It can take a couple of attempts.

Only one device at a time. Because the strap holds a single bond, don't leave it connected to your phone and another device (or the WHOOP app) at once — live heart rate will still show on all of them (that rides the bond-free standard profile), but none of them will have the real encrypted bond. If HR streams fine yet buzz, alarm, double-tap and history don't work, that's the tell: the strap isn't truly bonded to this device. Free it from everything else, then pair here.

Bonding to NOOP may take the strap's bond away from the WHOOP app, so the official app might need to re-pair afterwards.

The app always tells you what's live now, both in onboarding and on each screen.

What to expect when you start

NOOP computes your scores on your own device, so like any recovery wearable it needs a little data before everything fills in:

  • Live heart rate shows the moment the strap connects.
  • Strain and sleep appear after you've worn it and synced — the strap's last ~14 days offload automatically over the first few minutes.
  • Recovery needs a few nights for the app to learn your personal baseline, then sharpens each night. WHOOP makes you wait for the same reason.
  • In a hurry? Import your WHOOP export in Data Sources and your full history fills in about a minute.

Architecture

The Android app lives entirely under android/ — a self-contained Gradle project (Kotlin, Jetpack Compose, Room). It's organized by domain under android/app/src/main/java/com/noop/:

android/
  app/src/main/java/com/noop/
    ble/         CoreBluetooth-equivalent link: bonding, offload, live notifications
    protocol/    BLE frame parsing, CRC, command/event/packet decode (WHOOP 4.0 + 5.0/MG)
    data/        Room/SQLite persistence — migrations, streams, caches (WhoopRepository)
    analytics/   HRV / recovery / strain / sleep / correlation math (pure, testable)
    ingest/      WHOOP CSV + Apple Health + nutrition importers
    ui/          Compose screens, the design system (Palette / Metrics), charts
    widget/      home-screen widgets
  app/src/test/  JVM unit tests (run on Linux/CI, no device) — analytics, protocol, import
  • Protocol (com.noop.protocol) is platform-pure: it implements the on-wire frame format for both strap generations (WHOOP 4.0 = CRC8 poly 0x07 header, service 61080001-…; WHOOP 5.0/MG = CRC16-Modbus header, "puffin" packet types, service fd4b0001-…). Decoding is schema-driven and includes CRC8, CRC16-Modbus, and zlib CRC-32, frame framing, value interpretation, and historical-stream reassembly.
  • Analytics (com.noop.analytics) are pure, database-free functions grounded in published methods — RMSSD/SDNN from R-R intervals (Task Force 1996, Malik ectopic filtering), a 0–100 HRV-dominant recovery score, a 0–21 logarithmic strain scale (Karvonen %HRR + Edwards/Banister TRIMP), sleep/wake detection with approximate 4-class staging, and day-aligned/lagged correlations. Each is explicitly an approximation, not a reproduction of any proprietary model.
  • Storage (com.noop.data) keeps everything on-device in Room/SQLite — decoded-stream tables (hrSample, rrInterval, spo2Sample, skinTempSample, respSample), server-derived metric caches (sleepSession, dailyMetric), cursors, and a raw-frame outbox. Third-party deps: Room + Compose only.

This fork keeps the Android reimplementation as its own source of truth. The analytics and stored-data values are the same numbers the upstream Swift app computes; when a fix belongs upstream too, PR the Kotlin change to ryanbr/noop with its Swift twin.


Quickstart (Android)

Requirements: JDK 17 (a recent Android Studio JBR works), the Android SDK (compileSdk 34), and — to pair live — your own WHOOP strap. To just explore, you can import a CSV / Apple Health export instead.

# 1. Clone
git clone https://github.com/tanarchytan/noop.git NOOP
cd NOOP/android

# 2. Build the debug APK (Full flavor)
./gradlew assembleFullDebug        # ./gradlew.bat on Windows

# 3. Install on a connected device
./gradlew installFullDebug         # or: adb install app/build/outputs/apk/full/debug/*.apk

# Run the JVM unit tests (no device needed)
./gradlew testFullDebugUnitTest

Notes:

  • Flavors: full (the real app, com.noop.tan) and mock (com.noop.tan.mock, preloaded with synthetic data so every screen can be explored with no strap; its own id, so it can never write into the real app's database).
  • Build types: release (stable) and rc (the same build plus debuggability). An rc deliberately keeps the release applicationId and signing key, so installing a candidate over a release is an ordinary update that keeps your data. debug (.debug) is local dev only and is never published.
  • Build e.g. ./gradlew assembleFullRelease, assembleFullRc, assembleMockRc.
  • minSdk 26 (Android 8+), compileSdk 34. Stack: AGP / Gradle / Kotlin, KSP, Room, Compose.
  • Release APKs are built and signed by hand (or via the fork release workflow); the debug build falls back to the Android debug key, so CI needs no secrets.

How your data flows

WHOOP strap ──BLE──▶ com.noop.ble ──▶ com.noop.protocol (decode)
                                          │
WHOOP CSV   ─┐                            ▼
Apple Health ├─▶ com.noop.ingest ──▶ com.noop.data (local Room/SQLite)
Nutrition CSV┘                            │
                                          ▼
                          com.noop.analytics (recovery/strain/
                          HRV/sleep, on-device)
                                          │
                                          ▼
                          com.noop.ui (Jetpack Compose)

Every arrow stays on your machine.


Privacy

Offline by design. NOOP has no server, no telemetry, and no account. Your strap data, imports, and computed metrics live in a local SQLite database on your device and never leave it.


Attribution

NOOP stands on community interoperability and protocol-documentation work. With thanks:

  • ryanbr/noop — the upstream cross-platform NOOP this Android-only fork tracks and contributes back to.
  • johnmiddleton12/my-whoop — the WHOOP 4.0 BLE protocol; the protocol and storage layers are adapted from this work.
  • b-nnett/goose — the WHOOP 5.0 / MG BLE protocol documentation (the fd4b0001-… service family, CRC16-Modbus header, and "puffin" packet types) that NOOP's WHOOP 5.0 path is ported from.

NOOP contains no WHOOP proprietary code, firmware, logos, or assets, and performs no DRM circumvention. Full detail in ATTRIBUTION.md.


Disclaimer

NOOP is an independent, unofficial, non-commercial interoperability project. It is not affiliated with, endorsed by, or connected to WHOOP, Inc. All references to "WHOOP" are nominative — used only to identify the third-party hardware NOOP interoperates with.

NOOP is not a medical device. Heart rate, HRV, recovery, strain, sleep stages, SpO₂, respiratory rate, and skin temperature are approximations computed from published methods. They are not clinically validated and are not medical advice. Do not use them to diagnose, treat, or make health decisions — consult a qualified professional.

Provided as-is, with no warranty, for personal and educational use. You use it at your own risk. Read the full notice in DISCLAIMER.md.


License

NOOP is source-available under the PolyForm Noncommercial License 1.0.0: free for personal and other non-commercial use — read it, run it, fork it, and contribute. Commercial use is not granted by this license. (PolyForm Noncommercial is a proper software license with patent terms; it is deliberately not an OSI "open-source" licence, because that would permit the commercial use this project's non-commercial nature rules out.)

The license covers NOOP's own original code and docs. Protocol facts (frame layouts, command numbers, byte offsets) are uncopyrightable and free to reuse; bundled dependencies keep their own licenses (see NOTICE). By opening a pull request you agree your contribution is licensed under the same terms — see docs/CONTRIBUTING.md.

About

Offline WHOOP companion — pair your strap over Bluetooth, keep all data on your own device. No cloud, no account, no subscription.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages