feat: focus panel on rgb leds and speed boot
This commit is contained in:
+33
-4
@@ -21,6 +21,9 @@ from typing import Dict, List, Optional, Tuple
|
||||
|
||||
LED_ROOT = Path(os.environ.get("LED_ROOT", "/sys/class/leds"))
|
||||
CONFIG_PATH = Path(os.environ.get("LED_CONFIG", "/etc/led-manager/config.json"))
|
||||
# BOSS's device exposes more LED class entries than the physically controllable lights.
|
||||
# Keep the panel focused on the real RGB lights by default; override with --colors if needed.
|
||||
CONTROLLABLE_COLORS = tuple(c.strip().lower() for c in os.environ.get("LED_COLORS", "red,green,blue").split(",") if c.strip())
|
||||
NAME_RE = re.compile(r"^[A-Za-z0-9_.:+@=-]+$")
|
||||
|
||||
CSS = r"""
|
||||
@@ -102,8 +105,22 @@ def safe_name(name: str) -> str:
|
||||
return name
|
||||
|
||||
|
||||
def led_color(name: str) -> Optional[str]:
|
||||
low = name.lower()
|
||||
for color in CONTROLLABLE_COLORS:
|
||||
if low == color or low.startswith(color + ":") or low.startswith(color + "-") or low.startswith(color + "_"):
|
||||
return color
|
||||
return None
|
||||
|
||||
|
||||
def is_controllable_led_name(name: str) -> bool:
|
||||
return not CONTROLLABLE_COLORS or led_color(name) is not None
|
||||
|
||||
|
||||
def led_path(name: str) -> Path:
|
||||
name = safe_name(name)
|
||||
if not is_controllable_led_name(name):
|
||||
raise ValueError("led hidden by color filter")
|
||||
# /sys/class/leds entries are usually symlinks to /sys/devices/...
|
||||
# Do NOT require the resolved target to stay under LED_ROOT, otherwise every real sysfs LED POST is rejected.
|
||||
# safe_name() forbids slash and '..', so joining one basename under LED_ROOT is enough here.
|
||||
@@ -177,7 +194,14 @@ def load_config() -> Dict[str, Dict[str, object]]:
|
||||
leds = data.get("leds", data)
|
||||
if not isinstance(leds, dict):
|
||||
return {}
|
||||
return {safe_name(str(k)): v for k, v in leds.items() if isinstance(v, dict)}
|
||||
out = {}
|
||||
for k, v in leds.items():
|
||||
if not isinstance(v, dict):
|
||||
continue
|
||||
name = safe_name(str(k))
|
||||
if is_controllable_led_name(name):
|
||||
out[name] = v
|
||||
return out
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
@@ -185,7 +209,8 @@ def load_config() -> Dict[str, Dict[str, object]]:
|
||||
def save_config(config: Dict[str, Dict[str, object]]) -> None:
|
||||
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = CONFIG_PATH.with_suffix(CONFIG_PATH.suffix + ".tmp")
|
||||
payload = {"version": 1, "leds": config}
|
||||
filtered = {safe_name(str(k)): v for k, v in config.items() if isinstance(v, dict) and is_controllable_led_name(safe_name(str(k)))}
|
||||
payload = {"version": 1, "colors": list(CONTROLLABLE_COLORS), "leds": filtered}
|
||||
tmp.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n")
|
||||
os.replace(tmp, CONFIG_PATH)
|
||||
try:
|
||||
@@ -289,6 +314,8 @@ def list_leds() -> List[Dict[str, object]]:
|
||||
if not p.is_dir():
|
||||
continue
|
||||
name = p.name
|
||||
if not is_controllable_led_name(name):
|
||||
continue
|
||||
b = read_text(p / "brightness")
|
||||
mb = read_text(p / "max_brightness")
|
||||
tr_raw = read_text(p / "trigger")
|
||||
@@ -443,22 +470,24 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
|
||||
def main():
|
||||
global LED_ROOT, CONFIG_PATH
|
||||
global LED_ROOT, CONFIG_PATH, CONTROLLABLE_COLORS
|
||||
ap = argparse.ArgumentParser(description="Tiny web panel for /sys/class/leds")
|
||||
ap.add_argument("--host", default="0.0.0.0", help="listen host, default 0.0.0.0")
|
||||
ap.add_argument("--port", type=int, default=8088, help="listen port, default 8088")
|
||||
ap.add_argument("--led-root", default=str(LED_ROOT), help="LED sysfs root, default /sys/class/leds")
|
||||
ap.add_argument("--config", default=str(CONFIG_PATH), help="persistent config path, default /etc/led-manager/config.json")
|
||||
ap.add_argument("--colors", default=",".join(CONTROLLABLE_COLORS), help="comma-separated LED color prefixes to expose, default red,green,blue; empty means expose all")
|
||||
ap.add_argument("--no-apply", action="store_true", help="do not apply saved config on startup")
|
||||
args = ap.parse_args()
|
||||
LED_ROOT = Path(args.led_root)
|
||||
CONFIG_PATH = Path(args.config)
|
||||
CONTROLLABLE_COLORS = tuple(c.strip().lower() for c in args.colors.split(",") if c.strip())
|
||||
if not args.no_apply:
|
||||
results = apply_saved_config()
|
||||
if results:
|
||||
print(f"Applied saved LED config: {results}", flush=True)
|
||||
httpd = ThreadingHTTPServer((args.host, args.port), Handler)
|
||||
print(f"LED Manager Panel listening on http://{args.host}:{args.port} ; LED_ROOT={LED_ROOT}; CONFIG_PATH={CONFIG_PATH}", flush=True)
|
||||
print(f"LED Manager Panel listening on http://{args.host}:{args.port} ; LED_ROOT={LED_ROOT}; CONFIG_PATH={CONFIG_PATH}; COLORS={','.join(CONTROLLABLE_COLORS) or 'all'}", flush=True)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user