From a56d02a6fb5ff870c68d6c150cb143399d41b681 Mon Sep 17 00:00:00 2001 From: Ken Sanislo Date: Sat, 18 Apr 2026 12:32:12 -0700 Subject: [PATCH] HeadsetEcoMode: skip same-value writes to avoid G522 NACK 0x0B MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RE of lghub_agent.arm64 revealed that LGHUB's service-layer handler for 0x0618 HeadsetBatterySaverMode (on_headset_battery_saver_set_handler @ 0x100c21790) compares the requested new state against its cached current state and ONLY invokes the devio SetEcoModeState write on a genuine transition. Wire format is confirmed 0/1 (canonical doc correct); the G522 firmware rejects no-op writes with device-specific NACK 0x0B. BooleanValidator's prepare_write already contains the "skip if same as current" branch — it just fires only when current_value is supplied, which requires needs_current_value=True. For default-mask (0xFF) validators, needs_current_value defaults to False so Setting.write skips the pre-read and prepare_write gets current_value=None. HeadsetEcoMode now builds its validator explicitly and forces needs_current_value=True so Setting.write reads first, compares in prepare_write, and skips redundant writes — matching LGHUB exactly. Same fix may apply to other Centurion boolean features whose firmware rejects no-op writes, but leaving those unchanged until observed. --- lib/logitech_receiver/settings_templates.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index eb8d940d..954a1a22 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -1591,6 +1591,24 @@ class HeadsetEcoMode(settings.Setting): feature = _F.HEADSET_BATTERY_SAVER validator_class = settings_validator.BooleanValidator + @classmethod + def build(cls, device): + # LGHUB's service layer for 0x0618 HeadsetBatterySaverMode compares + # incoming new_state against its cached current_state and SKIPS the + # devio SetEcoModeState write when they match (verified in + # on_headset_battery_saver_set_handler @ 0x100c21790). The G522 + # firmware rejects no-op writes with device-specific NACK 0x0B. + # + # BooleanValidator's prepare_write already has the "skip if same as + # current" logic — it just needs needs_current_value=True to make + # Setting.write read the device state first. Default-mask (0xFF) + # BooleanValidators get needs_current_value=False, so we flip it here + # to match LGHUB's guard. + rw = settings.FeatureRW(cls.feature) + validator = settings_validator.BooleanValidator() + validator.needs_current_value = True + return cls(device, rw, validator) + class HeadsetDoNotDisturb(settings.Setting): name = "headset-do-not-disturb"