Skip to content

feat(hid): recognise Lightspeed nano receivers (G-series, e.g. G305)#388

Open
kiwimaker wants to merge 1 commit into
AprilNEA:masterfrom
kiwimaker:feat/lightspeed-receiver
Open

feat(hid): recognise Lightspeed nano receivers (G-series, e.g. G305)#388
kiwimaker wants to merge 1 commit into
AprilNEA:masterfrom
kiwimaker:feat/lightspeed-receiver

Conversation

@kiwimaker

Copy link
Copy Markdown

Summary

Adds recognition for the Logitech Lightspeed nano receiver (046d:c53f) — the receiver bundled with G-series wireless mice such as the G305. Before this change, receiver::detect() only matched Bolt (c548) and Unifying (c52b, c532) receivers; a Lightspeed receiver fell through to None, so any mouse paired to it was completely invisible (the mouse never appeared in list / the GUI, even though the keyboard on a Unifying receiver did).

A Lightspeed nano receiver is not a Unifying receiver, but it exposes the same HID++ 1.0 receiver registers (0x02 connections, 0xB5/0x5N pairing info) that this codebase already drives for Unifying. So the cleanest fix is to route it through the existing Unifying code path and differentiate it only where it's user-visible: the receiver's display name.

Changes

  • openlogi-hidpp / receiver::unifying::VPID_PAIRS — add 046d:c53f so detect() builds the (Unifying-protocol) receiver for it. Documented that this is a protocol-compat Lightspeed receiver, not Unifying proper.
  • openlogi-hid / route.rs — new LIGHTSPEED_PIDS list plus two small helpers: speaks_unifying_protocol(pid) (Unifying ∪ Lightspeed) and receiver_display_name(pid). device_route_for now routes Lightspeed receivers as DeviceRoute::Unifying (they were previously defaulting to Bolt, which would address pairing slots with the wrong register flow on the write path).
  • openlogi-hid / pairing.rsfamily_for classifies Lightspeed as ReceiverFamily::Unifying via the shared helper.
  • openlogi-hid / inventory/probe.rs — the enumerated receiver is now labelled via receiver_display_name, so a Lightspeed receiver surfaces as "Lightspeed Receiver" while Unifying stays "Unifying Receiver".
  • Tests: added coverage that Lightspeed PIDs route as Unifying and that the display name resolves correctly.

Testing

Verified on macOS with a real Logitech G305 (paired device wpid 0x4074) on its Lightspeed nano receiver:

# before
$ openlogi list        # G305 receiver not recognised — mouse invisible

# after
Lightspeed Receiver (E93B9602, vid=046d pid=c53f)
  └─ slot 1 ● mouse, wpid=4074 ... transports=equad
Unifying Receiver (E3C15593, vid=046d pid=c52b)
  └─ slot 1 ● keyboard, wpid=408a ...
  • cargo test -p openlogi-hid -p openlogi-hidpp — all pass (incl. the new tests).
  • cargo clippy / cargo fmt clean on the touched crates.

Notes / scope

  • I could only test 0xc53f (the receiver that shipped with my G305). Other Lightspeed receiver PIDs (0xc539, 0xc541, 0xc545, 0xc547, …) almost certainly work through the same path, but I've deliberately not added PIDs I couldn't verify — they can be appended to LIGHTSPEED_PIDS as people confirm them.
  • Naming lives in the app layer (openlogi-hid) rather than the vendored hidpp fork, keeping the protocol layer's notion of "this is the Unifying register protocol" intact.
  • The GUI's per-device "connected via … receiver" label (derived from DeviceRoute::Unifying) still reads "Unifying"; the primary receiver name shown in the inventory/carousel is correct. Happy to extend the GUI label too if you'd prefer a follow-up.

Logitech Lightspeed nano receivers (e.g. the one bundled with the G305)
were dropped by receiver::detect(), which only matched Bolt and Unifying
receivers, so a mouse paired to one never surfaced in the inventory.

A Lightspeed receiver exposes the same HID++ 1.0 receiver registers as
Unifying, so route it through the existing Unifying code path and only
differentiate the user-facing receiver name ("Lightspeed Receiver").

- add 046d:c53f to unifying::VPID_PAIRS so detect() builds the receiver
- add LIGHTSPEED_PIDS + speaks_unifying_protocol()/receiver_display_name()
- route Lightspeed as DeviceRoute::Unifying (was defaulting to Bolt) and
  classify it as ReceiverFamily::Unifying for pairing
- label the enumerated receiver via receiver_display_name()
- tests for Lightspeed routing and display name

Verified on macOS with a real G305 (paired device wpid 0x4074).
@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds support for Logitech Lightspeed nano receivers through the existing Unifying-compatible path. The main changes are:

  • Adds 046d:c53f to the HID++ Unifying receiver detection table.
  • Adds Lightspeed PID helpers and receiver display naming.
  • Routes Lightspeed receivers through DeviceRoute::Unifying.
  • Classifies Lightspeed receivers as Unifying-family pairing targets.
  • Labels Lightspeed receivers separately in inventory.

Confidence Score: 4/5

The Linux receiver-child classification path needs a fix before merging.

  • The new PID is handled consistently for detection, display, and write routing.
  • Linux child HID nodes can still miss the new Lightspeed PID and appear as direct devices.
  • The pairing path now accepts Lightspeed receivers before the pairing-command contract is separated from enumeration support.

crates/openlogi-hid/src/route.rs, crates/openlogi-hid/src/pairing.rs, and the Linux receiver-child filtering logic

Important Files Changed

Filename Overview
crates/openlogi-hid/src/route.rs Adds Lightspeed PID classification, display naming, and Unifying-route selection for Lightspeed receivers.
crates/openlogi-hid/src/pairing.rs Uses the shared Unifying-protocol helper to include Lightspeed receivers in pairing-family detection.
crates/openlogi-hid/src/inventory/probe.rs Uses the receiver display-name helper so Lightspeed receivers show their own name in inventory.
crates/openlogi-hid/src/lib.rs Re-exports the new Lightspeed PID list and receiver helper functions.
crates/openlogi-hidpp/src/receiver/unifying.rs Adds the Lightspeed nano receiver PID to the Unifying-compatible receiver ID table.

Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat(hid): recognise Lightspeed nano rec..." | Re-trigger Greptile

/// the same HID++ 1.0 receiver register protocol as Unifying, so they are
/// enumerated, routed, and paired through the Unifying code path; only the
/// user-facing receiver name (see [`receiver_display_name`]) differs.
pub const LIGHTSPEED_PIDS: &[u16] = &[0xc53f];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Lightspeed Children Stay Unfiltered

Adding 0xc53f makes Lightspeed receivers appear in the receiver inventory, but the Linux receiver-child sysfs filter still checks only Bolt and Unifying PIDs. On Linux, HID child nodes behind this receiver can still be treated as standalone devices, so the same physical mouse can show up with duplicate or wrong direct-device routing.

Fix in Codex Fix in Claude Code

if crate::BOLT_PIDS.contains(&product_id) {
Some(ReceiverFamily::Bolt)
} else if crate::UNIFYING_PIDS.contains(&product_id) {
} else if crate::speaks_unifying_protocol(product_id) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Pairing Flow Uses Enumeration Match

family_for now treats every Lightspeed PID as a Unifying pairing receiver, so run_pairing and unpair can write the Unifying pairing commands to 0xc53f. The compatibility noted for enumeration registers does not show that the pairing lock and unpair commands use the same contract, so a Lightspeed receiver that lists devices correctly but rejects those commands can fail pairing or apply the wrong receiver operation.

Fix in Codex Fix in Claude Code

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.

1 participant