Setup-Button pro Kamera: Relay ruft SETUP_<name>-URL auf

This commit is contained in:
Joachim Hummel committed 2026-10-17 21:11:24 +02:00
1 parent 146ba6c399
commit 70b63cb215
4 files changed
+50 -7

No files matched your search

+21 -1
View File
@@ -8,11 +8,14 @@ Endpunkte:
/status.json Status aller Kameras (vom Frontend abgefragt)
/<name>/stream.mjpg Livestream (multipart/x-mixed-replace)
/<name>/snapshot.jpg aktuelles Einzelbild
POST /<name>/setup ruft die in SETUP_<name> hinterlegte Kamera-URL auf
/stream.mjpg Alias für die erste Kamera (Abwärtskompatibilität)
/health Status aller Kameras
Konfiguration über die Datei .env (neben relay.py) oder Umgebungsvariablen:
CAMERA_<name>=<url> eine Zeile pro Kamera, z.B. CAMERA_cam1=http://...
SETUP_<name>=<url> optional: Konfigurations-URL der Kamera (Button "Setup" im Frontend),
z.B. http://IP/control?var=framesize&val=11
PORT (default 8000)
AUTH_USER / AUTH_PASS optional: Basic-Auth (nur sinnvoll mit HTTPS in Zoraxy)
"""
@@ -45,6 +48,8 @@ load_env(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"))
CAMERAS = {k[7:].lower(): v for k, v in os.environ.items() if k.startswith("CAMERA_") and v}
if not CAMERAS:
raise SystemExit("Keine Kameras konfiguriert: CAMERA_<name>=<url> in .env eintragen")
SETUPS = {k[6:].lower(): v for k, v in os.environ.items()
if k.startswith("SETUP_") and v and k[6:].lower() in CAMERAS}
PORT = int(os.environ.get("PORT", "8000"))
AUTH_USER = os.environ.get("AUTH_USER", "")
AUTH_PASS = os.environ.get("AUTH_PASS", "")
@@ -148,7 +153,7 @@ class Handler(BaseHTTPRequestHandler):
if not parts:
return self.send_bytes(200, "text/html; charset=utf-8", page("", ""))
if parts == ["status.json"]:
body = json.dumps({"cameras": [{"name": n, "online": is_online(h)}
body = json.dumps({"cameras": [{"name": n, "online": is_online(h), "setup": n in SETUPS}
for n, h in hubs.items()]}).encode()
return self.send_bytes(200, "application/json", body)
if parts == ["stream.mjpg"] or parts == ["snapshot.jpg"]:
@@ -170,6 +175,21 @@ class Handler(BaseHTTPRequestHandler):
return self.stream(hub)
self.send_bytes(404, "text/plain", b"Nicht gefunden")
def do_POST(self):
if not self.authorized():
return self.send_bytes(401, "text/plain", b"Login erforderlich",
{"WWW-Authenticate": 'Basic realm="Webcam"'})
parts = [p for p in self.path.split("?")[0].split("/") if p]
if len(parts) != 2 or parts[1] != "setup" or parts[0] not in SETUPS:
return self.send_bytes(404, "text/plain", b"Nicht gefunden")
try:
r = requests.get(SETUPS[parts[0]], timeout=5)
r.raise_for_status()
except requests.RequestException as ex:
print(f"{parts[0]}: Setup fehlgeschlagen: {ex}", flush=True)
return self.send_bytes(502, "text/plain", b"Kamera nicht erreichbar")
self.send_bytes(200, "text/plain", b"ok")
def stream(self, hub):
self.send_response(200)
self.send_header("Content-Type", f"multipart/x-mixed-replace; boundary={BOUNDARY}")