Skip to main content
Python provides powerful libraries for Bluetooth Low Energy communication. The Whoop reverse engineering project primarily uses pygatt and bleak for device interaction.

pygatt

pygatt is a Python library that wraps BlueZ’s gatttool, providing a simple API for BLE device interaction.

Installation

pip install pygatt

Scanning for Devices

Discover nearby BLE devices:
from pygatt import GATTToolBackend, BLEAddressType

adapter = GATTToolBackend(hci_device='hci0')
adapter.start()

for device in adapter.scan(timeout=5):
    print(device)
This scans for 5 seconds and prints all discovered devices with their MAC addresses and names.

Connecting to Whoop

Once you have the device MAC address:
device = adapter.connect('XX:XX:XX:XX:XX:XX', address_type=BLEAddressType.random)
Whoop 4.0 uses a random BLE address type. Always specify address_type=BLEAddressType.random when connecting.

Writing Commands

Send commands to the CMD_TO_STRAP characteristic:
import struct
import time

# Set alarm to ring in 10 seconds
unix_time = int(time.time()) + 10
unix_hex = struct.pack('<I', unix_time).hex()

package = f"aa10005723704201{unix_hex}00000000f226a8bd"

device.char_write(
    "61080002-8d6d-82b8-614a-1c8cb0f8dcc6",  # CMD_TO_STRAP UUID
    bytearray.fromhex(package),
    wait_for_response=False
)

CRC Calculation

Whoop packets include a CRC-32 checksum. Use crccheck to calculate:
import struct
from crccheck.crc import Crc32Base

def calculate_checksum(message):
    """Calculate Whoop CRC-32 checksum for a packet."""
    crc = Crc32Base
    crc._poly = 0x4C11DB7
    crc._reflect_input = True
    crc._reflect_output = True
    crc._initvalue = 0x0
    crc._xor_output = 0xF43F44AC
    
    output_int = crc.calc(message)
    return struct.pack("<I", output_int).hex()

# Example: building an alarm packet
unix_time = int(time.time()) + 10
unix_hex = struct.pack('<I', unix_time).hex()

package_data = f"aa10005723704201{unix_hex}00000000"
checksum = calculate_checksum(bytearray.fromhex(package_data))
full_package = f"{package_data}{checksum}"

device.char_write(
    "61080002-8d6d-82b8-614a-1c8cb0f8dcc6",
    bytearray.fromhex(full_package),
    wait_for_response=False
)
The CRC parameters were reverse-engineered using crcbeagle. See the CRC Reverse Engineering page for details.

Known Limitation

Direct writes from Python using pygatt do not consistently work with Whoop 4.0. Commands may:
  • Throw exceptions
  • Silently fail
  • Work intermittently
For reliable command execution, use gatttool instead. pygatt is still useful for scanning, connecting, and receiving notifications.

bleak

bleak is a modern, cross-platform Python BLE library that works on Windows, macOS, and Linux.

Installation

pip install bleak

Receiving Notifications

bleak excels at receiving notifications from BLE characteristics. Example script for monitoring heart rate:
python3 enable_notifications.py --address XX:XX:XX:XX:XX:XX 00002a37-0000-1000-8000-00805f9b34fb
This connects to the device and subscribes to the standard BLE Heart Rate Service characteristic (0x2A37).
Heart rate broadcasting must be enabled on the device first. If the device is not discoverable, you’ll get: ERROR: could not find device with address

Listening to Custom Characteristics

Monitor the DATA_FROM_STRAP characteristic for activity data:
python3 enable_notifications.py --address XX:XX:XX:XX:XX:XX 61080005-8d6d-82b8-614a-1c8cb0f8dcc6
After enabling activity mode with gatttool, this will print sensor data every second:
aa1800ff2802ad896566f06542016706000000000000010101...
aa1800ff2802ae896566f86043000000000000000000010101...
aa1800ff2802af896566085c42000000000000000000010101...

Writing with bleak

Like pygatt, bleak’s write operations are unreliable with Whoop 4.0. Commands sent via bleak may not trigger device responses consistently. Recommended approach:
  • Use bleak for notifications and data reception
  • Use gatttool for sending commands

Workflow: Python + gatttool

The most reliable workflow combines both tools:
1

Start notification listener with Python

python3 enable_notifications.py --address XX:XX:XX:XX:XX:XX 61080005-8d6d-82b8-614a-1c8cb0f8dcc6
2

Send command with gatttool

In another terminal:
sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a8238c03017d5ec627
3

Observe notifications

The Python script will receive and print notifications triggered by the gatttool command.

Example: Toggling Heart Rate Broadcast

from pygatt import GATTToolBackend, BLEAddressType
import subprocess
import time

# Connect with pygatt to verify device presence
adapter = GATTToolBackend(hci_device='hci0')
adapter.start()

MAC_ADDRESS = 'XX:XX:XX:XX:XX:XX'
device = adapter.connect(MAC_ADDRESS, address_type=BLEAddressType.random)

print("Device connected")

# Use gatttool to send enable command
enable_cmd = "aa0800a823080e016c935474"  # Enable heart rate broadcast
subprocess.run([
    'sudo', 'gatttool',
    '-i', 'hci0',
    '-t', 'random',
    '-b', MAC_ADDRESS,
    '--char-write',
    '-a', '0x0010',
    '-n', enable_cmd
])

print("Heart rate broadcast enabled")
time.sleep(2)

# Now heart rate service should be available
print("Device is now broadcasting heart rate")

Reverse Engineering CRC

If you need to reverse engineer checksums for other BLE devices, use crcbeagle:
git clone https://github.com/colinoflynn/crcbeagle
cd crcbeagle
touch examine.py
Create examine.py with your packet samples:
from crcbeagle.crcbeagle import CRCBeagle

crc = CRCBeagle()

def hex_to_int(hex):
    return [i for i in bytearray.fromhex(hex)]

data = """
aa100057236d4201d036656600000000f62deb81
aa100057236e42010c376566000000001023ccef
aa100057236f4201207d656600000000fea1e060
aa100057237042015011656600000000f226a8bd
""".split('\n')

data = [i for i in data if i]
checksums = [i[len(i) - 8:] for i in data]
data = [i[:len(i) - 8] for i in data]

crc.search(
    [hex_to_int(i) for i in data],
    [hex_to_int(i) for i in checksums]
)
Run the script to discover CRC parameters:
python examine.py

Summary

TaskBest Tool
Scanning for devicespygatt
Connectingpygatt or bleak
Receiving notificationsbleak
Sending commandsgatttool
CRC calculationcrccheck (Python)
CRC reverse engineeringcrcbeagle

Build docs developers (and LLMs) love