From e09f0de10715a57d67fee7bec9290fb33b0fd648 Mon Sep 17 00:00:00 2001 From: Ken Sanislo Date: Sat, 18 Apr 2026 13:21:12 -0700 Subject: [PATCH] HeadsetRGBColor: persist colors via FrameEnd 0x02, 0x01 for off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Further RE finding refines FrameEnd byte 0 semantics: 0x02 = persistent commit — saves to onboard NVS as baseline, survives the firmware's host-mode self-release window (color sticks) 0x01 = transient commit — for live preview/animation frames, doesn't wear out NVS; requires keepalive or continuous frames 0x00 = silently discarded by firmware (the old bug) For solaar's "pick a color and walk away" model, users expect the color to persist. Use 0x02 when writing a real color. When writing black (off), use 0x01 — matches LGHUB's turn_off_lighting so we don't save an all-black baseline to the device's NVS. This should make colors actually stay visible after the firmware auto-releases host mode, which was the root cause of the "color doesn't appear" symptom on the G522. --- lib/logitech_receiver/settings_templates.py | 22 ++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index 88c45ad4..709478c4 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -2028,12 +2028,24 @@ class HeadsetRGBColor(settings.Setting): resp.hex() if resp else resp, ) # FrameEnd: commit the frame. - # Byte 0 is frame_type: 0x01 = transient commit, 0x02 = persistent/final - # flush. The firmware silently discards frames when byte 0 is 0x00 — - # canonical protocol doc was wrong on this point. See + # Byte 0 is frame_type: 0x01 = transient commit, 0x02 = persistent + # (saves to onboard NVS as baseline; survives the firmware's + # host-mode self-release window). 0x00 is silently discarded by + # firmware — canonical protocol doc was wrong. See # HEADSET_RGB_HOSTMODE_WIRE_PROTOCOL.md. - resp = device.feature_request(_F.HEADSET_RGB_HOSTMODE, 0x60, b"\x01\x00\x00\x00") - logger.info("HeadsetRGBColor: FrameEnd resp=%s", resp.hex() if resp else resp) + # + # Use 0x02 for user-visible color picks so the color sticks even + # after the firmware releases host mode. Use 0x01 when setting + # black (lights off) — mirroring LGHUB's turn_off_lighting so we + # don't overwrite the NVS baseline with all-zeros. + is_off = r == 0 and g == 0 and b == 0 + frame_type = 0x01 if is_off else 0x02 + resp = device.feature_request(_F.HEADSET_RGB_HOSTMODE, 0x60, bytes([frame_type, 0x00, 0x00, 0x00])) + logger.info( + "HeadsetRGBColor: FrameEnd frame_type=0x%02X resp=%s", + frame_type, + resp.hex() if resp else resp, + ) except Exception as e: logger.warning("HeadsetRGBColor write failed for %s: %s", name, e) return None