Skip to main content

Dashboard

The Dashboard is the default landing page when accessing the admin interface. It provides a real-time overview of system health, active zones, climate issues, security status, access denials, and upcoming scheduled events.

Quick navigation: press Ctrl+K (Cmd+K on Mac) anywhere in the admin interface — or click the search icon in the top toolbar — to open the command palette. Type to jump to any admin page or straight to an entity by name (zones, devices, macros, triggers, schedules, UIs, subsystems, spaces, users). Entities open in place in an editor modal where supported; pages navigate directly.

GEM is a single server that owns every device connection and all automation; clients connect to it for real-time control:

Status Bar

A persistent status bar spans the top of the dashboard, showing key system metrics at a glance:

  • Version Badge — Current GEM version (e.g., v2.0.4359)
  • Site Mode Pill — Current effective Site Mode, color-coded by mode name (vacation = red, away = amber, sleep/night = indigo, home = green, party = purple). Click to manage modes. Hidden when no site modes are configured.
  • CPU — Current CPU load percentage
  • MEM — Memory usage percentage and free memory
  • Uptime — System uptime formatted as Xd Xh Xm
  • Clients — Number of connected UI clients (clickable, navigates to Clients page)
  • Dark Mode Toggle — Switch between light and dark admin themes (persisted in browser local storage)
  • Hostname — Server hostname (right-aligned)

Attention Pills

After the system metrics, the status bar displays color-coded attention pills that highlight items needing review:

  • Red (error) — Offline devices, missing backups
  • Amber (warning) — Access denials today, device alerts, stale backups (1-7 days)
  • Green (ok) — Backup current, or "All systems operational" when nothing needs attention

Each pill is clickable and navigates to the relevant admin page (Devices, Access Report, Device Health, or Backup).

Refresh Rates: CPU every 5 seconds, memory every 10 seconds, client count every 10 seconds, attention data every 60 seconds

Widgets

The admin dashboard is a real UI page (admin_dashboard), seeded at boot and rendered through the shared UI Page component — the same path user dashboards take. It renders an ordered set of widget tiles (six by default) that flow in a responsive grid, roughly 3 across on wide screens, 2 on medium, 1 on mobile. Each widget loads independently with its own loading spinner.

If the admin_dashboard page hasn't been seeded (an older deployment, or a fresh boot before the widget scan completes), the dashboard falls back to a built-in grid resolved from a legacy 3-tier admin_dashboard_config attribute chain (your override → site default → built-in default).

Customize Layout

Because the dashboard is a real UI page, you edit it exactly like any other. A pencil icon to the left of the dark-mode toggle opens the UI Pages editor, deep-linked to the admin dashboard (?page=admin_dashboard). From there you can:

  • Add any admin-only widget (or any end-user widget) to the page
  • Remove widgets you don't care about
  • Reorder widgets and set per-widget size and container config
  • Preview the result before saving

Changes take effect on the dashboard the next time it loads. A small dot on the customize icon indicates a legacy per-user admin_dashboard_config override is still in effect on the fallback path.

Details View

Each widget header has a details button (the three-dot icon, top-right). Click it to open that widget's full detail view in a centered modal overlay; click the close button or press Escape to dismiss it. The overlay floats above the page, so the rest of the dashboard stays in place underneath.

Most widgets render a compact summary by default — a single hero metric (e.g. "3 Offline", "All Quiet", "ALL SECURE") with one supporting line. Opening the details view reveals the full detail (per-zone lists, charts, controls).

Built-in default widgets

The widgets below are the built-in default set. Other admin-only widgets — including Monitor Tag Rollups (live pill row of every Monitor Tag), Active Zones, Climate Issues, Device Health, Upcoming Schedules, Security & Access (a combined tile rolling physical security state + access denials), Integration Health (per-protocol DriverHealth rollup), Energy & Demand (live power draw + active OpenADR events), and Orchestrator Tunnel (live status of the reverse tunnel to the cloud) — can be added from the UI Pages editor. End-user widgets registered in widget-metadata.js are also addable to the admin dashboard.

All summary tiles render through a shared SummaryHero card that tints the headline number and footer band by severity (error / warning / ok / info / neutral), so empty / healthy / degraded states are visually consistent across every widget.

Device Health

Shows disconnected devices or confirms all devices are operational.

Header Badge:

  • X Offline (red) — When devices are disconnected
  • All Online (green) — When all enabled devices are connected

All Online State: Displays a checkmark icon with "All devices operational" message

Offline Devices List: Each disconnected device shows:

  • Red status dot
  • Device name
  • IP address and device ID
  • Reload button — Re-runs the device's connect() driver method without leaving the dashboard. The icon spins while the reload is in flight and a toast confirms success or failure.
  • Settings button — Navigates to the Devices admin page filtered to that device

