Skip to content

feat: add UtechSmart Venus MMO (04d9:fc55) driver - #305

Draft
javinnav wants to merge 4 commits into
OpenMouse-Project:mainfrom
javinnav:feat/utechsmart-venus
Draft

javinnav wants to merge 4 commits into
OpenMouse-Project:mainfrom
javinnav:feat/utechsmart-venus

Conversation

@javinnav

Copy link
Copy Markdown

Summary

Adds a codec (src/utechsmart/) and a WebHID driver (src/drivers/utechsmart/) for the wired UtechSmart Venus MMO gaming mouse, USB 04d9:fc55 (Holtek controller), registered in the driver registry and vendor filters.

Supported settings:

  • reading the active profile's status (DPI stages, current stage, polling rate, button mappings)
  • button remapping on the 12 side buttons and the standard buttons, including keys with modifiers (for example Ctrl+1)
  • DPI stage values (200–16400, vendor label steps)
  • polling rate 125 / 250 / 500 / 1000 Hz

Not supported: LEDs, macros, and switching the active profile.

Device and connection

Model UtechSmart Venus MMO (16400 DPI, wired)
USB ID 04d9:fc55
Interface MI_02, vendor collection usage page 0xFFA0, usage 0x01
Reports feature report 2 (16 bytes, 8 data) and report 3 (64 bytes, 56 data)
Tested on Windows, Microsoft Edge, direct WebHID (no Bridge)

Protocol

Frames are [report 02, command, address lo, address hi, length, 0, 0, 0, data…].

  • F2 reads memory. Replies are [02 08 addrLo 60+addrHi len 00 FA FA data…].
  • F3 writes memory. Button and DPI writes take effect immediately, with no commit command.
  • Polling is written inside the sequence used by the vendor software: F5 00 → F3 with the 2-byte polling word → F1 02 08 → F5 01, with 100 ms pauses. F5 is a 0/1 bracket, not a rate divisor.

Memory layout (5 profiles):

  • active profile at 0x002C
  • polling word at 0x32 + 2·profile, value 1000 / Hz
  • DPI profile blocks at 0040/0100/01B0/0260/0310: header [count, 0, current stage, 0], then 6-byte stages [01, code, range, 0, 0, 0]. Codes use the Redragon M724 table, range 0/1/4 = ×1/×2/×3.
  • button tables at 0080/0140/01F0/02A0/0350: count 0x14, then 4-byte slots. A key is 90 00 usage 00; a key with modifiers is 8F mod usage 00.

The driver reads before each settings change, preserves unknown bytes, and verifies the result by readback. Button and DPI updates use minimal changed spans; polling writes the complete two-byte word, preserving its unknown second byte. It refuses to remove the last Left click.

Review scope

This draft is one device-focused change: 1,319 additions and 4 deletions across 11 files, including codec and fake-device tests. The existing commits are kept together so the corrected polling transaction and verification safeguards are present in the reviewed result. LEDs, artwork, macros and profile switching remain separate follow-ups.

Evidence

  • Read-only memory dumps of the owner's unit, compared against before/after changes made in the vendor software.
  • Static analysis of the vendor's configuration program, used to confirm the polling sequence and the F5 meaning. No vendor code or binaries are included.
  • The MIT-licensed Es00bac/UtechSmart-Venus-Pro-Linux-MMO-Mouse-Utility for the general frame format. Its use of F5 as a polling divisor did not match the hardware and hung the mouse at 125 Hz, so it is not used.
  • Redragon M724 codec in this repository for the DPI code table.

Tested on hardware

All on the owner's wired unit, through the OpenMouse UI with this package installed locally:

  • status read: DPI stages, polling rate and side-button mappings match the vendor software
  • side-button remap Ctrl+1 → 1: the new key was typed immediately
  • DPI stage change and restore: speed change noticeable
  • polling 1000 → 500 → 1000 Hz: owner reported continued operation; a subsequent full 1280-byte memory dump was byte-identical to the original backup. The owner later also reported 1000 → 125 → 1000 Hz without a hang; that later trial was not independently dumped.
  • button and DPI changes persisted across unplugging and replugging the mouse

Unknowns

  • The effective USB report rate has not been measured. Only the stored polling value is verified.
  • Polling persistence across a replug has not been tested.
  • The meaning of F1 02 08 is taken from the vendor software's apply sequence, not fully understood.
  • The ten-stage DPI capacity and the exact ×2/×3 band boundaries are inferred.
  • LED records (0x0448 + 8·profile) were identified but are not implemented.

Checks

  • npm run check (mouse-protocol): pass, 3104/3104 tests, pack dry-run OK
  • OpenMouse npm run check with the local package: build passes; the JS bundle-size gate is exceeded by 2,564 bytes (3,952,564 / 3,950,000). OpenMouse main passes with 8,685 bytes of headroom, and its budget comment notes it "leaves room for one more release", which provides context for discussing a budget adjustment with maintainers; no adjustment has been approved. The local install also updated unrelated dependencies, so this is not an isolated driver-only size measurement. No OpenMouse change is part of this PR. OpenMouse tests with the local package: 658 tests, 652 pass, 0 fail, 6 cancelled. The same 6 cancellations (test/discord-card.test.ts, test/feedback-security.test.ts) occur with the published 0.59.0 package: their hanging-fetch mocks wait on AbortSignal.timeout, which does not keep Node's event loop alive. Unrelated to this driver.

No matching OpenMouse PR is required: the existing UI works with this driver. The generic profile selector is shown although this driver has no setProfile; that can be raised separately.

Pure codecs for the UtechSmart Venus MMO wired mouse: F2/F3 feature-report
framing, reply validation, DPI (x1/x2/x3 range bands), button actions
including modifier combos, status decoding and minimal write plans.
Decoded against a memory dump and vendor-UI readings from real hardware;
F1 commit and F5 polling commands remain unverified.
Registers the 04d9:fc55 config collection (0xFFA0:0x01) as a UtechSmart
client, disjoint from the Redragon Holtek mice by product id. Reads the
active profile's DPI stages, polling rate and 20 button slots, and writes
DPI, polling and button mappings (including modifier combos) through
minimal F3 frames verified by readback. F1 commit frames stay disabled
and the F5 polling frame stays behind a constant until hardware testing
decides both.
F5 is a 0/1 session bracket on the Venus (04d9:fc55), not a polling
divisor. Sending F5 with the divisor hung the mouse at 125 Hz.

Polling now writes the full two-byte word at 0x32+2p inside
F5(0) / F3 / F1 02 08 / F5(1), with the delays used by the vendor
software, then reads it back. The sequence was verified on hardware
(stored word 04->02->04, no other byte changed). Unchanged rates send
nothing; failures attempt one F5(1) close and keep the original error.
Button and DPI writes are unchanged. Effective report rate is not
measured.
Require both feature reports 2 and 3 on the same vendor collection,
since long reads use report 3. Report valuesVerified only when every
enabled DPI stage of the active profile decodes from a known code and
range, and expose an additive exact flag on decoded stages.

Update the driver notes with what was exercised on the owner's unit;
effective polling rate and polling persistence across a replug remain
unmeasured.
@coderabbitai

coderabbitai Bot commented Nov 14, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@github-actions github-actions Bot added type: enhancement PR title category: enhancement area: protocol Protocol codecs and shared types area: drivers Device drivers and transport integration area: tests Protocol and driver tests area: dependencies Dependency manifests and lockfiles labels Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: dependencies Dependency manifests and lockfiles area: drivers Device drivers and transport integration area: protocol Protocol codecs and shared types area: tests Protocol and driver tests type: enhancement PR title category: enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant