Cockpit on a second screen
Park cscli dashboard on a spare monitor or a Pi Zero in the kitchen. Glanceable RPM, speed,
coolant, oil, fuel — plus a live sparkline per metric and a Google Maps link to the current
GPS point.
cscli is a small Python CLI that talks to your CarStream server over HTTPS + WebSocket.
Live ASCII gauges, sparklines, an RPM/Speed trend chart, single-field giant readouts,
raw JSON streaming, and a one-shot health check — all from the terminal.
┌──── CarStream — Audi Q8 ──────────────────────────────────────────────┐│ RPM ████████████████░░░░░░░░░░░░░░ 3420 rpm ▁▂▄▆█▆▄▃▂▁▂▃▅▇█││ Speed ██████████░░░░░░░░░░░░░░░░░░░░ 72 km/h ▁▁▂▃▄▆▇█▇▆▅▄▃▂▁││ Throttle ███████░░░░░░░░░░░░░░░░░░░░░░░░ 23 % ▁▁▂▂▃▃▄▅▆▆▆▅▄▃▁││ Load ████░░░░░░░░░░░░░░░░░░░░░░░░░░ 14 % ▁▁▁▂▂▃▃▄▄▄▃▃▂▁▁││ Coolant ███████████████████░░░░░░░░░░░ 91 °C ▆▆▆▇▇▇▇█████▇▇▆││ Oil ███████████████████░░░░░░░░░░░ 97 °C ▅▅▆▆▆▇▇▇▇█████▇││ Fuel ████████████████░░░░░░░░░░░░░░ 56 % █████▇▇▇▇▆▆▆▅▅▅│└────────────────────────────────────────────────────────────────────────┘pipx install carstream-clipython -m venv .venv && source .venv/bin/activatepip install carstream-cliRequires Python ≥ 3.10.
cscli logincscli login --email you@example.com # skip the email promptcscli logoutThe JWT is cached at ~/.config/carstream/credentials.json with mode 0600.
Tokens expire (typically 24 h); on 401 the CLI tells you to run cscli login again.
| Command | What it does |
|---|---|
cscli login | Email + password prompt. JWT cached at ~/.config/carstream/credentials.json. |
cscli logout | Forget cached credentials. |
cscli devices | Table of cars with ONLINE / STALE / OFFLINE pill and friendly last-seen. |
cscli status | End-to-end health check — server, auth, WebSocket, per-Pi liveness. Cron-friendly exit codes. |
cscli dashboard | Live ASCII gauges + sparklines + RPM/Speed trend chart + status panel. |
cscli watch <field> | Single-field giant ASCII readout via pyfiglet, with sparkline + frame-age. |
cscli tail | Stream raw frames as JSON-per-line to stdout. |
Cockpit on a second screen
Park cscli dashboard on a spare monitor or a Pi Zero in the kitchen. Glanceable RPM, speed,
coolant, oil, fuel — plus a live sparkline per metric and a Google Maps link to the current
GPS point.
Pipe telemetry into anything
cscli tail writes one JSON object per line. Compose with jq, awk, datamash, or your
own script. The hot path skips JSON parsing entirely so the throughput is essentially network-bound.
Cron-monitored fleet
cscli status exits non-zero on any red — perfect for */5 * * * * health pings into Slack,
Discord, or Healthchecks.io.
Giant single-metric readout
cscli watch speed gives you a full-screen pyfiglet number. Drop it on a Pi Zero with a
cheap HDMI screen for a head-up display in the garage or on the workbench.
cscli dashboard # auto-picks if you only have one carcscli dashboard --car "Audi Q8" # or --car <car_id>, for a specific carThe dashboard renders:
plotext).The status panel pre-fetches /api/telemetry/<id>/last_position on startup so the GPS row + Maps
link populate instantly, even before the first WebSocket frame. While showing the parked
position the GPS line is suffixed with (last known) and a Parked: <ISO timestamp> row appears
below — both vanish as soon as a live GPS-bearing frame arrives.
The Maps link uses OSC-8 hyperlinks — clickable in iTerm2, modern xterm, gnome-terminal, wezterm,
kitty; plain text on terminals that don’t support it. Ctrl-C exits cleanly.
cscli watch speed # giant ASCII number, sparkline belowcscli watch rpm --font slantcscli watch fuel_level --font doomUseful for putting on a secondary monitor, a Pi Zero in the kitchen, or a head-up workshop display.
cscli tail # one JSON object per linecscli tail --pretty # pretty-printed JSONcscli tail --field rpm # only frames carrying that field; output is {field, ts}Pipe it into your own tooling:
cscli tail | jq 'select(.speed > 100)'cscli tail --field rpm | jq -r .rpm | datamash mean 1cscli tail --field fuel_level | awk -F: '{print $2}'The hot path skips JSON parsing entirely and writes the raw WebSocket frame straight to stdout — your downstream consumer can re-parse if needed.
cscli statusServer health · https://carstream.live ✓ Credentials present (you@example.com) ✓ Server reachable (200 in 84 ms) ✓ Auth valid (3 cars) ✓ WebSocket upgrades successfully
Devices: ✓ Audi Q8 ONLINE 1s ago ⚠ BMW F36 STALE 18s ago ✗ VW Polo OFFLINE never
All systems healthy.Exits 0 if all green, 1 on any red. Wire it into cron:
*/5 * * * * cscli status >/dev/null 2>&1 || \ curl -X POST <slack-webhook> -d '{"text":"CarStream unhealthy"}'cscli devices┌─────────┬──────────┬──────────────────────────────────┬──────────────────┬───────────┐│ Status │ Name │ Car ID │ Device │ Last seen │├─────────┼──────────┼──────────────────────────────────┼──────────────────┼───────────┤│ ONLINE │ Audi Q8 │ a1491b52-0db3-4649-acba-... │ 89de0a7570734f07 │ 2s ago ││ STALE │ BMW F36 │ 4db27641-09a5-4fc3-bc03-... │ ed12bab94fccf357 │ 18s ago ││ OFFLINE │ VW Polo │ — │ (unassigned) │ never │└─────────┴──────────┴──────────────────────────────────┴──────────────────┴───────────┘When more than one car is paired to your account, every streaming subcommand
(dashboard, watch, tail) refuses to auto-pick and prints the cars to choose from
(select by name or id):
cscli dashboard --car "Audi Q8"With exactly one paired car, --car is optional.