191 lines
5.6 KiB
Bash
191 lines
5.6 KiB
Bash
#!/bin/sh
|
|||
|
|
# weather - query current weather and forecasts for any location via wttr.in.
|
||
|
|
#
|
||
|
|
# wttr.in (https://github.com/chubin/wttr.in) is a console-oriented weather
|
||
|
|
# service backed by World Weather Online data. It supports several output
|
||
|
|
# formats: a graphical ANSI view for terminals (the default), one-line text
|
||
|
|
# formats (built-in presets 1-4 or a custom %-notation string), a rich JSON
|
||
|
|
# document (?format=j1 / j2) for scripts and APIs, plus PNG / HTML /
|
||
|
|
# Prometheus metrics.
|
||
|
|
#
|
||
|
|
# This script is a thin, friendly wrapper around the wttr.in HTTP API. It
|
||
|
|
# builds the request URL from the supplied location and options, fetches the
|
||
|
|
# data with curl and (when JSON is requested) pretty-prints it with jq.
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# weather [options] [location]
|
||
|
|
#
|
||
|
|
# Options:
|
||
|
|
# -l, --location LOC Location: city, airport code, domain, IP,
|
||
|
|
# "lat,lon" coordinates, or "~Name" for a custom label.
|
||
|
|
# Default: auto-detect from the request IP.
|
||
|
|
# -u, --units U Unit system: m (metric, default), u (USCS/imperial),
|
||
|
|
# M (metric with wind speed in m/s).
|
||
|
|
# -L, --lang LANG Output language code, e.g. de, fr, ru, zh-cn.
|
||
|
|
# -f, --format FMT Output format:
|
||
|
|
# j1 -> rich JSON document (default for --json)
|
||
|
|
# j2 -> JSON document, imperial units
|
||
|
|
# 1-4 -> built-in one-line presets
|
||
|
|
# "%..." -> custom one-line %-notation format
|
||
|
|
# Omit to get the default graphical terminal view.
|
||
|
|
# -j, --json Alias for --format j1 (emit JSON).
|
||
|
|
# -0 Current weather only.
|
||
|
|
# -1 Current weather + today's forecast.
|
||
|
|
# -2 Current weather + today's + tomorrow's forecast.
|
||
|
|
# -q, --quiet Quiet: no "Weather report" header / city name.
|
||
|
|
# -A Force ANSI output (ignore User-Agent detection).
|
||
|
|
# -h, --help Show this help text.
|
||
|
|
#
|
||
|
|
# Environment:
|
||
|
|
# WTTRAPI Base endpoint. Default: https://wttr.in
|
||
|
|
# WEATHER_TIMEOUT curl connect/read timeout in seconds. Default: 20
|
||
|
|
#
|
||
|
|
# Dependencies: curl, jq (jq only needed for the JSON format).
|
||
|
|
#
|
||
|
|
# Examples:
|
||
|
|
# weather
|
||
|
|
# weather London
|
||
|
|
# weather -u u -L de Berlin
|
||
|
|
# weather -f 3 "New York"
|
||
|
|
# weather -f "%l: %c %t (feels %f), wind %w %m" Paris
|
||
|
|
# weather -j Tokyo
|
||
|
|
|
||
|
|
set -eu
|
||
|
|
|
||
|
|
WTTRAPI="${WTTRAPI:-https://wttr.in}"
|
||
|
|
TIMEOUT="${WEATHER_TIMEOUT:-20}"
|
||
|
|
|
||
|
|
# Single-letter options (view/quiet/units) concatenated into one run.
|
||
|
|
SHORT=""
|
||
|
|
LANG_CODE=""
|
||
|
|
FORMAT=""
|
||
|
|
LOCATION=""
|
||
|
|
|
||
|
|
usage() {
|
||
|
|
sed -n '/^# Usage:/,/^# Examples:/p' "$0" | sed 's/^# \{0,1\}//'
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- Parse arguments. ---
|
||
|
|
while [ "$#" -gt 0 ]; do
|
||
|
|
case "$1" in
|
||
|
|
-h|--help)
|
||
|
|
usage
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
-l|--location)
|
||
|
|
[ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; }
|
||
|
|
LOCATION="$2"
|
||
|
|
shift 2
|
||
|
|
;;
|
||
|
|
-u|--units)
|
||
|
|
[ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; }
|
||
|
|
case "$2" in
|
||
|
|
m|u|M) SHORT="${SHORT}$2" ;;
|
||
|
|
*) echo "weather: unknown unit '$2' (use m, u or M)" >&2; exit 1 ;;
|
||
|
|
esac
|
||
|
|
shift 2
|
||
|
|
;;
|
||
|
|
-L|--lang)
|
||
|
|
[ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; }
|
||
|
|
LANG_CODE="$2"
|
||
|
|
shift 2
|
||
|
|
;;
|
||
|
|
-f|--format)
|
||
|
|
[ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; }
|
||
|
|
FORMAT="$2"
|
||
|
|
shift 2
|
||
|
|
;;
|
||
|
|
-j|--json)
|
||
|
|
FORMAT="j1"
|
||
|
|
shift
|
||
|
|
;;
|
||
|
|
-0|-1|-2|-q|-A)
|
||
|
|
SHORT="${SHORT}${1#-}"
|
||
|
|
shift
|
||
|
|
;;
|
||
|
|
--)
|
||
|
|
shift
|
||
|
|
LOCATION="${LOCATION:+$LOCATION }$(printf '%s' "$*" | sed 's/^ //')"
|
||
|
|
break
|
||
|
|
;;
|
||
|
|
-*)
|
||
|
|
echo "weather: unknown option '$1' (try --help)" >&2
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
# Positional: append to location (preserves multi-word locations).
|
||
|
|
LOCATION="${LOCATION:+$LOCATION }$1"
|
||
|
|
shift
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
|
||
|
|
# --- Assemble the query string. ---
|
||
|
|
# wttr.in accepts a leading run of single-letter options, then '&'-joined
|
||
|
|
# long options (key=value). We build both parts and join them.
|
||
|
|
QUERY="$SHORT"
|
||
|
|
|
||
|
|
# Long options: format and lang.
|
||
|
|
LONG=""
|
||
|
|
if [ -n "$FORMAT" ]; then
|
||
|
|
case "$FORMAT" in
|
||
|
|
j1|j2)
|
||
|
|
LONG="${LONG:+$LONG&}format=${FORMAT}"
|
||
|
|
;;
|
||
|
|
[1-4])
|
||
|
|
LONG="${LONG:+$LONG&}format=${FORMAT}"
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
# Custom %-notation one-line format -> URL-encode it.
|
||
|
|
ENC=$(printf '%s' "$FORMAT" | jq -sRr @uri)
|
||
|
|
LONG="${LONG:+$LONG&}format=${ENC}"
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
fi
|
||
|
|
if [ -n "$LANG_CODE" ]; then
|
||
|
|
LONG="${LONG:+$LONG&}lang=$(printf '%s' "$LANG_CODE" | jq -sRr @uri)"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Combine short + long parts into a single query string.
|
||
|
|
if [ -n "$QUERY" ] && [ -n "$LONG" ]; then
|
||
|
|
QUERY="${QUERY}&${LONG}"
|
||
|
|
elif [ -z "$QUERY" ] && [ -n "$LONG" ]; then
|
||
|
|
QUERY="$LONG"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# URL-encode the location (spaces -> %20, etc.). Empty means auto-detect.
|
||
|
|
if [ -n "$LOCATION" ]; then
|
||
|
|
LOC_ENC=$(printf '%s' "$LOCATION" | jq -sRr @uri)
|
||
|
|
else
|
||
|
|
LOC_ENC=""
|
||
|
|
fi
|
||
|
|
|
||
|
|
URL="${WTTRAPI}/${LOC_ENC}"
|
||
|
|
if [ -n "$QUERY" ]; then
|
||
|
|
URL="${URL}?${QUERY}"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# --- Fetch. ---
|
||
|
|
# wttr.in returns the graphical ANSI art whenever the request looks like it
|
||
|
|
# comes from a console client (curl's default User-Agent), and HTML for
|
||
|
|
# browsers. So we keep curl's default identity. The "-A" flag above is a
|
||
|
|
# wttr.in URL option ("force ANSI") and is already part of the query string.
|
||
|
|
|
||
|
|
RESP=$(curl -s -L --max-time "$TIMEOUT" "$URL") || {
|
||
|
|
echo "weather: failed to reach $WTTRAPI" >&2
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- Emit. ---
|
||
|
|
if [ "$FORMAT" = "j1" ] || [ "$FORMAT" = "j2" ]; then
|
||
|
|
if command -v jq >/dev/null 2>&1; then
|
||
|
|
printf '%s\n' "$RESP" | jq . 2>/dev/null || printf '%s\n' "$RESP"
|
||
|
|
else
|
||
|
|
printf '%s\n' "$RESP"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
printf '%s\n' "$RESP"
|
||
|
|
fi
|
||
|
|
|
||
|
|
exit 0
|