A real-time, interactive terminal dashboard for Frigate NVR/CCTV hosts — htop for your Frigate box. Stdlib-only Python, one file, no required dependencies.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
JD Bungart 21a7dc8fc8
All checks were successful
ci / battery (push) Successful in 18s
release / release (push) Successful in 29s
release: v1.4.2 — header clock (RFC 5322), swap used/total, dynamic field reflow
2026-07-19 08:00:01 +08:00
.forgejo/workflows test: add exhaustive unittest suite (114 tests) and wire into CI 2026-07-19 03:31:45 +08:00
doc docs: update terminal screenshot (healthy state, 5m history) 2026-07-19 07:52:30 +08:00
frigate_top release: v1.4.2 — header clock (RFC 5322), swap used/total, dynamic field reflow 2026-07-19 08:00:01 +08:00
img/badges release: v1.1.0 — SPDX source header, README badges + restyle 2026-07-14 01:49:55 +08:00
scripts build: single-file frigate-top.pyz via zipapp 2026-07-19 01:10:33 +08:00
tests fix(header): reflow fields dynamically instead of dropping them 2026-07-19 07:59:39 +08:00
.gitignore Initial release: frigate-top v1.0.0 2026-07-13 05:07:47 +08:00
CHANGELOG.md release: v1.4.2 — header clock (RFC 5322), swap used/total, dynamic field reflow 2026-07-19 08:00:01 +08:00
CONTRIBUTING.md test: cover graph rendering via a fake curses screen (14 tests, 128 total) 2026-07-19 03:54:52 +08:00
DISCLAIMER.md Initial release: frigate-top v1.0.0 2026-07-13 05:07:47 +08:00
LICENSE Initial release: frigate-top v1.0.0 2026-07-13 05:07:47 +08:00
NOTICE Initial release: frigate-top v1.0.0 2026-07-13 05:07:47 +08:00
pyproject.toml feat(web): basic-auth gated web frontend (auto-refreshing SVG) 2026-07-19 07:29:00 +08:00
README.md docs(readme): terminal and web screenshots side-by-side as the hero image 2026-07-19 07:49:09 +08:00
requirements.in Initial release: frigate-top v1.0.0 2026-07-13 05:07:47 +08:00
requirements.txt Initial release: frigate-top v1.0.0 2026-07-13 05:07:47 +08:00
VERSION.md release: v1.4.2 — header clock (RFC 5322), swap used/total, dynamic field reflow 2026-07-19 08:00:01 +08:00

frigate-top

htop for your Frigate NVR box — a real-time terminal dashboard for Frigate CCTV hosts

CI License: Apache 2.0 Python 3.7+ deps: zero · stdlib platform: Linux

frigate-top is a real-time, interactive terminal dashboard for Frigate NVR/CCTV hosts — htop for your Frigate box. It shows host-level metrics (CPU, memory, disk I/O, network I/O + errors, NVIDIA GPU, CPU iowait, load) and Frigate-specific metrics (skipped frames, detector inference time, detection FPS, camera FPS, online/offline cameras, estimated supported cameras) across a grid of block-bar graphs (one GPU set per detected GPU). Each graph fills the full width over the configured timeframe, carries a -span … now time axis, colours its fill and current value green/amber/red by warn/crit thresholds, and marks the newest sample with a bright endpoint.

It is stdlib-only Python shipped as a single .pyz file — no pip, no required dependencies, no build step. The same dashboard can also be served in a browser with --web: an auto-refreshing SVG behind HTTP Basic auth, rendered by the exact same drawing code.

frigate-top in the terminal — host and Frigate metrics across a grid of block-bar graphs frigate-top in the browser — the same dashboard served as SVG with --web

The same dashboard, twice: in the terminal (left) and served in a browser with --web (right) — rendered by the same drawing code.

