HeadsetEcoMode: skip same-value writes to avoid G522 NACK 0x0B

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.
This commit is contained in:
Ken Sanislo 2026-04-18 12:32:12 -07:00
parent 10b1d8baff
commit a56d02a6fb
1 changed files with 18 additions and 0 deletions

View File

@ -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"