Intent
Self/home ambient. A splash without weather feels oddly austere; this is a low-value, high-delight widget that signals the tool is personal, not just technical.
Fetcher kind + safety
- `Fetcher` (cached). TTL 30 min — weather doesn't change that fast, and it keeps the wttr.in / open-meteo load polite.
- `Safety::Safe`. Host hardcoded; config-provided fields are `location` (a name like "Tokyo") and `units`, neither of which is an exfil vector. See `project_trust_model.md` — fixed-host + no credentials = Safe.
Data source
Two interchangeable backends:
- wttr.in (default). No API key, returns compact JSON on `?format=j1`, tolerant of location strings ("Tokyo", "94043", airport codes).
- open-meteo.com. No key, forecast data. Location needs lat/lon; we'd either ask the user to provide it or geocode via `open-meteo.com/v1/geocoding`.
Both hosts are hardcoded in the fetcher struct; config can't redirect.
Payload shape
`Body::Entries`:
| key |
value |
| `condition` |
`Partly cloudy` (with `icon` = ☁️) |
| `temp` |
`18°C / 64°F` |
| `feels_like` |
`16°C` |
| `humidity` |
`72%` |
| `wind` |
`4 m/s SW` |
| `sunset` |
`18:14` |
Some renderers (`bignum`) will want a scalar `temp` — that's a layout concern, the fetcher returns the full entry set and the layout picks what to show.
Config schema sketch
```toml
[[widgets]]
id = "weather"
fetcher = "weather"
[widgets.weather]
location = "Tokyo" # name, zip, airport code; wttr.in parses loosely
source = "wttr" # "wttr" | "open_meteo"
units = "metric" # "metric" | "imperial"
```
Design questions
- Location auto-detect: wttr.in does `?format=j1` + no location → IP-based geolocation. That's convenient on a home network, misleading on VPN. Default: explicit location. Document `location = ""` as opt-in auto-detect.
- Missing location, default mode: don't auto-geolocate without opt-in — the privacy surprise ("splashboard knows where I am") beats the convenience.
- Forecast: separate `weather_forecast` widget so users can opt into the bigger payload independently.
- Icon selection: a static map from condition string → emoji. wttr.in's condition codes are stable.
- Offline: show the last cached payload with a staleness indicator ("weather (cached 3h ago)"). Don't error.
Related widgets (see #41)
- weather_forecast (3-day) — separate
- sunrise_sunset — subset that could be folded in here
- moon_phase — parallel ambient widget, different data source
Out of scope
- Hyper-local radar / precipitation maps. Wrong tool.
- Multi-location rollups (weather in 3 cities). Users wire 3 widgets.
Intent
Self/home ambient. A splash without weather feels oddly austere; this is a low-value, high-delight widget that signals the tool is personal, not just technical.
Fetcher kind + safety
Data source
Two interchangeable backends:
Both hosts are hardcoded in the fetcher struct; config can't redirect.
Payload shape
`Body::Entries`:
Some renderers (`bignum`) will want a scalar `temp` — that's a layout concern, the fetcher returns the full entry set and the layout picks what to show.
Config schema sketch
```toml
[[widgets]]
id = "weather"
fetcher = "weather"
[widgets.weather]
location = "Tokyo" # name, zip, airport code; wttr.in parses loosely
source = "wttr" # "wttr" | "open_meteo"
units = "metric" # "metric" | "imperial"
```
Design questions
Related widgets (see #41)
Out of scope