Skip to content

refactor: unify into single weather module with shared geocoding and multi-API consensus command #4

@SuaveIV

Description

@SuaveIV

The repo has two independent weather scripts — weather.nu (wttr.in) and meteo.nu (Open-Meteo) — each with its own copy of the same helper functions. Adding a third API means a third copy. At that point the duplication is worth fixing properly.

This refactor may also be a good opportunity to move the module into its own repo (nu_mod_weather). A five-command module with shared helpers, a geocoding layer, and its own cache strategy is no longer really a "scripts" folder drop-in — it's a package. A dedicated repo also keeps the original standalone scripts available for people who just want something simple, without mixing two different installation models in the same place. That decision doesn't need to be made upfront, but it's worth keeping in mind as the scope grows.

Problems with the current structure

  • format-temp, beaufort-scale, beaufort-icon, wind-dir-icon, resolve-cache-dir, is-cache-valid, and http-get-with-retry are duplicated verbatim across both scripts. A bug fix has to be applied twice.
  • Location resolution works differently in each: weather.nu defers to wttr.in's opaque lookup, meteo.nu calls Open-Meteo's geocoding endpoint. The same input string can resolve to different results.
  • The imperial/metric decision uses country-name string matching, and the logic is slightly inconsistent between the two scripts.
  • The scripts can't share data or call each other's internals without going through the CLI layer.
  • weather.nu is named after its command rather than its source, which will look inconsistent once the other source-named scripts exist.

Minimum Nushell version

0.111.0

Naming rationale

weather.nu is renamed to wttr.nu so all source files are named after their API, not their command. The weather command name moves to wx.nu, which is the most useful command in the module and the natural default for someone who just wants the weather. Individual backends are still available by name for scripts or when you want a specific source.

Migration note

Existing users calling weather "New York" will get the combined wx output after upgrading instead of wttr.in specifically. This is a superset of the old behaviour, but worth a note in the README. Users of weather.nu who have Nerd Fonts enabled will also see emoji by default after migration unless $env.NERD_FONTS = "1" is set.

Proposed module layout

weather/
  mod.nu        # re-exports all commands
  helpers.nu    # all shared helpers (unexported)
  wttr.nu       # exports `wttr`     (wttr.in)
  meteo.nu      # exports `meteo`    (Open-Meteo)
  yr.nu         # exports `yr`       (MET Norway / Yr.no)
  nws.nu        # exports `nws`      (NOAA / NWS — US only)
  wx.nu         # exports `weather`  (multi-API combined view — the main entry point)

Single entry in config.nu:

use scripts/weather

weather becomes the combined command. Users who want a specific backend can call wttr, meteo, yr, or nws directly.

Shared geocoding layer

Geocoding moves out of the individual scripts and into helpers.nu. Every command gets the same canonical location record — coordinates, display name, country code — rather than resolving the city string on its own.

Preferred geocoder: Nominatim (nominatim.openstreetmap.org)

  • Global coverage, no API key
  • Returns country code, which drives the imperial/metric decision
  • Needs a User-Agent header and stays under 1 req/sec — the existing 15-minute cache handles this automatically

Fallback chain if a geocoder fails:

Nominatim → Photon → Open-Meteo geocoder → error

Photon (photon.komoot.io) is also OSM-based, no key, and more typo-tolerant. Open-Meteo's geocoder is already in meteo.nu and serves as the last resort before giving up. If all three fail, the error should name all three and suggest the user check their connection rather than just reporting whichever one failed last.

Geocoding cache

Geocoding gets its own cache directory (nu_geocode_cache) with a much longer TTL than weather data. City coordinates don't change, so caching them for days is fine.

Unified data flow

flowchart TD
    A[city string] --> B[Nominatim geocode\nhelpers.nu]
    B --> C[name / country_code / lat / lon]
    C --> D[wttr.nu]
    C --> E[meteo.nu]
    C --> F[yr.nu]
    C --> G{US location?}
    G -->|yes| H[nws.nu]
    G -->|no| I[skip]
    D --> J[weather\nwx.nu]
    E --> J
    F --> J
    H --> J
