GWOLVES-BATTERY(1)marcp.xyzGWOLVES-BATTERY(1)

NAME

gwolves-battery - Read G-Wolves HSK Pro 8K battery level on Linux

SYNOPSIS

gwolves-battery              # first mouse found
gwolves-battery all          # all connected mice
gwolves-battery <serial>     # specific mouse (partial match)

DESCRIPTION

The G-Wolves HSK Pro 8K is a great mouse, but there is no Linux software for it. The only way to see the battery level is a web driver (WebHID, Chrome only). That is not something I want to open just to check a percentage, so I reverse-engineered the protocol.

The method: run the web driver once, capture the USB traffic with tshark while the driver polls the battery, then dig through the packets in Wireshark. The battery query turned out to be a simple pair of HID feature reports on interface 2 of the receiver: one SET_REPORT to request the data, one GET_REPORT to read it back. The battery percentage sits at byte 5 of the response.

The result is a single C file (~200 lines) using libusb, no other dependencies. It supports multiple mice at once, each identified by the receiver's unique serial number.

Update 2026-09-03: the HSK Pro 2.0 8K uses a different command. This time no capture was needed: the web driver is plain JavaScript, and its bundle contains both battery functions (getOldBattery and getBatPer) with the command bytes in the clear. The program now sends the old command first and falls back to the new one. It also matches on the vendor ID only, because a firmware update changed the receiver's product ID from 5817 to 0017.

PROTOCOL

Device:  33e4:* (any G-Wolves receiver, or the mouse on cable), Interface 2

Step 1: SET_REPORT (request battery)
  bmRequestType  0x21   Host-to-device, Class, Interface
  bRequest       0x09   SET_REPORT
  wValue         0x0300 Feature Report, ID 0
  wIndex         2
  Data           64 bytes, one of:
    old (HSK Pro 8K)      00 02 8f 01 00 00 ...
    new (HSK Pro 2.0 8K)  00 00 02 02 00 83 00 ...

Step 2: GET_REPORT (read response, old after ~10ms, new after ~100ms)
  bmRequestType  0xa1   Device-to-host, Class, Interface
  bRequest       0x01   GET_REPORT
  wValue         0x0300
  wIndex         2
  Response
    old  a1 02 8f 01 [CABLE] [BATTERY] 00 ...      percentage at byte 5
    new  a1 00 02 02 00 83 [CABLE] [BATTERY] ...   percentage at byte 7

  First byte a1 = command understood, a0 = not understood (wrong generation).
  CABLE is 1 when the mouse itself answers on the cable while charging.
  A receiver whose mouse is off or asleep answers battery 0 ("no link").

EXAMPLES

$ gwolves-battery all
Found 1 mouse/mice:

1. Mouse 24A4E23D2605: 🔋 78%

$ gwolves-battery
🔋 Battery: 78% (Mouse: 24A4E23D2605)

BUILDING

$ curl -LO https://marcp.xyz/files/gwolves-battery.tar.gz
$ tar xzf gwolves-battery.tar.gz
$ cd G-Wolves-Battery
$ make            # needs gcc, make, libusb
$ make install    # copies to ~/.local/bin

NOTES

Non-root USB access needs the udev rule from mouse-udev(1). It matches on the vendor ID, so it covers this receiver too.

Since it is a fast single binary, it drops straight into a status bar. I call it from dwmblocks; anything that runs a shell command works.

SEE ALSO

gwolves-battery.tar.gz (source, Makefile, README), mouse-udev(1), mouse-accel(1)

marcp.xyz2026-09-03GWOLVES-BATTERY(1)