Solaar/lib/logitech_receiver
Ken Sanislo 9b627410b6 common: render RGB color values as 0xrrggbb in config and solaar show
24-bit RGB values stored in PerKeyLighting per-key maps and in
LEDEffectSetting `.color` fields currently dump as decimal integers in
both the YAML config file and `solaar show` output:

    per-key-lighting: {1: -1, 2: 16733440, 3: 16755200, ...}
    LEDs Keys: {1:-1, 2:16733440, 3:16755200, ...}

Hex is the canonical RGB representation. Render colors as `0xrrggbb`
everywhere: solaar show output, repr, and YAML config dumps. Both new
values and legacy values from pre-existing YAML configs migrate
transparently.

Implementation: a `ColorInt(int)` subclass in `common.py`.

  - `str(c)` / `repr(c)` → `'0xrrggbb'` for 0..0xFFFFFF; falls back to
    decimal for out-of-range values so sentinels like
    `COLORSPLUS["No change"] = -1` still display naturally.
  - Constructor accepts ints AND hex strings (`'0xrrggbb'` or
    `'#rrggbb'`) so pre-existing configs that wrote decimal continue
    to load.
  - YAML representer emits a hex int literal (`tag:yaml.org,2002:int`
    with style `'0xrrggbb'`). YAML 1.1 parses hex int literals back as
    plain ints with no custom loader registration — values round-trip
    cleanly without a custom YAML tag.

Wiring:

  - `Range` gains a `value_type=int` field; `MapRangeValidator.validate_read`
    wraps results through `rng.value_type(...)`. PerKeyLighting's
    `_COLOR_RANGE` sets `value_type=ColorInt`; other Range users keep
    the int default with no behavior change.
  - PerKeyLighting overrides `update()` and `update_key_value()` to wrap
    raw ints in `ColorInt` at write time. `type(v) is int` (exact
    match, not isinstance) deliberately excludes NamedInt sentinels and
    avoids re-wrapping existing ColorInts.
  - `LEDEffectSetting.__init__` wraps the `color` param in `ColorInt`
    with the same guard, so zone-effect color round-trips as hex
    through `yaml.dump(setting)` / `val_to_string`.
  - `MapRangeValidator.to_string` re-wraps raw ints loaded from YAML
    (which `yaml.safe_load` returns as plain Python ints regardless of
    the choice's `value_type`) through `rng.value_type` before
    formatting. Without this, `solaar show` would render legacy saved
    values as decimal even after the rest of the pipeline is hex-aware.

Coverage: 12 new unit tests across `test_common.py` and
`test_settings_validator.py`:

  - ColorInt str/repr, equality with plain int, hex-string constructor
    (0x / 0X / # prefixes), out-of-range fallback to decimal, YAML
    dump format, plain-int round-trip on load, dict-value formatting.
  - MapRangeValidator.to_string: plain-int re-wrap via value_type,
    pass-through for already-wrapped ColorInts, NamedInt sentinel
    preservation, and no behavior change for int-typed Ranges.

Existing test fixtures updated: _PERKEY_COLOR_RANGE now carries
value_type=ColorInt to match runtime _COLOR_RANGE.

702 tests pass; pre-commit clean.
2026-05-12 12:51:36 -04:00
..
__init__.py Refactor: Distinguish module from package 2024-09-15 09:18:51 -04:00
base.py centurion: support PRO X 2 LIGHTSPEED headphones Centurion features (#3150) 2026-04-14 11:43:23 -04:00
base_usb.py device: Support per-slot unpair on Lightspeed receivers (CLI + GUI) (#3183) 2026-04-17 09:34:58 -04:00
centurion.py centurion: support PRO X 2 LIGHTSPEED headphones Centurion features (#3150) 2026-04-14 11:43:23 -04:00
centurion_constants.py centurion: support PRO X 2 LIGHTSPEED headphones Centurion features (#3150) 2026-04-14 11:43:23 -04:00
common.py common: render RGB color values as 0xrrggbb in config and solaar show 2026-05-12 12:51:36 -04:00
descriptors.py device: fix interface for K845 2026-04-14 11:45:17 -04:00
desktop_notifications.py testing: upgrade desktop notifications tests to take notifications availability into account 2025-01-02 10:47:53 -05:00
device.py Add regional keyboard layouts (ISO_QWERTY, QWERTZ, AZERTY, JIS) and fix copyright 2026-05-10 17:52:55 -04:00
diversion.py rules: remove use of XTest and use uinput in all cases 2026-03-08 20:58:43 -04:00
exceptions.py Use double quotes for module level docstrings 2024-06-02 09:54:21 -04:00
hidpp10.py cli: Fix crash when showing notification flags. (#3070) 2025-12-12 04:54:10 -05:00
hidpp10_constants.py device: Handle composite IntFlag.name on Python < 3.11 (#3187) 2026-04-16 11:38:25 -04:00
hidpp20.py common: render RGB color values as 0xrrggbb in config and solaar show 2026-05-12 12:51:36 -04:00
hidpp20_constants.py centurion: support PRO X 2 LIGHTSPEED headphones Centurion features (#3150) 2026-04-14 11:43:23 -04:00
i18n.py Unify imports in logitech package 2024-05-23 16:42:18 -04:00
listener.py centurion: support PRO X 2 LIGHTSPEED headphones Centurion features (#3150) 2026-04-14 11:43:23 -04:00
notifications.py centurion: support PRO X 2 LIGHTSPEED headphones Centurion features (#3150) 2026-04-14 11:43:23 -04:00
onboard_eq.py centurion: support PRO X 2 LIGHTSPEED headphones Centurion features (#3150) 2026-04-14 11:43:23 -04:00
receiver.py device: Support per-slot unpair on Lightspeed receivers (CLI + GUI) (#3183) 2026-04-17 09:34:58 -04:00
settings.py Add per-key RGB color painter and replace MAP_CHOICE color validator 2026-05-10 17:52:55 -04:00
settings_new.py settings: add setting for HAPTIC feature 2025-11-12 14:50:46 -05:00
settings_templates.py common: render RGB color values as 0xrrggbb in config and solaar show 2026-05-12 12:51:36 -04:00
settings_validator.py common: render RGB color values as 0xrrggbb in config and solaar show 2026-05-12 12:51:36 -04:00
special_keys.py device: add special keys from Logitech 2025-10-16 20:57:15 -04:00