Loading

Country code from Nominatim becomes the single source of truth for units, replacing the country-name string matching in both current scripts.

Cache layout

Each source keeps its own cache directory under $nu.cache-dir, which is the standard location for this in Nushell 0.111.0+. The old /tmp fallback in the current scripts gets dropped.

nu_geocode_cache    # long TTL — coordinates don't change
nu_weather_cache    # wttr (15 min)
nu_meteo_cache      # meteo (15 min)
nu_yr_cache         # yr (15 min)
nu_nws_cache        # nws (15 min)

Each command keeps its own --clear-cache flag for clearing just that source. The weather command gets an additional --clear-all-caches flag that wipes every directory the module owns, including nu_geocode_cache. This matters because the geocoding cache has a long TTL — if a city resolves to bad coordinates, --clear-cache on an individual source won't help since the bad geocode result will just get reused. --clear-all-caches is the escape hatch for that.

Default city configuration

Each command resolves its default city in priority order: source-specific variable first, shared fallback second, then auto-detect from IP.

$env.WTTR_CITY  → $env.WEATHER_CITY → auto-detect    (wttr)
$env.METEO_CITY → $env.WEATHER_CITY → auto-detect    (meteo)
$env.YR_CITY    → $env.WEATHER_CITY → auto-detect    (yr)
$env.NWS_CITY   → $env.WEATHER_CITY → auto-detect    (nws)

Most users only need to set $env.WEATHER_CITY once and every command picks it up. The per-source variables exist for edge cases — an airport code that resolves better in one API than another, or a coastal location where you want yr to default to a slightly different point. Existing users who already have $env.WTTR_CITY or $env.METEO_CITY set don't need to change anything.

Icon mode

The two current scripts handle icons inconsistently — weather.nu defaults to Nerd Fonts, meteo.nu defaults to plain text. The module standardises this:

  • Default: emoji, since it works without any font setup
  • Nerd Font mode: opt-in via $env.NERD_FONTS = "1", consistent with how meteo.nu already handles it
  • Plain text: -t / --text flag as before

Raw mode

--raw defaults to text mode since most pipe targets (JSON, scripts, clipboard) don't want icons or ANSI. Icon flags and a new --color flag still apply when passed explicitly — to_gui supports emoji/Nerd Font glyphs, ANSI colour, and ANSI hyperlinks. Full priority:

--raw alone               → text, no icons, no ANSI
--raw --emoji             → emoji glyphs, no ANSI
--raw --nerd              → Nerd Font glyphs, no ANSI
--raw --color             → text, no icons, ANSI colour + hyperlinks preserved
--raw --emoji --color     → emoji glyphs, ANSI colour + hyperlinks preserved
--raw --nerd --color      → Nerd Font glyphs, ANSI colour + hyperlinks preserved

