Add web UI reboot/shutdown controls and system action API

This commit is contained in:
2026-02-13 02:35:50 +01:00
parent 0d4e769292
commit 761fc49461
3 changed files with 71 additions and 1 deletions

View File

@@ -1,5 +1,7 @@
import json
import queue
import subprocess
import threading
from typing import Any, Dict
from flask import Flask, Response, jsonify, render_template, request, stream_with_context
@@ -70,6 +72,27 @@ class WebPortal:
self.network_manager.refresh_state()
return jsonify({"ok": True, "message": message})
@self.app.route("/api/system/<action>", methods=["POST"])
def system_action(action: str) -> Response:
commands = {
"reboot": ["systemctl", "reboot"],
"shutdown": ["systemctl", "poweroff"],
}
cmd = commands.get(action)
if cmd is None:
return jsonify({"ok": False, "message": "Unknown action"}), 400
self.state.update_status(f"System action requested: {action}", "")
def _exec() -> None:
try:
subprocess.run(cmd, capture_output=True, text=True, timeout=20, check=False)
except Exception:
pass
threading.Thread(target=_exec, daemon=True, name=f"system-{action}").start()
return jsonify({"ok": True, "message": f"{action} triggered"})
@self.app.route("/events/serial")
def serial_events() -> Response:
@stream_with_context

View File

@@ -59,6 +59,20 @@ button:hover {
filter: brightness(0.95);
}
.button-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.btn-secondary {
background: #3b4958;
}
.btn-danger {
background: #b42318;
}
.error {
color: var(--error);
font-size: 14px;

View File

@@ -36,6 +36,15 @@
<h2>Serial Monitor</h2>
<a href="/serial">Zur Live-Serial-Seite</a>
</section>
<section class="card">
<h2>System</h2>
<div class="button-row">
<button id="rebootBtn" class="btn-secondary">Reboot</button>
<button id="shutdownBtn" class="btn-danger">Shutdown</button>
</div>
<div id="systemMsg"></div>
</section>
</main>
<script>
@@ -86,7 +95,7 @@
const msg = document.getElementById('connectMsg');
if (!ssid) {
msg.textContent = 'Bitte SSID wählen.';
msg.textContent = 'Bitte SSID waehlen.';
return;
}
@@ -109,8 +118,32 @@
setTimeout(refreshStatus, 1000);
}
async function triggerSystemAction(action) {
const msg = document.getElementById('systemMsg');
const label = action === 'reboot' ? 'Reboot' : 'Shutdown';
const ok = window.confirm(`System wirklich ${label.toLowerCase()} ausfuehren?`);
if (!ok) {
return;
}
msg.textContent = `${label} wird gestartet...`;
try {
const resp = await fetch(`/api/system/${action}`, {method: 'POST'});
const data = await resp.json();
if (resp.ok && data.ok) {
msg.textContent = `${label} ausgeloest.`;
} else {
msg.textContent = `Fehler: ${data.message || 'Aktion fehlgeschlagen'}`;
}
} catch (e) {
msg.textContent = `${label} fehlgeschlagen.`;
}
}
document.getElementById('scanBtn').addEventListener('click', scan);
document.getElementById('connectBtn').addEventListener('click', connectWifi);
document.getElementById('rebootBtn').addEventListener('click', () => triggerSystemAction('reboot'));
document.getElementById('shutdownBtn').addEventListener('click', () => triggerSystemAction('shutdown'));
refreshStatus();
setInterval(refreshStatus, 5000);