Refresh Rate: Every 10 seconds


Climate Issues

Shows climate/HVAC zones whose current temperature has drifted from setpoint in the wrong direction for their system mode. Routine HVAC cycle swing is filtered out using a 2° tolerance.

Header Badge:

  • ON TARGET (green) — All climate zones within tolerance
  • X OFF (red) — Number of zones outside tolerance

Empty State: "All zones at temperature"

Issue Detection by Mode:

  • Cool — Flags when temperature exceeds setpoint by more than 2°
  • Heat — Flags when temperature falls below setpoint by more than 2°
  • Auto — Uses dual setpoints (heat_setpoint / cool_setpoint) when available, otherwise falls back to a single setpoint as both bounds
  • Off — Skipped entirely

Each Issue Row Shows:

  • Mode glyph (fire icon for too hot, water icon for too cold) with red/blue accent
  • Zone name
  • Mode pill (COOLING or HEATING)
  • Target setpoint and humidity (if available)
  • Current temperature with delta from setpoint (e.g., +3.2°)

Worst offenders sort to the top. Click a row to jump to that zone in the Zones page.

Refresh Rate: Every 30 seconds


Security & Access

Combined two-section widget. The top half mirrors physical security state (unlocked doors, open gates, disarmed security) — derived live from the same shared activeZones store the Zones of Interest widget publishes — and the bottom half lists the 10 most recent denied access attempts within the last 24 hours.

Header Badges:

  • SECURE (green) or X OPEN (red) — physical security summary
  • X DENIED (red, clickable) — denial count, opens Access Report

Physical Security section:

  • ALL SECURE stamp with "All doors locked, gates closed, security armed" plus today's access-event count when no items need attention
  • Otherwise: alert banner + per-row tiles for every unlocked/open/disarmed zone, click to jump to the zone

Access Denials section:

  • No denials in the last 24h empty state
  • Otherwise: pulsing red rows showing username, device, access type chip (CARD / PIN / FACE), denial reason, and time ago. Click a row to open it in the Access Report.

Refresh Rate: Denials poll every 15 seconds; security state updates live via the shared zone subscription set.


Zones of Interest

Shows every enabled zone currently in a non-rest state, grouped by subsystem. The widget uses live socket subscriptions for in-flight attribute changes (capped at 50 concurrently subscribed zones) so tiles update immediately without waiting for the next poll.

Header Badge: X Active — Total active zone count

Empty State: "All quiet — nothing active"

Activity Detection:

SubsystemActive When
lights, fans, fire, waterstate = on (or level > 0)
shadesstate = open or position > 0 && < 100
gates, garages, liftsstate = open
doors, locksstate = unlocked
securitystate = disarmed
powerstate = off (inverted — surfaces tripped or unplugged outlets)
AVsource > 0 (shows source device name)

Subsystems cameras, climate, and location are excluded.

Group Headers: Each subsystem group shows an icon, label, and active count. Alert-style subsystems (doors, locks, gates, garages, security, power) sort to the top.

Tile Decorators:

  • Lights — Circular dial showing level percentage
  • Shades — Vertical fill bar showing position
  • Doors / Locks — Pulsing open-lock glyph
  • Gates / Garages — Pulsing subsystem glyph
  • Security — Pulsing shield-broken glyph (amber)
  • Power — Pulsing power-off glyph (red)
  • Fans — Spinning fan glyph
  • Other — Static subsystem glyph in accent color

Each tile shows zone name, current state (level %, position %, source name, or state), and time since the change in the corner. Click a tile to open that zone's live control inline in a modal (the same control component the zone renders in user UIs — dimmer, climate, gate, lock, etc.) so you can act on the zone without leaving the dashboard.

AV Quick-Off: AV tiles whose source is currently > 0 show a small power button. Click it to send source: 0 to the zone without leaving the dashboard.

Refresh Rate: Every 30 seconds for full re-evaluation; live socket subscriptions handle mid-interval changes for visible zones.


Upcoming Events

Shows the next 5 enabled macro schedules sorted by upcoming run time.

Header Badge: X Scheduled — Number of upcoming schedules shown

Empty State: "No scheduled events"

Schedule Timeline: Each entry shows:

  • Time — Next run time (hour:minute format), or "Variable" for unresolved variable-based schedules
  • Name — Schedule name
  • Date — Next run date, or the raw rule expression for variable schedules

Variable Schedule Resolution: Schedules using variable references (e.g., [$sunrise-15]) are resolved by looking up the current variable value and applying any offset in minutes.

Click Action: Clicking a schedule navigates to the Macro Schedules admin page filtered to that schedule.

Refresh: Loaded once on page load


Alarm Feed

