Add manual AP mode override via web UI
- State Machine prüft /tmp/babycam-ap-override: 'on'/'off' = manuell, fehlt = Automatik - Neue API-Endpunkte: POST /ap/on, /ap/off, /ap/auto - Access-Point-Karte zeigt jetzt 3 Buttons (An / Aus / Auto) + aktuellen Modus - Manuell überschreibt die Automatik, Auto gibt Kontrolle zurück an die State Machine Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d9514e23d7
commit
7cfc958cf0
30
app/main.py
30
app/main.py
|
|
@ -10,6 +10,7 @@ import subprocess
|
|||
import json
|
||||
import re
|
||||
import time
|
||||
import os
|
||||
from flask import Flask, Response, render_template, request, jsonify
|
||||
|
||||
app = Flask(__name__)
|
||||
|
|
@ -113,6 +114,7 @@ def get_system_status():
|
|||
"ips": ips,
|
||||
"wifi_ssid": wifi_ssid,
|
||||
"ap_active": ap_active,
|
||||
"ap_override": get_ap_override(),
|
||||
"cam_available": cam_available,
|
||||
"uptime": _get_uptime(),
|
||||
}
|
||||
|
|
@ -159,6 +161,34 @@ def wifi_scan():
|
|||
return jsonify(get_wifi_networks())
|
||||
|
||||
|
||||
@app.route("/ap/<action>", methods=["POST"])
|
||||
def ap_control(action):
|
||||
override_file = "/tmp/babycam-ap-override"
|
||||
if action == "on":
|
||||
with open(override_file, "w") as f:
|
||||
f.write("on")
|
||||
return jsonify({"success": True, "mode": "on"})
|
||||
elif action == "off":
|
||||
with open(override_file, "w") as f:
|
||||
f.write("off")
|
||||
return jsonify({"success": True, "mode": "off"})
|
||||
elif action == "auto":
|
||||
try:
|
||||
os.remove(override_file)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return jsonify({"success": True, "mode": "auto"})
|
||||
return jsonify({"success": False, "error": "Unbekannte Aktion"}), 400
|
||||
|
||||
|
||||
def get_ap_override():
|
||||
try:
|
||||
with open("/tmp/babycam-ap-override") as f:
|
||||
return f.read().strip().lower()
|
||||
except FileNotFoundError:
|
||||
return "auto"
|
||||
|
||||
|
||||
@app.route("/wifi/connect", methods=["POST"])
|
||||
def wifi_connect():
|
||||
data = request.get_json()
|
||||
|
|
|
|||
|
|
@ -219,10 +219,18 @@
|
|||
</div>
|
||||
<div class="card">
|
||||
<label>Access Point</label>
|
||||
<div class="value">
|
||||
<div class="value" style="display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;">
|
||||
<span class="badge {{ 'on' if status.ap_active else 'off' }}">
|
||||
{{ "aktiv" if status.ap_active else "aus" }}
|
||||
</span>
|
||||
<span style="font-size:.75rem;color:#666;">
|
||||
{{ {"on":"manuell an","off":"manuell aus","auto":"automatisch"}[status.ap_override] }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="display:flex;gap:.4rem;margin-top:.6rem;flex-wrap:wrap;">
|
||||
<button onclick="setAP('on')" class="{{ 'primary' if status.ap_override == 'on' else '' }}" style="font-size:.75rem;padding:.3rem .7rem;">An</button>
|
||||
<button onclick="setAP('off')" class="{{ 'primary' if status.ap_override == 'off' else '' }}" style="font-size:.75rem;padding:.3rem .7rem;">Aus</button>
|
||||
<button onclick="setAP('auto')"class="{{ 'primary' if status.ap_override == 'auto' else '' }}" style="font-size:.75rem;padding:.3rem .7rem;">Auto</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
|
|
@ -281,6 +289,12 @@
|
|||
document.getElementById("wifi-password").focus();
|
||||
}
|
||||
|
||||
async function setAP(action) {
|
||||
const res = await fetch(`/ap/${action}`, { method: "POST" });
|
||||
const data = await res.json();
|
||||
if (data.success) setTimeout(() => location.reload(), 800);
|
||||
}
|
||||
|
||||
async function connectWifi() {
|
||||
if (!selectedSSID) return;
|
||||
const password = document.getElementById("wifi-password").value;
|
||||
|
|
|
|||
|
|
@ -2,15 +2,21 @@
|
|||
"""
|
||||
Babycam Network State Machine
|
||||
Verwaltet die drei Betriebsmodi:
|
||||
A – Heimnetz (wlan0 verbunden)
|
||||
B – Kein WLAN (AP auf wlan1)
|
||||
A – Heimnetz (wlan0 verbunden, AP aus)
|
||||
B – Kein WLAN (AP auf wlan1 automatisch)
|
||||
C – Setup-Modus (AP läuft, User konfiguriert WLAN)
|
||||
|
||||
Override-Datei: /tmp/babycam-ap-override
|
||||
"on" → AP immer an (manuell)
|
||||
"off" → AP immer aus (manuell)
|
||||
fehlt → Automatik
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
|
|
@ -23,21 +29,30 @@ AP_INTERFACE = "wlan1"
|
|||
CLIENT_INTERFACE = "wlan0"
|
||||
AP_IP = "192.168.50.1"
|
||||
CHECK_INTERVAL = 30 # Sekunden
|
||||
OVERRIDE_FILE = "/tmp/babycam-ap-override"
|
||||
|
||||
|
||||
def run(cmd, check=False):
|
||||
result = subprocess.run(
|
||||
cmd, shell=True, capture_output=True, text=True
|
||||
)
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if check and result.returncode != 0:
|
||||
log.error(f"Fehler bei: {cmd}\n{result.stderr}")
|
||||
return result
|
||||
|
||||
|
||||
def get_override():
|
||||
"""Liest die manuelle Override-Einstellung. Gibt 'on', 'off' oder None zurück."""
|
||||
try:
|
||||
with open(OVERRIDE_FILE) as f:
|
||||
val = f.read().strip().lower()
|
||||
if val in ("on", "off"):
|
||||
return val
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def is_wlan0_connected():
|
||||
result = run(
|
||||
f"nmcli -t -f GENERAL.STATE device show {CLIENT_INTERFACE}"
|
||||
)
|
||||
result = run(f"nmcli -t -f GENERAL.STATE device show {CLIENT_INTERFACE}")
|
||||
return "100 (connected)" in result.stdout
|
||||
|
||||
|
||||
|
|
@ -68,12 +83,31 @@ def main():
|
|||
current_mode = None
|
||||
|
||||
while True:
|
||||
override = get_override()
|
||||
connected = is_wlan0_connected()
|
||||
ap_running = is_ap_running()
|
||||
|
||||
if override == "on":
|
||||
# Manuell: AP immer an
|
||||
if current_mode != "MANUAL_ON":
|
||||
log.info("Modus: Manuell – Access Point erzwungen AN.")
|
||||
if not ap_running:
|
||||
start_ap()
|
||||
current_mode = "MANUAL_ON"
|
||||
|
||||
elif override == "off":
|
||||
# Manuell: AP immer aus
|
||||
if current_mode != "MANUAL_OFF":
|
||||
log.info("Modus: Manuell – Access Point erzwungen AUS.")
|
||||
if ap_running:
|
||||
stop_ap()
|
||||
current_mode = "MANUAL_OFF"
|
||||
|
||||
else:
|
||||
# Automatik
|
||||
if connected:
|
||||
if current_mode != "A":
|
||||
log.info("Modus A: Heimnetz aktiv.")
|
||||
log.info("Modus A: Heimnetz aktiv – AP aus.")
|
||||
if ap_running:
|
||||
stop_ap()
|
||||
current_mode = "A"
|
||||
|
|
|
|||
Loading…
Reference in New Issue