diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..17ad747 --- /dev/null +++ b/README.md @@ -0,0 +1,189 @@ +# prognosis + +Hour-by-hour weather for the terminal, with official Polish warnings and pollen. + +Companion to [wego](https://github.com/schachmat/wego), which renders only four +dayparts per day. `prognosis` fills the gap wego cannot: an hourly table, a +temperature chart, IMGW warnings for your own powiat, and pollen counts with +qualitative bands. + +No API key. Written in Go with **no third-party modules** — `make ci` fails if +one creeps in. + +``` + ! Upał level 1 08-10 11:00 -> 08-10 20:00 (85%) + Prognozuje się upał. Temperatura maksymalna wyniesie od 30°C do 33°C. + +Krakow, PL Mon 10 Aug 05:15 up 20:01 down GMT+2 + day 12-30° dry sun 12h24m of 14h45m daylight + pollen grass 10.5 low mugwort 4.5 low ragweed 1.4 + + hr temp feels conditions + 13 28° part cloudy + 14 29° + 15 30° (28) mainly clear + ... + + 30°│ ▄▄▄▄▄██████████▄▄▄▄▄▄▄▄▄▄ + │▄▄▄▄▄██████████████████████████████▄▄▄▄▄ + 26°│████████████████████████████████████████ + │█████████████████████████████████████████████ + 22°│█████████████████████████████████████████████▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + 12 14 16 18 20 22 + 22-30° +``` + +## Install + + make build # build ./prognosis + make install # build and install into ~/.local/bin + make cross # dist/ binaries for linux-amd64, linux-arm64, android-arm64 + make ci # gofmt, vet, tests, and the no-dependencies check + +Needs `~/.local/bin` on `PATH`. For the phone, copy `dist/prognosis-android-arm64` +across — no interpreter, no shebang, nothing to install. + +## Configuration + +`~/.config/prognosis/config`, written with commented defaults on first run +(`prognosis -config` prints the path). One `KEY=VALUE` per line, `#` comments. +Flags override the file; the file overrides the built-in defaults. + +Columns are chosen and ordered freely: + + columns=hour,icon,temp,feels,conditions,wind,gusts,rain + +Available: `hour icon temp feels conditions mm rain wind gusts dir humidity dew +uv cloud pressure visibility`. Only the fields you select are requested from the +API. An unknown name is a startup error listing the valid ones, never a silently +blank column. + +`icons=` picks the glyph set: `nerd` (default), `emoji` or `none`. Nerd Font +glyphs are single-width and monochrome, so they follow the terminal palette; +emoji are colour glyphs from a fallback font and are not all one cell wide. + +`display_lang=` is `en` or `pl`, covering everything prognosis writes itself — +headers, condition names, labels, dates, pollen species and bands. **IMGW +publishes its warning text in Polish only**, so that text stays Polish in either +language: an invented English rendering of an official warning would be worse +than the original. + +## Usage + + prognosis # next 12 hours where you live + prognosis -l krakow # somewhere else + prognosis 50.0617,19.9373 # or by coordinates + prognosis -n 24 # next 24 hours + prognosis -d 3 # three days, 24h each (max 15) + prognosis --no-graph # table only + prognosis --no-color # plain text + +Location comes from `location=` in `~/.wegorc`, so wego and prognosis never +disagree about where you are. `-l` overrides it for one run. + +Quote a name that contains a comma or a space. `-l Wiry, PL` is two arguments +once the shell has finished with it, and prognosis refuses it rather than +quietly forecasting for whatever `Wiry,` alone resolves to. + +An ambiguous name is never guessed. prognosis lists what matched and fetches +nothing, so a request for one place can't silently return another: + + $ prognosis -l "Wiry, PL" + prognosis: "Wiry, PL" is ambiguous - nothing fetched. + 1 Wiry, PL Greater Poland 52.3205,16.8532 + 2 Wiry, PL Lower Silesia 50.8367,16.6467 + Re-run with -pick N, or give coordinates as the place. + +`-pick N` chooses one and remembers it, so you only do this once per name. It +also re-resolves rather than reading the cache, which is what lets it correct a +name that was cached wrongly. Exit status is 2 for both the ambiguity and an +out-of-range `-pick`. + +Output is pipe-safe: colour switches off when stdout is not a terminal, so +`prognosis > file` is clean UTF-8. Notes and failures go to stderr, so use +`2>&1` for an unattended job. + +## Data sources + +| Source | Used for | +|---|---| +| [Open-Meteo forecast](https://open-meteo.com/) | hourly temps, precipitation, codes, daily summary, sun times | +| [Open-Meteo air quality](https://open-meteo.com/en/docs/air-quality-api) | pollen per species | +| [Open-Meteo geocoding](https://open-meteo.com/en/docs/geocoding-api) | place name → coordinates | +| [GUGiK](https://services.gugik.gov.pl/uug/) | coordinates → TERYT powiat code (Poland) | +| [IMGW](https://danepubliczne.imgw.pl/) | official meteorological warnings | + +Geocoding and TERYT results are cached in `~/.cache/prognosis/cache.json`. +Forecast, warnings and pollen are fetched every run, so three HTTP calls. + +The cache is written one entry per line with its keys sorted, so it is readable +and hand-editable. It is also treated as disposable but never as worthless: an +entry that will not parse is skipped and the rest of the file is kept, and a file +that will not parse at all is moved to `cache.json.bad` rather than overwritten, +so a typo costs you the lookups but not what you wrote. + +## Warnings + +IMGW publishes every warning in Poland, each tagged with the TERYT codes of the +powiats it covers. GUGiK turns your coordinates into that code, so warnings are +filtered to your area rather than the whole country. + +Three states, kept deliberately distinct — **silence must never be mistaken for +all-clear**: + +- warnings printed — in force for your powiat +- nothing printed — checked, none in force +- `warnings: could not check IMGW` — the check itself failed +- `warnings: IMGW covers Poland only` — the location is abroad + +## Thresholds and why they are what they are + +Nothing here is a matter of taste, except where it says so. + +**Temperature colours** use IMGW's own warning criteria, so a red temperature +means the met office would issue a warning about it: + +| | criterion | colour | +|---|---|---| +| Silny mróz, stopień 1 | `Tmin ≤ -15°C` | bright blue | +| Upał, stopień 1 | `Tmax ≥ 30°C` | red | +| Upał, higher level | `Tmax > 35°C` | bright red | + +The divisions between (0, 10, 20) are round numbers, not thresholds from any +source; they only subdivide the range nobody warns about. + +**Pollen bands**, grains/m³, from Polish clinical sources: + +- **grass** — 20 = first nasal symptoms in ~25% of sufferers, 50 = symptoms in + all tested, 65 = intensified in over 75%, 120 = dyspnoea after 30 minutes + ([alergen.info.pl](http://www.alergen.info.pl/Alergeny/Pylek_trawy)) +- **birch** — 80 provokes symptoms in over 95% of allergics ([mp.pl](https://www.mp.pl/pacjent/alergie/lista/105140,jakie-czynniki-wplywaja-na-stezenie-alergenow-wziewnych)) +- **mugwort** — over 70 counts as high, intensified symptoms (mp.pl) + +Birch and mugwort have a single published anchor each, so they get a two-way +split rather than four bands — their "low" is weaker evidence than grass's. +Alder, olive and ragweed have no Polish threshold I could source and are shown +as bare numbers rather than banded on a guess. + +## Notes for anyone editing this + +- **Colour uses ANSI slots 0–15 only**, never 256-colour indices. It runs on a + terminal whose palette remaps the low slots to shades of green; a hardcoded + `38;5;196` would be the one non-green thing on screen. +- **Pad cells to width before colouring them.** Escape sequences carry no + visible width, so padding a coloured string misaligns the whole column. This + is invisible when piped (colour off) and obvious in a real terminal. +- **Open-Meteo hourly arrays start at 00:00 local.** Slicing from the front + reports this morning, not the hours ahead. See `openmeteo.WindowStart`. +- **The table does not shrink to fit.** Column widths are fixed; the default set + needs 34 columns. `TestTableMinimumWidthIsKnown` pins that figure. Use + `columns=` for a narrow terminal. +- **GUGiK's default search radius is 100 m**, which finds nothing in the + mountains or deep countryside — indistinguishable from being abroad. The + request asks for more; GUGiK clamps it to its own 5 km maximum. +- **The chart downsamples.** A week is 168 hourly points; columns cover several + hours on long spans and the header says `Nh/col` when they do. + +## Licence + +GNU General Public License v3. See `LICENSE`. |
