diff options
| -rwxr-xr-x | bin/weather-fetch.sh | 22 | ||||
| -rw-r--r-- | weather.env.example | 3 |
2 files changed, 13 insertions, 12 deletions
diff --git a/bin/weather-fetch.sh b/bin/weather-fetch.sh index 4b37b17..e9f205b 100755 --- a/bin/weather-fetch.sh +++ b/bin/weather-fetch.sh @@ -21,10 +21,8 @@ if [ ! -r "$ENV_FILE" ]; then exit 1 fi -set -a # shellcheck source=/dev/null . "$ENV_FILE" -set +a if [ -z "${CITY:-}" ]; then echo "weather-fetch: CITY is empty in $ENV_FILE" >&2 @@ -32,20 +30,21 @@ if [ -z "${CITY:-}" ]; then fi UNITS="${UNITS:-metric}" -mkdir -p "$CACHE_DIR" - # Location data shares a directory with UDT's other generated state, all of -# which is 600. Set the mode before anything lands, so no file is briefly +# which is 600. Set the mode before anything lands, the +# directory included, so no file is briefly # world-readable. umask 077 +mkdir -p "$CACHE_DIR" + # Temp files sit beside the target, because rename is only atomic within a # filesystem. The widgets read the cache on their own 2s cadence, so a fetch # killed mid-write would otherwise hand them half a cache. TMP="$CACHE.tmp.$$" FC="$TMP.forecast" AIR="$TMP.air" -trap 'rm -f "$TMP" "$FC" "$AIR"' EXIT +trap 'rm -f "$TMP" "$FC" "$AIR" "$LOC.tmp.$$"' EXIT # Geocode once per CITY/COUNTRY pair. The cache's first field records which # pair it answers, so editing weather.env re-geocodes on the next run. @@ -53,7 +52,7 @@ PAIR="$CITY,${COUNTRY:-}" lat="" lon="" cached="" if [ -r "$LOC" ]; then IFS=$'\t' read -r cached lat lon < "$LOC" - [ "$cached" = "$PAIR" ] || lat="" + [ "$cached" = "$PAIR" ] && [ -n "$lon" ] || lat="" fi if [ -z "$lat" ]; then read -r lat lon < <(curl -fsS --max-time 15 --get \ @@ -66,7 +65,8 @@ if [ -z "$lat" ]; then echo "weather-fetch: cannot geocode $PAIR" >&2 exit 1 fi - printf '%s\t%s\t%s\n' "$PAIR" "$lat" "$lon" > "$LOC" + printf '%s\t%s\t%s\n' "$PAIR" "$lat" "$lon" > "$LOC.tmp.$$" + mv -f "$LOC.tmp.$$" "$LOC" fi if [ "$UNITS" = imperial ]; then @@ -98,13 +98,13 @@ curl -fsS --max-time 15 --get "https://air-quality-api.open-meteo.com/v1/air-qua # Flatten to the line format lib/weather.lua parses. The 24 hours start at # the current hour: the first hourly slot later than an hour before now. -if ! jq -r --arg units "$UNITS" --slurpfile air "$AIR" ' +if ! jq -r --arg units "$UNITS" --rawfile air "$AIR" ' .current.time as $now | ([.hourly.time | to_entries[] | select(.value > $now - 3600) | .key] | first) as $i | "units\t\($units)", (.current | "current\t\(.time)\t\(.temperature_2m)\t\(.apparent_temperature)\t\(.weather_code)\t\(.is_day)\t\(.relative_humidity_2m)\t\(.wind_speed_10m)\t\(.wind_direction_10m)"), "sun\t\(.daily.sunrise[0])\t\(.daily.sunset[0])", - ($air[0].current.european_aqi // empty | "air\t\(.)"), + (($air | fromjson?) | .current.european_aqi // empty | "air\t\(.)"), (.hourly as $h | range($i; [$i + 24, ($h.time | length)] | min) as $k | "hour\t\($h.time[$k])\t\($h.temperature_2m[$k])\t\($h.weather_code[$k])\t\($h.precipitation_probability[$k])\t\($h.is_day[$k])\t\($h.uv_index[$k])"), (.daily as $d | range(0; $d.time | length) as $k @@ -115,7 +115,7 @@ if ! jq -r --arg units "$UNITS" --slurpfile air "$AIR" ' fi # A 200 whose body is not a usable forecast must not replace a good cache. -if ! grep -q $'^current\t[0-9]*\t-\\?[0-9]' "$TMP"; then +if ! grep -q $'^current\t[0-9]\+\t-\\?[0-9]' "$TMP"; then echo "weather-fetch: forecast has no current temperature" >&2 exit 1 fi diff --git a/weather.env.example b/weather.env.example index 7996d44..69606c4 100644 --- a/weather.env.example +++ b/weather.env.example @@ -5,8 +5,9 @@ # The forecast comes from Open-Meteo (https://open-meteo.com), which needs no # key. CITY is geocoded once through Open-Meteo's geocoding API; COUNTRY is the # two-letter ISO code that picks the right place among same-named ones. +# This file is sourced by bash, so a name with spaces needs quotes. # UNITS is metric (C, km/h) or imperial (F, mph). -CITY=Example City +CITY="Example City" COUNTRY=XX UNITS=metric |
