From c34dcf764c3ad78abac4228455c1faf8949feee8 Mon Sep 17 00:00:00 2001 From: hermes Date: Mon, 5 Oct 2026 15:43:48 +0200 Subject: [PATCH] Add TRMNL plugin markup --- app/trmnl.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 app/trmnl.py diff --git a/app/trmnl.py b/app/trmnl.py new file mode 100644 index 0000000..74a1030 --- /dev/null +++ b/app/trmnl.py @@ -0,0 +1,56 @@ +"""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=). +""" +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))