The Transient App: Inside frethop/TRMNL-thisday

How a serverless Python script and a GitHub Action bypass traditional hosting to pipe historical data directly into an e-ink display.

4 min read • View on GitHub • More from frethop

A mechanical bridge spanning a chasm between a chaotic archive and a pristine stone tablet, with a mechanical pigeon mid-flight. This illustrates the transient data pipeline between the web and an e-ink display.
A transient connection bridging the chaotic web and a static display.
Key Takeaways

The Compute That Does Not Exist

Most ambient displays rely on a polling architecture. The device wakes up, pings a server, and asks for fresh data. This requires a server to be listening 24 hours a day, waiting for a request that might only come once every few hours. The infrastructure overhead is disproportionate to the utility.

This project flips the model. It uses a GitHub Action as an invisible, transient server. At 1:30 AM every day, the CI pipeline boots up, runs a Python scraping script, pushes the payload via a webhook to the TRMNL cloud API, and immediately self-destructs. There is no database, no persistent API, and no hosting bill.

The transient data pipeline in action.

The Python code contained here needs to scrape the page and is used to generate plugin data.

Project README, Repository documentation · frethop/TRMNL-thisday README

Pre-Rendering for Dumb Glass

E-ink displays are notoriously slow to update and often lack the processing power to handle complex local templating. To solve this, the Python script does not just scrape and forward raw historical facts. It acts as an automated typesetter.

A close-up of a vintage metal typesetting tray being populated by a precise mechanical arm. This represents the Python script pre-rendering HTML blocks before sending them to the display.
Constructing the presentation layer before delivery.

The application dynamically constructs the HTML <div> tags and assigns them to specific column variables. By injecting pre-formatted HTML directly into the Liquid templates, the project shifts the rendering burden away from the low-power device and onto the transient compute pipeline.

itemsColOne = ""
for event in events[0:2]:
    itemsColOne += f'<div class="item">{event}</div>'
# These pre-rendered strings are then pushed via webhook

The Lexicographical Time Machine

Scraping historical data introduces a unique sorting problem. Standard lexicographical sorting algorithms fail when comparing ancient and modern dates. The string "800 AD" will incorrectly sort after "1900 AD" because the character "8" comes after "1". The script handles this with a subtle but vital piece of string manipulation.

if linetext[0] <= '9':
    pos = linetext.find(" ")
    if pos < 4:
        linetext = "0" + linetext

By identifying dates with fewer than four digits and padding them with a leading zero, the scraper guarantees that chronological integrity is maintained before the data is chunked and sent to the display. It is a brute-force solution, but it is entirely appropriate for the constrained environment.

The Push Paradigm

The standard pattern for TRMNL integrations involves the device polling a hosted JSON endpoint. This project proves the viability of the webhook push pattern for daily ambient data. When your data only changes once every 24 hours, event-driven architecture is not just cheaper, it is technically superior.

A split composition showing a person repeatedly banging a door knocker on the left, and a single envelope smoothly entering a mailbox on the right. This illustrates the difference between polling and webhook push architectures.
Polling a server versus waiting for a webhook delivery.