Commit d40e76

2026-06-06 00:00:22 Labor: Uploaded via inline attachment
/dev/null .. labor23/01_infrastruktur/hardware-inventar/geräteliste/labor23-terminal/bildschirm-dimmung/brightness-server.py
@@ 0,0 1,128 @@
+ #!/usr/bin/env python3
+ """
+ Einfacher HTTP-Server zum Steuern der Bildschirmhelligkeit (und Farbtemperatur)
+ über wl-gammarelay-rs.
+
+ Aufruf-Beispiele (z. B. aus Home Assistant per rest_command):
+ GET /set?b=0.5 -> Helligkeit auf 50 %
+ GET /set?b=0.3&t=4000 -> Helligkeit 30 %, Farbtemperatur 4000 K
+ GET /get -> aktuelle Helligkeit als Zahl (z. B. 0.5)
+ GET / -> kurze Hilfe / Status
+
+ Laeuft als Benutzer 'kiosk' INNERHALB der labwc-Sitzung, damit 'busctl --user'
+ den Daemon direkt erreicht (XDG_RUNTIME_DIR ist dann korrekt gesetzt).
+
+ Optional: einfacher Schutz per Token. Wenn unten TOKEN gesetzt ist, muss jede
+ Anfrage ?token=... mitschicken. Standard: aus (leer).
+ """
+
+ import os
+ import subprocess
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+ from urllib.parse import urlparse, parse_qs
+
+ # ----- Einstellungen (bei Bedarf anpassen) -----
+ HOST = "0.0.0.0" # 0.0.0.0 = im ganzen LAN erreichbar
+ PORT = 8080
+ TOKEN = "" # z. B. "geheim123" setzen, dann ?token=geheim123 noetig
+ DBUS_DEST = "rs.wl-gammarelay"
+ DBUS_PATH = "/"
+ DBUS_IFACE = "rs.wl.gammarelay"
+
+ # Falls XDG_RUNTIME_DIR aus irgendeinem Grund fehlt, sinnvollen Default setzen
+ # (1001 ist die UID des kiosk-Users auf diesem Geraet).
+ os.environ.setdefault("XDG_RUNTIME_DIR", "/run/user/1001")
+
+
+ def busctl(*args):
+ """Ruft busctl --user auf und gibt (ok, ausgabe) zurueck."""
+ cmd = ["busctl", "--user", *args]
+ try:
+ out = subprocess.run(
+ cmd, capture_output=True, text=True, timeout=5
+ )
+ if out.returncode != 0:
+ return False, (out.stderr or out.stdout).strip()
+ return True, out.stdout.strip()
+ except Exception as e:
+ return False, str(e)
+
+
+ def set_brightness(value):
+ value = max(0.0, min(1.0, float(value)))
+ return busctl("set-property", DBUS_DEST, DBUS_PATH, DBUS_IFACE,
+ "Brightness", "d", f"{value:.3f}")
+
+
+ def set_temperature(kelvin):
+ kelvin = int(max(1000, min(10000, int(kelvin))))
+ return busctl("set-property", DBUS_DEST, DBUS_PATH, DBUS_IFACE,
+ "Temperature", "q", str(kelvin))
+
+
+ def get_brightness():
+ ok, out = busctl("get-property", DBUS_DEST, DBUS_PATH, DBUS_IFACE,
+ "Brightness")
+ if not ok:
+ return ok, out
+ # Ausgabe sieht aus wie: d 0.5
+ parts = out.split()
+ return True, parts[-1] if parts else out
+
+
+ class Handler(BaseHTTPRequestHandler):
+ def _reply(self, code, text):
+ body = (text + "\n").encode()
+ self.send_response(code)
+ self.send_header("Content-Type", "text/plain; charset=utf-8")
+ self.send_header("Content-Length", str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+
+ def do_GET(self):
+ u = urlparse(self.path)
+ q = parse_qs(u.query)
+
+ if TOKEN and q.get("token", [""])[0] != TOKEN:
+ return self._reply(403, "forbidden")
+
+ if u.path == "/" or u.path == "/help":
+ return self._reply(
+ 200,
+ "brightness-server\n"
+ "/set?b=0.5 Helligkeit (0.0-1.0)\n"
+ "/set?b=0.3&t=4000 Helligkeit + Temperatur (K)\n"
+ "/get aktuelle Helligkeit",
+ )
+
+ if u.path == "/get":
+ ok, out = get_brightness()
+ return self._reply(200 if ok else 500, out)
+
+ if u.path == "/set":
+ results = []
+ if "b" in q:
+ ok, out = set_brightness(q["b"][0])
+ results.append("brightness ok" if ok else f"brightness FEHLER: {out}")
+ if "t" in q:
+ ok, out = set_temperature(q["t"][0])
+ results.append("temp ok" if ok else f"temp FEHLER: {out}")
+ if not results:
+ return self._reply(400, "fehlender Parameter b oder t")
+ failed = any("FEHLER" in r for r in results)
+ return self._reply(500 if failed else 200, "; ".join(results))
+
+ return self._reply(404, "unbekannter Pfad")
+
+ # Logging in der Konsole knapp halten
+ def log_message(self, fmt, *args):
+ pass
+
+
+ if __name__ == "__main__":
+ srv = ThreadingHTTPServer((HOST, PORT), Handler)
+ print(f"brightness-server laeuft auf {HOST}:{PORT}")
+ try:
+ srv.serve_forever()
+ except KeyboardInterrupt:
+ pass
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9