The HEADSET_SIGNATURE_EFFECTS_ALLOWED allowlist was keyed on "33", a
model byte no real G522 reports. DeviceInfo (0x0100) func 0 returns
0x32 for the G522 (0x44 for the G325) — confirmed against every saved
diagnostic log, including our own development unit. The 0x33/0x45
values came from the protocol doc, which had both transcribed
off-by-one (a shifted read of a G HUB USB capture).
Effect: the SOLAAR_EXPERIMENTAL masking suppressed the headset
signature-effect settings on every G522, not just unvalidated models.
Re-key to "32" and correct the modelId comments.
NVconfig-saved colors (0x8071 RGBEffects boot effects, 0x0622 HeadsetRGB
signature effects) persist to device storage, so an unvalidated control
can durably misconfigure a device. Gate them behind a per-model
allowlist: every field is hidden and every slot suppressed unless the
exact model is known-good. SOLAAR_EXPERIMENTAL=true bypasses the masking
for testers. Non-persistent effect parameters (zone effects, LED
directions) keep their default-allow blocklist — unchanged.
device_quirks.py is rewritten around two per-feature allowlists with
their own accessors, replacing the flat blocklist QUIRKS dict.
Centurion device identification: _get_ids_centurion now derives modelId
from the firmware-stable model_id byte (G522 0x33, G325 0x45) instead of
productId, which is shared across the headset family and varies by
firmware (G522 0x0508 -> 0x0509) — it could never key a quirk reliably.
Approved models: G502 X PLUS and G515 TKL for the 0x8071 boot effects,
G522 for the 0x0622 signature effects (startup primary only, shutdown
both colors, no speed, passive slot suppressed pending RE).
Software-managed LED persistence and power management for devices that
expose RGBEffects (0x8071) — primarily G515 LIGHTSPEED TKL, but the same
infrastructure works on any 0x8071 device that supports SW takeover.
Core mechanism: RGBControl toggle drives a Set SWControl(mode=3, flags)
handshake. While SW control is held, the host owns the LED pipeline: zone
effects, per-key paint, idle/sleep transitions, and the NvConfig boot/exit
animations. On release, the firmware resumes its onboard profile.
Major pieces:
- rgb_power.py — new module hosting the software RGB power manager:
ACTIVE / DIMMING / IDLE / SLEEPING state machine driven by firmware
onUserActivity events, smooth 5-second dim ramp (zone or per-key), idle
Static color snap, software sleep timer, wake handler that re-pushes
saved state. Includes the cleanup hook that runs on device close (and,
optionally, fires the cap 0x0040 shutdown trigger).
- RGBControl (settings_templates) — switch-style render via BooleanValidator
(true_value=3 / false_value=0) plus a full _claim_sw_control /
_release_sw_control pair: profile-management mode, SetSWControl, per-key
flag reset, manager start, cleanup registration, and a post-claim
repaint pass so the device immediately reflects Solaar's saved zone +
per-key state.
- RGBEffectSetting — zone-effect Setting subclass for 0x8071. Handles
per-key/zone coexistence: per-key paint dominates only when zone is
Static and the user has explicitly opted in via the lock icon; under
animations or before opt-in, the zone wire push is the visible layer.
- RGBIdleEffect / RGBIdleTimeout / RGBSleepTimeout — Solaar-managed idle
behavior. Choice list: "No change" → Dim → Static (snap to color) →
device-specific animations. Static idle substitutes the idle color for
unset per-key cells via effective_zone_base_color's state-aware lookup.
- RgbStartupAnimation / RgbShutdownAnimation — toggle-and-color rows for
RGBEffects NvConfig caps 0x0001 and 0x0040, exposed only on devices
that answer the probe. Shutdown trigger fires SetRgbPowerMode(0) at
cleanup time so the firmware plays the configured animation on exit.
- PerKeyLighting — per-key painter improvements: explicit-opt-in
dominance over zone effects, BUSY retry, FrameEnd suppression on
per-cell failure, single-shot prep sequence (SetEffectByIndex into the
out-of-range slot) on mice with firmware effect cards.
- device_quirks.py — small per-model quirks table keyed by device.modelId
(stable across USB/BLE/wireless). Currently used to mark RGBEffects
NvConfig color slots as inert on devices where the firmware accepts
but ignores the bytes (G502 X PLUS startup colors).
- config_panel.py — HeteroKeyControl gains a TOGGLE field kind that
renders as a Gtk.Switch (used by the boot-effect rows). Visual gate
greys out RGB settings whose prerequisites aren't met: rgb_zone_* /
rgb_idle_* / rgb_sleep_timeout require LED Control = Solaar; per-key
additionally requires every zone effect to be Static. Visual-only —
doesn't touch the persister's user lock-icon state.
- tests/test_rgb_power.py — coverage for the power manager state
machine, dim ramp, idle effects, wake path, and per-key/zone
coexistence.
Closespwr-Solaar/Solaar#3149.