diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-18 14:16:28 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-18 14:16:28 +0200 |
| commit | 179e4e1faf8774f58139398f804ade2971de56d1 (patch) | |
| tree | 1afe1b6a9421dea83ed2bb702fe410cc35ad18ee /docs/superpowers/specs/2026-10-18-mirage-weather-cards-design.md | |
| parent | e54d9405dd818cd3ee7c203b8ec1d21713470904 (diff) | |
| download | conky-theme-udt-179e4e1faf8774f58139398f804ade2971de56d1.tar.gz conky-theme-udt-179e4e1faf8774f58139398f804ade2971de56d1.zip | |
docs: spec for the Mirage-derived weather cards
Five weather cards (now, hourly, week, details, sky) adapted from the
Conky-Mirage theme and drawn in this repo's design language, with the
data source moving from OpenWeatherMap to Open-Meteo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/superpowers/specs/2026-10-18-mirage-weather-cards-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-10-18-mirage-weather-cards-design.md | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-10-18-mirage-weather-cards-design.md b/docs/superpowers/specs/2026-10-18-mirage-weather-cards-design.md new file mode 100644 index 0000000..b2ee825 --- /dev/null +++ b/docs/superpowers/specs/2026-10-18-mirage-weather-cards-design.md @@ -0,0 +1,176 @@ +# Mirage weather cards: design + +Date: 2026-10-18 + +## Goal + +With most system cards moved to quickshell, the conky dashboard keeps the +time-and-place cards. This adds a fuller weather set, adapted from the +Conky-Mirage theme by alexsson-xexpanderx +(https://github.com/alexsson-xexpanderx/Conky-themes/tree/master/Conky-Mirage), +drawn in this repository's own design language. + +Mirage's tiles come over as ideas and maths, not as code. Mirage's look (glass +cut from the wallpaper, wallpaper-derived palette, animated icons) does not. + +## Scope + +In: + +- Five weather cards: `now`, `hourly`, `week`, `details`, `sky`. +- The current `weather` card is removed; `now` and `sky` cover it. +- Data moves from OpenWeatherMap to Open-Meteo (no key; hourly, daily, UV and + air quality available). +- Credit to Mirage in each new widget's header comment and in README.md. + +Out (deliberately): + +- Animated icons. The dashboard stays at its 2s update interval. +- Wallpaper glass, wallpaper palette, Mirage's fold-to-strip, its 12h option. +- Mirage's system tile. +- IP-based geolocation. Location comes from `weather.env` only. +- A second conky instance. Everything runs in the existing `dashboard.lua` + instance on the Hyprland special workspace `dash`. + +## Look + +The hybrid rule: DESIGN.md wins wherever it has an answer, and Mirage supplies +only what DESIGN.md calls a card's variable band. + +- Palette roles, fonts (`FONT_MONO`, `FONT_HEAVY`, `FONT_UI`) and chrome come + from `lib/card.lua`. +- Icons are Nerd Font weather glyphs, as today. Moon phases use the Nerd Font + `moon_*` glyph set (28 phases). Every new codepoint is rendered and looked + at before it is trusted (DESIGN.md, Glyphs). +- Every size derives from the card's rect. + +## Cards + +| Card | Header left | Big value (top-right) | Rows | Variable band | +|---|---|---|---|---| +| `now` | condition glyph | current temperature, `body` | feels like, high / low, rain chance, description in `FONT_UI` | none | +| `hourly` | glyph + "Next 24h" | next hour's temperature, `body` | none | temperature curve over 24 hours; every 3rd hour labelled with glyph and temperature; rain-chance bars under the curve in `rule` | +| `week` | glyph + "Week" | none (band carries the value) | one per day: day name, glyph, min, range bar, max | the range bar in each row, positioned on the week's overall min..max scale | +| `details` | glyph + "Details" | none | wind (speed + compass point), humidity, UV, AQI | a small wind compass with a needle at the wind direction | +| `sky` | glyph + "Sky" | moon-phase glyph, `heading` | sunrise, sunset, day length, moon illumination % | the daylight arc, moved from today's `widgets/weather.lua` | + +### Colour thresholds + +Measured quantities use the `ok` / `warning` / `critical` roles: + +| Quantity | ok | warning | critical | +|---|---|---|---| +| UV index | < 3 | 3 to < 8 | >= 8 | +| European AQI | <= 40 | <= 80 | > 80 | + +Humidity, wind and temperatures stay in `value` / `body`. This restores a +`critical` user to the board, closing the open question in HANDOFF about +DESIGN.md's three-states rule. + +### Failure states + +- No cache, or a cache that fails to parse: every card keeps its chrome and + draws `no weather data`. +- A missing field: `--`, never `0`. +- A stale cache (older than `STALE_AFTER`, 45 min): cards draw, and `now` + shows the age, as the current card does. +- No air-quality line: AQI row shows `--`; the rest of the card is unaffected. + +## Data + +### Fetcher: `bin/weather-fetch.sh` + +Rewritten in bash with curl and jq, the tools it already uses. + +1. Reads `~/.config/udt/weather.env`: `CITY`, `COUNTRY`, `UNITS` + (`metric` or `imperial`). `KEY` is gone. +2. Geocodes once via `https://geocoding-api.open-meteo.com/v1/search` + (name = `CITY`, filtered by `COUNTRY` code), caching `lat lon` in + `~/.cache/udt/weather-loc`. The cache is reused while `CITY`/`COUNTRY` are + unchanged (the cache file records them). +3. Calls `https://api.open-meteo.com/v1/forecast` with `timeformat=unixtime`, + `timezone=auto`, `forecast_days=7`, `current` (temperature, apparent + temperature, weather code, is_day, humidity, wind speed and direction), + `hourly` (temperature, weather code, precipitation probability, is_day, + uv_index), `daily` (weather code, min, max, precipitation probability max, + sunrise, sunset). `imperial` adds `temperature_unit=fahrenheit` and + `wind_speed_unit=mph`; `metric` uses `kmh`. +4. Calls `https://air-quality-api.open-meteo.com/v1/air-quality` with + `current=european_aqi`. Failure here is not fatal: the `air` line is + omitted. +5. jq flattens both responses into the line format below, written to a temp + file and renamed onto `~/.cache/udt/weather.txt` (atomic, as today). A + forecast failure leaves the previous cache in place and exits non-zero. + +### Cache format: `~/.cache/udt/weather.txt` + +Tab-separated, one record per line, first field the record type. Numbers as +Open-Meteo emits them; epochs in seconds. + +``` +units metric +current <epoch> <temp> <feels> <code> <is_day> <humidity> <wind> <wind_deg> +sun <sunrise_epoch> <sunset_epoch> +air <european_aqi> +hour <epoch> <temp> <code> <precip_prob> <is_day> <uv> +day <epoch> <code> <min> <max> <precip_prob> +``` + +`hour` covers the 24 hours starting at the current hour; `day` covers 7 days +starting today. `sun` is today's. Line-oriented so the Lua side parses with +`gmatch` and needs no JSON parser. + +### Lua: `lib/weather.lua` + +Rewritten for Open-Meteo, same contract as today: pure functions on strings +and numbers, nothing raises, nil on bad input. + +- `ICON` / `icon(code, is_day)`: WMO weather code to Nerd Font glyph. +- `describe(code)`: WMO code to a short English description. +- `parse(src)`: returns `{units, current, sun, air, hours, days}` or nil when + `current`, `sun` or at least one `hour` is missing. +- `moon(epoch)`: returns `phase` (0..1, 0 = new) and `illumination` (0..1), + from the synodic month against a known new moon. `moon_icon(phase)` picks one + of the 28 glyphs. +- `uv_role(uv)`, `aqi_role(aqi)`: threshold role names per the table above. +- Kept from today: `compass`, `arrow`, `sun_t`, `is_day`, `STALE_AFTER`, + `is_stale`, `age_str`. Removed: OWM-specific `kmh` and `beaufort`, unless a + card ends up using them. + +The UV shown in `details` is the `hour` record for the current hour. + +## Widgets + +New files in `widgets/`: `now.lua`, `hourly.lua`, `week.lua`, `details.lua`, +`sky.lua`. Each reads the cache through `lib/weather.lua` (same read-and-cache +pattern as today's `weather.lua`, which is deleted). The daylight arc moves to +`sky.lua`. + +`lib/card.lua` gets ICON entries for the new card titles if needed. + +## Layout + +`now` takes the old `weather` slot (col 1, row 7, w 3, h 5). `hourly`, +`week`, `details` and `sky` go into the open right side of the 16x12 grid +(cols 10 to 16). Exact cells are set in the plan and adjusted on the live +board. + +## Config and docs + +- `weather.env.example`: drop `KEY` and its OpenWeatherMap notes. +- README.md: weather section rewritten for Open-Meteo and the five cards, + Mirage credit, layout example updated from `weather` to the new names. +- `conky.conf.in`: unchanged (`texeci 900` on the same script). + +## Testing + +- `test/test_weather.lua` rewritten against a fixture + `test/fixtures/weather.txt` (location-free; any names use `Example City`): + - WMO code to glyph and description, day and night, unknown code fallback. + - `parse`: full fixture, missing `air`, missing `current` (nil), garbage + input (nil), empty string (nil). + - `moon`: known full and new moon dates within one day of tolerance. + - `uv_role` / `aqi_role` at each threshold boundary. +- Fetcher: run once by hand against the live API and inspect the cache. +- Each card rendered offscreen with `test/render.lua` at its layout size, and + the live board checked after `./restart.sh` for size beside neighbours. |