What it does

  • Draws a live, auto-refreshing dashboard grouped into SYSTEM / NETWORK / FRIGATE bands with colour-coded graph borders (blue = system, yellow = network, orange = Frigate).
  • Shows a header table grouped into identity (hostname, uptime, OS, kernel), hardware (CPU, memory, GPU model + driver), Frigate (version, camera counts, supported-camera estimate, detector), and streams/storage (resolution, retention, interface, disk) — plus a status line of active warnings.
  • Summarises tracked-object activity for the selected timeframe in the header (Detections 1m 4 person 2 vehicle 1 animal 2 face 1 plate), pulled from /api/events — a fixed category breakdown (person / vehicle / animal / recognised face / recognised plate, plus other) so the structure is visible even at zero. The API is polled at most every few seconds and cached.
  • Collapsible sections — press s / w / f / g / t to fold the System / Network / Frigate / GPU / Thermal groups; everything except Frigate starts collapsed so it fits smaller terminals. Handles live terminal resizing without dropping graphs.
  • Auto-fit (--auto-fit) expands as many sections as fit the terminal, in a fixed priority order (Frigate → System → GPU → Thermal → Network), recomputed on resize; it yields to manual control the moment you toggle a section. When you'd rather expand everything, the graph area scrolls (/, PgUp/PgDn, Home/End) with the header pinned, instead of clipping graphs off the bottom.
  • Detects multiple GPUs and draws a labelled GPU0 / GPU1 … util/mem/temp set for each, in their own GPU band (shown only when a GPU is present).
  • Auto-detects thermal sensors (AMD k10temp, Intel coretemp/PCH, ARM, and super-I/O boards) and draws a THERMAL band with CPU temperature, system/board temperature, and one graph per active fan — read from /sys/class/hwmon with no dependency, falling back to lm-sensors when needed. The first spinning fan is labelled CPU, the rest SYS1 / SYS2 …; rename them with --fan-labels. Chassis-intrusion reporting is opt-in via --chassis-intrusion (many boards latch a spurious alarm).
  • Graphs Frigate skipped frames and NET errors in red on any nonzero value — these should always be zero, so they catch the eye immediately.
  • Renders block-bar graphs by default (--braille for dense Braille area charts, --ascii for plain ASCII). Fills always span the full width and scroll left over the true timeframe; the x-axis reports how much history is currently shown.
  • Encodes health in colour: bars and the title value turn amber past the warn threshold and red past crit, with a dashed guide line at each threshold and a ▲/▼/→ trend arrow versus the window start.
  • Marks absent sensors (no GPU, no Frigate) with a dimmed no sensor panel instead of a misleading flat line.
  • Estimates how many cameras the detector(s) can keep up with — a throughput bound (detector inferences/sec × headroom ÷ configured detect fps), which is the real limit on a Frigate box. It is motion-independent (uses the configured detect fps, not the noisy live rate); a single Coral at 5 fps works out to ~18 cameras. Tune the safety margin with --detector-headroom (default 0.75).
  • Auto-detects the Frigate API at common ports (5000, 8971, 80, 443) and protocols (HTTP/HTTPS) (override with --frigate-url); tracks disk and the busiest network interface (pin with --disk / --net-iface).
  • Lets you switch history timeframe on the fly (1, 5, p for 15m, c for custom) and export samples to CSV (--csv).
  • Graphs the things an NVR actually cares about: Disk usage % and Frigate Recordings % (a filling disk is the classic failure), NVMe drive temperatures, GPU encoder/decoder utilization (Frigate's ffmpeg hwaccel), Frigate's own CPU/memory footprint, and host Swap %.
  • Per-box settings file — an INI config (--settings, $FRIGATE_TOP_CONFIG, ~/.config/frigate-top/config.ini, /etc/frigate-top/config.ini) with precedence CLI env file default; --write-config prints a commented starter. Every threshold, fan label, and toggle lives here.
  • Health-check mode (--check) samples once and exits non-zero on any critical condition — drop it in cron / systemd / Uptime-Kuma. --fahrenheit shows °F.
  • Aggregates all network interfaces by default (pin one with --net-iface); the NETWORK header shows which.

Install

frigate-top is stdlib-only, so the simplest install is a single self-contained file — a zipapp bundle you drop on PATH: no pip, no dependencies, and it sidesteps the externally-managed-environment (PEP 668) error on modern Debian/Ubuntu entirely.

Single file — recommended (no pip, no dependencies):

# fetch the zipapp bundle from the latest release and drop it on PATH
sudo curl -fsSL https://git.jdneer.com/jd/frigate-top/releases/download/latest/frigate-top.pyz \
  -o /usr/local/bin/frigate-top
sudo chmod +x /usr/local/bin/frigate-top
frigate-top

It's one file — Python 3.7+ and the standard library only, no install step. (Optional: apt install python3-yaml for richer Frigate config parsing — a regex fallback is used without it.) Run it as frigate-top (installed), python3 frigate-top.pyz, or python3 -m frigate_top from a checkout.

Note: as of v1.3.0 frigate-top is a package, so there's no single frigate_top.py to download anymore. The single-file install is the frigate-top.pyz zipapp attached to each release; the download/latest/ URL above always resolves to the newest one.

Build & copy it yourself (e.g. to deploy a specific commit to a host):

python3 scripts/build_pyz.py                        # -> dist/frigate-top.pyz
scp dist/frigate-top.pyz myhost:~/                  # copy over
ssh myhost 'sudo install -m 0755 ~/frigate-top.pyz /usr/local/bin/frigate-top'

As a managed command with pipx (isolated venv, PEP 668-friendly):

sudo apt install pipx          # if not already present
PIP_INDEX_URL=https://git.jdneer.com/api/packages/jd/pypi/simple/ pipx install frigate-top

From the Forgejo package registry with pip:

pip install --index-url https://git.jdneer.com/api/packages/jd/pypi/simple/ frigate-top
# optional extra for richer YAML config parsing:
pip install --index-url https://git.jdneer.com/api/packages/jd/pypi/simple/ 'frigate-top[yaml]'

On a PEP 668 system (recent Debian/Ubuntu), pip won't touch the system Python — use the single file or pipx above, install into a python3 -m venv first, or append --break-system-packages if you accept the consequences.

From source:

git clone https://git.jdneer.com/jd/frigate-top.git
cd frigate-top
pip install .          # or: pip install '.[yaml]'

Usage

Run frigate-top (installed or the .pyz on PATH) or python3 -m frigate_top (from a source checkout).

Controls (keyboard):

  • m — toggle memory graph between percentage and bytes (used / cached / free)
  • n — toggle network graphs between Mbps and MB/s
  • s / w / f / g / t — collapse or expand the System / Network / Frigate / GPU / Thermal sections (only Frigate starts expanded)
  • 1 / 5 / p — set history to 1 / 5 / 15 minutes
  • c — set a custom history timeframe (in minutes)
  • / (or j / k), PgUp / PgDn, Home / End — scroll the graph area when it overflows the terminal
  • q — quit

Examples:

# Default 1-minute history, 1-second refresh
frigate-top

# 10-minute history
frigate-top --timeframe 10

# Point to a Frigate API and config
frigate-top --frigate-url http://localhost:5000 --config /config/config.yml

# Pin a specific network interface / disk device
frigate-top --net-iface eth0 --disk nvme0n1

# Write samples to CSV
frigate-top --csv /tmp/frigate-top.csv

# Dense Braille area charts, or plain ASCII with no colour
frigate-top --braille
frigate-top --ascii --no-color

# One-off snapshot for cron or health checks (works headless)
frigate-top --snapshot

# Thermal: name the fans to match the case and enable intrusion reporting
frigate-top --fan-labels "CPU,Rear,Top" --chassis-intrusion

# Generate a starter settings file, then edit it
mkdir -p ~/.config/frigate-top
frigate-top --write-config > ~/.config/frigate-top/config.ini

# Health check for cron / systemd / Uptime-Kuma (exit code != 0 on any CRIT)
frigate-top --check

# Fahrenheit, and pin one interface instead of the aggregate
frigate-top --fahrenheit --net-iface eth0

# Serve the same dashboard in a browser, behind Basic auth
frigate-top --web --web-port 8080 --web-user admin --web-password s3cret

Web frontend

--web serves the exact same dashboard as an auto-refreshing SVG over HTTP — no terminal needed — behind HTTP Basic auth. It renders the curses drawing code to an off-screen surface, so the browser view matches the TUI (same graphs, colours, and layout), with every section expanded and the page scrolling in the browser. See the side-by-side with the TUI at the top of this page.

# Localhost only, on port 8080, with a fixed password
frigate-top --web --web-password s3cret

# If you omit --web-password, a random one is generated and printed at startup:
#   frigate-top 1.3.0 web UI:  http://localhost:8080/
#     user:     admin
#     password: kJ3f-Qa9tZ2   (generated — set --web-password or $WEB_PASSWORD to pin it)
  • Binds to 127.0.0.1 by default. --web-bind 0.0.0.0 exposes it to the network — the traffic is plain HTTP, so put it behind a TLS reverse proxy (nginx, Caddy, Traefik) before reaching it beyond a trusted LAN.
  • Credentials come from --web-user / --web-password (or $WEB_PASSWORD, or the settings file). The refresh cadence follows --refresh.
  • No WebSocket: the page polls dashboard.svg once per refresh interval over plain HTTP. Responses are gzip-compressed (SVG shrinks ~10×), so a 1 s refresh costs a few tens of KB/s.
  • Stdlib-only: a single-threaded sampler feeds a http.server — no extra dependencies, no database. History lives in memory for the session (browsing historic data is a later milestone).

Per-host configuration

For host-specific tuning — fan labels, thresholds, a pinned interface, the Frigate URL — use a settings file rather than a long command line or a shell alias. Generate a starter with frigate-top --write-config > ~/.config/frigate-top/config.ini, then edit it; see Configuration for the precedence and full key list.

Requirements

  • Python 3.7+ (Linux — reads /proc and /sys)
  • No required dependencies. frigate-top runs on the standard library alone.
  • Optional, detected at runtime:
    • PyYAML (frigate-top[yaml]) — richer Frigate config parsing; a minimal regex fallback is used without it
    • nvidia-smi — NVIDIA GPU stats
    • docker or podman — Frigate container status
    • A reachable Frigate API — camera/detector stats and the detections summary
    • lm-sensors (sensors) — only a fallback for the Thermal band; /sys/class/hwmon is read first and covers most boxes without it

Configuration

Every option can be set three ways, in order of precedence: command-line flag environment variable settings file built-in default.

Settings file

For per-box configuration, use an INI settings file instead of long command-line flags. Generate a commented starter with frigate-top --write-config, then save it to one of (first found wins): the path given to --settings, $FRIGATE_TOP_CONFIG, ~/.config/frigate-top/config.ini, or /etc/frigate-top/config.ini.

[frigate-top]
frigate_url = http://localhost:5000
fan_labels = CPU, Rear Exhaust, Top
fan_max_rpm = 4000, 2000, 2000
chassis_intrusion = true
auto_fit = true
fahrenheit = false
cpu_temp_crit = 90
disk_full_crit = 90

Keys are the lowercased environment-variable names from the table below. A CLI flag or env var still overrides the file, so the same file works across hosts with per-host tweaks layered on top.

Environment variables

Variable Default Description
TIMEFRAME 1 Graph history in minutes
REFRESH 1.0 Refresh interval in seconds
MAX_TIMEFRAME 60 Maximum allowed history in minutes
FRIGATE_CONFIG "" Path to Frigate config.yml
FRIGATE_URL "" Frigate API URL, e.g. http://localhost:5000
INSECURE 0 Set to 1 to skip TLS verification for a remote HTTPS Frigate (loopback always allowed)
NET_IFACE "" Network interface to monitor (auto-detected if empty)
DISK_DEVICE "" Disk device to monitor, e.g. sda / nvme0n1 (auto-detected if empty)
CSV_FILE "" Append samples to CSV
ASCII 0 Set to 1 for ASCII graph characters
BRAILLE 0 Set to 1 for dense Braille area charts instead of block bars
NO_COLOR 0 Set to 1 to disable colors
AUTO_FIT 0 Set to 1 to expand as many sections as fit the terminal (priority: frigate, system, gpu, thermal, network)
FRIGATE_TOP_CONFIG "" Path to an INI settings file (see above)
FAHRENHEIT 0 Set to 1 to display temperatures in °F
NO_THERMAL 0 Set to 1 to disable thermal detection and the THERMAL section
FAN_MAX_RPM "" Fixed fan-graph max RPM: one value for all, or comma-separated per fan (blank = auto-scale)
LOAD_WARN 0 Load-average warning threshold (0 = off)
CPU_TEMP_CRIT 95 CPU temperature critical °C
SYS_TEMP_CRIT 95 System/board temperature critical °C
NVME_TEMP_WARN 70 NVMe temperature warning °C
NVME_TEMP_CRIT 85 NVMe temperature critical °C
DISK_FULL_WARN 80 Storage full warning threshold
CPU_WARN 80 CPU warning threshold
CPU_CRIT 95 CPU critical threshold
MEM_WARN 80 Memory warning threshold
MEM_CRIT 95 Memory critical threshold
GPU_WARN 80 GPU utilization warning threshold
GPU_CRIT 95 GPU utilization critical threshold
GPU_TEMP_WARN 80 GPU temperature warning °C
CPU_TEMP_WARN 85 CPU temperature warning °C (Thermal band)
SYS_TEMP_WARN 85 System/board temperature warning °C (Thermal band)
CHASSIS_INTRUSION 0 Set to 1 to enable chassis-intrusion detection (off by default)
FAN_LABELS "" Comma-separated fan names applied in detection order, e.g. CPU,Rear,Top
DISK_IOWAIT_WARN 20 I/O wait warning threshold
DISK_FULL_CRIT 85 Storage full critical threshold
NET_WARN 1000 Network throughput warning Mbps
FRIGATE_SKIPPED_WARN 1.0 Skipped FPS warning threshold
FRIGATE_INFERENCE_WARN 100 Detector inference time warning ms
DETECTOR_HEADROOM 0.75 Fraction of detector throughput used to size the supported-cameras estimate

Files

File Description
frigate_top/ The dashboard — a stdlib-only package (sampler, graphs, tui, cli, …)
scripts/build_pyz.py Bundles the package into the single-file frigate-top.pyz
pyproject.toml Package metadata (version read from __version__)
requirements.in / requirements.txt Optional pinned + hash-verified PyYAML (see below)
VERSION.md / CHANGELOG.md Versioning and release notes
CONTRIBUTING.md Contributor guide — layout and where to touch the code
LICENSE / NOTICE / DISCLAIMER.md Apache-2.0 licence and terms

Optional supply-chain hardening

PyYAML is optional. If you want it hash-verified, requirements.txt pins it with SHA-256 hashes:

pip install --require-hashes -r requirements.txt

Regenerate after changing requirements.in:

pip install pip-tools && pip-compile --generate-hashes requirements.in -o requirements.txt

Versioning

frigate-top follows Semantic Versioning. The version lives once in frigate_top/_version.py as __version__ and flows to the header title and the built package — see VERSION.md for the release process and CHANGELOG.md for history.

Notes

  • If the terminal is too small or not a TTY, use --snapshot for a one-off reading.
  • The first sample is used as a baseline and is not shown in the graphs, so deltas are meaningful.
  • Frigate config auto-detection tries /config/config.yml, /config/config.yaml, /etc/frigate/config.yml, /opt/frigate/config/config.yml, and ~/frigate/config/config.yml.
  • MQTT broker monitoring is intentionally not included because the broker is optional and containerized; the Frigate API provides the same health signals.
  • Security posture: the tool is read-only — it shells out only to fixed commands (nvidia-smi, docker/podman ps, df) with no shell or user-interpolated arguments, parses YAML with safe_load, and writes its optional --debug log to a per-user directory ($XDG_RUNTIME_DIR) opened with O_NOFOLLOW. TLS certificates are verified for remote HTTPS Frigate instances; verification is relaxed only for loopback hosts (where self-signed certs are the norm) or when --insecure is passed.

License

Licensed under the Apache License 2.0. See NOTICE for attribution and DISCLAIMER.md for terms regarding use, third-party trademarks, warranty, and liability.

frigate-top is an independent project and is not affiliated with or endorsed by the Frigate NVR project, NVIDIA, Google, or Docker.