Skip to content

Logitech G PRO X 2 LIGHTSPEED battery remains at 100% #539

Open

Description

@platonz

Description

Logitech G PRO X 2 LIGHTSPEED battery remains at 100%

Description

HeadsetControl always reports 100% battery for my Logitech G PRO X 2 LIGHTSPEED, even after more than two hours of active wireless use with no charging cable connected.

The value remains at 100% after switching the headset off, unplugging the LIGHTSPEED receiver, waiting, reconnecting the receiver, and switching the headset back on.

Headset

  • Device: Logitech G PRO X 2 LIGHTSPEED
  • USB receiver vendor ID: 0x046d
  • USB receiver product ID: 0x0af7
  • Connection: LIGHTSPEED USB receiver
  • Charging cable connected: No

Environment

  • Operating system: Windows 11 x64 (10.0.26200)
  • HeadsetControl version: continuous-52-gfe086cd
  • HeadsetControl commit: fe086cda8fc50d15c3aea90c748c8f3346b6e601
  • HIDAPI version reported by HeadsetControl: 0.15.0
  • Command: headsetcontrol.exe -b -o json

Steps to reproduce

  1. Connect the LIGHTSPEED USB receiver.
  2. Switch the headset on and use it wirelessly for more than two hours.
  3. Ensure no charging cable is connected.
  4. Run headsetcontrol.exe -b -o json repeatedly.
  5. Observe that every query reports 100%.
  6. Switch the headset off.
  7. Unplug the receiver and wait 10 seconds.
  8. Reconnect the receiver, switch the headset on, and query again.

Expected result

The reported battery percentage should decrease after extended active wireless use.

Actual result

Every query reports:

{
  "name": "HeadsetControl",
  "version": "continuous-52-gfe086cd",
  "api_version": "1.4",
  "hidapi_version": "0.15.0",
  "device_count": 1,
  "devices": [
    {
      "status": "success",
      "device": "Logitech G PRO X 2 LIGHTSPEED",
      "vendor": "Logitech",
      "product": "PRO X 2 LIGHTSPEED",
      "id_vendor": "0x046d",
      "id_product": "0x0af7",
      "battery": {
        "status": "BATTERY_AVAILABLE",
        "level": 100
      }
    }
  ]
}

Five consecutive direct queries returned the same result:

Battery  BatteryStatus      DeviceStatus
100      BATTERY_AVAILABLE  success
100      BATTERY_AVAILABLE  success
100      BATTERY_AVAILABLE  success
100      BATTERY_AVAILABLE  success
100      BATTERY_AVAILABLE  success

Scheduled queries over more than two hours also returned 100% with exit code 0 and empty stderr.

Additional information

  • Packaged HeadsetControl 3.1.0 detects this receiver but says the headset does not support battery reporting.
  • The continuous build detects the newer CAP_BATTERY_STATUS capability, but the returned value does not change.
  • No experimental HID commands were sent. Only the standard battery/JSON command was used.
  • No Logitech audio driver or G Hub is installed for this test; Windows uses its generic audio driver.

On which OS does the problem happen?

Win11

Activity

  1. platonz commented on Jul 15, 2026

    @platonz
    Author

    Comment for #539 — ready to post (copy below the line)


    Follow-up with new data: I reverse-engineered the receiver protocol directly
    (Python + hidapi, raw HID captures against VID 046D / PID 0AF7, Windows 11,
    no G HUB installed) and can now describe exactly what the battery channel
    returns and when it changes.

    Protocol summary (verified on hardware, 2026-07-15)

    The receiver's vendor interface is usage page 0xFFA0, report ID 0x51,
    64-byte reports. Framing:

    request : 51 <msg_len> 00 <svc_idx> <fn<<4|swid> <args...> 00-pad
    reply   : same shape; errors are 51 <len> 00 FF <svc> <fnSw> <code>
    

    The receiver exposes a small service table (enumerable via service 0x01):
    service 0x03 is a proxy that tunnels messages to the headset — the same
    mechanism the sidetone/noise/timeout commands from #314 use. Through it, the
    headset's own service table can be walked (inner service 0x01):

    inner idx service id meaning (verified)
    0x04 0x0104 battery
    0x06 0x0108 inactive timeout (read returned my configured 30 min)
    0x07 0x0604 sidetone (matches #314)
    0x08 0x0602 mic noise reduction (matches #314)

    Battery read

    TX: 51 08 00 03 1A 00 03 00 04 0A          (proxy → inner svc 0x04, fn0)
    RX: 51 03 00 03 1A 00                      (ack)
    RX: 51 0B 00 03 10 00 06 00 04 0A 64 64 00 (data)
                                  ^^ ^^ ^^
                               level  ?  charge flag
    

    Payload is [level%, 0x64, chargeFlag]. The charge flag is live: it flips
    00 → 02 within seconds of plugging the charging cable in, and back on
    unplug (verified repeatedly while polling every 15 s). So the channel itself
    works and is not a stale snapshot.

    Drain-test result: not a bug — the level is just very slow

    Overnight drain test (polling the raw service every 5 minutes during
    continuous playback):

    20:27  100% -> 99%   (64 64 00 -> 63 63 00)
    ...
    00:17   96% -> 95%   (60 60 00 -> 5f 5f 00)
    00:52   95% -> 94%
    01:07   94% -> 93%
    

    The level byte decrements honestly at ~1% per 25–35 minutes — consistent
    with the advertised ~50-hour battery. So neither HeadsetControl nor the
    firmware is stuck
    : a typical 2-hour test session drains ~4%, and near
    full charge the value simply sits at 100 long enough that every short test
    I ran (including the one this issue is based on) never saw it move. I
    consider the original report resolved as a misunderstanding, though the
    protocol details below may still be useful.

    Notes

    • Full capture transcripts (feature-table walks, error frames, plug/unplug
      timeline, drain log) available on request, plus standalone Python probe
      scripts.
    • The second payload byte mirrors the level exactly (100/100, 99/99, …,
      93/93 across the whole drain) — a duplicate/secondary level field.
    • The charging flag could be a nice addition for HeadsetControl: the same
      read that returns the level already contains it (byte 3: 0x00 on
      battery, 0x02 with the cable in), so BATTERY_CHARGING could be
      reported for this headset instead of always BATTERY_AVAILABLE.
    • Receiver-level service 0x01 fn0/fn1 enumerate the service directory
      ([count] / suffix-list of [id_hi id_lo ver_hi ver_lo] entries) — handy
      for supporting future devices generically.
  2. ChrisLauinger77 commented on Jul 29, 2026

    @ChrisLauinger77
    Contributor

    I have this headset running - works like a charm but it seems you need to update headsetcontrol. It will report much more in JSON in the newer version. see also #512

    You also wanna check if there is a firmware update for the headset.

    I use https://github.com/ChrisLauinger77/QontrolPanel which build headsetcontrol lib in CI. Maybe you wanna try it ... No headsetcontrol seperatly installed would be needed then.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions