Skip to content

feat(keychron): add M6 wired and receiver support - #33

Merged
snekxs merged 1 commit into
OpenMouse-Project:mainfrom
ydw1904:codex/keychron-m6
Aug 29, 2026
Merged

feat(keychron): add M6 wired and receiver support#33
snekxs merged 1 commit into
OpenMouse-Project:mainfrom
ydw1904:codex/keychron-m6

Conversation

@ydw1904

@ydw1904ydw1904 commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a dedicated Keychron M6 WebHID driver for wired USB (3434:d060) and the paired 2.4 GHz Keychron Link-KM receiver (3434:d029).
  • Limits receiver support to its verified ffc1:01 control collection, so the existing Nape Pro raw-HID receiver path remains separate.
  • Supports status reads plus DPI and polling-rate writes with read-back verification.

Scope

Hardware validation

  • Verified a physical Keychron M6 in wired USB mode and through the Link-KM 2.4 GHz receiver.
  • Both paths use status b3/b4 and settings b5/b6 reports on ffc1:01.
  • On each path, DPI stage 0 changed 400 -> 1200 -> 400 and read back after each step.
  • On each path, polling changed 500 Hz -> 1000 Hz -> 500 Hz and read back after each step.
  • The mouse finished each test at its original 400 DPI and 500 Hz settings.

Tests

  • TypeScript build passes.
  • Full suite: 525 passing tests.
  • Package contents were inspected after the build. The repository's npm check wrapper could not run because this environment does not provide npm; its build and test steps were run directly.

Assisted by Codex.

@ydw1904
ydw1904force-pushed the codex/keychron-m6 branch 2 times, most recently from b08a43c to 201cd48CompareAugust 23, 2026 08:49
@ydw1904ydw1904 changed the title feat(keychron): add M6 wired supportfeat(keychron): add M6 wired and receiver supportAug 23, 2026
@ydw1904
ydw1904 marked this pull request as ready for review August 23, 2026 08:54
@ydw1904

Copy link
Copy Markdown
ContributorAuthor

Companion PR in the web app: OpenMouse-Project/openmouse#128 (draft, blocked on this one).

Worth flagging because this PR alone does not make the M6 usable. KeychronM6HidClient is detected fine — the registry auto-detects it from the report descriptor — but the app's pulsarClient() in src/device/controller.ts is a negative check: any active client not listed in DEDICATED is assumed to be Pulsar and routed into showPulsarExplorer(), which calls the Pulsar-only describeCollections(). The device lists in the sidebar as "Keychron · Available" and activation fails with client.describeCollections is not a function.

#128 adds one entry to NEEDS_OPEN to fix it. It is a draft only because it imports @openmouse/protocol/drivers/keychron/m6-hid, so its type check cannot pass until this merges and the protocol pin moves.

Verified on wired hardware with both PRs applied: connects, reports 800 DPI / 500 Hz / battery, and a DPI change flashes and reads back.

@snekxs
snekxs merged commit 81e7f9f into OpenMouse-Project:mainAug 29, 2026
1 check passed
snekxs added a commit that referenced this pull request Aug 29, 2026
Combines the M6 PR (#33) and Nape Pro PR (#29), which both branched before
the other landed and independently touched keychron/index.ts and registry.ts.
Retires the generic KeychronHidClient (keychron/hid.ts): nape-hid.ts is a
strict superset of it and both matched the exact same product IDs, so every
Nape Pro device was claimable by two drivers (caught by registry.test.ts).
This matches this PR's own stated intent ("keep the Keychron driver
Nape-only").
Build and full test suite pass (544/544).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
snekxs added a commit that referenced this pull request Aug 29, 2026
Combines the M6 PR (#33) and Nape Pro PR (#29), which both branched before
the other landed and independently touched keychron/index.ts and registry.ts.
Retires the generic KeychronHidClient (keychron/hid.ts): nape-hid.ts is a
strict superset of it and both matched the exact same product IDs, so every
Nape Pro device was claimable by two drivers (caught by registry.test.ts).
This matches this PR's own stated intent ("keep the Keychron driver
Nape-only").
Build and full test suite pass (544/544).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dwei30 pushed a commit to dwei30/openmouse that referenced this pull request Aug 30, 2026
The M6 driver in @openmouse/protocol is enough for the registry to detect
the mouse, but not for the app to drive it: the sidebar lists it as
"Keychron · Available" and activating it fails with
client.describeCollections is not a function
pulsarClient() is a negative check — any active client not listed in
DEDICATED is assumed to be a Pulsar client and routed into
showPulsarExplorer(), which calls describeCollections(), a method only the
Pulsar drivers have. A new driver class is therefore broken by default
until it is named here, and the error points nowhere near the cause.
KeychronM6HidClient joins NEEDS_OPEN, next to the Nape Pro's
KeychronHidClient: its open() is what attaches the input-report listener
its query/reply exchanges depend on, and NEEDS_OPEN feeds DEDICATED, so
one entry covers both.
Verified on a wired Keychron M6: the device connects and reports 800 DPI,
500 Hz, and battery, and a DPI change flashes to the mouse and reads back.
Needs the M6 driver from OpenMouse-Project/mouse-protocol#33.
Sign up for freeto 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.

2 participants

@ydw1904@snekxs