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.