Skip to content

Add Arbiter Polar 65+ Gaming Keyboard 0x371C:0xA009 (14108:40969) #9

Closed

Description

@snekxs

Missing device: Arbiter Polar 65+ Gaming Keyboard 0x371C:0xA009 (14108:40969)

Reporter evidence (verbatim)

  • Model: Polar 65+ Gaming Keyboard, vendor string "Arbiter"
  • IDs: decimal 14108:40969 = hex 0x371C:0xA009
  • Bridge log:
[17:29:46] USB device added: path=\\?\usb#vid_371c&pid_a009#a&32a63c54&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed} vendor=14108 "Arbiter", product=40969 "Polar 65+ Gaming Keyboard", serial="", driver="usbccgp", guid=f27d5fb4-eba4-4668-b166-5e2317ada669
  • Transport: USB (usbccgp), wired. OS / browser / Bridge version still needed.
  • Symptom: Arbiter Studio shows in changelog 2026-10-30, but this board does not appear in the web connect picker.

Code findings

  • Catalog src/arbiter/index.ts (ARBITER_PRODUCTS, from vendor bundle retrieved 2026-10-30, docs/arbiter-support.md) contains 8 entries:
    • 0x1A81:0x2072 x2 (FF01/FF02), 0x1A81:0x208E (FF02), 0x1A81:0x2077 (FF01)
    • 0x371C:0xA005 (FF02), 0x371C:0xA006 (FF01), 0x371C:0xA003 (FF02), 0x371C:0xA001 (FF02)
    • No 0xA009. Every entry is verified: false.
  • Driver src/drivers/arbiter/hid.ts: productFor() matches exact (vendorId, productId) + hasCollection(collections, entry.usagePage); isSupported() returns false for 0x371C:0xA009, so registry yields no driver.
  • Filters src/drivers/vendors.ts: ARBITER_HID_FILTERS = ARBITER_PRODUCTS.map(...) — picker only lists cataloged PID+usagePage pairs, so 0xA009 is filtered out of SUPPORTED_HID_FILTERS. This matches the reported symptom (added but not visible).
  • Web app OpenMouse-Project/openmouse is already on @openmouse/keyboard-protocol@1.4.0 (includes Arbiter), so this is a missing-catalog-entry, not a version lag.

What this means

Single missing-driver cause: no settings writes can reach this board until a catalog entry + filter exists. Not a per-setting bug.

Diagnostics still needed from reporter

  1. Copy diagnostics output (web Overview → Refresh, under identity) or about://device-log line — need HID collections / usage page (0xFF01 vs 0xFF02) and usage.
  2. Confirm: wired vs 2.4GHz vs BT, OS + browser, Bridge version if used.
  3. Vendor web app (app.arbiterstudio.com) behavior for 0xA009 if accessible: protocol class (yl/s2/i2/u2), travel/binding table sizes, layers/profiles, superTap — or a USB capture of the vendor app configuring this board. 0xA009 was not in the bundle analyzed 2026-10-30, so the row cannot be copied from an existing one.

