2026-04-04 14:51:28 -04:00
2026-04-04 13:53:57 -04:00
2026-04-04 14:51:28 -04:00
2025-10-14 20:09:28 -04:00
2026-04-04 14:29:11 -04:00
2026-04-04 13:53:57 -04:00
2026-04-04 14:29:11 -04:00
2025-10-14 20:09:28 -04:00
2026-04-04 13:53:57 -04:00
2026-04-04 13:53:57 -04:00
2026-04-04 13:53:57 -04:00
2026-04-04 14:39:21 -04:00
2026-04-04 13:53:57 -04:00

APC UPS Dashboard

A production-ready FastAPI + Redis dashboard for monitoring multiple APC UPS devices via apcupsd NIS. Single-admin auth (argon2 + session cookies), CSRF, rate limiting, security headers, SSE live updates, 7-day history, connection-health tracking, battery degradation trends, HTML email alerts with batching and silent-hours, and Prometheus metrics.

Features

Monitoring

  • Multiple UPS managed dynamically via UI/REST (stored in Redis)
  • Real-time dashboard with SSE, fleet-overview panel, per-UPS state indicators
  • 7-day snapshot history, per-minute watts averages, daily energy + cost
  • Connection-health tracker — COMMLOST detection after 3× interval, recovery INFO alerts
  • Battery-health sampling (1/min) with 7-day trend + decline %
  • CSV export (history/events/energy), event log, alert management + ack

Alerts (email-only)

  • Severity taxonomy: CRITICAL (ONBATT/COMMLOST/RUNTIME_LOW/BCHARGE_LOW), WARNING (LOAD_HIGH/REPLACEBATT/SELFTEST_FAIL/TEMP_HIGH/XFER_BURST/VOLT_DEV), INFO (REACHABLE/LINE_RESTORED)
  • Coalesced HTML emails (one per UPS per poll cycle), severity-colored
  • 30-min per-message cooldown, silent-hours deferral (CRITICAL always delivered), retries via tenacity
  • Test-email button and optional daily summary

Security & ops

  • Single-admin auth (argon2 password, signed session cookie, double-submit CSRF)
  • SSRF host validation (rejects loopback/link-local; private IPs gated by ALLOW_PRIVATE_IPS)
  • SMTP password only from env — never persisted to Redis
  • Subprocess timeout on apcaccess (10s), rate-limiting on auth/config, security headers + CSP
  • Non-root container (UID 10001), pinned python:3.12.7-slim multi-stage build
  • /healthz, /readyz, /metrics (Prometheus), JSON structured logs with request-ID correlation
  • GitHub Actions pipeline runs ruff + pytest before building/publishing the image

Quick start (docker-compose)

cp .env.example .env
# Required in production — edit .env:
#   SESSION_SECRET=<run: python -c "import secrets; print(secrets.token_urlsafe(48))">
#   SMTP_PASSWORD=<your SMTP password, if using alerts>
docker compose up --build

Open http://localhost:10280 — the app redirects to /setup where you create the admin account on first boot. Subsequent visits require login.

Environment variables

Var Required Default Notes
SESSION_SECRET yes (prod) ephemeral 32+ random bytes, signs session cookies
ADMIN_USERNAME no admin
ADMIN_PASSWORD_HASH no unset Skip setup wizard by pre-seeding an argon2 hash
REDIS_URL no redis://redis:6379/0 Supports redis://:pw@host:port/db
SMTP_PASSWORD no unset Read only from env, never stored
ALLOW_PRIVATE_IPS no true Set false to block RFC1918 UPS hosts
TRUST_PROXY no false Enable Secure cookies + HSTS when behind HTTPS proxy
LOG_LEVEL no INFO DEBUG/INFO/WARNING/ERROR
SESSION_MAX_AGE_SECONDS no 1209600 14 days
RATE_LIMIT_ENABLED no true Disable in tests
TZ no UTC

Generate values:

# SESSION_SECRET
python -c "import secrets; print(secrets.token_urlsafe(48))"
# ADMIN_PASSWORD_HASH (optional pre-seed)
python -c "from passlib.hash import argon2; print(argon2.hash('mysecret'))"

Heads-up — $ escaping in .env: argon2 hashes look like $argon2id$v=19$m=...$<salt>$<hash>. Docker Compose treats $word as variable substitution in .env, so pasting a raw hash produces warnings (The "argon2id" variable is not set…) and the container receives a mangled, unverifiable hash — you won't be able to log in. Quoting does not help. Either double every $ ($$argon2id$$v=19$$m=...), or skip this variable entirely and use the /setup wizard (recommended).

apcupsd server requirements

Your remote APC UPS hosts must run apcupsd with the Network Information Server (NIS) enabled:

  1. Install: sudo apt-get install apcupsd
  2. Edit /etc/apcupsd/apcupsd.conf:
    UPSTYPE usb
    NISIP 0.0.0.0
    NISPORT 3551
    NETSERVER on
    
  3. Restart: sudo systemctl restart apcupsd
  4. Test: apcaccess status and nc -vz <server-ip> 3551

Development

python3.12 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt

# Run tests (uses fakeredis)
pytest tests/

# With coverage
coverage run -m pytest tests/ && coverage report

# Lint
ruff check .

# Local dev server (needs apcaccess binary + REDIS_URL env)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Persistence

Config and history live in Redis via an AOF-backed redis-data volume. To wipe state:

docker compose down && docker volume rm apcupsd-client_redis-data

API (summary)

Path Auth Notes
/ (dashboard), /config, /events, /alerts, /settings session Jinja pages
/login, /setup, /logout open/session
/healthz, /readyz, /metrics open Prom metrics on /metrics
GET /api/stream session SSE snapshots
GET /api/ups, /api/ups/{name}/{status|history|events|energy|health|battery_health} session
GET /api/ups/fleet/overview session Aggregate fleet summary
GET /api/ups/{name}/export?format=csv&kind=history|events|energy&since_days=N session Streaming CSV
GET /api/events, /api/alerts, /api/alerts/active session
POST /api/alerts/{id}/ack session+CSRF
GET/POST/PUT/DELETE /api/config/{ups,smtp,ui}/... session (+CSRF for writes) Rate-limited

License

MIT

S
Description
FastAPI + Redis dashboard for monitoring APC UPS devices via apcupsd
Readme
342 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 45.7%
JavaScript 33.4%
CSS 11.5%
HTML 7.7%
Shell 0.9%
Other 0.8%