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.
- The project leverages GitHub Actions as a transient cron job to push data to an e-ink display, eliminating the need for a persistent server.
- Instead of sending raw JSON data, the Python script pre-renders HTML strings to bypass the rendering limitations of the target device.
- A custom zero-padding algorithm ensures ancient historical dates sort correctly when processed lexicographically.
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 Python code contained here needs to scrape the page and is used to generate plugin data.
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.
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.