Skip to content

Python CLI — cscli

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.

cscli dashboard
┌──── CarStream — Audi Q8 ──────────────────────────────────────────────┐
│ RPM ████████████████░░░░░░░░░░░░░░ 3420 rpm ▁▂▄▆█▆▄▃▂▁▂▃▅▇█│
│ Speed ██████████░░░░░░░░░░░░░░░░░░░░ 72 km/h ▁▁▂▃▄▆▇█▇▆▅▄▃▂▁│
│ Throttle ███████░░░░░░░░░░░░░░░░░░░░░░░░ 23 % ▁▁▂▂▃▃▄▅▆▆▆▅▄▃▁│
│ Load ████░░░░░░░░░░░░░░░░░░░░░░░░░░ 14 % ▁▁▁▂▂▃▃▄▄▄▃▃▂▁▁│
│ Coolant ███████████████████░░░░░░░░░░░ 91 °C ▆▆▆▇▇▇▇█████▇▇▆│
│ Oil ███████████████████░░░░░░░░░░░ 97 °C ▅▅▆▆▆▇▇▇▇█████▇│
│ Fuel ████████████████░░░░░░░░░░░░░░ 56 % █████▇▇▇▇▆▆▆▅▅▅│
└────────────────────────────────────────────────────────────────────────┘
Terminal window
pipx install carstream-cli

Requires Python ≥ 3.10.

Terminal window
cscli login
cscli login --email you@example.com # skip the email prompt
cscli logout

The 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.

CommandWhat it does
cscli loginEmail + password prompt. JWT cached at ~/.config/carstream/credentials.json.
cscli logoutForget cached credentials.
cscli devicesTable of cars with ONLINE / STALE / OFFLINE pill and friendly last-seen.
cscli statusEnd-to-end health check — server, auth, WebSocket, per-Pi liveness. Cron-friendly exit codes.
cscli dashboardLive ASCII gauges + sparklines + RPM/Speed trend chart + status panel.
cscli watch <field>Single-field giant ASCII readout via pyfiglet, with sparkline + frame-age.
cscli tailStream 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.

Terminal window
cscli dashboard # auto-picks if you only have one car
cscli dashboard --car "Audi Q8" # or --car <car_id>, for a specific car

The dashboard renders:

  • Gauge cluster with bar + color-coded value + a 60-sample sparkline per metric.
  • RPM/Speed trend chart over the same window (two y-axes via plotext).
  • Status panel: car name, ONLINE / STALE / OFFLINE pill (5 s / 30 s thresholds, matches the web UI), GPS coordinates, frame age, and an OSC-8 Open in Google Maps ↗ link.

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.

Terminal window
cscli watch speed # giant ASCII number, sparkline below
cscli watch rpm --font slant
cscli watch fuel_level --font doom

Useful for putting on a secondary monitor, a Pi Zero in the kitchen, or a head-up workshop display.

Terminal window
cscli tail # one JSON object per line
cscli tail --pretty # pretty-printed JSON
cscli tail --field rpm # only frames carrying that field; output is {field, ts}

Pipe it into your own tooling:

Terminal window
cscli tail | jq 'select(.speed > 100)'
cscli tail --field rpm | jq -r .rpm | datamash mean 1
cscli 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.

Terminal window
cscli status
Terminal window
Server 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:

Terminal window
*/5 * * * * cscli status >/dev/null 2>&1 || \
curl -X POST <slack-webhook> -d '{"text":"CarStream unhealthy"}'
Terminal window
cscli devices
Terminal window
┌─────────┬──────────┬──────────────────────────────────┬──────────────────┬───────────┐
│ 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):

Terminal window
cscli dashboard --car "Audi Q8"

With exactly one paired car, --car is optional.