Skip to content

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.

Eneru browser dashboard

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_control is 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.

Status and event wording

The dashboard translates NUT status flags and Eneru event identifiers into plain language. For example, OL CHRG appears as Utility power · Battery charging, OB LB appears as Battery low · Running on battery, and POWER_RESTORED appears as Power restored. Safety states take priority, so FSD appears first as Shutdown in progress even if the UPS also reports OL or OB.

Vendor-specific status tokens are not hidden. The dashboard appends them as a custom state after any recognized status. This translation happens only in the browser: the REST API, SQLite history, and logs retain the exact NUT values for integrations and troubleshooting. The terminal UI is unchanged.

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.