Skip to content
led-ticker

Reference: CLI

led-ticker exposes one console script (led-ticker) with a default “run the display” mode and validate, plugin, and webui subcommands. Day-to-day development is wrapped in make targets that drive uv, pytest, ruff, and the docs site. The console script is registered in pyproject.toml as led-ticker = "led_ticker.app:main".

Runs the display loop against a TOML config. With no subcommand, the CLI loads the config, builds the playlist, opens the panel, and starts rendering until interrupted.

FlagDefaultNotes
--config / -cconfig.tomlPath to the TOML configuration file. Resolved relative to the working dir.
--backend(config)Override the display backend (e.g. headless). Takes precedence over [display] backend in the config. Unknown names fail with a clear registry error. Primarily used by the make try Docker flow to run headless without modifying your deploy config.

The LED_TICKER_BACKEND environment variable is the env equivalent of --backend (the flag wins when both are set) — it’s how compose.try.yaml selects the headless backend.

If the config path doesn’t exist, the CLI prints a hint pointing at config.example.toml and exits with status 1.

Terminal window
led-ticker --config config/config.toml
led-ticker --config config/config.toml --backend headless

Validates a TOML config without opening the panel — checks every widget builds, every transition resolves, and every reference (font names, image paths, color shapes) is well-formed. Useful in CI and as a pre-flight check before deploying a config to a Pi.

Terminal window
led-ticker validate config/config.toml
led-ticker validate config/config.toml --json
led-ticker validate --strict config/config.toml
FlagDefaultNotes
pathPath to the config file. Required unless --list-fields is given.
--jsonoffEmit machine-readable JSON instead of human-readable text. Used by the creating-a-config skill.
--strictoffPromote all warnings to errors. Also checks that gif/image path fields exist on disk. Use in CI to enforce a warning-clean config.
--fixoffApply auto-fixable migrations (key renames) to the config file in-place. Comments in the TOML file are not preserved.
--list-fieldsPrint all valid fields for a widget type and exit (e.g. --list-fields message). No path argument required.

Exits 0 on a clean config, 1 on validation errors (or promoted warnings with --strict), 2 if the config file isn’t found. See Tools: validate for the full output format (human + JSON), exit codes, and CI examples.

Manage plugins. Two pairs of verbs: add / remove edit the manifest (config/requirements-plugins.txt) only — the Docker path, where a docker compose restart triggers a startup reconcile that installs from the manifest (no rebuild) — and install / uninstall also run pip, for dev / source-checkout installs. (The old led-ticker plugins is a deprecated alias of plugin status.)

Terminal window
# Docker: edit the manifest, then restart
led-ticker plugin add pool # + line in requirements-plugins.txt (no pip)
led-ticker plugin remove pool # − line (no pip)
docker compose restart # installs/uninstalls on the next start — no rebuild
# Dev / source checkout: also pip-install now
led-ticker plugin install pool # add + pip install
led-ticker plugin uninstall pool # remove + pip uninstall
led-ticker plugin status # plugins loaded/failed for the config
led-ticker plugin list # the catalog ([declared] / [installed])
led-ticker plugin search pool # search the catalog

