python – Persist keyboard backlight color on linux laptop

System76 sells laptops that come preloaded with their Ubuntu spinoff Pop_OS. The laptop keyboards have backlights that can change color and brightness, but so far I haven’t found any way to keep the setting when I restart the machine; it goes back to bright white.

Here’s someone else struggling with the same problem somewhat recently. The comments link to a useful python script, but it doesn’t seem to be finished to the point where it would do what’s wanted. (It’s primary goal seems to be a GUI, with persistence on the wishlist.)

I finally got sick of it and implemented the behavior I wanted using systemd, which I’m using to call a python script.
Since other people have asked the same questions as me in the past, I figured I’d share my solution, but the first step is to ask for feedback.

Is this a good solution to the problem? Are there mistakes or other reasons people shouldn’t use it?

#!/usr/bin/env python3

import argparse as ag
import os.path as path
import re

print("Attempting to set system76 keyboard backlight color and brightness.")

"""
Partially cribbed from
https://github.com/ahoneybun/keyboard-color-chooser/blob/master/keyboard-color-switcher.py

Called by 
/etc/systemd/system/system76-kb-backlight.service
as decribed here:
https://www.howtogeek.com/687970/how-to-run-a-linux-program-at-startup-with-systemd/

Example file /etc/systemd/system/system76-kb-backlight.service
``
(Unit)
Description=Set color and brightness of system-76 laptop keyboard backlight

(Service)
Type=simple
ExecStart=systemd-cat /path_to_this_executable/set_system76_kb_backlight -g 55 -r FF -B 150

(Install)
WantedBy=multi-user.target
``
(Don't forget to enable the service.)
"""

def color_fragment(string):
    if re.fullmatch(r'^(0-9A-F){2}$', string):
        return string
    else:
        raise ag.ArgumentTypeError(f'"{string}" is not a two-digit hex value.')

def brightness_fragment(string):
    if re.fullmatch(r'^(0-9){1,3}$', string) and 0 <= int(string) and 255 >= int(string):
        return string
    else:
        raise ag.ArgumentTypeError(f'"{string}" is not an integer 0-255.')

arg_parse = ag.ArgumentParser(description="Set the color and brightness of the system76 keyboard backlight.")
arg_parse.add_argument('-r', help="The red RGB value (00 to FF).", default="00", type=color_fragment)
arg_parse.add_argument('-g', help="The green RGB value (00 to FF).", default="00", type=color_fragment)
arg_parse.add_argument('-b', help="The blue RGB value (00 to FF).", default="00", type=color_fragment)
arg_parse.add_argument('-B', help="The brightness (0 to 255).", default="48", type=brightness_fragment)

args = arg_parse.parse_args()
red = args.r
green = args.g
blue = args.b
brightness = args.B
color = f'{red}{green}{blue}'

ledPath = "/" + path.join('sys', 'class', 'leds', 'system76_acpi::kbd_backlight') + '/'
if not path.exists(ledPath):
    ledPath = "/" + path.join('sys', 'class', 'leds', 'system76::kbd_backlight') + '/'

regions = ('left', 'center', 'right', 'extra')
region_paths = (ledPath + f'color_{r}' for r in regions)
brightness_path = ledPath + 'brightness'
settings = {brightness_path: brightness,
            **{rp: color for rp in region_paths}}

for (p,s) in settings.items():
    with open(p, 'w') as f:
        f.write(s)

print("Successfully set system76 keyboard backlight brightness.")
arg_parse.exit(0)
```