Activity

  1. snekxs commented on Oct 31, 2026

    @snekxs
    MemberAuthor

    Reporter follow-up (verbatim):

    HID device added: vendorId=14108, productId=40969, name='Polar 65+ Gaming Keyboard', serial='', deviceIds=['\\?\hid#vid_371c&pid_a009&mi_00#a&275b93a8&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd'], reportDescriptor=''
    windows 11 pro 25h2, Brave browser, wired
    

    Notes: only mi_00 \kbd (boot keyboard) enumerated here, reportDescriptor empty. Config collection (0xFF01/0xFF02, usage 1) not yet captured — still need web Overview → Refresh / about://device-log listing with HID collections + usage pages to place the 0x371C:0xA009 catalog row.

  2. snekxs commented on Oct 31, 2026

    @snekxs
    MemberAuthor

    Full about://device-log from reporter (verbatim, wired Win11 Pro 25H2, Brave):

    HIDUser[22:17:06] HID device added: vendorId=14108, productId=40969, name='Polar 65+ Gaming Keyboard', serial='', deviceIds=['\\?\hid#vid_371c&pid_a009&mi_00#c&174920be&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd'], reportDescriptor=''
    HIDUser[22:17:06] HID device added: vendorId=14108, productId=40969, name='Polar 65+ Gaming Keyboard', serial='', deviceIds=['\\?\hid#vid_371c&pid_a009&mi_03#c&18a1ce81&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}'], reportDescriptor=''
    HIDUser[22:17:06] HID device added: vendorId=14108, productId=40969, name='Polar 65+ Gaming Keyboard', serial='', deviceIds=['\\?\hid#vid_371c&pid_a009&mi_02#c&30655ac6&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}'], reportDescriptor=''
    HIDUser[22:17:06] HID device added: vendorId=14108, productId=40969, name='Polar 65+ Gaming Keyboard', serial='', deviceIds=['\\?\hid#vid_371c&pid_a009&mi_01&col02#c&c8e1d04&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}'], reportDescriptor=''
    

    Notes:

    • 4 HID interfaces: mi_00 (kbd), mi_01&col02, mi_02, mi_03. All with empty reportDescriptor, so usage page (0xFF01 vs 0xFF02) still unknown.
    • mi_01&col02 ... &0001 is the most likely config-collection candidate (other Arbiter boards expose config on a non-kbd interface), but cannot be confirmed without collections.
    • Still needed: WebHID device.collections dump (usagePage/usage per collection) via navigator.hid.requestDevice({filters: []}) + log, or a USB capture of the vendor web app configuring 0xA009 to determine protocol class (yl/s2/i2/u2) + table sizes + layers/profiles/superTap.
  3. snekxs commented on Oct 31, 2026

    @snekxs
    MemberAuthor

    WebHID collections dump from reporter (stitched pt1+pt2, 0x371C:0xA009):

    • usagePage: 65281 = 0xFF01, usage: 1, type: 1
    • inputReports[0]: reportId: 0, reportCount: 64, reportSize: 8, usages: [4278255618] = 0xFF010002
    • outputReports[0]: reportId: 0, reportCount: 64, reportSize: 8, usages: [4278255619] = 0xFF010003
    • featureReports: [], children: []

    This matches the Arbiter transport (64-byte frames, report id 0) on the 0xFF01 config collection. So the missing catalog row is vendorId 0x371C, productId 0xA009, usagePage 0xFF01, usage 1.

    Still open: protocol class (yl/s2/i2/u2), travelKeys/bindingKeys, layers/profiles/superTap for 0xA009. 0xA009 was not in the vendor bundle analyzed 2026-10-30. Existing 0xFF01 rows for reference: 0x1A81:0x2072 (yl, 68/68, 1 layer, 1 profile), 0x1A81:0x2077 (yl, 68/68), 0x371C:0xA006 (s2, 69/69). Do not copy blind — needs vendor-app capture or hardware trial.

  4. snekxs commented on Oct 31, 2026

    @snekxs
    MemberAuthor

    Capture verified: unseenArbiter.pcapng (27MB, USBPcap linktype 249, 148,874 packets) contains Arbiter-protocol traffic for this board.

    Decoded 64-byte frames with valid checksums ((85 - sum(b0..62)) & 0xFF == b63, logDevice=0):

    • getFirmware (cmd 2): req dsize=0, reply dsize=8 data 0900440000210004
    • getSerial (cmd 13): req dsize=0, reply dsize=19 (serial redacted, format ZDK70...0386)
    • getUserMode (cmd 53): req dsize=6, reply dsize=10 data 0000000101001f210000

    This matches ARBITER_COMMAND (2/13/53) and the 64-byte report-0 framing, so the capture is the vendor app talking to 0x371C:0xA009 on its 0xFF01 collection. Correct device, correct interface.

    Still needed for the catalog row: getTravel (cmd 1) + getKeyMapping (cmd 27) reads to fix travelKeys/bindingKeys, layers/profiles/superTap. Current capture only shows firmware/serial/userMode — a longer capture while clicking actuation / key-remap / lighting pages in the vendor app (or noting layers/profiles/SuperTap in the UI) will settle the protocol class (yl/s2/i2/u2).

  5. github-actions commented on Oct 31, 2026

    @github-actions

    🎉 This issue has been resolved in version 1.5.0 🎉

    The release is available on:

    Your semantic-release bot 📦🚀

  6. snekxs commented on Oct 31, 2026

    @snekxs
    MemberAuthor

    Update 2: root cause of the post-release silence + fix PR

    The overlooked console dump was the key. It shows the board exposes two vendor config collections: 0xFF01 (in/out usages ...02/...03) and 0xFF02 (usages ...03/...04), plus the boot-keyboard and consumer collections. #11 put the row on 0xFF01 — the wrong one, which is why the board claimed but never answered.

    Three independent lines of proof that 0xFF02 is the live interface:

    1. USB capture: every vendor-app frame flows on USB interface 3 (endpoints 0x04/0x84); interface 2 is completely silent.
    2. Live vendor bundle (app2.arbiterstudio.com): defines Polar 65+, 14108:40969, usagePage 65282, and its device picker only claims collections with usagePage == 65282. 65282 appears 3x in the bundle, 65281 zero times.
    3. Bundle entry details match the captures: Main+FN layers, 3 profiles, SuperTap, i2-class wire behavior (travel 3x29/29/10, keymap 5x136).

    Fix: PR #12 moves the 0xA009 row to 0xFF02 and pins it in tests. Suite 384 pass / 0 fail, build clean.

    Also discovered: the same vendor bundle lists a second Polar 65+ variant, product 40976 (0x371C:0xA010), on 0xFF02 with a different protocol class — no capture exists for it yet, so no row. If another 65+ shows up with a different PID, that's the first place to look.

  7. github-actions commented on Oct 31, 2026

    @github-actions

    🎉 This issue has been resolved in version 1.6.1 🎉

    The release is available on:

    Your semantic-release bot 📦🚀

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions