fix(manager): survive Bluetooth reconnects without stale lightbar or stuck menu#1
Conversation
…stuck menu A Bluetooth controller re-registers on a new evdev/hidraw node on every reconnect (e.g. when un/re-paired for console use). Two failures surfaced from that churn: - The lightbar was written via raw hidraw output reports, which race the hid-playstation driver and get dropped over Bluetooth, so the colour stayed on the firmware default. Drive it through the kernel LED class (:rgb:indicator) via a new root helper (controller-led); the driver then handles USB/BT framing. Removes the CRC / USB-BT report code and the fd. - Instances were keyed by the evdev path, so a reconnect looked like a new device: the tray menu was rebuilt, recycling item ids across a structural change and leaving mode entries stuck disabled. Key instances by stable identity (uniq/MAC) and rebind in place on reconnect; drop a vanished pad only after a short grace window. A reconnect is now a no-op for the menu. Adds tests/test_reconcile.py covering the reconcile decisions and wires it into validate-repo.sh (which also shellchecks controller-led now).
…onnects A Bluetooth DualSense reconnects on a *reused* /dev/input/eventX path (the number is recycled) while the device underneath is new: its :rgb:indicator LED node is renumbered (the name follows the inputN counter, which always bumps), its old remapper grab has died, and the firmware has reset the lightbar to default blue. The reconcile only rebound on an evdev *path* change, so this reconnect was missed entirely — the pad came back un-remapped and on the wrong colour, and the daemon kept writing the mode colour to the now-dead old LED node (so a later mode switch changed no colour). - _apply_led() now re-resolves the :rgb:indicator node from the live evdev path on every write and never trusts a cached name, so a colour cannot land on a stale node. - _poll() detects a reconnect by a presence transition (a previously absent instance reappearing), not only by a path change, and re-asserts the whole mode via rebind() — restarting the remapper, re-gating the hidraw node and repainting the lightbar one poll after the pad is stably present (which also makes the colour stick past the device/Steam bring-up). - refresh_led(), run each monitor tick outside the reconcile lock, repaints when the live LED node differs from the last painted one, covering a renumber that occurs without a polled absence. Tests: test_reconcile.py gains Scenario E (node absent at bind -> painted once it appears), F (node renumbers on a reused path -> repaint the new node once) and G (reconnect on a reused evdev path -> rebind/re-assert, no menu churn). Document the lightbar path and reconnect handling in the architecture overview and a troubleshooting entry.
|
Follow-up commit Real root cause. A Bluetooth DualSense reconnects on a reused Changes.
Verification.
Known residual. A single LED write issued at the exact instant of reconnect can still be |
Problem
After un/re-pairing a DualSense for console use, it reconnects over Bluetooth on a
new evdev/hidraw node. Two failures followed from that:
reports, which race the
hid-playstationdriver and get dropped overBluetooth, so the pad stayed on its firmware default (blue) instead of the
mode colour (e.g. green for Emulate-Xbox).
reconnect looked like a new device. The resulting tray-menu rebuild recycled
item ids across a structural change and left the mode entries stuck disabled.
Fix
:rgb:indicator) through a new,tightly-scoped root helper
controller-led(mirrors thecontroller-hidraw-gatepattern: name/vendor validation, NOPASSWD sudoers). The driver handles USB/BT
framing, so the colour survives reconnects. Removes the CRC / USB-BT report
code and the persistent hidraw fd.
uniq/MAC, elsevendor:product) instead of the evdev path. A reconnect is recognised as thesame pad and rebound in place (only the remapper restarts); a vanished pad
is dropped only after a short grace window. A reconnect is now a no-op for the
tray menu.
Verification
tests/test_reconcile.py(new) asserts the reconcile decisions:reconnect → rebind / no menu change, blip < grace → kept, absence > grace →
removed, distinct pad → structural change. Wired into
validate-repo.sh(Check 7);
controller-ledadded to the shellcheck set.validate-repo.shpasses; verified live: manager sets the lightbar green viacontroller-ledon apply, no remapper flap.Deploy note
Adds a root helper + sudoers line — re-run
./install.shon the target.