- Rust 100%
A Rust CLI meant to run from .bashrc/.zshrc: - SMHI SNOW1g v1 point forecasts (PMP3g was retired 2026-03-31), with Open-Meteo as fallback outside SMHI's Nordic coverage - Normalised forecast model cached as JSON; cache hits render in ~5 ms and stale data is shown while a detached background refresh runs - Location via --lat/--lon, --place (geocoded), CoreLocation on macOS, or IP geolocation on every platform - ASCII artwork, hourly strip with temperature sparkline and rain bars, daily outlook, sunrise/sunset; Swedish by default, English optional - Mid-luminance palette with >= 3:1 contrast on both light and dark terminals; colours off when piped so lolcat gets clean text - IPv4-first resolver so a broken IPv6 route cannot stall cold starts Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|---|---|---|
| macos | ||
| src | ||
| tests/fixtures | ||
| .gitignore | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
weather-disp
A blazingly fast, cached weather report for your shell, built for .bashrc /
.zshrc. Swedish first: forecasts come from SMHI
(the SNOW1g point-forecast API), with Open-Meteo as
an automatic fallback outside SMHI's Nordic coverage.
Linköping måndag 14 september 14:28 · SMHI 14:28
.-. Lätt regn
( ). 17.5°C
(___(__) ↖ SO 4 m/s (byar 8)
' ' ' ' ☂ 0.1 mm (63 %) · moln 8/8
' ' ' ' fukt 70 % · 1020 hPa
☀ 06:24 ☾ 19:22
15 16 17 18 19 20 21 22 23 00 01 02
☂ ☁ ☂ ☁ ☁ ☁ ☁ ☾ ☾ ☾ ☾ ☾☁
18° 18° 17° 16° 15° 15° 14° 13° 13° 12° 11° 11°
████████▇▇▇▇▆▆▆▆▆▆▅▅▅▅▅▄▄▄▄▄▄▄▃▃▃▃▃▂▂▂▂▂▁▁▁▁▁▁▁▁
☂ ▁▁▁ ▂▂▂
tis 15 ☁ 10° 18° ☂ –
ons 16 ☂ 13° 18° ☂ 7.0 mm
tor 17 ☀☁ 11° 16° ☂ 0.4 mm
Everything stays within about 50 columns. A ↻ after the source time means
stale data is being refreshed in the background; offline · cachad 14:28
means the network was unreachable.
Why it is fast
- Cache hit ≈ 5 ms end to end. The forecast is normalised once and stored
as a small JSON file in the platform cache dir (
~/Library/Caches/weather-dispon macOS,~/.cache/weather-dispon Linux,%LOCALAPPDATA%\weather-dispon Windows). Rendering a hit never touches the network. - Stale-while-revalidate. When the cache is older than
--ttl(30 min by default) the stale report is printed immediately and a detached background process refreshes it. Your prompt never waits. A lock file stops ten new tabs from refreshing ten times. - Bounded cold start. The first run (or data older than six hours) fetches
in the foreground, capped by
--timeout(6 s). IPv4 addresses are tried before IPv6 so a broken IPv6 route cannot eat the budget. - No async runtime, no OpenSSL: a single static binary with
rustls.
Location
Resolved in this order:
--lat/--lonor--place "Uppsala"(geocoded, cached per place).- Native sensor – on macOS, CoreLocation (Wi-Fi positioning). The first
run makes macOS ask whether your terminal app may use your location; allow it
under System Settings → Privacy & Security → Location Services. If the
sensor fails, foreground runs skip it for 12 hours (background refreshes and
--refreshkeep trying) so a cold start stays quick. - IP geolocation (ipinfo.io, then ipapi.co) on every platform.
Linux and Windows currently go straight to step 3; the sensor module is the
place to add GeoClue / Windows.Devices.Geolocation backends.
Install
cargo install --path .
Then in ~/.zshrc or ~/.bashrc:
# Full panel on every new shell
command -v weather-disp >/dev/null && weather-disp
# …or rainbow
command -v weather-disp >/dev/null && weather-disp | lolcat
# …or one line in the prompt (cache hit only costs a few ms)
RPROMPT='$(weather-disp -1 --color never)'
Colours are disabled automatically when stdout is not a terminal or NO_COLOR
is set, so lolcat gets clean text to paint. The palette is mid-luminance on
purpose: every colour keeps at least 3:1 contrast against both white and black,
so light and dark terminal themes both work (there is a test for it).
Usage
weather-disp # auto-locate, full panel
weather-disp -1 # one line: ☂ 18° Lätt regn · ↖ 4 m/s · Linköping
weather-disp -p Kiruna # named place
weather-disp --lat 59.33 --lon 18.07
weather-disp --lang en --ascii # English, pure-ASCII glyphs
weather-disp --hours 24 --days 7 --no-art
weather-disp --json # cached forecast as JSON
weather-disp --refresh # bypass the cache now
weather-disp --offline # never touch the network
weather-disp --refresh --verbose # explain location lookup and fetching
Every option can also be set through the environment, e.g. WEATHER_DISP_PLACE,
WEATHER_DISP_LANG, WEATHER_DISP_TTL, WEATHER_DISP_NO_SENSOR=1,
WEATHER_DISP_CACHE_DIR. See weather-disp --help.
Data sources
- SMHI Open Data – Meteorological Forecasts (SNOW1g v1),
CC BY 4.0. The 27
symbol_codevalues are the condition vocabulary used throughout; other sources are mapped onto it. - Open-Meteo forecast and geocoding APIs, CC BY 4.0.
- Nominatim for reverse geocoding sensor coordinates (called at most once per refresh, and only when you have moved > 2 km).
- ipinfo.io / ipapi.co for IP fallback.
Development
cargo test
cargo clippy --all-targets
cargo build --release
Modules: smhi/openmeteo (providers → model::Forecast), location/geo/
sensor (where), cache (atomic JSON files + lock), render/style
(output), sun (sunrise/sunset), meteo (feels-like, wind), app (flow).