# web ## Purpose Flask application that exposes everything to humans: camera grid and single-camera views, event timeline, recordings browser, sensor dashboard, system/arm controls, kiosk mode, and the pets / visitors / wildlife journals. The web process runs as one `SubsystemProcess` supervised by `vigilar/main.py`, reads state directly from SQLite, and publishes a handful of config/control MQTT messages on behalf of the user. ## Key files - `vigilar/web/app.py` — `create_app(cfg)` factory (note: `vigilar/web/__init__.py` is empty; the factory lives here) - `vigilar/web/blueprints/cameras.py` — HLS streams, snapshots, per-camera view - `vigilar/web/blueprints/events.py` — event log list, acknowledgement, and `text/event-stream` SSE endpoint for live updates - `vigilar/web/blueprints/recordings.py` — browse, download, delete recordings; serves decrypted `.vge` content - `vigilar/web/blueprints/sensors.py` — sensor status and history - `vigilar/web/blueprints/system.py` — health, arm/disarm, UPS, admin settings, Web Push subscription registration, PIN management; publishes `vigilar/camera/{id}/config` for threshold updates - `vigilar/web/blueprints/kiosk.py` — fullscreen 2x2 ambient grid - `vigilar/web/blueprints/pets.py` — pet registration, sightings, labeling, training-image upload - `vigilar/web/blueprints/visitors.py` — face profiles, visits, labeling, privacy controls - `vigilar/web/blueprints/wildlife.py` — wildlife sightings journal, stats, CSV export ## MQTT topics **Subscribes:** none. The web process does not subscribe to the bus at import time; the SSE endpoint accumulates messages in an in-process queue but nothing wires the MQTT bus into that queue today (see Notes). **Publishes:** - `vigilar/camera/{camera_id}/config` — runtime camera config updates (sensitivity / motion thresholds) from the system settings page ## Database tables All access is read-mostly via `vigilar.storage.queries`. Touches essentially every table: `cameras`, `events`, `recordings`, `sensors`/`sensor_states`, `system_events`, `arm_state_log`, `alert_log`, `push_subscriptions`, `pets`/`pet_sightings`/`pet_training_images`/`pet_rules`, `face_profiles`/`face_embeddings`/`visits`, `wildlife_sightings`, `timelapse_schedules`. ## Depends on - `storage` — engine passed via `app.config["DB_ENGINE"]` / `VIGILAR_CONFIG` - `camera` — reads HLS segments from `cfg.system.hls_dir` and decrypts recordings via `vigilar.storage.encryption` - `alerts` — uses `vigilar.alerts.pin.hash_pin`/`verify_pin` and manages `push_subscriptions` - `config_writer` — persists camera/alert config changes back to TOML ## Consumed by - Browsers and phones — HTML dashboard, PWA with Web Push (VAPID), HLS grid via `hls.js`, MJPEG fallback for single-camera low-latency view, SSE for the live event timeline ## Notes Templates use Jinja2 with a Bootstrap 5 dark theme; the camera grid uses `hls.js` for multi-camera HLS playback with an MJPEG fallback on the single-camera page. The app is a PWA (service worker + manifest) with VAPID Web Push for mobile notifications. The event-timeline SSE endpoint lives around line 93 of `blueprints/events.py` and holds a per-client `queue.Queue` fed by a module-level `broadcast_sse_event` function. A dedicated MQTT → SSE bridge (`vigilar/web/sse_bridge.py`) runs inside the web process, subscribes to `Topics.EVENTS_PUBLISHED`, and calls `broadcast_sse_event` for every classified event emitted by the events subsystem — so the in-browser event timeline updates live without a page refresh.