Skip to main content
gatttool is a command-line utility from the BlueZ Bluetooth stack for interacting with BLE devices. It is the most reliable method for sending commands to the Whoop 4.0 device.

Why gatttool?

While Python libraries like pygatt and bleak are useful for scanning and receiving notifications, they struggle with writing commands to Whoop 4.0. Commands sent via Python:
  • Sometimes throw exceptions
  • Sometimes silently fail
  • Work inconsistently
gatttool provides reliable command execution, though it also works “randomly” - it’s still more consistent than Python alternatives.
Even gatttool doesn’t achieve 100% reliability with Whoop 4.0, but it’s the best available option for writing characteristics.

Installation

On Debian/Ubuntu:
sudo apt-get install bluez
On Arch Linux:
sudo pacman -S bluez bluez-utils
Verify installation:
gatttool --help

Basic Command Structure

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n <hex_data>
Parameters:
  • -i hci0 - Bluetooth adapter to use (usually hci0)
  • -t random - Address type (Whoop uses random BLE addresses)
  • -b XX:XX:XX:XX:XX:XX - Device MAC address
  • --char-write - Perform a characteristic write
  • -a 0x0010 - Characteristic handle (0x0010 = CMD_TO_STRAP)
  • -n <hex_data> - Hex data to write (no spaces, no 0x prefix)
sudo is required for gatttool to access Bluetooth hardware.

Common Commands

Set Alarm

Set an alarm to ring in 10 seconds:
sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa10005723704201126d6566000000003d59d8fd
The packet structure:
  • aa10005723 - Header
  • 70 - Packet counter
  • 4201 - Alarm command flags
  • 126d6566 - Unix timestamp (little-endian)
  • 00000000 - Padding
  • 3d59d8fd - CRC-32 checksum

Enable Heart Rate Broadcasting

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a823080e016c935474
After enabling, the device becomes discoverable via the standard BLE Heart Rate Service and will broadcast heart rate data.

Disable Heart Rate Broadcasting

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a823070e00c7e40f08

Start Activity Recording

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a8238c03017d5ec627
This starts activity mode. The device will send notifications on DATA_FROM_STRAP (handle 0x0018) every second with heart rate and sensor data.

Stop Activity Recording

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a8238d0300dc040351
Stops activity mode and ends the notification stream.

Enable Health Monitor

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a823060301f2bc064cb

Disable Health Monitor

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a823050300e44e25be

Trigger Data Retrieval

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a8230e16001147c585
This command is also sent when the alarm is tapped. It triggers a burst of notifications on DATA_FROM_STRAP.

Reboot Device

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a823d41d003c2e2fe6

Erase Device

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa10005723cf19fefefefefefefefe002f8744f6
This command erases all data on the device. Use with caution.

Characteristic Handles

Whoop 4.0 uses handle 0x0010 for the writable CMD_TO_STRAP characteristic:
HandleUUIDNameAccessPurpose
0x001061080002-8d6d-82b8-614a-1c8cb0f8dcc6CMD_TO_STRAPWriteSend commands
0x001261080003-8d6d-82b8-614a-1c8cb0f8dcc6CMD_FROM_STRAPNotifyCommand responses
0x001561080004-8d6d-82b8-614a-1c8cb0f8dcc6EVENTS_FROM_STRAPNotifyEvents
0x001861080005-8d6d-82b8-614a-1c8cb0f8dcc6DATA_FROM_STRAPNotifySensor data
All commands to the device are written to handle 0x0010.

Workflow: gatttool + Python Notifications

For the best reverse engineering workflow:
1

Start Python notification listener

In one terminal, listen for notifications using bleak:
python3 enable_notifications.py --address XX:XX:XX:XX:XX:XX 61080005-8d6d-82b8-614a-1c8cb0f8dcc6
2

Send command with gatttool

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

Observe data stream

The Python script will receive notifications every second with heart rate and sensor data:
aa1800ff2802ad896566f06542016706000000000000010101...
aa1800ff2802ae896566f86043000000000000000000010101...
4

Stop activity

sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a8238d0300dc040351

Interactive Mode

gatttool also supports an interactive mode for exploring devices:
sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX -I
This opens an interactive prompt. Common commands:
connect                 # Connect to device
primary                 # List primary services
characteristics         # List all characteristics
char-read-hnd 0x0010    # Read characteristic at handle
char-write-req 0x0010 aa0800a823080e016c935474  # Write with response
char-write-cmd 0x0010 aa0800a823080e016c935474  # Write without response
disconnect              # Disconnect
exit                    # Exit interactive mode
For sending Whoop commands, use char-write-cmd (write without response) as most Whoop commands don’t expect acknowledgment.

Troubleshooting

This is common with Whoop 4.0. The device sometimes ignores commands. Try:
  1. Re-sending the command (gatttool is “randomly” reliable)
  2. Verify the checksum is correct
  3. Check packet counter byte (though testing shows it’s not validated)
  4. Ensure device is not already in the requested state
  • Check the device is powered on and in range
  • Verify the MAC address is correct
  • Ensure -t random is specified (Whoop uses random addresses)
  • Try scanning first: sudo hcitool lescan
gatttool requires root access:
sudo gatttool ...
If heart rate broadcasting was disabled, the device won’t be discoverable until you re-enable it:
sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n aa0800a823080e016c935474

Packet Counter Mystery

Commands include a packet counter byte, but testing shows:
  • Sending commands with the same counter value works
  • Counter values don’t need to be sequential
  • The device doesn’t validate or enforce counter behavior
This suggests the packet counter is unused or only used for debugging/logging on the device side.

See Also

Build docs developers (and LLMs) love