Native C17 Linux RGB keyboard controller for ASUS Vivobook ITE5570
Find a file
2026-11-09 19:01:00 +01:00
docs Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
packaging Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
src Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
tests Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
.gitignore Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
LICENSE Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
meson.build Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
README.md Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00
THIRD_PARTY_NOTICES.md Initial public release of Labfy RGB 2026-11-09 19:01:00 +01:00

Labfy RGB

A small native C17 Linux command-line controller for the RGB keyboard on the ASUS Vivobook S16 M5606UA, using its ITE5570 I2C HID controller (0B05:5570) and the standard HID LampArray protocol.

Labfy RGB talks to Linux hidraw through open, close and HID Feature ioctls. It is a userspace application, not a kernel module. Static colors need no daemon. It works independently of Sway, Wayland, X11 and QuickShell.

  • Dynamically discovers the matching device; never assumes /dev/hidraw1.
  • Checks the bus, VID/PID and complete known HID report descriptor before control.
  • Reads live lamp capabilities before converting RGB and intensity values.
  • Supports static colors, intensity, off, and explicit firmware control.
  • Serializes cooperating instances and attempts firmware recovery on update errors.
  • Keeps command history separate from hardware observations.

Tested hardware and compatibility

Item Verified on the tested machine
Laptop ASUS Vivobook S16 M5606UA
Controller ITE5570, I2C HID 0B05:5570
Kernel HID driver hid-generic
Operating system Arch Linux, x86-64
Logical lamps One, ID 0
Programmability Programmable RGB
Maximum R/G/B/intensity values 255 / 255 / 255 / 255
Minimum update interval 3000 microseconds
Physical tests Static RGB colors, including the eight examples below, and return to ASUS firmware control
Unprivileged access Confirmed with the targeted udev rule and an active local session

Firmware control returned the tested keyboard to white. Other firmware versions may choose a different effect or color.

Other Vivobook models, other VID/PID combinations, USB devices and descriptor variants are not validated or currently accepted. Even another 0B05:5570 may have a different descriptor. The strict profile is deliberate: a familiar product name or report length is not enough to safely identify field semantics. The protocol may exist on other hardware, but that is not a compatibility claim.

Build

Requirements: Linux headers, a C17 compiler, Meson and Ninja. No Python or HID library is required for the controller implementation.

On Arch Linux, the usual build tools are provided by base-devel, meson and ninja. The optional permission helper also needs Bash, systemd's udevadm, coreutils and getfacl from acl.

git clone https://git.labfytools.com/fy59/labfy-rgb.git
cd labfy-rgb
meson setup build --prefix="$HOME/.local"
meson compile -C build
meson test -C build --print-errorlogs

GitHub mirror: https://github.com/labfytools/labfy-rgb.

Meson selects C17 and enables -Wall -Wextra -Wpedantic -Werror with GCC/Clang.

Install

Install the executable for your user:

meson install -C build

With the prefix above, the executable is $HOME/.local/bin/labfy-rgb. Make sure that directory is in your session's PATH. A system installation can instead use a suitable prefix and an administrator-managed install step.

Installing the executable does not install udev rules, configure an autostart, create a systemd service or modify desktop configuration.

Device permissions

The device may initially be root-only. Do not use chmod 666, run your desktop shell as root or grant access to all HID devices.

The opt-in rule in packaging/70-labfy-rgb.rules matches only:

SUBSYSTEM=="hidraw", KERNEL=="hidraw*", SUBSYSTEMS=="hid", KERNELS=="0018:0B05:5570.*", TAG+="uaccess"

On a systemd-logind system, uaccess grants access to the active local seat user. A remote/headless session is not automatically entitled to access. This grants access to the matching HID node as a whole, not just its lighting reports.

First inspect the read-only installation plan:

bash packaging/apply-udev-rule.sh --check

Then, only if you want to install and activate that rule:

sudo -- bash packaging/apply-udev-rule.sh --apply

The helper validates the narrow rule, dynamically identifies exactly one matching controller, refuses to overwrite a different rule, installs it as root-owned 0644, reloads udev rules and triggers a change event only for that hidraw node. It displays the resulting ACL. It sends no HID reports and changes no kernel modules. Reloading rules reparses the system's rule set; other rules for the targeted event may also run.

Verify as your normal user:

labfy-rgb status

For diagnostics, inspect the discovered node with getfacl. A successful rule installation alone does not prove that an ACL was granted. Logind seat membership and the resulting ACL must be checked. Do not broaden permissions as a fallback. Automatic reapplication after reboot or device re-enumeration is expected from udev matching, but was not physically tested by rebooting or reconnecting the internal keyboard during development.

Commands

labfy-rgb status
labfy-rgb status --json
labfy-rgb probe
labfy-rgb set '#8000FF'
labfy-rgb set '#8000FF' 60
labfy-rgb brightness 30
labfy-rgb off
labfy-rgb auto
Command Behavior
status [--json] Read-only HID identity, descriptor and array attributes; local command history is labeled separately
probe Select lamp 0 with SET 0x42, then read attributes with GET 0x43; this is an HID write, although it does not request a color or mode change
set '#RRGGBB' [P] Set a uniform RGB color at intensity P, default 100%; retain host control after success
brightness P Reapply the last locally recorded color at absolute intensity 0–100%; refuse if that color is unknown
off Send RGBI zero under host control; preserve a previously known color for a later brightness command
auto Explicitly restore autonomous firmware control; do not promise the previous color
--dry-run COMMAND [ARG...] Validate arguments and print the plan without opening HID or reading state

