Web dashboard¶
Eneru ships a small browser dashboard served by the embedded API server. There is no external service, build toolchain, or third-party JavaScript. It is a thin client over the REST API: every value it shows comes from an endpoint, and all logic stays server-side.
Enabling it¶
The dashboard is served automatically whenever the API is enabled. There is no separate switch:
api:
enabled: true
bind: "127.0.0.1" # expose only where you trust the network, or enable auth
port: 9191
Open http://<host>:9191/ in a browser.
What it shows¶
- UPS status cards: status badge, battery charge (with a threshold-colored
bar), runtime, and load, from
/api/v1/ups. Click a card to open a detail panel with live status, power quality (input/output/battery voltage, frequencies, temperature), the UPS's configuration, its redundancy-group membership, and remote-health for that source. The detail panel reads a shared config + remote-health snapshot taken once per refresh, so opening it costs no extra requests. - Redundancy groups: a healthy/required rollup (how many member UPSes are currently healthy vs the quorum target), when configured.
- On-battery / shutdown banner: driven by live UPS and redundancy status (not stale events), so it appears when a UPS goes on battery or shutdown is imminent and clears as soon as power returns.
- History graphs: hand-rolled SVG line charts for battery charge, load,
runtime, and input voltage, from
/api/v1/ups/{name}/history, with a range selector (1 hour → 1 year, or All). Charts scale to the panel width and redraw on resize. - Event timeline: power/diagnostic/lifecycle events from
/api/v1/events, with filters for source, event type, and detail text, a range selector, and a Load older button that pages further back through the full retained history. - Delete events: when signed in, select events with the row checkboxes and use Delete selected to remove them (auth-gated; the server enforces it). Only currently-visible selected rows are deleted.
- Control panel: command buttons and writable-variable forms, shown only
when you are signed in and
nut_controlis enabled. The controls reflect the configured command/variable allowlists; the server enforces them regardless of what the UI renders.
The page polls every 10 seconds.
Theme¶
A Theme switcher in the header offers System / Light / Dark, persisted in
the browser's localStorage. The default is System, which follows the OS
light/dark preference with no flash (it's pure CSS); choosing Light or Dark pins
the theme regardless of the OS setting.
Authentication¶
When authentication is enabled, use Sign in to log in
with a local user. The dashboard stores the returned session token in the
browser's sessionStorage and sends it as a Bearer header. There is no
cookie, so there is no CSRF surface. Read views follow the tiered policy (open
unless api.auth.require_for_reads); control actions always require sign-in.
The Sign in button appears whenever auth is enabled — the dashboard re-checks
/api/v1/config on every refresh, so it shows up on its own once auth becomes
active. When auth is off there is nothing to sign into. If a login fails, the
dashboard shows the server's actual reason. Creating a user with eneru user
create auto-enables auth
within seconds, no restart, so signing in works without hand-editing the config.
Security¶
The HTML is served with a strict Content-Security-Policy (default-src
'self') and X-Content-Type-Options: nosniff. Only the packaged asset names are
servable, so path traversal is not possible. The dashboard assets themselves
contain no secrets. They are static files; sensitive data only ever flows
through the authenticated API.