#!/bin/bash # Fetch current weather into a cache file. Run from conky's ${execi}, so it # prints nothing on success and never blocks the dashboard: the widget only # ever reads the cache. # # Exits non-zero with a message on stderr when it cannot fetch, and leaves any # existing cache untouched rather than replacing good data with an error. set -u ENV_FILE="${WEATHER_ENV:-$HOME/.config/udt/weather.env}" CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/udt" CACHE="$CACHE_DIR/weather.json" if [ ! -r "$ENV_FILE" ]; then echo "weather-fetch: no $ENV_FILE (copy weather.env.example)" >&2 exit 1 fi set -a # shellcheck source=/dev/null . "$ENV_FILE" set +a if [ -z "${KEY:-}" ]; then echo "weather-fetch: KEY is empty in $ENV_FILE" >&2 exit 1 fi mkdir -p "$CACHE_DIR" # The temp file sits in the same directory as the target, because rename is # only atomic within a filesystem. The widget reads this cache on its own 2s # cadence, so a fetch killed mid-write would otherwise hand it half a response. TMP="$CACHE.tmp.$$" trap 'rm -f "$TMP"' EXIT # The response carries the configured city and its coordinates. That is not a # secret, but it is location data and it shares a directory with UDT's other # generated state, all of which is 600. Set the mode before the body lands in # the file rather than after, so it is never briefly world-readable. umask 077 URL="https://api.openweathermap.org/data/2.5/weather" if ! curl -fsS --max-time 15 --get "$URL" \ --data-urlencode "appid=$KEY" \ --data-urlencode "units=${UNITS:-metric}" \ --data-urlencode "lang=${LANG_CODE:-en}" \ --data-urlencode "q=${CITY},${COUNTRY}" \ -o "$TMP" 2>/dev/null; then echo "weather-fetch: request failed" >&2 exit 1 fi # Belt and braces behind `curl -f`. Measured against the live API: a bad key # returns 401 and an unknown city 404, so curl -f already rejects both and this # branch is not what catches them. It stays for the case -f cannot see: a 200 # whose body is not usable weather, which is what the polybar script this was # ported from actually hit, since it ran curl WITHOUT -f and so received error # bodies with a success exit. Without this, such a body would reach the parser # and replace a good cache. if [ "$(jq -r '.cod // empty' "$TMP" 2>/dev/null)" != "200" ]; then echo "weather-fetch: API error: $(jq -r '.message // "unknown"' "$TMP" 2>/dev/null)" >&2 exit 1 fi mv -f "$TMP" "$CACHE"