#!/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 m = re.match(r"terminate-user gabi\b", line) if m or "session terminated" in line.lower(): actions.append({"icon": "πŸ’»", "title": "Gabi's PC", "detail": "Session terminated"}) continue if "Gaja is not online" in line: actions.append({"icon": "πŸ’»", "title": "Gaja's PC", "detail": "Not logged in β€” no action needed"}) continue if "Gaja's PC is unreachable" in line: actions.append({"icon": "❌", "title": "Gaja's PC", "detail": "Unreachable (ping failed)"}) continue if "Gaja's PC: logged out" in line: actions.append({"icon": "πŸ’»", "title": "Gaja's PC", "detail": "Logged out"}) 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 = """\ Kids Devices β€” OFF

πŸ“Ί Kids Devices

TV + Gabi's computer

""" if __name__ == "__main__": port = int(__import__("os").environ.get("PORT", "10000")) server = HTTPServer(("0.0.0.0", port), Handler) print(f"πŸš€ Open http://localhost:{port}") server.serve_forever()