#3303·Solaar

per-key-lighting CLI write crashes: TypeError on None _value when live_readable is False

Author: PillowFort-GitCreated Aug 17, 2026Updated Aug 17, 2026

Solaar version: 1.1.20 (distribution package, Python 3.14) Device: Logitech G502 X PLUS, wireless via Lightspeed receiver (046d:c547, HID++ wireless PID 4099)

Problem

Any attempt to set a per-key colour from the CLI crashes before a single byte reaches the device:

solaar config "G502 X PLUS" per-key-lighting 3 0x00FF00
solaar: error: Traceback (most recent call last):
  File ".../solaar/cli/__init__.py", line 244, in run
    m.run(c, args, _find_receiver, _find_device)
  File ".../solaar/cli/config.py", line 223, in run
    result, message, value = set(dev, setting, args, False)
  File ".../solaar/cli/config.py", line 284, in set
    result = setting.write_key_value(int(k), value, save=save)
  File ".../logitech_receiver/settings_templates.py", line 4104, in write_key_value
    self.update_key_value(zone_id, value, save)
  File ".../logitech_receiver/settings_templates.py", line 3880, in update_key_value
    super().update_key_value(key, self._wrap_color(value), save)
  File ".../logitech_receiver/settings.py", line 300, in update_key_value
    self._value[int(key)] = value
TypeError: 'NoneType' object does not support item assignment

The setting is listed correctly by solaar config <device>, with the expected eight zones:

per-key-lighting = {1:No change, 2:No change, ... 8:No change}

rgb_control is true and rgb_zone_1 is Static, so the documented prerequisites are met. The failure is independent of them.

Cause

PerKeyLighting sets live_readable = False, which is correct in itself — 0x8081 has no GetIndividualRgbZones, so there is genuinely nothing to read back from the device.

The consequence is that the setting's _value is never populated on a fresh CLI process. Settings.update_key_value() at settings.py:300 then does:

python
self._value[int(key)] = value

against None.

The GUI path presumably initialises the map before any write, which would be why this only shows up from the CLI.

Suggested fix

Have write_key_value / update_key_value initialise _value to an empty dict (or seed it from the persister, then from validator.choices defaulting to "No change") when it is None, rather than assuming a prior read populated it.

Workaround

Driving the feature directly works fine, which confirms the device and protocol are healthy and the problem is purely in the settings layer:

python
dev.feature_request(0x8081, 0x10, bytes([zone, r, g, b]))  # setIndividualRgbZones
dev.feature_request(0x8081, 0x70, b"\x00")                 # FrameEnd

All eight zones paint correctly this way.