Skip to content

amorimdub/trmnl

Repository files navigation

trmnl-byos

A Build-Your-Own-Server backend for a custom TRMNL e-ink device. It speaks the TRMNL device API, renders an 800×480 dashboard itself, and serves it to the device as a 1-bit BMP — no TRMNL cloud involved.

Point your device's firmware at this server (custom server URL) and it will fetch its screens from here.

What it does

  • Implements the TRMNL device endpoints: /api/setup, /api/display, /api/log.
  • Renders a dashboard with satori (layout → SVG) + resvg (SVG → raster), then encodes a 1-bit 800×480 BMP — the format the TRMNL panel expects.
  • Auto-registers devices by MAC on first contact (in-memory; see below).

Endpoints

Method & path Purpose
GET /api/setup Device first-boot registration. Returns api_key + friendly_id.
GET /api/display Polled every refresh_rate s. Returns the next image_url.
POST /api/log Device log sink.
GET /images/:f.bmp Serves a rendered screen (1-bit BMP).
GET /preview PNG preview of the current dashboard — open it in a browser.
GET /api/devices Operator view of devices that have checked in.
GET /healthz Liveness/readiness probe.

Configuration

All via environment variables (see .env.example):

Var Default Notes
PORT 8080 HTTP listen port.
PUBLIC_BASE_URL (req) Absolute URL the device can reach. Falls back to the Host header.
REFRESH_RATE 900 Device poll interval, seconds.
DEVICE_NAME TRMNL Shown in the dashboard header.
TZ UTC IANA timezone for the clock/date.
LOG_LEVEL info pino log level.

Local development

npm install
npm run dev      # tsx watch on src/server.ts
# then open http://localhost:8080/preview

Simulate a device:

curl -H "ID: AA:BB:CC:DD:EE:FF" localhost:8080/api/setup
curl -H "ID: AA:BB:CC:DD:EE:FF" -H "Battery-Voltage: 4.05" -H "RSSI: -62" \
     localhost:8080/api/display

Customizing the dashboard

src/widgets.ts returns the dashboard content — it currently returns static placeholders (weather / calendar / tasks). Replace it with real data sources; src/screen.ts lays out whatever it returns. The screen is plain satori-style elements (no JSX needed).

Container

docker build -t trmnl-byos .
docker run -p 8080:8080 -e TZ=America/Sao_Paulo trmnl-byos

CI (.github/workflows/build.yml) typechecks, builds, and publishes a linux/amd64 image to ghcr.io/<owner>/trmnl on every push to main.

Deployment

This app is deployed to the meduza Kubernetes cluster via Flux. The manifests live in the spot GitOps repo under apps/trmnl/.

Notes

  • State is in-memory. Devices and rendered images don't survive a restart; devices simply re-register (by MAC) on their next poll. Add a real store if you need persistence.
  • The 1-bit BMP encoder lives in src/bmp.ts and thresholds on luminance.

About

Build-Your-Own-Server backend for a custom TRMNL e-ink device

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors