Skip to content

Add diagnostic support for additional WHOOP 5 GATT families #688

Closed

Description

@tigercraft4

Summary

The decompiled WHOOP Android app models more BLE service families than NOOP currently scans/connects to. NOOP currently supports WHOOP 4 (61080001-...) and the Maverick/Goose WHOOP 5/MG family (fd4b0001-...). The official app also defines separate PUFFIN, MONUMENT, and SYMPHONY UUID families.

This should start as detection/diagnostic support, not full functional support, because the decompile suggests different connection/framing paths for at least some variants.

Evidence from decompiled app

op0.o defines six strap generations:

  • GEN_4
  • MAVERICK
  • PUFFIN
  • GOOSE
  • MONUMENT
  • SYMPHONY

Relevant decompiled references:

  • /whoop-decompiled/sources/op0/o.java
    • Companion.a() returns GEN_4, MAVERICK, GOOSE, MONUMENT, SYMPHONY
    • Companion.c() returns only PUFFIN, suggesting a separate Puffin connection path
    • GOOSE reuses the MAVERICK UUIDs
  • /whoop-decompiled/sources/op0/p.java
    • PUFFIN: 11500001-6215-11ee-8c99-0242ac120002
    • MONUMENT: 8a580001-2fe8-4796-9267-b87a2b0c8234
    • SYMPHONY: 59830001-5955-419b-bb8d-c8262926af23

The same 0001/0002/0003/0004/0005/0007 pattern exists for each family:

  • service
  • command write
  • command notify
  • events notify
  • data notify
  • memfault notify

Current NOOP state

NOOP currently has only two primary families wired:

  • WHOOP 4: 61080001-8d6d-82b8-614a-1c8cb0f8dcc6
  • WHOOP 5/MG: fd4b0001-cce1-4033-93ce-002d5875f58a

Examples:

  • Packages/WhoopProtocol/Sources/WhoopProtocol/DeviceFamily.swift
  • Strand/BLE/WhoopModel.swift
  • android/app/src/main/java/com/noop/ble/WhoopBleClient.kt

Important caveat

Do not implement this as a blind UUID swap.

The decompiled yh0.a path throws for Castle generations (MONUMENT, SYMPHONY) with the message that Rev1 command packets are not supported and Rev2CommandPacket should be used. That means Monument/Symphony likely need separate framing/packet handling, not just the existing fd4b logic under different UUIDs.

PUFFIN also appears to have a distinct connection path in jw1/*.

Proposed scope

  1. Add explicit protocol metadata for the extra service families.
  2. Add scan/diagnostic logging that can identify these services when present.
  3. Do not advertise full support until connection/framing is verified on hardware.
  4. For Puffin, investigate whether the existing WHOOP 5 frame path can be reused after UUID substitution.
  5. For Monument/Symphony, treat as unsupported-detected until Rev2 framing is mapped.

Acceptance criteria

  • NOOP docs/protocol metadata list PUFFIN, MONUMENT, and SYMPHONY UUID families.
  • If a device advertises one of these services, the strap log records a clear detected-but-unsupported message rather than silently missing it.
  • Existing WHOOP 4 and fd4b WHOOP 5/MG scan/connect behavior is unchanged.
  • No Castle/Rev2 commands are sent unless the correct framing is implemented and tested.

Activity

  1. ryanbr commented on Jul 21, 2026

    @ryanbr
    Owner

    Good direction, and the caveat is spot-on — detection-only (log "detected-but-unsupported"), no blind UUID swap, and Monument/Symphony (Castle) genuinely need Rev2 framing, not the fd4b path under new UUIDs. Scoping to metadata + scan-detection with no Castle/Rev2 commands and unchanged 4.0/fd4b behaviour is the right call. The UUIDs are protocol facts, fine to reimplement + attribute like the other RE work.

    One thing to resolve before implementing — a naming collision. NOOP already uses "puffin" throughout as its codename for the fd4b / WHOOP 5.0/MG framing: PuffinPacketType, puffinCommandFrame/Framing, PuffinExperiment, PuffinFrameRecorder, DeviceFamily.swift ("Whoop 5.0 / MG … 'puffin' packet types"), docs/PROTOCOL.md ("WHOOP 5.0 ('puffin') protocol"). But the decompile's op0 taxonomy lists PUFFIN as a separate UUID family (11500001), distinct from MAVERICK/GOOSE — and MAVERICK/GOOSE are the fd4b UUIDs NOOP actually implements.

    So:

    • NOOP's internal "puffin" ≠ the app's PUFFIN. What NOOP calls "puffin" is really Maverick/Goose (fd4b); the app's real PUFFIN (11500001) is a device NOOP has never handled.
    • If this adds an 11500001 family named "puffin," it collides badly with the existing puffin naming everywhere. The new family needs a distinct name, and the fd4b "puffin" codename should stay untouched.
    • Latent doc point: NOOP's "puffin" label is arguably a misnomer inherited from b-nnett/goose (its fd4b framing is Maverick/Goose in the app's own taxonomy). Worth a one-line clarification in PROTOCOL.md whichever way this lands.

    Implementation notes

    • This touches the scan path (more sensitive than a pure decode helper): detect the UUIDs on discovery and log detected-but-unsupported, but never route them into the connect/framing path. Mirror the existing DeviceFamily / scanService structure on both platforms.
    • Acceptance criteria are good; add explicit tests that a Monument/Symphony/11500001 advertisement logs the unsupported message and does not attempt a connect or send any command.

    Priority read: Monument/Symphony (and possibly PUFFIN 11500001) look like future/unreleased generations the app models speculatively, so this is forward-looking groundwork rather than a fix for something users hit today — useful, but lower urgency than the reported bugs.

    Happy to implement the detection-only version once the naming for the new family is settled (i.e. what to call the 11500001 family so it doesn't clash with the fd4b "puffin" codename).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions