No description
Find a file
Love Billenius dba1011b84 Add weather-disp: cached SMHI weather report for the shell
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>
2026-09-14 14:56:22 +02:00
macos Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00
src Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00
tests/fixtures Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00
.gitignore Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00
build.rs Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00
Cargo.lock Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00
Cargo.toml Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00
README.md Add weather-disp: cached SMHI weather report for the shell 2026-09-14 14:56:22 +02:00

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-disp on macOS, ~/.cache/weather-disp on Linux, %LOCALAPPDATA%\weather-disp on 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:

  1. --lat/--lon or --place "Uppsala" (geocoded, cached per place).
  2. 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 --refresh keep trying) so a cold start stays quick.
  3. 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

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).