Repository navigation
Conversation
… receiver Its dongle enumerates as SinoWealth 258a:002f, the M690 PRO receiver's product id, and answers the same bank framing with its own footprints (identify "2315", settings-bank model byte 0x11). RedragonM690ProHidClient now resolves a profile from the settings block: the M690 PRO keeps its full behavior, the Kilonova 3335IC is recognized and read for identity, link and battery, and every write path is refused. Nothing past the model byte of the Kilonova's bank is decoded, and no write has been tried on hardware. Evidence: captures/cosmic-byte-kilonova-3335ic/ (node-hid, read-only) and docs/kilonova-3335ic.md. The framing numbers shared with the M690 PRO are pinned together by src/sinowealth/index.test.ts.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
The Kilonova 3335IC's settings bank is still not decoded enough to write, but its DPI table is now understood and can be reported as read-only data: - The stage table lives at 0x0d in the settings bank: six 16-bit little-endian values where the DPI is (index + 1) * 100. The active stage is the high nibble of byte 0x0b (1-based) and the stage count is the low nibble, the same semantics as the M690 PRO. - Evidence: the mouse's own DPI button reports each stage change on input report 7 ([11 04 stage byte ..]) through OpenMouse Bridge; pressing through all six stages reproduced the bank's exact values, and a power-cycle confirmed the mouse boots to the saved active stage. The driver now reports dpi, dpiStages and activeDpiStage with valuesVerified: true while settingsReady stays false, so the app shows the DPI read-out but still refuses every write. Adds a sinowealthDecodeKilonovaConfig unit test and updates the recognition test.
…or Kilonova 3335IC
| /** Snaps to the nearest vendor DPI value, writes it, and returns that value. */ | ||
| async setDpiStageValue(stage: number, dpi: number): Promise<number> { | ||
| if (this.kilonova) { | ||
| const label = redragonM690ProNearestDpi(dpi); |
There was a problem hiding this comment.
🟠 High redragon/m690-pro-hid.ts:368
Kilonova DPI writes throw for supported values such as 400, 1600, and 6400: redragonM690ProNearestDpi snaps them to 500, 1500, and 6500, which sinowealthWithKilonovaStageDpi rejects. Snap to the Kilonova's supported DPI values instead.
| const label = redragonM690ProNearestDpi(dpi); | |
| const label = [400, 800, 1600, 3200, 6400, 16000].reduce((nearest, value) => Math.abs(value - dpi) < Math.abs(nearest - dpi) ? value : nearest); |
🤖 Copy this AI Prompt to have your agent fix this:
In file @src/drivers/redragon/m690-pro-hid.ts around line 368:
Kilonova DPI writes throw for supported values such as `400`, `1600`, and `6400`: `redragonM690ProNearestDpi` snaps them to `500`, `1500`, and `6500`, which `sinowealthWithKilonovaStageDpi` rejects. Snap to the Kilonova's supported DPI values instead.
| if (this.kilonova) { | ||
| const label = redragonM690ProNearestDpi(dpi); | ||
| await this.writeBlocks((block) => sinowealthWithKilonovaStageDpi(block, stage, label), (block) => block); | ||
| return label; | ||
| } | ||
| const label = redragonM690ProNearestDpi(dpi); | ||
| await this.writeConfig((block) => redragonM690ProWithStageDpi(block, stage, label)); | ||
| return label; |
There was a problem hiding this comment.
🟠 High redragon/m690-pro-hid.ts:367
On a fresh client, setDpiStageValue(0, 800) writes the Kilonova’s stage as 400 DPI but reports success, silently changing the onboard value. The branch selects redragonM690ProWithStageDpi before writeBlocks queues and completes device identification, so the Kilonova byte whitelist accepts the wrong encoding; choose the encoder inside the callback, after identification.
- if (this.kilonova) {
- const label = redragonM690ProNearestDpi(dpi);
- await this.writeBlocks((block) => sinowealthWithKilonovaStageDpi(block, stage, label), (block) => block);
- return label;
- }
const label = redragonM690ProNearestDpi(dpi);
- await this.writeConfig((block) => redragonM690ProWithStageDpi(block, stage, label));
+ await this.writeBlocks(
+ (block) => this.kilonova
+ ? sinowealthWithKilonovaStageDpi(block, stage, label)
+ : redragonM690ProWithStageDpi(block, stage, label),
+ (block) => block,
+ );
return label;🤖 Copy this AI Prompt to have your agent fix this:
In file @src/drivers/redragon/m690-pro-hid.ts around lines 367-374:
On a fresh client, `setDpiStageValue(0, 800)` writes the Kilonova’s stage as 400 DPI but reports success, silently changing the onboard value. The branch selects `redragonM690ProWithStageDpi` before `writeBlocks` queues and completes device identification, so the Kilonova byte whitelist accepts the wrong encoding; choose the encoder inside the callback, after identification.
…ites to known bytes Replace the hardcoded Kilonova DPI index map with the (dpi/100)-1 formula so any 100-step value 400-16000 can be written. Detect the Kilonova in writeBlocks and refuse any change that touches bytes beyond the known DPI/polling fields (0x0a low nibble, 0x0b, DPI stages 0x0d-0x18), so button/lighting/layout bytes are never guessed at. Expose Kilonova polling rates and the full DPI option range, and add the dpiStageEditor UI hint. setButtonMapping throws 'read-only' for Kilonova.
Device
Cosmic Byte Kilonova 3335IC, through its 2.4 GHz dongle, which enumerates as SINOWEALTH
258a:002f, the M690 PRO receiver's product id. Windows 11. Everything here was verified read-only against that one receiver; the wired path of this mouse has not been seen yet.Today the registry claims the id by USB shape, and then the settings-block check refuses the device with "not the Redragon M690 PRO's": the Kilonova answers the same SinoWealth bank framing (libratbag's
driver-sinowealth.c) with its own footprints. Identify replies ASCII"2315"("2945"on the M690 PRO) and byte 9 of the settings bank is0x11(0x13). Rather than losing the receiver, this PR gives the M690 PRO client a second, read-only profile.What the PR does
src/sinowealth/index.ts, a framing-family module (thesrc/compx/pattern): the shared report/length/command constants, the two known profiles (M690 PRO"2945"/0x13, Kilonova 3335IC"2315"/0x11), and encode/check/decode helpers for identify, link and status. Its duplicated numbers are pinned to@openmouse/protocol/redragonbysrc/sinowealth/index.test.ts. Exported as a./sinowealthpackage subpath and from the package root.RedragonM690ProHidClientresolves its settings block against those profiles: the M690 PRO behaves exactly as before; a Kilonova is recognized and read; a model byte on this shared receiver id that matches no profile is still refused with the sameNotM690ProErrormessage as before.nullotherwise), "2.4 GHz receiver" / "mouse off or asleep" connection detail, settings grid hidden, and every write path refused with a read-only note before any write is attempted.MouseStatus["brand"]gains"Cosmic Byte". The registry entry keeps its"Redragon"label, with a comment pointing at the GearHub precedent for a device wearing another brand's label until its own status arrives.captures/cosmic-byte-kilonova-3335ic/(the read-only node-hid session) anddocs/kilonova-3335ic.md(captures, observed facts vs hypotheses, what would unlock the rest).What was observed on real hardware (read-only, mouse awake and linked, 70 % battery)
[05 01 32 33 31 35 00 00]->"2315"[05 80 01 01 00 00 00 00]-> linked[05 90 11 46 00 00 00 00]-> wireless, battery 0x46 = 70 %05 21-> 154 bytes, model byte0x11at 9,a5 00marker at 152-15305 22-> 88 bytes, payload all zeroschrome-report5-stall.hex), not captured here.Not decoded, and left alone
The Kilonova's bank interior. Its stage-table bytes (observed
00 07 00 0f 00 1f 00 3f 00 9fafter the stage count at byte 11) do not name the M690 PRO's DPI positions 1-24, so OpenMouse hides the settings grid rather than mapping the M690 PRO's layout onto another device's memory. Byte 10 is0x04(1000 Hz on the M690 PRO map), but the rate was not measured, so nothing decodes it. The zero-payload button bank keeps its slot grid as a hypothesis only.Consequences for the application
The brand map already keys by string, so the device type is accepted as-is; but two
MouseUiHintsthis driver relies on (statusNote,forceShowBattery) were not rendered by the app, so with the current shell the Kilonova's page shows the device and nothing else. The matching shell change is OpenMouse-Project/openmouse#1201 (draft): it renders the status note on the connected status line and keeps a–battery column until OpenMouse Bridge can read the percent. I verified both sides locally:npm run checkin this repository (build, 2677 tests,npm pack --dry-run), thennpm run build,npm testandnpm run sizein OpenMouse with the patched shell, all pass. A matching openmouse-landing-page supported-devices entry for the Kilonova will follow in that repository;src/features/hardware-test/supported-devices-data.tsstays untouched until it lands.CI note: first-time contributor, so the workflows need maintainer approval to run.