A Python service that generates natural, kid-friendly weather descriptions using OpenWeatherMap and an LLM API (can be a small, self hosted model).
I wrote a blog post about how I use it as part of a weather eInk display:
The display is driven by a $40 single board computer and shows weather forecasts that can be easily understood.
Transforms complex weather data into simple, engaging descriptions that kids can understand. Instead of "Partly cloudy with 70% chance of precipitation," you get "It might rain later - maybe bring an umbrella! 🌧️"
The service includes:
- Command-line weather reports with HTML rendering
- A shared service layer (
WeatherReportService) used by every entrypoint - API caching for performance
- LLM interaction logging and replay capabilities
- Support for multiple LLM providers with automatic fallback
- Python 3.9+
- uv for dependency management (recommended) or pip
- OpenWeatherMap API key (free tier works fine)
- LLM API key (DeepSeek, OpenAI, OpenRouter, or any OpenAI-compatible API)
- Install uv if you haven't already:
curl -LsSf https://astral.sh/uv/install.sh | sh-
Clone this repository
-
Create and activate a virtual environment:
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate- Install dependencies:
uv sync
# OR without uv: pip install .- Copy the example environment file and configure your API keys:
cp .env.example .env
# Edit .env with your API keysBefore running these commands, make sure your .env file is properly configured with API keys as described in the Installation section.
Generate a weather report for a specific location:
uv run python -m kidsweather --lat 38.9 --lon -77.0Load test data from a file (files live in test_data/ by default):
uv run python -m kidsweather --load dc1Save weather data for testing (writes to test_data/ unless you supply --save with a different path):
uv run python -m kidsweather --lat 38.9 --lon -77.0 --save dc_latestLog LLM interactions:
uv run python -m kidsweather --lat 38.9 --lon -77.0 --log-interactionsSee what the model was told:
uv run python -m kidsweather --lat 38.9 --lon -77.0 --verboseRender weather report as HTML to a file:
uv run python -m kidsweather --render page.htmlThis generates a standalone HTML file with weather information, styled for display on e-ink screens or other devices.
Replay logged LLM interactions with different prompts or models:
uv run python replay.py --log-id 5
uv run python replay.py --log-id 5 --new-model deepseek-coder
uv run python replay.py --log-id 5 --prompt "You are a pirate weather forecaster."{
"description": "It's cool right now, maybe wear a light jacket. 🧥 The rest of the day looks cloudy but nice!",
"daily_forecasts": [
"Monday: Cloudy day ☁️",
"Tuesday: Rain coming later 🌧️",
"Wednesday: Still rainy",
"Thursday: Cloudy again"
],
"temperature": 47,
"feels_like": 44,
"conditions": "overcast clouds",
"high_temp": 59,
"low_temp": 47,
"icon_url": "http://openweathermap.org/img/wn/[email protected]",
"alerts": [],
"last_updated": "Friday, April 11 at 11:30 AM"
}- OpenWeatherMap: Get a free API key at https://openweathermap.org/api
- LLM Provider: Choose from DeepSeek, OpenAI, OpenRouter, or any OpenAI-compatible API
The project is organized as a Python package with a clear separation of concerns:
- Core (
kidsweather/core/): Contains the main service orchestration and settings managementsettings.py: Loads environment variables into a single dataclass tree and ensures required directories existservice.py: TheWeatherReportServiceorchestrates data fetch, formatting, LLM generation, and logging
- Clients (
kidsweather/clients/): External API integration layersweather_client.py: Fetches current conditions and optional historical summaries from OpenWeatherMap, applying diskcache when configuredllm_client.py: Wraps the primary and optional fallback LLM providers, normalising JSON responses and caching successful calls
- Formatting (
kidsweather/formatting/): Data preparation and output generationweather_formatter.py: Prepares both the LLM prompt context and the data needed for displayhtml.py: Containsrender_to_file()function for HTML rendering using Jinja2 templates for e-ink displays
- Infrastructure (
kidsweather/infrastructure/): Cross-cutting concernscache_provider.py: Cache construction and management utilitiesllm_logging.py: Persists LLM interactions to SQLite for replay and debugging
- Templates (
kidsweather/templates/): Jinja2 templates for HTML rendering - Tests (
kidsweather/tests/): Unit and integration tests
The main CLI entry point is kidsweather/__main__.py, which delegates to the Click-based command interface in main.py.
- Uses OpenWeatherMap's One Call API 3.0
- Supports any OpenAI-compatible chat completions API
- Configuration lives in
kidsweather/core/settings.pyand is shared across every entrypoint - Temperatures are in Fahrenheit
- Caches API and LLM responses for 10 minutes using
diskcache - Logs LLM interactions to
llm_log.sqlite3for replay - Includes optional automatic fallback between LLM providers
- HTML rendering uses Jinja2 templates from
kidsweather/templates/ - Organized package structure with clear separation of concerns
