Fall back to FrameEnd 0x01; log _absent cache skips
FrameEnd 0x02 (persistent) returns LOGITECH_INTERNAL (0x05) on the G522 even after a successful SetHostModeState + SetRgbZonesSingleValue sequence. Persistent commit probably requires onboard profile state we haven't mapped. Use 0x01 (transient) so the LEDs at least refresh visually; sort out persistence once we have a wireshark capture of the LGHUB commit sequence. check_feature_settings now logs at INFO when it skips a setting cached in the persister's _absent list despite the feature being present on the device. Without this, a previous run's failed build() silently suppresses the setting forever — users see no panel and no diagnostic.
This commit is contained in:
parent
c45449b212
commit
1bcee309d3
|
|
@ -2052,19 +2052,14 @@ class HeadsetRGBColor(settings.Setting):
|
|||
resp.hex() if resp else resp,
|
||||
)
|
||||
# FrameEnd: commit the frame.
|
||||
# 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.
|
||||
#
|
||||
# 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]))
|
||||
# Byte 0 is frame_type: 0x01 = transient commit, 0x02 = persistent.
|
||||
# G522 firmware rejects 0x02 with LOGITECH_INTERNAL (0x05) — the
|
||||
# persistent commit path may require additional state (e.g. onboard
|
||||
# profile activation) we haven't mapped yet. Stick with 0x01 for
|
||||
# now so the LEDs at least refresh visually; we can revisit 0x02
|
||||
# once a wireshark capture of the LGHUB sequence is available.
|
||||
resp = device.feature_request(_F.HEADSET_RGB_HOSTMODE, 0x60, bytes([0x01, 0x00, 0x00, 0x00]))
|
||||
frame_type = 0x01
|
||||
logger.info(
|
||||
"HeadsetRGBColor: FrameEnd frame_type=0x%02X resp=%s",
|
||||
frame_type,
|
||||
|
|
@ -2809,34 +2804,46 @@ def check_feature_settings(device, already_known) -> bool:
|
|||
for sclass in SETTINGS:
|
||||
if sclass.feature:
|
||||
known_present = device.persister and sclass.name in device.persister
|
||||
if not any(s.name == sclass.name for s in already_known) and (known_present or sclass.name not in absent):
|
||||
try:
|
||||
setting = check_feature(device, sclass)
|
||||
except Exception as err:
|
||||
# on an internal HID++ error, assume offline and stop further checking
|
||||
if (
|
||||
isinstance(err, exceptions.FeatureCallError)
|
||||
and err.error == hidpp20_constants.ErrorCode.LOGITECH_ERROR
|
||||
):
|
||||
logger.warning(f"HID++ internal error checking feature {sclass.name}: make device not present")
|
||||
device.online = False
|
||||
device.present = False
|
||||
return False
|
||||
else:
|
||||
logger.warning(f"ignore feature {sclass.name} because of error {err}")
|
||||
already = any(s.name == sclass.name for s in already_known)
|
||||
if already:
|
||||
continue
|
||||
if not known_present and sclass.name in absent:
|
||||
# Silent-skip cache from an earlier run's failed build(). Log at
|
||||
# INFO so field diagnostics reveal when a setting is suppressed
|
||||
# this way — users can clear stale entries by deleting their
|
||||
# solaar config or we can add retry logic later.
|
||||
if sclass.feature in device.features:
|
||||
logger.info(
|
||||
"check_feature_settings: skipping %s — cached in _absent despite feature %s being present; "
|
||||
"delete the setting from ~/.config/solaar/config.yaml to retry",
|
||||
sclass.name,
|
||||
sclass.feature,
|
||||
)
|
||||
continue
|
||||
try:
|
||||
setting = check_feature(device, sclass)
|
||||
except Exception as err:
|
||||
# on an internal HID++ error, assume offline and stop further checking
|
||||
if isinstance(err, exceptions.FeatureCallError) and err.error == hidpp20_constants.ErrorCode.LOGITECH_ERROR:
|
||||
logger.warning(f"HID++ internal error checking feature {sclass.name}: make device not present")
|
||||
device.online = False
|
||||
device.present = False
|
||||
return False
|
||||
else:
|
||||
logger.warning(f"ignore feature {sclass.name} because of error {err}")
|
||||
|
||||
if isinstance(setting, list):
|
||||
for s in setting:
|
||||
already_known.append(s)
|
||||
if sclass.name in new_absent:
|
||||
new_absent.remove(sclass.name)
|
||||
elif setting:
|
||||
already_known.append(setting)
|
||||
if sclass.name in new_absent:
|
||||
new_absent.remove(sclass.name)
|
||||
elif setting is None:
|
||||
if sclass.name not in new_absent and sclass.name not in absent and sclass.name not in device.persister:
|
||||
new_absent.append(sclass.name)
|
||||
if isinstance(setting, list):
|
||||
for s in setting:
|
||||
already_known.append(s)
|
||||
if sclass.name in new_absent:
|
||||
new_absent.remove(sclass.name)
|
||||
elif setting:
|
||||
already_known.append(setting)
|
||||
if sclass.name in new_absent:
|
||||
new_absent.remove(sclass.name)
|
||||
elif setting is None:
|
||||
if sclass.name not in new_absent and sclass.name not in absent and sclass.name not in device.persister:
|
||||
new_absent.append(sclass.name)
|
||||
if device.persister and new_absent:
|
||||
absent.extend(new_absent)
|
||||
device.persister["_absent"] = absent
|
||||
|
|
|
|||
Loading…
Reference in New Issue