Move the off.sh script to a pure Python implementation -- devices.py.

This commit is contained in:
2026-06-08 22:59:39 +02:00
parent b15f87e3d5
commit c5b9036644
3 changed files with 140 additions and 147 deletions

View File

@ -2,80 +2,9 @@
"""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
from devices import shutdown_all
class Handler(BaseHTTPRequestHandler):
@ -91,26 +20,12 @@ class Handler(BaseHTTPRequestHandler):
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"
actions = shutdown_all()
status = "ok"
except Exception as e:
raw_output = str(e)
actions = [{"icon": "", "title": "Error", "detail": 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()
@ -167,7 +82,7 @@ HTML = """\
<body>
<div class="card">
<h1>📺 Kids Devices</h1>
<p>TV + Gabi's computer</p>
<p>TV + Gabi's computer + Gaja's computer</p>
<button id="btn" onclick="run()">TURN OFF</button>
<div id="output"></div>
</div>