This commit is contained in:
Matthew Ryan Dillon 2025-06-19 08:38:55 -04:00
parent fc964d3ac4
commit 5d4937e57a
4 changed files with 68 additions and 2 deletions

View file

@ -0,0 +1,35 @@
# TRMNL Weather & Pollen Report
A custom TRMNL plugin that fetches and displays weather and pollen data.
## Setup
1. Set up a virtual environment and install dependencies:
```bash
python -m venv .venv
source .venv/bin/activate
uv sync
```
2. Set required environment variables:
```bash
export WEATHER_API_KEY="your_openweathermap_api_key"
export AUTH_TOKEN="your_chosen_auth_token"
```
3. Run the application:
```bash
fastapi run main.py --port 8887
```
## Docker
Build and run with Docker:
```bash
docker build -t trmnl-report .
docker run -p 8887:8887 -e WEATHER_API_KEY=your_key -e AUTH_TOKEN=your_token trmnl-report
```
## API
Access the API at `http://localhost:8887/?token=your_auth_token`

23
main.py
View file

@ -7,6 +7,7 @@ import pprint
import zoneinfo
from fastapi import FastAPI, HTTPException
from cachetools import TTLCache
import httpx
@ -16,6 +17,9 @@ eastern = zoneinfo.ZoneInfo("America/New_York")
app = FastAPI()
# Initialize caches for 15 minutes
weather_cache = TTLCache(maxsize=100, ttl=900) # 15 minutes
pollen_cache = TTLCache(maxsize=100, ttl=900) # 15 minutes
CONFIG = {
# salem, ma
@ -89,6 +93,10 @@ def fallback_handler(func):
@fallback_handler
async def fetch_pollen(zipcode):
# Check the cache first
if zipcode in pollen_cache:
return pollen_cache[zipcode]
url = f"https://www.pollen.com/api/forecast/current/pollen/{zipcode}"
headers = {
"User-Agent": (
@ -104,7 +112,7 @@ async def fetch_pollen(zipcode):
response = await client.get(url, headers=headers)
response.raise_for_status()
data = response.json()
return [
result = [
{
"forecast_date": format_datetime(
datetime.fromisoformat(data["ForecastDate"])
@ -119,17 +127,25 @@ async def fetch_pollen(zipcode):
],
}
]
# Cache the result
pollen_cache[zipcode] = result
return result
@fallback_handler
async def fetch_weather(lat, lon, weather_api_key):
# Check the cache first
cache_key = (lat, lon)
if cache_key in weather_cache:
return weather_cache[cache_key]
url = f"https://api.openweathermap.org/data/3.0/onecall?lat={lat}&lon={lon}&appid={weather_api_key}&units=imperial"
async with httpx.AsyncClient() as client:
response = await client.get(url)
response.raise_for_status()
data = response.json()
current, periods = data["current"], data["daily"][:2]
return [
result = [
{
"forecast_date": format_datetime(datetime.fromtimestamp(current["dt"])),
"current_temp": int(round(current["temp"])),
@ -154,6 +170,9 @@ async def fetch_weather(lat, lon, weather_api_key):
],
}
]
# Cache the result
weather_cache[cache_key] = result
return result
@app.get("/")

View file

@ -7,6 +7,7 @@ requires-python = ">=3.13"
dependencies = [
"fastapi[standard]>=0.115.12",
"httpx>=0.28.1",
"cachetools>=5.0.0",
]
[tool.pyright]

11
uv.lock generated
View file

@ -24,6 +24,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" },
]
[[package]]
name = "cachetools"
version = "6.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8a/89/817ad5d0411f136c484d535952aef74af9b25e0d99e90cdffbe121e6d628/cachetools-6.1.0.tar.gz", hash = "sha256:b4c4f404392848db3ce7aac34950d17be4d864da4b8b66911008e430bc544587", size = 30714, upload-time = "2025-06-16T18:51:03.07Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/f0/2ef431fe4141f5e334759d73e81120492b23b2824336883a91ac04ba710b/cachetools-6.1.0-py3-none-any.whl", hash = "sha256:1c7bb3cf9193deaf3508b7c5f2a79986c13ea38965c5adcff1f84519cf39163e", size = 11189, upload-time = "2025-06-16T18:51:01.514Z" },
]
[[package]]
name = "certifi"
version = "2025.4.26"
@ -390,12 +399,14 @@ name = "trmnl-report"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "cachetools" },
{ name = "fastapi", extra = ["standard"] },
{ name = "httpx" },
]
[package.metadata]
requires-dist = [
{ name = "cachetools", specifier = ">=5.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" },
{ name = "httpx", specifier = ">=0.28.1" },
]