fix(openlogi-hid): enumerate Bluetooth-LE-direct HID++ mice (Lift, Signature)#28
Conversation
…gnature) BLE-direct Logitech mice expose their HID++ long-report collection under HID usage page 0xff43/0x0202 — not the 0xff00/0x0002 used over USB and receivers — and on macOS carry only the long report (0x11), no short (0x10). Two changes make them controllable: - Broaden the enumeration filter to accept both the USB/receiver and the BLE vendor collections. - Up-convert outgoing short HID++ reports to long for BLE-only channels. hidpp 0.2 hardcodes a short-report ping, which macOS rejects with kIOReturnNotFound when the device exposes no short report; reframing it as a zero-padded long report (same header bytes) lets the device answer. Gated on usage_page == 0xff43, so the receiver/USB path is unchanged. Also refresh the `list` no-device hint: BLE-direct mice need Input Monitoring permission, and drop the stale "Bluetooth not enumerated" note. Verified on a real Logitech Lift: list, diag features (32 features), and a DPI write round-trip all succeed.
There was a problem hiding this comment.
ℹ️ Minor suggestions only — the fix is well-scoped and the unit tests cover the new behavior.
Reviewed changes — initial review of the BLE-direct HID++ enumeration fix: a widened usage-page filter plus a long-only channel mode that up-converts short HID++ requests to long when the transport (BLE-direct on macOS) doesn't expose the short report.
- Widen the enumeration filter to two
(usage_page, usage_id)pairs —0xFF00 / 0x0002(USB / Bolt / Unifying / BT-classic) plus0xFF43 / 0x0202(BLE-direct, e.g. Lift / Signature), via a constant array and anis_hidpp_long_collectionhelper. - Add a
long_onlyflag toAsyncHidChannel— set wheninfo.usage_page == HIDPP_BLE_USAGE_PAGE.write_reportre-frames short HID++ reports as zero-padded0x11long reports for these channels; the receiver / USB path is unchanged. - Debug-log every Logitech HID node at enumeration time —
OPENLOGI_LOG=debugsurfaces vendor pages from new BLE devices without a rebuild. - Refresh the
list"no devices" hint — point at macOS Input Monitoring permission for BLE-direct mice; drop the stale "Bluetooth not enumerated" note; keep the Logi Options+ caveat and the Bolt-onlyhidpp 0.2note. - Unit tests — filter membership across USB / BLE / generic-desktop pages, plus short→long up-conversion (header preserved, payload zero-padded, non-short reports passed through).
ℹ️ Nitpicks
- The PR body has a copy/paste duplication in the
transport.rsbullet — the sentence starting with "Add along_onlychannel flag…" and the "long-only approach Solaar uses over BLE" sentence appear twice. Worth tidying before merge so the description matches the diff.
Claude Opus | 𝕏
|
Thanks for the review! Good catch — the To reiterate the key safety point: the short→long up-conversion is gated strictly on the BLE vendor page ( |
… table Addresses PR review: `long_only` was derived from a separate `HIDPP_BLE_USAGE_PAGE` constant while collection membership used the data-driven `HIDPP_LONG_COLLECTIONS` table — so a future long-only transport would have to update both sites in lockstep, or silently regress to short-report writes. Encode `long_only` as a third column in the table and look it up via `is_long_only_collection()`, making a new collection a single-line addition. Adds a unit test for the flag.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — re-review of the single follow-up commit since the prior pullfrog review. The new commit retires the extensibility risk I flagged in the snapshot: long_only is now derived from the HIDPP_LONG_COLLECTIONS table itself, not from a separate HIDPP_BLE_USAGE_PAGE constant.
- Folded
long_onlyinto theHIDPP_LONG_COLLECTIONStable — table is now[(u16, u16, bool); 2]with(0xff00, 0x0002, false)and(0xff43, 0x0202, true). Adding a new long-only transport is a single-row edit; theHIDPP_BLE_USAGE_PAGEconstant is gone. - Added
is_long_only_collection(usage_page, usage_id)helper — used inopen_hidpp_channelto derive the channel'slong_onlyflag from the table. Returnsfalsefor pages not inHIDPP_LONG_COLLECTIONS(consistent with the filter gating opens). - Added
only_ble_collection_is_long_onlyunit test — asserts the BLE row is long-only, the USB / receiver row is not, and a non-matching page returnsfalse.
Claude Opus | 𝕏
|
I did local testing, Master 3S can now connect via Bluetooth, thank you for your contribution. Of course, there are still issues with the image display, I see that #26 has resolved this problem. |
PR AprilNEA#28 (merged) rewrote transport.rs to support BLE-direct HID++ mice (Lift/Signature) via the 0xFF43/0x0202 collection. This commit rebases the MX Master 4 B detection on top of that new structure: transport.rs: - Adds is_logitech_mouse() for generic-desktop mouse nodes (0x0001/0x0002) - Widens enumerate_hidpp_devices() to also admit these nodes alongside the HID++ long-report collections already handled by PR AprilNEA#28 - Documents the safety invariant: generic-desktop nodes are safe because open_hidpp_channel returns None on non-HID++ endpoints and fallback_direct_mouse re-filters by known-good PID inventory.rs: - Corrects MX_MASTER_4_PID: 0xb042 → 0xb048 (BLE-direct variant) - Corrects MX_MASTER_4_NAME: "MX Master 4" → "MX Master 4 B" - Adds fallback_direct_mouse() (per-device, keyed by PID) - Adds hidutil_direct_mouse_fallback() wrapped in spawn_blocking, with debug! logging on failure - Ungates the hidutil fallback: runs whenever MX Master 4 B is absent from inventories (not only when the list is empty) - Adds probe_direct().or_else(fallback_direct_mouse) in probe_one Cargo.toml: adds serde_json dependency for the ndjson parser Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Summary
Bluetooth-LE-direct Logitech mice (e.g. Logitech Lift, Signature series)
were never enumerated —
listreported "no devices" even with the mouseconnected and working. This makes them fully controllable (inventory,
battery, DPI) without a Bolt receiver.
Root cause
Three layered issues, found by inspecting what macOS actually reports for a
BLE-connected Lift:
Logitech exposes its HID++ long-report collection under usage page
0xFF00 / 0x0002. Over BLE it uses0xFF43 / 0x0202instead, so thedevice was filtered out before it was ever probed.
only the long report (
0x11) — there is no short-report (0x10)collection.
hidpp 0.2'sdetermine_version()hardcodes a short-reportping with no long fallback, so
IOHIDDeviceSetReportreturnskIOReturnNotFound.macOS requires Input Monitoring permission to open them — unlike a Bolt
receiver, which is a vendor device.
Changes
transport.rs— broaden the enumeration filter to accept both theUSB/receiver (
0xFF00/0x0002) and BLE (0xFF43/0x0202) HID++ vendorcollections. Add a
long_onlychannel flag (set whenusage_page == 0xFF43)that up-converts outgoing short HID++ reports to long (
0x11, zero-padded,same header bytes). The device answers with a long report, which
hidppparses and matches by header regardless of width — the same long-only
approach Solaar uses over BLE. The receiver/USB path is byte-for-byte
unchanged. Adds unit tests for the filter and the up-conversion.
list.rs— refresh the "no devices" hint: BLE-direct mice need InputMonitoring permission; drop the stale "Bluetooth not enumerated" note.
Testing
Verified on a real Logitech Lift (BLE-direct,
046d:b031, config key2b031):list— device, kind, online, battery 45% ✓diag features— 32 HID++ features dumped (incl.0x2201DPI,0x1b04reprog controls; no
0x2111, so no SmartShift — expected for the Lift) ✓diag dpi— read 1000 → write 1200 → read-back 1200 → restore 1000 ✓cargo fmt --all --check,cargo clippy --workspace -- -D warnings, andcargo testall pass.Follow-up (not in this PR)
This fixes HID++ control for BLE-direct mice. The device render for the
Lift (
2b031) isn't in the asset registry onassets.openlogi.orgyet, sothe GUI falls back to the synthetic silhouette — functional, just no photo.
Adding
2b031to the asset host is a separate follow-up.