All four management verbs accept a catalog name or a raw pip spec (a git+https://… URL or a PyPI requirement, like pip), default the manifest to config/requirements-plugins.txt (run from your project root; --config <path> for a non-standard layout, warned if it’d write outside config/), and dedup by plugin while preserving comments. add/install take --source git|pypi and --unpinned; all take --dry-run.

Run these on the host, not inside the container — they edit a host file, and the container mounts ./config read-only, so docker compose exec led-ticker led-ticker plugin add … fails. If you run led-ticker purely via Docker and have no host led-ticker command, edit config/requirements-plugins.txt by hand (cp config/requirements-plugins.example.txt config/requirements-plugins.txt) and restart — docker compose restart. It’s the same manifest.

plugin add / plugin remove (manifest only — Docker)

Section titled “plugin add / plugin remove (manifest only — Docker)”

add resolves the name to a pip requirement and writes it to config/requirements-plugins.txt (pinned to the catalog’s recommended ref by default; a typo close to a catalog name gets a “did you mean …?”). Re-adding the same plugin is idempotent (“already declared” if unchanged). remove drops the matching line (idempotent — “not in …” if absent). Neither runs pip — they only edit the manifest. Run docker compose restart to apply: a startup reconcile installs/uninstalls from the manifest on the next start (no rebuild needed). The container is read-only at runtime; plugins install onto the ticker-plugins volume, not into the image.

The bundled pool/baseball/crypto entries currently pin to @main, so the default (“pinned”) and --unpinned write the same @main reference today — pass a tagged pip spec (e.g. …@v1.2.0) for a fixed production pin until the catalog ships tags.

plugin install / plugin uninstall (manifest + pip — dev / source checkout)

Section titled “plugin install / plugin uninstall (manifest + pip — dev / source checkout)”

install does what add does and pip-installs the requirement under led-ticker’s core dependency constraints (a plugin can pull new libraries but can’t move a version core pins). uninstall does what remove does and pip uninstalls the distribution. install --save-only is a deprecated alias of add. Exits with pip’s exit code on a failed install (the manifest is still updated).

FlagDefaultNotes
--sourcefirst source(add/install) git or pypi — which catalog source to use.
--unpinnedoff (pinned)(add/install) Write @main / a bare PyPI name instead of the catalog ref. (Bundled refs are @main today, so this is a no-op for them — see above.)
--dry-runoffPrint the plan without changing anything.
--config / -c(none)Non-standard layout only: use requirements-plugins.txt next to this config instead of ./config/.

Lists the plugins discovered for a config — both the ones that loaded and any that failed — without opening the panel. For each loaded plugin it prints the namespace, its source, and the surfaces it contributes (widgets, transitions, fonts, or hooks only); failed plugins are listed with their load error. Handy for confirming a freshly installed plugin is being picked up before you deploy. --config / -c selects the config (defaults to the top-level). Exits 0 — printing No plugins found. when the config is absent or lists no plugins — and 2 only if a config that exists can’t be parsed.

list prints the bundled catalog — each plugin’s name, summary, and the widgets/transitions it provides — marking each [declared] (present in requirements-plugins.txt, so it installs on the next restart) and/or [installed] (importable in this environment now). search filters that catalog by a case-insensitive substring over the name, summary, and provided names. --config / -c locates the manifest for the [declared] check.

The repo’s Makefile wraps the common dev commands so you don’t have to remember the underlying uv / pnpm / docker invocations. Run make help to print the same list straight from the Makefile.

TargetWhat it does
make devuv sync --extra dev plus install the pre-commit and pre-push hooks.
make hooks(Re)install just the pre-commit and pre-push git hooks.
make testRun pytest with coverage (no Docker or hardware needed).
make lintRun ruff check over src/ and tests/.
make typecheckRun pyright over src/.
make formatRun ruff format over src/ and tests/.
make cleanRemove build artifacts and caches. Does not stop containers — use make down for that.

The everyday ladder for running a sign, in roughly the order you use it: setupbuildupupdaterestartdown, with logs alongside. None of these hard-code a Compose profile — set COMPOSE_PROFILES=webui in .env to include the web UI.

TargetWhat it does
make setupOne-command bootstrap: check Docker, seed config/ + .env, and bring the sign up. Usage: make setup [MODE=try|deploy].
make buildBuild the production Docker image only — no start; single image runs on both Pi 4 and Pi 5. Prerequisite for the *-docker diagnostics below.
make upStart the sign from the existing image without rebuilding.
make updateUpdate a running deploy after git pull: rebuild the image at the real version and recreate the services.
make restartRestart the sign without rebuilding.
make downStop and remove the sign’s containers.
make logsFollow the sign’s logs (Ctrl-C to stop).
TargetWhat it does
make tryRun led-ticker with no hardware: headless engine plus web-UI preview at http://localhost:8080.
make try-downStop and remove the try-it containers (and their tmpfs volume).
TargetWhat it does
make validateRun led-ticker validate against a config TOML. Usage: make validate CONFIG=config/config.toml.
make panel-testCycle the panel through full-frame R/G/B/White/Black to diagnose hardware/wiring/driver issues. Usage: make panel-test [CONFIG=config/config.longboi.toml] [HOLD=2]. See panel-test.
make panel-test-dockerSame as make panel-test, but runs inside the production Docker image. Stop the running ticker first (docker compose stop). Usage: make panel-test-docker [CONFIG=…] [HOLD=…].
make panel-map-revealPaint the chain-calibration pattern (no mapper applied) so physical chain order and panel orientation are visible. Usage: make panel-map-reveal [CONFIG=…] [HOLD=2]. See panel-map.
make panel-map-reveal-dockerSame as make panel-map-reveal, but runs inside the production Docker image. Stop the running ticker first.
make panel-map-deriveGenerate a Remap: mapper string from a layout file (no hardware needed). Usage: make panel-map-derive LAYOUT=layout.txt [CONFIG=…]. See panel-map.
make panel-map-derive-dockerSame as make panel-map-derive, but runs inside the production Docker image. Stop the running ticker first. Usage: make panel-map-derive-docker CONFIG=… LAYOUT=/tmp/grid.txt.
make panel-map-verifyPaint a coherent full-canvas diagnostic to verify a mapper string. Usage: make panel-map-verify [CONFIG=…] [MAPPER="Remap:…"] [HOLD=2]. See panel-map.
make panel-map-verify-dockerSame as make panel-map-verify, but runs inside the production Docker image. Stop the running ticker first.
TargetWhat it does
make docs-devStart the Astro Starlight dev server at http://localhost:4321/ (also rebuilds demo gifs).
make docs-buildBuild the docs site to docs/site/dist/.
make docs-check-llmsBuild the docs and verify the llms.txt Markdown export.
make docs-lintLint the docs site (prettier --check plus astro check).
make docs-formatAuto-format the docs site with prettier.
TargetWhat it does
make render-demoRender a single demo gif. Usage: make render-demo CONFIG=path/to.toml OUT=out.gif.
make plan-gifRecommend a render --duration for a demo and flag a # render-duration: header that would clip. Usage: make plan-gif CONFIG=path/to.toml. See gif-plan.
make render-emoji-previewsRe-generate the per-slug emoji preview PNGs in docs/site/public/emoji/.
make render-long-demosRender every long-running widget demo (data-fetch widgets) into docs/site/public/demos-long/.
make render-long-demoRender one long-running demo by name. Usage: make render-long-demo NAME=widget-weather.
make render-pinned-demosRender every pinned short-render demo into docs/site/public/demos-pinned/.
make derive-phoenixRe-derive config/assets/phoenix.* from the vendored CC0 source.
make derive-prideRe-generate config/assets/pride.* (CC0 rainbow flag).
make derive-heart-tunnelRe-generate config/assets/heart-tunnel-opaque.jpg (CC0 heart-tunnel source).
make setup-demo-fontsDownload the Atkinson Hyperlegible fonts into docs/site/demos-long/fonts/.

The render-long-demos target only runs locally — the demos make live HTTP calls and some need API keys that aren’t available in CI. Source TOMLs live in docs/site/demos-long/; rendered output is committed to docs/site/public/demos-long/. The render-pinned-demos target also runs locally for the same reason — pinned gifs are stable showcases on widget pages, intentionally not regenerated on every deploy. Source TOMLs live in docs/site/demos-pinned/; rendered output is committed to docs/site/public/demos-pinned/. Both targets are fail-soft: a single broken TOML doesn’t block the rest from rendering — failures get listed at the end and the target exits non- zero.

The production deployment uses docker compose. With the repo cloned on a Pi and a config/config.toml in place, the standard invocation is:

Terminal window
docker compose up -d

The compose file mounts ./config read-only into the container so you edit the TOML on the host and the container picks it up on restart. Its restart: unless-stopped policy brings the ticker back up automatically on boot and after a crash.

--led-* flags belong to the rgbmatrix library, not led-ticker

Section titled “--led-* flags belong to the rgbmatrix library, not led-ticker”

The led-ticker CLI accepts --config / -c, --backend, and the validate subcommand’s path, --json, --strict, --fix, and --list-fields. Hardware tuning — panel geometry, GPIO mapping, brightness, RP1 mode — is configured through the [display] block in your TOML, not via command-line flags. The rgbmatrix library’s own --led-* CLI flags exist (and [display] maps onto them internally) but you don’t pass them to led-ticker. See Reference: Config options for the [display] field list.

The repo ships test stubs for the C-only rgbmatrix module so the entire suite runs on macOS / CI without hardware. The stubs are found automatically: pyproject.toml sets [tool.pytest.ini_options] pythonpath = ["tests/stubs"], so both make test and a direct pytest (e.g. from your editor’s test runner) put the stub rgbmatrix on the import path — no PYTHONPATH env var to set yourself.