frigate-top (1.4.2)
Installation
pip install --index-url frigate-topAbout this package
Real-time terminal dashboard for Frigate NVR/CCTV servers
frigate-top
htop for your Frigate NVR box — a real-time terminal dashboard for Frigate CCTV hosts
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.
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, plusother) 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/tto 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, Intelcoretemp/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/hwmonwith no dependency, falling back tolm-sensorswhen needed. The first spinning fan is labelledCPU, the restSYS1 / 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 (
--braillefor dense Braille area charts,--asciifor 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 sensorpanel 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(default0.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,pfor 15m,cfor 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-configprints 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.--fahrenheitshows °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.pyto download anymore. The single-file install is thefrigate-top.pyzzipapp attached to each release; thedownload/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),
pipwon't touch the system Python — use the single file orpipxabove, install into apython3 -m venvfirst, or append--break-system-packagesif 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/ss/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 minutesc— set a custom history timeframe (in minutes)↑/↓(orj/k),PgUp/PgDn,Home/End— scroll the graph area when it overflows the terminalq— 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.1by default.--web-bind 0.0.0.0exposes 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.svgonce 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
/procand/sys) - No required dependencies.
frigate-topruns 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 statsdockerorpodman— 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/hwmonis read first and covers most boxes without it
- PyYAML (
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
--snapshotfor 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 withsafe_load, and writes its optional--debuglog to a per-user directory ($XDG_RUNTIME_DIR) opened withO_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--insecureis 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.