Skip to content

Add support for the SteelSeries Rival Pro (1038:1880) - #1

Open
alexandru-g wants to merge 2 commits into
Advve:mainfrom
alexandru-g:feat/rival-pro
Open

alexandru-g wants to merge 2 commits into
Advve:mainfrom
alexandru-g:feat/rival-pro

Conversation

@alexandru-g

Copy link
Copy Markdown

Add support for the SteelSeries Rival Pro (1038:1880)

This adds the SteelSeries Rival Pro, a wireless-only mouse that connects through a UWB receiver (1038:1880). Device support comes from my rivalcfg PR, flozz/rivalcfg#294, which isn't merged yet. Until it is, profiles.json and golden.json here are generated from rivalcfg 4.17.0 plus the three commits of that PR. Only the Rival Pro is added; the other profiles and golden cases are unchanged. Once the PR lands in a rivalcfg release, the two tools can be re-run from the release as usual.

New settings

All of them use value types the app already supports (choice, range, X/Y DPI, buttons):

Setting Group Values
Sensitivity presets Performance Up to 5, 1-65000 DPI, separate X/Y
Polling rate Performance 125-8000 Hz
Click response Performance 0-20 ms
Scroll jump protection Performance off, 100-1500 ms
Motion sync, LAN mode, Max performance Performance on / off
Buttons mapping Buttons Same layout as the Rival 3 Gen 2
Sleep timer Power 1-20 min
LED indicator Power polling-rate, battery, off
Low power mode polling rate Power 125-1000 Hz, led-only

Battery level and firmware version are also supported.

Changes

Export tool and Rust port

  • multidpi_range_choice_xy: optional preset_suffix (fixed bytes appended after each preset, "xyxy" mapping only). The Rival Pro stores the lift-off and landing distances there.
  • firmware_version: rivalcfg profiles can now have a parse function. The export identifies it by probing, the same way the battery lambdas are handled, and writes format: "ascii". Without a format, the raw-bytes behavior is unchanged.
  • battery_level: new rival_pro format (level in byte 2, no charging state, because the battery is swappable).
  • output_choices tables that map every value of input_range to itself are exported as output_choices_identity: true. They are rebuilt in Setting::expand_output_choices() when the catalog loads. Without this, the Rival Pro's 65000-entry DPI table grew profiles.json from 570 KB to 3.4 MB.
  • view::group_of: the new settings go to Performance and Power. Before, they would have fallen through to Lighting.

UI

  • The device header shows "Wireless" for wireless devices whose name has no connection mode (it showed "Wired").
  • The unit is now detected in descriptions like "(debounce, ms)".
  • Polish strings for the new labels, descriptions and choices. A native speaker should check them.

CLAUDE.md is updated for the new export fields.

Testing

  • cargo test: all 5037 golden cases match rivalcfg byte for byte (94 of them new, all for the Rival Pro), plus new unit tests for preset_suffix and the firmware and battery formats.
  • npm run check: no errors. cargo clippy --all-targets: only the existing type_complexity warning in buttons.rs.
  • Built the AppImage locally on Arch Linux (NO_STRIP=true npx tauri build --bundles appimage).
  • With the real receiver plugged in, the backend detects the Rival Pro and reads firmware 2.14.2 and the battery level. Setting writes were tested on the hardware through rivalcfg (see Add support for the SteelSeries Rival Pro (1038:1880) flozz/rivalcfg#294); the packets here are byte-identical to those.

Known limitations

  • The receiver answers battery queries itself, even when the mouse is off or asleep. So the device always counts as online while the receiver is plugged in, and changes made while the mouse is off may not reach it.
  • Like other devices, settings aren't read back from the mouse. The UI shows profile values or factory defaults, so "Re-apply" or switching profiles overwrites what is stored on the mouse.
  • The README still says 76 models and 4943 golden packets. I left it alone in case you'd rather update it when releasing.
  • The SteelSeries Sensei Pro is probably the same mouse in a different shell, so this profile likely works for it too. I don't have one to test, and its receiver may use a different USB product ID, so it isn't included.

Profiles and golden cases generated from rivalcfg 4.17.0 plus the Rival
Pro commits of flozz/rivalcfg#294. Only the Rival Pro is added; the other
profiles are unchanged.

- multidpi_range_choice_xy: optional preset_suffix (bytes appended after
  each preset, "xyxy" only)
- firmware_version: "ascii" format for profiles with a parse function
- battery_level: "rival_pro" format (level in byte 2, no charging state)
- Identity output_choices tables are exported as a flag and rebuilt at
  load time, so the 65000-entry DPI table does not bloat profiles.json
- New settings are grouped under performance and power instead of lighting
- Show "Wireless" in the device header for wireless devices whose name has
  no connection mode
- Detect the unit in descriptions such as "(debounce, ms)"
- Polish translations for the new labels, descriptions and choices
@Advve

Advve commented Nov 1, 2026

Copy link
Copy Markdown
Owner

Hi @alexandru-g, thank you for this PR: tested on the hardware, byte-exact golden
tests, and an honest list of limitations. 🙏

I've gone through the changes and the code looks good to me:

  • The snake_case switch for BatteryFormat keeps the existing aerox/rival values working.
  • output_choices_identity is a nice solution. profiles.json stays small, and since the UI only gets
    the range, the expanded table never leaves the backend.
  • Rejecting preset_suffix for anything but xyxy is the right call, and the new firmware/battery formats
    default to the old behaviour for every existing device. I also checked that the regenerated
    profiles.json and golden.json only add the Rival Pro.

One thing I'd like to keep, though: RivalGUI's device support always matches a released version of
rivalcfg. So I'd prefer to wait until flozz/rivalcfg#294 is merged and released before merging this,
in case anything changes during the review there.

When it's out, could you:

  1. Rebase on main.
  2. Regenerate profiles.json and golden.json from that rivalcfg release with the two tools (as you
    suggested).
  3. Ping me here.

I'll merge it then. I'll update the README counts (77 models, the golden packet count) myself when I make
the release.

A couple of small things for whenever you touch it again (none of them blocking):

  • Polish wording, as a native speaker. "Lift-off distance" is usually "Wysokość odcięcia (LOD)" in
    Polish. The "click response" description could match its label: "Czas reakcji przycisków (eliminacja
    drgań styków)". I'm happy to adjust these myself after merging, if you prefer.
  • If a firmware string ever contains letters (e.g. 2.14.2b), the ASCII parser would show no version.

I'll approve the CI run in the meantime, so you can see the checks pass.
Thanks!

Sign up for free to 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