Live feed of open alarms from the alarm center — active and latched-unacknowledged events with severity, message, and source. Shelved alarms are hidden from the feed.

Header Badge:

  • CLEAR (green) — no open alarms
  • X UNACKED or X OPEN (red) — open alarms needing attention

The summary tile shows the open count tinted by severity; the details view lists each alarm with a per-alarm acknowledge button. Settings: minimum severity filter, only-unacknowledged filter, max rows, and an Allow Acknowledge toggle.

Refresh: Live (alarm event subscription only)


Monitor Tag Rollups

Optional widget — add from the UI Pages editor. Renders a live pill row of every configured Monitor Tag with rollup state color-coded green/red/amber/grey, plus a dashed "inert" treatment for tags that aren't yet configured. Subscribes to live monitor_tag_state_change events so the row stays in sync without polling.

The tile header shows X DOWN or ALL UP depending on the current rollup set. Click any pill to jump to the Monitoring page. See Monitoring — Tags Tab for the underlying model.


Integration Health

Optional widget — add from the UI Pages editor. Per-protocol rollup of every device grouped by driver (BACnet, Modbus, OPC UA, KNX, MQTT, Lutron, etc.) with online/offline counts and DriverHealth telemetry pulled live off each device's health.getSnapshot() (see Commercial Protocol Stack and the per-driver pages for what DriverHealth surfaces).

Severity per row:

  • Red dot — at least one device in that protocol is offline
  • Amber dot — consecutive operation failures have reached a driver's threshold, or the transport client has recycled since boot
  • Green dot — fully healthy

Header Badge: X alerts (red), X warn (amber), or All Healthy (green) when nothing needs attention.

