#!/usr/bin/env python3 """Simple web server: one button to turn off all kids' devices.""" import json import re import subprocess from pathlib import Path from http.server import HTTPServer, BaseHTTPRequestHandler SCRIPT = Path(__file__).parent / "off.sh" def parse_output(text: str) -> list[dict]: """Parse off.sh output into structured action items.""" actions = [] lines = text.strip().splitlines() for line in lines: line = line.strip() if not line: continue # Time window messages m = re.match(r"PRE:\s*(.+)seconds?\s*to\s*go\.", line) if m: actions.append({"icon": "β³", "title": "Time Window", "detail": f"Before active hours β {m.group(1)}s until start"}) continue m = re.match(r"POST:\s*(.+)seconds?\s*passed\.", line) if m: actions.append({"icon": "β³", "title": "Time Window", "detail": f"After active hours β {m.group(1)}s past end"}) continue m = re.match(r"ACTIVE:\s*(.+)", line) if m: actions.append({"icon": "π’", "title": "Active Hours", "detail": m.group(1)}) continue # TV checks if "TV is unreachable" in line: actions.append({"icon": "β", "title": "TV", "detail": "Unreachable (ping failed)"}) continue if "TV is turning OFF" in line: actions.append({"icon": "πΊ", "title": "TV", "detail": "Already off β no action needed"}) continue if "mWakefulness=Awake" in line: actions.append({"icon": "πΊ", "title": "TV", "detail": "Screen turned off (power key sent)"}) continue # User checks if "Gabi is not online" in line: actions.append({"icon": "π€", "title": "Gabi's PC", "detail": "Not logged in β no action needed"}) continue if "terminate-user gabi" in line or "terminated" in line.lower(): actions.append({"icon": "π»", "title": "Gabi's PC", "detail": "Session terminated"}) continue # If no structured items found, fallback to raw if not actions: actions.append({"icon": "π", "title": "Output", "detail": text.strip() or "(empty)"}) return actions class Handler(BaseHTTPRequestHandler): def do_GET(self): if self.path == "/" or self.path == "/index.html": self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") self.end_headers() self.wfile.write(HTML.encode()) else: self.send_error(404) def do_POST(self): if self.path == "/run": try: result = subprocess.run( ["bash", str(SCRIPT)], capture_output=True, text=True, timeout=30, ) raw_output = (result.stdout + result.stderr).strip() or "Script executed (no output)." status = "ok" if result.returncode == 0 else f"exit {result.returncode}" except FileNotFoundError: raw_output = f"Script not found at {SCRIPT}" status = "error" except subprocess.TimeoutExpired: raw_output = "Script timed out after 30 seconds." status = "error" except Exception as e: raw_output = str(e) status = "error" actions = parse_output(raw_output) if status == "ok" else [{"icon": "β", "title": "Error", "detail": raw_output}] self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(json.dumps({"status": status, "actions": actions}).encode()) else: self.send_error(404) def log_message(self, format, *args): pass HTML = """\
TV + Gabi's computer