Files
nicholas a9a9ea086d
Deploy Dashboard / gitleaks (push) Successful in 3s
Deploy Dashboard / build (push) Successful in 6s
Deploy Dashboard / deploy (push) Successful in 9s
Deploy master automatically after Gitleaks scan
2026-10-07 08:52:03 -05:00

5.9 KiB

Household dashboard

Read-only household display at https://dashboard.uuard.com, available on the LAN/VPN through Traefik's allow-local middleware. Home Assistant and its household dashboard remain available at their original URL. The wall display now opens this dashboard on startup.

The plain HTML/JavaScript page shows the same seven-day rolling schedule (today plus six days), enlarged 12-hour clock, 12 hourly weather slots, and the Next task list. It has no buttons, links, forms, checkboxes, or editing actions. Calendar events include recurrence expansion, all-day and multi-day events, locations, overlapping events, past-event dimming, and a current-time line in America/Chicago.

A small Python server reads the five calendars and four task lists directly from Stalwart using the existing read-only dashboard@uuard.com account. Weather uses Home Assistant's existing weather.forecast_home entity via a read-only forecast subscription. Each calendar, task list, and weather feed refreshes independently every five minutes, with bounded requests and backoff after errors. The display polls once a minute, retains the last successful data, and shows which sources failed or became stale. Crowded lists and calendar details rotate automatically every 20 seconds. Credentials never reach the browser. Only fixed static files and GET snapshot/health routes are served.

Themes

Skylight is the default theme, based on the official Skylight Home/Schedule references: light panels, pale blue rail, serif date/time headings, pastel events, and orange current-day/time markers. Read-only calendar, weather and Next tasks remain visible. Its styling lives in static/skylight.css; the original Home Assistant styling remains in static/style.css.

Use https://dashboard.uuard.com/?theme=homeassistant for the original dark theme, or ?theme=skylight for the default. Unknown themes fall back to Skylight. Theme selection uses the URL so the wall display remains free of interactive controls. Both themes show only Next; the backend retains the existing source integration.

Reference: https://skylight.zendesk.com/hc/en-us/articles/49738702477723-The-Home-Screen

Run on ward-vm-apps-02

The existing Ansible SOPS secret-rendering flow provisions /etc/home-ops/household-dashboard.json before Compose starts. Its encrypted source is the matching secret_files entry in secrets/sops/production.sops.yml. The file is owned by root, readable by the container's group, and mounted read-only.

Once the secret has been rendered:

docker compose up -d --build

The file contains the existing read-only Stalwart dashboard account's credentials and a dedicated Home Assistant long-lived access token. The token belongs to Household dashboard service, a local-only account in system-read-only, with no interactive login credentials. It has read permission and no entity control permission; it is independent of the original dashboard user's browser sessions. The frontend receives household display data only, never passwords or tokens. No HA configuration changes or restart are required. The app runs as UID 65534 and exposes port 8124. Traefik applies TLS and local-network restrictions and injects a shared proxy credential; direct requests for household data are rejected. The proxy credential is also stored in SOPS and rendered into the edge stack environment. Credentials must be rendered on both hosts before recreating their services.

Deployment ownership

The dashboard app, apps-02 Compose inventory entry, edge DNS record, Traefik route, proxy environment variable, encrypted credentials, and wall-display startup URL must all be included in the deployed revision. Ansible intentionally stashes host checkout changes, including untracked files, before checking out that revision; manual host edits are temporary and will disappear on the next deployment.

This repository owns the standalone application. Production infrastructure and SOPS credentials live in nicholas/home-ops; its deployment references the published image in stacks/apps/automation/household-dashboard/compose.yaml. Application source and releases are owned here; home-ops no longer builds its duplicate app copy.

Master pushes run Gitleaks, build a Docker image tagged with the commit SHA, push it to the Gitea registry, and deploy to apps-02. Pull requests run Gitleaks only. There are no automated test or lint gates. Local tests remain available below.

Deployment uses a repository-scoped runner and a restricted SSH key. The host command serializes deployment, pulls the exact image, waits up to two minutes for readiness, and restores the previous image on failure. The selected image is stored in an ignored Compose .env file, so routine home-ops deployments retain it. Registry and SSH credentials are Gitea Actions secrets backed up in SOPS; calendar/weather credentials stay on the host and never reach the workflow. Infrastructure changes still deploy through home-ops.

The host inventory includes this Compose project. Routine source updates require docker compose up -d --build to rebuild the application image.

Gunicorn serves the app with one worker and four request threads. The container has a read-only filesystem and resource limits; runtime dependencies are pinned in requirements.lock. /health/live checks the process only, while authenticated /health reports readiness based on required source freshness. Upstream failures do not make process liveness fail. The cache is in memory.

The browser bounds fetch time, backs off on failure, refreshes at midnight, and reloads automatically when the application version changes.

Local development and checks

python3 -m venv .venv
.venv/bin/pip install -r requirements.lock
.venv/bin/python -m unittest discover -s tests
npm test

Use the example in config.example.json to understand the server configuration. Supply actual credentials through the existing SOPS-managed file; never commit credential files or use frontend environment variables for secrets.