--color enables both ANSI colour and ANSI hyperlinks together — the location field links to the relevant source URL (e.g. https://wttr.in/New+York), which to_gui can render as a clickable link. --color has no effect outside of --raw since the normal display path always renders colour and links.

APIs: MET Norway and NWS

If the module is getting built out anyway, adding a fourth API isn't much more work — the shared geocoding and helpers do the heavy lifting; each script is just the fetch and display logic for that source. Four also makes for a more complete picture than three, since these cover meaningfully different strengths:

Command Source Strength Gap
wttr wttr.in Moon/astronomy, broad format support Opaque, no real alerts
meteo Open-Meteo AQI, UV, snowfall, fast No moon data
yr MET Norway Best non-US precision, real alerts Needs User-Agent care
nws NOAA / NWS Best US alerts, most authoritative for the US US-only

MET Norway (api.met.no) — the backend behind Yr.no. No API key, just a User-Agent header, global coverage. The main reason to add it is real alert data. wttr.nu infers severity from weather codes, which is a rough approximation. MET Norway has an actual alert endpoint. It also has better precision at high latitudes and marine/coastal data neither current script exposes.

NWS (api.weather.gov) — US-only, but the most authoritative source for US alerts by a wide margin. No API key. The US-only limitation is handled automatically: weather checks the country code from Nominatim and skips the NWS column for non-US locations. Users outside the US never see it; users inside the US get it for free.

Combined command: weather

weather (exported from wx.nu) calls the four internal build functions directly (not the CLI commands), gets structured records back, then compares or merges them.

This only works cleanly once the scripts share a geocoding step and return structured records from internal functions — which is the other reason to do this refactor.

Comparison mode (default)

All sources side by side. Where they disagree is useful information in itself. The NWS column appears automatically for US locations and is omitted everywhere else — weather knows from the geocoded country code and doesn't need to be told.

              wttr.in    Open-Meteo    MET Norway    NWS
────────────────────────────────────────────────────────
Temperature   72°F       70°F          73°F          71°F
Feels Like    68°F       67°F          69°F          68°F
Wind          15 mph NW  13 mph NW     16 mph NW     14 mph NW
Rain          0.1 in     0.08 in       0.1 in        0.09 in

Consensus mode (--consensus flag)

Merges into a single record. Numeric fields are averaged across whichever sources are active; source-specific fields come from wherever they're actually available:

Field Source
Temperature, wind, precip Average of all active sources
Moon / astronomy wttr only
AQI Open-Meteo only
Weather alerts NWS (US) or MET Norway (everywhere else)
Precipitation probability Most conservative (highest value)

An --uncertainty flag could add a stddev column to numeric fields.

--source flag

Each backend is callable directly (wttr, meteo, yr, nws), so --source is technically redundant. Worth adding anyway — it keeps everything accessible through the single weather entry point without needing to remember which individual commands exist, and it makes source-pinning explicit in scripts and aliases rather than relying on knowing the module internals.

weather --source wttr "Tokyo"
weather --source meteo "Tokyo"
weather --source yr "Tokyo"
weather --source nws "Tokyo"    # errors outside the US

Valid values: wttr, meteo, yr, nws. Passing an unrecognised value should error with the list of valid options rather than silently falling back to the combined view.

Latency

Four sequential HTTP calls would be slow. Two options: use par-each for parallel fetches (error handling is limited in Nushell), or lean on warm caches. If the individual commands have run recently, weather costs almost nothing since the caches are already populated.

Source failures

If a weather source fails during a weather fetch, it is skipped and noted in the output rather than taking down the whole command. A failed NWS fetch in the US shows the remaining three columns with a nws: failed note.

Geocoding uses a fallback chain (Nominatim → Photon → Open-Meteo) so a single geocoder outage is handled silently. If all three geocoders fail, that is the one unrecoverable error — every weather source depends on coordinates, so there is nothing sensible to show. The error should name all three failed geocoders and suggest checking the user's connection.

Checklist

  • Extract shared helpers into helpers.nu
  • Add Nominatim geocoding to helpers.nu with fallback chain (Nominatim → Photon → Open-Meteo) and long-TTL cache
  • Implement $env.WEATHER_CITY shared fallback with per-source variable priority order
  • Standardise icon mode: emoji default, Nerd Fonts via $env.NERD_FONTS = "1", text default for --raw unless icon flag explicitly passed; add --color flag to preserve ANSI and hyperlinks in raw mode for to_gui
  • Rename weather.nu to wttr.nu and update its exported command name to wttr
  • Refactor wttr.nu to use shared geocoding and helpers
  • Refactor meteo.nu to use shared geocoding and helpers
  • Implement yr.nu against MET Norway API
  • Implement nws.nu against NWS API (gracefully skips outside the US)
  • Write wx.nu exporting weather — comparison and consensus modes, --source flag, --clear-all-caches, NWS column conditional on country code, skip-and-note on source failure
  • Update mod.nu to re-export all five commands
  • Update README with new installation instructions, command rename note, icon mode change, and weather usage
  • Add privacy note to README covering IP exposure via ipapi.co, city string logging by geocoders, coordinate exposure to weather APIs, and wttr.in opacity
  • Update TESTING.md with wttr, yr, nws, and weather test cases

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions