diff --git a/README.md b/README.md index e69de29..aed623e 100644 --- a/README.md +++ b/README.md @@ -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` \ No newline at end of file diff --git a/main.py b/main.py index 7233928..b9a42d9 100644 --- a/main.py +++ b/main.py @@ -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("/") diff --git a/pyproject.toml b/pyproject.toml index 621c953..30a8326 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ requires-python = ">=3.13" dependencies = [ "fastapi[standard]>=0.115.12", "httpx>=0.28.1", + "cachetools>=5.0.0", ] [tool.pyright] diff --git a/template.liquid b/template.liquid index 6effdab..2a18d45 100644 --- a/template.liquid +++ b/template.liquid @@ -1,94 +1,73 @@ -
forecast at {{pollen.forecast_date}}
+{{period.period}} | - {% endfor %} -
---|
{{period.index}} | - {% endfor %} -
forecast at {{weather.forecast_date}}
+- {% for period in weather.periods %} - | {{period.period}} | - {% endfor %} -
---|---|
low | - {% for period in weather.periods %} -{{period.low}} F | - {% endfor %} -
high | - {% for period in weather.periods %} -{{period.high}} F | - {% endfor %} -
humidity | - {% for period in weather.periods %} -{{period.humidity}}% | - {% endfor %} -
pressure | - {% for period in weather.periods %} -{{period.pressure}} | - {% endfor %} -
sunrise | - {% for period in weather.periods %} -{{period.sunrise}} | - {% endfor %} -
sunset | - {% for period in weather.periods %} -{{period.sunset}} | - {% endfor %} -
desc | - {% for period in weather.periods %} -{{period.desc}} | - {% endfor %} -