Skip to content

feat: support the Logitech G502 Lightspeed and G915 - #30

Merged
PyFlat-JR merged 4 commits into
mainfrom
feature/g502-lightspeed
Oct 16, 2026
Merged

PyFlat-JR merged 4 commits into
mainfrom
feature/g502-lightspeed

Conversation

@PyFlat-JR

Copy link
Copy Markdown
Member

What does this PR do?

Adds a second Logitech HID++ protocol for devices that report a battery voltage (0x1001) instead of a percentage, with two models on it: the G502 Lightspeed (receiver C539, cable C08D) and the G915 keyboard (receiver C547, cable C33E). Closes #21 and part of #25. The protocol is based on the changes @Luckylucasv shared in #21.

  • Protocol: LogitechVoltageBatteryProtocol sits on LogitechHidppProtocol, the same as the existing mouse and headset protocols, at device index 1. Bytes 4-5 hold the voltage in mV, and byte 6 holds flags.
  • Percentage: the mV are mapped through the Linux kernel's 100-entry 0x1001 table (hidpp20_map_battery_capacity), interpolated linearly. It's an estimate, since G HUB uses its own per-device table. PercentFromMillivolts moved into LogitechHidppProtocol, and the headset protocol uses it too.
  • Flags: these follow the kernel and Solaar.
    • Bit 7 means external power. Only while it's set do bits 0-2 give the charge state: 0 charging, 1 full, 2 not charging.
    • Bit 5 is the critical-level bit, never "full".
    • 0 mV (off or asleep) throws, so the plugin keeps the last value instead of showing 0%.
  • Charging: while charging there's no percentage, because the voltage reads high under charge, as on Rapoo, AULA and MCHOSE. Full reads 100%.
  • G915 and the G502 X PLUS: they share the C547 receiver. The G915 has 0x1001 and no 0x1004, so the existing fallback rule from feat: support the Logitech G502 X PLUS #29 keeps each one on its own receiver.
  • Hardware test: Reads_the_raw_logitech_battery_voltage prints the raw mV and flags next to the parsed reading, for calibrating against G HUB.
  • Linux: udev lines for c539, c08d and c33e (c547 is already there), with the guide's copy kept in sync.
  • Docs: README rows ("approx.", no level while charging) and the protocol in AGENTS.md.

Checklist

  • I have read AGENTS.md and CONTRIBUTING.md.
  • dotnet build and dotnet test pass locally (0 warnings, 343 tests).
  • I updated README.md / AGENTS.md if this changes what they describe.
  • If this touches a device backend: I tested it against the real hardware (see below).

Device/hardware tested against (if applicable)

G502 Lightspeed: tested by its owner (@Luckylucasv) in #21 on Windows, on an earlier state of this branch (before the full kernel table and the no-level-while-charging change):

  • Receiver: 96% Discharging. Cable: Charging, with the percentage jumping to about 100.
  • Compared with G HUB at 90-100%: 2-4% apart. One reading showed G HUB at 72% while the plugin showed 97%; that is not explained yet.
  • Off: the last value stays, shown as stale. With the receiver and the cable both connected, the mouse appears once and reads from the cable.
  • In Macro Deck 3 over 1-2 hours, the variables and widget behaved.

G915: tested by its owner (@ReqwiemG) in #25 on Windows, with the G502 X PLUS and PRO X 2 LIGHTSPEED on their own receivers and G HUB running:

  • Receiver, with all receivers and no cables: 80% Discharging (3990 mV) against G HUB's 82%. The G502 X PLUS still read correctly on its own receiver.
  • Cable: Charging (4042 mV, flags 0x80) while G HUB showed 82% charging. The plugin estimated 87% then, which is why charging now shows no percentage.

@PyFlat-JR
PyFlat-JR merged commit 054108c into main Oct 16, 2026
3 checks passed
@PyFlat-JR
PyFlat-JR deleted the feature/g502-lightspeed branch October 16, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Device Request]: Logitech G502 Lightspeed

1 participant