rgb-control — ASUS ROG Flow Z13 (GZ302EA) keyboard + lightbar RGB
=================================================================

Controls the detachable keyboard backlight and the lid edge lightbar via the
Aura HID protocol (preferred, /dev/hidraw*, no root) with a kernel sysfs
fallback (/sys/class/leds, needs the udev rule).

Components
----------
  rgb_hid.py   Direct Aura HID protocol (write-only 0x5D reports).
  rgb_core.py  Zone discovery, color/brightness, and state persistence.
  rgb-cli      CLI: list / set / brightness / on / off / restore.
  rgb-gui      GTK color-picker GUI.
  rgb-idled    Power-aware idle daemon (GNOME/Wayland user service): dims on
               lock, turns off when the screen powers off, restores on wake.
  install.sh   Installs udev rule, tmpfiles rule, restore service + resume
               hook, CLI symlink, and desktop entry. Safe to re-run.

Usage
-----
  rgb list                     show zones and last-applied state
  rgb set keyboard '#00ddcc'   set a color (zone id, name, or 'all')
  rgb brightness keyboard 2    0..max
  rgb on | rgb off
  rgb restore                  reapply the last saved color/brightness

Staying-on across suspend and re-dock
-------------------------------------
The Aura hardware loses its color on suspend/resume and when the detachable
keyboard is unplugged and re-docked — it comes back dark and (previously)
stayed dark until reboot. The HID protocol is write-only, so there is nothing
to "read back"; instead we persist the last applied color/brightness and
reapply it:

  * State file: $XDG_STATE_HOME/rgb-control/state.json (default
    ~/.local/state/rgb-control/state.json). Written on every set/brightness by
    rgb_core; overridable via $RGB_STATE_FILE.
  * rgb-restore.service (system oneshot) runs `rgb restore` with a fixed
    RGB_STATE_FILE so it works regardless of $HOME.
  * Triggers:
      - /etc/systemd/system-sleep/rgb-restore  -> on resume (post).
      - udev 99-asus-rgb.rules  -> on RGB-LED add and on USB add of the
        keyboard (0b05:1a30) / lightbar (0b05:18c6), covering re-dock + boot.
  * `rgb restore` retries discovery for a few seconds so it tolerates the
    device still enumerating when the trigger fires.

Dim on lock / off when the screen powers off
--------------------------------------------
rgb-idled is a tiny always-on user service that reacts to the desktop with no
polling (it listens to signals GNOME already emits):

  * locked              -> dim to a low glow   (RGB_DIM_BRIGHTNESS, default 1)
  * screen powered off  -> lights fully off
  * unlocked / active   -> restore the saved colour + brightness

Signals used (GNOME / Wayland):
  * org.gnome.ScreenSaver ActiveChanged        -> lock / unlock
  * org.gnome.Mutter.IdleMonitor WatchFired    -> the same idle clock GNOME uses
    to blank (power off) the display

Dim/off are transient: they use rgb_core's persist=False path, so state.json
keeps the user's real preference and that's exactly what gets restored on wake
(this also covers resume-from-suspend without a separate hook).

Tunables (set in ~/.config/systemd/user/rgb-idled.service):
  RGB_DIM_BRIGHTNESS      brightness level while locked            (default 1)
  RGB_SCREEN_OFF_SECONDS  auto = follow GNOME's screen-blank delay (default);
                          an integer = explicit seconds; 0/never = dim-only.
  After editing: systemctl --user daemon-reload && \
                 systemctl --user restart rgb-idled

Install
-------
  ./install.sh        # prompts for sudo (writes to /etc, enables service)

Files written by install.sh:
  /etc/udev/rules.d/99-asus-rgb.rules
  /etc/tmpfiles.d/asus-rgb.conf
  /etc/systemd/system/rgb-restore.service          (enabled)
  /etc/systemd/system-sleep/rgb-restore            (executable)
  ~/.local/bin/rgb -> rgb-cli
  ~/.local/share/applications/rgb-control.desktop
  ~/.config/systemd/user/rgb-idled.service        (enabled, user service)
