Keep battery and connection type live: refresh by visibility, follow the cable - #10
Merged
snekxs merged 1 commit intoSep 19, 2026
Conversation
…the cable Two user reports against the same root cause: the connected device's status only ever moved while the window was focused and the Performance tab was closed, and nothing re-scanned interfaces once something was connected. In practice battery stayed frozen while glancing at the app from another window or while sitting on the Performance tab, and plugging the cable in or out (a different product id over USB than over the receiver) was not noticed until the app was relaunched. Refresh cadence now follows what the user can see instead of stopping: 5 s focused (unchanged), 15 s visible but unfocused, 30 s with the Performance tab open, 60 s hidden/minimized. The Performance tab's staged values only reset when the device value actually changes, so an unchanged read has no visible effect there. A 3 s interface enumeration (nothing opened) drives transport hot-swap: - Connected interface vanished: connect to the same mouse on its sibling transport if present; otherwise drop to the list and keep watching, reconnecting on its own when the mouse or its sibling is back. - Sibling appeared while on the receiver: read it, confirm it is the same unit (unitId, after a product-string pre-filter), and move to the wired side. Dongle inserted while wired does not switch. - Launch-time auto-reconnect also accepts the remembered mouse's sibling. Verified on a DeathAdder V3 Pro (0x00b6 wired / 0x00b7 receiver): dongle pulled -> "disconnected" within a scan; cable in -> auto-reconnected wired; dongle back in -> stayed wired; cable out -> wireless in ~3 s; cable in -> wired in ~3 s. All without touching the app.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two things I hit as a user of 0.0.28/0.0.29 with a DeathAdder V3 Pro, both fixed in
use-mouse-connection.tsonly:Why
The background refresh skipped outright whenever the window was not the focused one, or the Performance tab was open. Those are the two most common ways to actually look at the app (glancing at it from a game or browser; sitting on DPI/polling), so in practice the status was frozen. And nothing re-enumerated interfaces after a connect, while the cable enumerates as a different product id (
0x00b6) from the receiver (0x00b7), so "now wired" is a new interface appearing, not a status change on the one that's open.Refresh cadence follows visibility instead of stopping
The Performance tab's staged values only reset when
status.dpietc. actually change (DevicePerformanceTab.tsx:71), so an unchanged background read has no visible effect mid-edit; the hid-open-lock still makes a tick that lands during a write skip. I kept it gentle (30 s) rather than the full 5 s out of respect for the "CONFIRMED jarring" note, but it no longer stops.Transport hot-swap
A 3 s
listCandidateInterfaces()(plain hidapi enumeration, nothing opened; measured at 19 calls/min in the dev log) while connected or while waiting for a vanished mouse:unitIdmatch; product-string match with transport words stripped is the pre-filter), and move to the wired side. Wired is where the mouse actually is at that point and the only side that reports charging. Dongle inserted while wired deliberately does not switch.isSiblingTransportis exported for testing; the only network of assumptions is "same vendor, same product string modulo wired/wireless/dongle/receiver words", and the status read'sunitIdguards the switch on top of that.Verified live (dev build, DeathAdder V3 Pro, firmware 2.1)
From the timestamped dev log: reads on
00b6while wired; cable out →00b7opened at 16:51:35; cable back in →00b6at 16:51:45. Sequence I ran: dongle pulled → "disconnected" toast within a scan; cable in → auto-reconnected as Wired; dongle back in → stayed Wired; cable out → Wireless in ~3 s; cable in → Wired in ~3 s. No clicks.tsc --noEmitclean.Not tested on macOS/Linux; nothing here is platform-specific beyond hidapi enumeration that already existed.