Files
wiki-jokes/app/trmnl.py
T
2026-10-05 15:43:48 +02:00

57 lines
1.7 KiB
Python

"""TRMNL e-ink display feed.
Serves today's jokes in the JSON shape TRMNL's Private Plugin "polling"
strategy expects, so a device can render them with the TRMNL design system.
Polling URL: https://wiki-jokes.obahan.xyz/trmnl.json
If TRMNL_TOKEN is set, the device must send it as ?token=*** header
(polling headers: trmnl-token=<token>).
"""
import os
from fastapi import Request
from fastapi.responses import JSONResponse
from . import db
from .generator import today_local
REFRESH_INTERVAL_SECONDS = 900 # jokes only change once a day; match TRMNL cadence
def _batch():
"""Today's batch, falling back to the latest available (stale flag set)."""
day = today_local()
batch = db.get_day(day)
stale = False
if batch is None:
batch = db.get_latest()
stale = True
return batch, stale
def payload(batch: dict, stale: bool) -> dict:
return {
"refresh_interval": REFRESH_INTERVAL_SECONDS,
"day": batch["day"],
"stale": stale,
"article_title": batch["article_title"],
"article_url": batch["article_url"],
"jokes": [{"text": j} for j in batch["jokes"]],
}
def trmnl_endpoint(request: Request):
token = os.environ.get("TRMNL_TOKEN", "")
if token:
supplied = request.query_params.get("token") or request.headers.get("trmnl-token")
if supplied != token:
return JSONResponse({"error": "unauthorized"}, status_code=403)
batch, stale = _batch()
if batch is None:
# Keep the last screen on the device instead of blanking it.
return JSONResponse(
{"status": "no_data", "refresh_interval": REFRESH_INTERVAL_SECONDS},
status_code=204,
)
return JSONResponse(payload(batch, stale))