Telnet (ANSI terminal) rendering backend for led-ticker — watch your sign in a terminal.
Project description
led-ticker-telnet
Watch your led-ticker sign live in any ANSI-capable terminal — the same content that drives your LED panels streams over TCP as true-color ANSI escape sequences.
Every client sees the current frame the moment swap() fires, at panel cadence. Open two terminals, disconnect one, start another — the sign keeps rendering without interruption.
Prerequisites
- A working led-ticker install (core ≥ 2.2).
- An ANSI-capable terminal (any modern terminal on macOS, Linux, or Windows Terminal). True-color support (24-bit) recommended; most terminals released after 2017 support it.
telnetorncon the connecting machine (both are standard on macOS and most Linux distros).
Install
This plugin auto-registers via the led_ticker.plugins entry point — once the package is installed, no [plugins] config change is needed.
Into a containerized led-ticker (recommended): add the package to your config/requirements-plugins.txt and rebuild:
led-ticker-telnet
# in your led-ticker checkout
docker compose up -d --build
Standalone (a venv that already has led-ticker):
pip install led-ticker-telnet
See the led-ticker Plugins docs for the constraint-based install the Docker image uses.
Select the backend
In your config/config.toml, set the backend field under [display]:
[display]
backend = "telnet.telnet"
rows = 16
cols = 32
chain_length = 5
brightness = 80
default_scale = 1
The backend name is telnet.telnet — the first telnet is the plugin namespace (from the entry-point registration), and the second names the backend within that namespace.
All other [display] fields (rows, cols, chain_length, default_scale, etc.) work exactly as they do with the rgbmatrix backend. Your widgets, sections, and transitions config is unchanged.
Connect
Once led-ticker starts with the telnet backend, connect from any machine on the same network:
telnet <host> 2300 # default; see Configuration to change
nc <host> 2300 # default; see Configuration to change
Replace <host> with the hostname or IP of the machine running led-ticker. When connecting locally: telnet localhost 2300.
The terminal renders one text row per two canvas rows, using the Unicode UPPER HALF BLOCK character (▀) with the top pixel as the foreground color and the bottom pixel as the background color. This doubles the vertical pixel density in most terminals.
Disconnecting cleanly (Ctrl-C or closing the terminal window) is safe — led-ticker logs the departure and continues running.
Configuration via environment variables
The telnet backend reads two environment variables at startup:
| Variable | Default | Description |
|---|---|---|
LED_TICKER_TELNET_PORT |
2300 |
TCP port to listen on |
LED_TICKER_TELNET_HOST |
0.0.0.0 |
Bind address (all interfaces) |
Set them in your shell or in Docker Compose:
# shell
export LED_TICKER_TELNET_PORT=3000
# docker compose (compose.yaml)
environment:
LED_TICKER_TELNET_PORT: "3000"
# LED_TICKER_TELNET_HOST: "0.0.0.0" # bind address; default is all interfaces
Known limitations
brightness is a no-op. The [display] brightness field is required by led-ticker's backend protocol but has no effect on ANSI output — adjust brightness/contrast in your terminal instead.
pixel_mapper_config is accepted but ignored. The telnet backend accepts the pixel_mapper_config argument (required by led-ticker's backend constructor convention) but does not apply any chain-folding or U-mapper remapping — it renders the logical canvas directly to the terminal. Physical-panel layout concerns (serpentine chains, rotation) are a hardware-level concern irrelevant to a terminal preview.
TOML [display] sub-keys are not readable by plugin backends today. The port and host are therefore environment-only — there is no port = 2300 or host = "0.0.0.0" key in the [display] block.
A future led-ticker-core [display.<backend>] → from_config(cls, cfg) mechanism would close this: plugins could define their own sub-table and receive a typed config object at startup. Port and host would then be first-class TOML knobs alongside brightness, rows, and the rest. This is the key usability gap to resolve in a follow-up core change.
Behavior under error conditions
- Bind failure (port in use, insufficient permissions): the backend logs a warning and continues without a server. The sign renders normally; clients simply cannot connect until the conflict is resolved and led-ticker restarts.
- Slow or stalled client:
swap()writes to the client without waiting for a drain, so the render loop never blocks. Bytes a client hasn't read accumulate in that connection's in-process write buffer; to keep a stalled client (suspendednc, terminal under Ctrl-S flow control, congested link) from growing that buffer without bound,swap()checks each client's write-buffer size and skips the write for any client over a ~4 MiB cap — that frame is dropped for that client, which recovers automatically once it starts reading again. The render loop and other clients are unaffected. - Client disconnect mid-session: detected on the next
swap()write; the client is pruned and rendering continues.
Development
cd plugins/telnet
uv sync --extra dev # resolves led-ticker-core from PyPI
uv run pytest -q
uv run ruff check src tests
uv run pyright src
The plugin imports only the public led_ticker.plugin surface — tests/test_import_purity.py enforces it.
Headless smoke / profiling image
Dockerfile.smoke runs led-ticker fully headless (no rgbmatrix, no hardware) with this backend and the bundled smoke config — handy for a quick visual check or profiling the render path on any machine:
cd plugins/telnet
make smoke-run # build + run, then: telnet localhost 2300
make smoke-stop # stop + remove the container
Profile a different engine/Python or change the port:
docker build -f Dockerfile.smoke --build-arg CORE_VERSION=2.3.0 -t led-ticker-telnet-smoke .
docker run -d -p 3000:3000 -e LED_TICKER_TELNET_PORT=3000 led-ticker-telnet-smoke
Links
- led-ticker — the core project
- Docs site · Plugin system
- Plugin authoring guide
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file led_ticker_telnet-0.1.0.tar.gz.
File metadata
- Download URL: led_ticker_telnet-0.1.0.tar.gz
- Upload date:
- Size: 17.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
454de3b62bf8300b56fdb26f081fa165c106531461174b2b4bedb810fd4e3336
|
|
| MD5 |
4d520c0a85f89ace201a7b37ee52f28c
|
|
| BLAKE2b-256 |
bde68a2f9faa2c7831c8ede587d1bfb6bee69d5803374ce242ccd01971bb8442
|
Provenance
The following attestation bundles were made for led_ticker_telnet-0.1.0.tar.gz:
Publisher:
publish.yml on JamesAwesome/led-ticker-plugins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
led_ticker_telnet-0.1.0.tar.gz -
Subject digest:
454de3b62bf8300b56fdb26f081fa165c106531461174b2b4bedb810fd4e3336 - Sigstore transparency entry: 2008476824
- Sigstore integration time:
-
Permalink:
JamesAwesome/led-ticker-plugins@af4a9602f47f3cbf461972ff96770924c6cc4859 -
Branch / Tag:
refs/tags/telnet-v0.1.0 - Owner: https://github.com/JamesAwesome
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@af4a9602f47f3cbf461972ff96770924c6cc4859 -
Trigger Event:
release
-
Statement type:
File details
Details for the file led_ticker_telnet-0.1.0-py3-none-any.whl.
File metadata
- Download URL: led_ticker_telnet-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
733281bacf23319b9a4ce1e0923ec075e13c1be89cadb947d25b70c9e55102d0
|
|
| MD5 |
c66a3caa1c3421ea60e25a76a8539710
|
|
| BLAKE2b-256 |
ddd9add5870daef54493d0c14c087aaca206b72600dddfa593dd03a250c5b356
|
Provenance
The following attestation bundles were made for led_ticker_telnet-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on JamesAwesome/led-ticker-plugins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
led_ticker_telnet-0.1.0-py3-none-any.whl -
Subject digest:
733281bacf23319b9a4ce1e0923ec075e13c1be89cadb947d25b70c9e55102d0 - Sigstore transparency entry: 2008477019
- Sigstore integration time:
-
Permalink:
JamesAwesome/led-ticker-plugins@af4a9602f47f3cbf461972ff96770924c6cc4859 -
Branch / Tag:
refs/tags/telnet-v0.1.0 - Owner: https://github.com/JamesAwesome
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@af4a9602f47f3cbf461972ff96770924c6cc4859 -
Trigger Event:
release
-
Statement type: