blob: d4e31c084d185ac2a2232727e2f5ca7344c83173 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
#!/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"
|