Per Row Shows:

  • Driver display_name (from each driver's getMetaData()) and device count
  • Recent client-recycle count when DriverHealth is supported
  • Online / offline chip counts

Click a row to jump to the Devices page filtered to that driver.

Details view appends a per-driver DriverHealth detail table — device, online state, consecutive failures vs. threshold, last-success age, client recycles, and the last transport-level error message — for every protocol that exposes DriverHealth. Drivers without it still appear in the rollup with just online/offline counts.

Refresh Rate: Configurable via refresh_ms (default 15 seconds).


Energy & Demand

Optional widget — add from the UI Pages editor. Live power-draw rollup across the device fleet plus a banner for any active OpenADR demand-response events.

Power Rollup: Power devices are discovered generically through the attribute registry — the same metric-role path the Power Flow widget uses. Any driver tagged with power metric roles (Tesla Powerwall, Enphase, Fronius, SolarEdge, Sense, Cordex and others) contributes its live load (site demand) automatically, with no per-driver wiring here; when a driver doesn't report load directly it's derived from grid + solar + battery. All values are normalized to kW.

The hero shows total kW, the body lists every reporting device with its kW contribution, and negative values render green to mark exports / generation. Click a source row to jump to that device.

Demand-Response Banner: Whenever any openadr2 device is tracking a demand-response event — including a scheduled event that has not reached its start time yet — a pulsing color-graded banner (amber → red → purple as signal_level rises through 1 → 2 → 3) appears above the source list. Shows event count, end time, and an opted-out marker. Click to open the OpenADR admin page. See OpenADR.

Header Badge:

  • DR ACTIVE (red) — at least one OpenADR event is in progress
  • X kW (blue) — current total demand
  • No meters (grey) — nothing reporting power

Summary Severity: error/warning when a DR event is active (severity follows the highest signal level), info otherwise, neutral when no meters report.

Refresh Rate: Configurable via refresh_ms (default 10 seconds).


Orchestrator Tunnel

Optional widget — add from the UI Pages editor. Live status of the reverse tunnel that gives the cloud orchestrator remote access to this server. When the tunnel is enabled (the tunnel setting in gem.json), GEM makes a secure outbound connection — no inbound firewall ports are opened — and this widget surfaces whether that connection is currently up.

Header Badge / Hero State:

  • Connected (green) — tunnel is authenticated to the orchestrator
  • Connecting / Reconnecting / Waiting (amber/info) — bound but not yet connected (waiting on a binding, dialing, or backing off between attempts)
  • Disabled (grey) — the tunnel is turned off
  • Identity Fail (red) — the orchestrator failed the pinned-identity check; the tunnel refuses to serve and stops reconnecting

Detail rows (details view):

  • Enabled — whether the tunnel is on, with the disable reason when off
  • Orchestrator URL and Tenant — the bound destination
  • Site / Install ID — when present
  • Identity pinned — whether the orchestrator's public key is pinned (no (unpinned) is flagged amber while running)
  • Active streams — proxied requests currently in flight
  • Last connected / Last disconnected — age of the most recent transport events
  • Reconnect in — backoff delay before the next attempt while disconnected
  • Config source — whether the active config came from gem.json or the tunnel system attribute
  • Last error / Reconcile error — most recent transport or lifecycle error
Enable or disable without a restart

The tunnel can be turned on or off at runtime by setting a tunnel system attribute (a JSON value such as {"enabled": true} or {"enabled": false}). This overrides the gem.json tunnel setting and takes effect immediately — no restart needed. The widget links straight to the System Attributes grid to add or edit it. The attribute replaces the whole gem.json tunnel object, so include every field you need (it may also carry a bootstrap url / tenant_id).

Refresh Rate: Configurable via refresh_ms (default 15 seconds).


Auto-Refresh Behavior

All intervals are automatically stopped when navigating away from the dashboard and cleaned up when the component is destroyed. Live zone subscriptions are also unsubscribed on unmount. Intervals only run while the browser URL is on /admin or /admin/index.

Widget / MetricRefresh Interval
CPU Load5 seconds
Memory10 seconds
Connected Clients10 seconds
Dashboard Report60 seconds
Security & Access (denials half)15 seconds
Device Health10 seconds
Zones of Interest30 seconds (+ live socket updates)
Climate Issues30 seconds
Site Mode30 seconds
Upcoming Events60 seconds
Alarm FeedLive (subscription only)
Monitor Tag RollupsLive (subscription only)
Integration Health15 seconds (configurable)
Energy & Demand10 seconds (configurable)
Orchestrator Tunnel15 seconds (configurable)

Per-widget refresh intervals are tunable in each widget's config block from the UI Pages editor — for example, setting refresh_ms to 60000 on the Active Zones widget slows its backstop poll without affecting live subscriptions.

AI Assistant

The admin header includes an AI Assistant button (gem icon) that opens a slide-out drawer on the right side of the screen. The AI assistant can:

  • Configure devices, zones, rooms, and automations using natural language
  • Run system diagnostics to find disconnected devices, orphan zones, and empty macros
  • Create triggers and schedules
  • Bulk-create many Triggers in one atomic call ("alert me whenever ANY door opens after 10pm", "email me if any thermostat goes above 80") — pre-validates every entry and rolls back the batch if any insert fails
  • Create user accounts with roles, notification profile, sites, and initial credentials in a single confirmation
  • Bulk-commission many devices in one atomic call (driver, IP, port, credentials, and any extra attributes per entry — rolled back as a unit if any insert fails)
  • Set the same Attribute on many targets in one atomic call ("disable history on every battery_level attribute", "set timezone=America/Chicago on all clients", "remove the deprecated old_address attribute everywhere") — scope by explicit id list or a server-side filter (driver / subsystem / name / label / enabled), capped at 200 targets, supports dry_run to preview the resolved set, and rolls back every prior write if any single target fails
  • Clone an entire UI — duplicates the ui row plus every ui_zone, ui_control, ui_macro, and ui_page_ui link in one atomic call (optionally deep-copying ui_page rows when the new UI needs to diverge layout-wise)
  • Clone a macro and its steps under one or many new names ("clone the goodnight macro for every kid's room") — deep-copies every macro_step verbatim, atomic rollback if any clone or step copy fails, hard-capped at 25 clones per call
  • Answer "what's coming up?" / "what's scheduled today?" — lists the next macro_schedule fires inside a configurable window (default 24h, max 168h), parsed from each schedule's cron rule (sunset/sunrise variable rules are skipped)
  • Navigate to any admin page
  • Undo recent changes (update/attribute mutations are reversible — bulk creators like users, triggers, AV zones, notification profiles, UI zone wiring, UI clones, and bulk device creation require manual cleanup)

The assistant shows context-aware quick action buttons that change based on the current admin section. For example, on the System page you'll see "Find Offline" and "Configure Selected", while on the Automation page you'll see "Create Scene" and "Add Trigger".

note

The AI Assistant requires an Anthropic API key configured as the anthropic_api_key system attribute. The fastest way to enable it on a fresh install is the Setup page — when AI is unavailable, the AI Guided Setup card flips into an inline form that accepts your sk-ant-... key, validates it against /v1/messages with a 1-token probe, stores it as a secure system attribute, flips ai.enabled=true in gem.json (atomic temp+rename), and re-initializes the assistant without a server restart. If a key is already on file but the assistant is disabled (e.g. set during install with ai.enabled left false), the card instead shows the masked key (sk-ant-••••••••XXXX) with a one-click Enable AI Assistant button plus a Use a different key option. See Attributes to add or change the key later.

Responsive Layout

Screen WidthGrid Layout
> 1200px3 columns, 2 rows
768px - 1200px2 columns, 3 rows
< 768px1 column, scrollable