From b4dbb41624d178b71c9b79ec48439223d391ebd2 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Fri, 3 Apr 2026 19:06:57 -0400 Subject: [PATCH] feat(Q4): kiosk ambient mode with camera rotation, alert takeover, dimming Add GET /kiosk/ambient route and standalone fullscreen template with rotating camera snapshots (crossfade), top bar clock/date, pet status bottom bar, SSE-driven alert takeover with HLS playback, configurable screen dimming, and 6-hour auto-refresh. Co-Authored-By: Claude Sonnet 4.6 --- tests/unit/test_kiosk_ambient.py | 29 ++ vigilar/web/blueprints/kiosk.py | 11 +- vigilar/web/templates/kiosk/ambient.html | 465 +++++++++++++++++++++++ 3 files changed, 504 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_kiosk_ambient.py create mode 100644 vigilar/web/templates/kiosk/ambient.html diff --git a/tests/unit/test_kiosk_ambient.py b/tests/unit/test_kiosk_ambient.py new file mode 100644 index 0000000..66764e4 --- /dev/null +++ b/tests/unit/test_kiosk_ambient.py @@ -0,0 +1,29 @@ +import pytest +from vigilar.config import VigilarConfig +from vigilar.web.app import create_app + + +@pytest.fixture +def kiosk_app(): + cfg = VigilarConfig() + app = create_app(cfg) + app.config["TESTING"] = True + return app + + +def test_ambient_route_exists(kiosk_app): + with kiosk_app.test_client() as c: + rv = c.get("/kiosk/ambient") + assert rv.status_code == 200 + + +def test_ambient_contains_clock(kiosk_app): + with kiosk_app.test_client() as c: + rv = c.get("/kiosk/ambient") + assert b"clock" in rv.data + + +def test_ambient_contains_hls(kiosk_app): + with kiosk_app.test_client() as c: + rv = c.get("/kiosk/ambient") + assert b"hls.min.js" in rv.data diff --git a/vigilar/web/blueprints/kiosk.py b/vigilar/web/blueprints/kiosk.py index 3eb7947..8704883 100644 --- a/vigilar/web/blueprints/kiosk.py +++ b/vigilar/web/blueprints/kiosk.py @@ -1,4 +1,4 @@ -"""Kiosk blueprint — fullscreen 2x2 grid for TV display.""" +"""Kiosk blueprint — fullscreen 2x2 grid and ambient mode.""" from flask import Blueprint, current_app, render_template @@ -10,3 +10,12 @@ def kiosk_view(): cfg = current_app.config.get("VIGILAR_CONFIG") cameras = cfg.cameras if cfg else [] return render_template("kiosk.html", cameras=cameras) + + +@kiosk_bp.route("/ambient") +def ambient_view(): + cfg = current_app.config.get("VIGILAR_CONFIG") + cameras = cfg.cameras if cfg else [] + raw_kiosk = cfg.kiosk if cfg and hasattr(cfg, "kiosk") else None + kiosk_cfg = raw_kiosk.model_dump() if raw_kiosk is not None and hasattr(raw_kiosk, "model_dump") else raw_kiosk + return render_template("kiosk/ambient.html", cameras=cameras, kiosk_config=kiosk_cfg) diff --git a/vigilar/web/templates/kiosk/ambient.html b/vigilar/web/templates/kiosk/ambient.html new file mode 100644 index 0000000..59482d6 --- /dev/null +++ b/vigilar/web/templates/kiosk/ambient.html @@ -0,0 +1,465 @@ + + + + + + + Vigilar — Ambient + + + + +
+
+
--:--
+
Loading…
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+ +
+ Loading pet status… +
+
+ + +
+
+ +
Alert
+
+
+
+ + +
+ + + + +