Color strings must be exactly #RRGGBB; quote them in a shell. Intensities are decimal integers from 0 through 100. Wire levels are rounded to the live channel maxima. The HID intensity is distinct from the WMI asus::kbd_backlight setting; Labfy RGB does not adjust that setting or assume a synchronized relationship.

Saturated color examples

Color Command
Violet labfy-rgb set '#8000FF'
Blue labfy-rgb set '#0040FF'
Cyan labfy-rgb set '#00FFFF'
Green labfy-rgb set '#00FF40'
Pink labfy-rgb set '#FF0080'
Red labfy-rgb set '#FF0000'
Orange labfy-rgb set '#FF6000'
Yellow labfy-rgb set '#FFFF00'

Firmware recovery and limitations

A color operation verifies live capabilities, disables autonomous mode, waits the device's minimum interval and sends one range update for lamp 0. On success, it leaves host control enabled so that the color persists.

If a host-control/update operation fails, Labfy RGB attempts one firmware restoration and reports the original error. It also handles ambiguous/short ioctl returns conservatively, without blindly retrying SET reports. auto does not require a successful capability read or a known color cache.

SIGKILL, default SIGINT/SIGTERM handling, power loss or disconnection can interrupt recovery. The keyboard can remain in host control. When access is available, use:

labfy-rgb auto

An ioctl acknowledgement is not an optical measurement or a guarantee of lasting visual state. Suspend/resume, other tools and firmware may alter the keyboard. Do not run another lighting controller concurrently: advisory locking only serializes cooperating Labfy RGB instances. Suspend/resume restoration and the interaction with WMI brightness have not been comprehensively characterized.

Runtime state and JSON

Normal user state lives at $XDG_RUNTIME_DIR/labfy-rgb/state.bin, with a 0700 directory and 0600 file. Without XDG_RUNTIME_DIR, the fallback is /run/user/UID. Paths, ownership and permissions are checked; symlinks are rejected at the opened boundaries. Root uses a separate namespace and does not import the old root cache. State is versioned, bounded and atomically published, with no native-struct serialization. A replaced device node invalidates its cached identity.

The cache records the last acknowledged command, not current physical color. status --json schema 1 exposes commanded_mode, nullable last_color and last_percent, while observed_mode remains unknown. Missing/corrupt state does not become an invented color. A successful firmware restoration can still return a cache error if local state could not be invalidated or saved.

Exit code Meaning
0 Success or valid dry-run
2 Invalid arguments
3 Device absent or identity mismatch
4 Permission denied
5 Unsupported capabilities, descriptor/protocol mismatch or ambiguous discovery
6 I/O failure, including a failed operation whose recovery succeeded
7 Last color unknown/unreadable for brightness; no SET sent
8 Original operation and its single recovery both failed; device state uncertain
9 Runtime state/cache error; read the diagnostic, because firmware may already have been restored
10 Another cooperating instance holds the device lock

Tests

Seven Meson suites use memory transports, syscall doubles and temporary files. They do not send reports to a real keyboard. Tests cover color parsing, RGBI conversion, exact report bytes, discovery, identity rejection, cache validation, process concurrency, errors, exit codes and single-attempt recovery.

CC=clang meson setup build-clang
meson test -C build-clang --print-errorlogs

CC=gcc meson setup build-sanitize -Db_sanitize=address,undefined -Db_lundef=false
meson test -C build-sanitize --print-errorlogs

CC=clang meson setup build-clang-sanitize -Db_sanitize=address,undefined -Db_lundef=false
meson test -C build-clang-sanitize --print-errorlogs

udevadm verify packaging/70-labfy-rgb.rules
bash -n packaging/apply-udev-rule.sh
git diff --check

Development validation used GCC 16.2.1 and Clang 23.1.4 on Arch Linux. Other Linux distributions/toolchain versions are not claimed as hardware-tested platforms. GNU-compatible linker wrapping is used for Linux transport tests.

Optional QuickShell integration

QuickShell is not required for the CLI. An existing Control Center can call it through native Process objects using argument arrays, never shell command strings, sudo or pkexec. Serialize writes, handle exit/error states, and refresh with status --json after completion. Keep the distinction between command history and physical measurements visible.

The tested local integration used QuickShell 0.3.1 on SwayFX 0.6 with saturated presets and a firmware button. It is not automatically installed by this project; no private desktop configuration is bundled. See docs/quickshell.md. Startup restoration must remain opt-in; this CLI installs no autostart or service.

Protocol and acknowledgements

See docs/protocol.md for the accepted report layout and THIRD_PARTY_NOTICES.md for reference implementations and license notices. No third-party implementation source is vendored.

License

MIT, copyright (c) 2026 fy59. See LICENSE.