#!/bin/bash # weather-tag.sh — print a one-word weather tag for the current moment at # (LATITUDE, LONGITUDE), using the OpenWeather "Current weather" endpoint. # # Output: a single lowercase word from this fixed vocabulary, or empty: # thunderstorm rain rainshower snow snowstorm # fog windy cloudy clear # # Used by ambient-record.sh to suffix each 30-minute chunk's filename so the # file collection self-documents what the audio actually captured. # # Failure-safe: if WEATHER_ENABLED≠true, the API key is missing, the network # is down, the API returns an error, or the response can't be parsed, this # script prints NOTHING and exits 0. Callers MUST treat empty output as # "no tag — fall back to the un-tagged filename". Never blocks the recorder. # # Required setup: # 1. Sign up at https://openweathermap.org/api → free "Current weather" plan # 2. Put the key in .env: OPENWEATHER_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxx # 3. Confirm LATITUDE / LONGITUDE are set in .env (already needed by sunrise.py) # 4. Flip WEATHER_ENABLED=true in sky-cam.conf # # Manual test: ./weather-tag.sh (prints e.g. "cloudy" or nothing) set -uo pipefail # NOT -e: we want to swallow every failure into "no tag" SCRIPT_DIR="$(dirname "$(realpath "$0")")" source "$SCRIPT_DIR/sky-cam.conf" 2>/dev/null || exit 0 source "$SCRIPT_DIR/.env" 2>/dev/null || true [ "${WEATHER_ENABLED:-false}" = "true" ] || exit 0 [ -n "${OPENWEATHER_API_KEY:-}" ] || exit 0 [ -n "${LATITUDE:-}" ] || exit 0 [ -n "${LONGITUDE:-}" ] || exit 0 # ── Fetch current conditions ───────────────────────────────────────────────── # 5s connect / 8s total — must never delay the recorder noticeably. url="https://api.openweathermap.org/data/2.5/weather?lat=${LATITUDE}&lon=${LONGITUDE}&appid=${OPENWEATHER_API_KEY}&units=metric" resp=$(curl -s --connect-timeout 5 --max-time 8 "$url" 2>/dev/null) || exit 0 [ -n "$resp" ] || exit 0 # ── Parse condition ID + wind speed ────────────────────────────────────────── # Condition codes: https://openweathermap.org/weather-conditions # OpenWeather may return multiple entries in weather[]; the first is primary. if command -v jq >/dev/null 2>&1; then code=$(echo "$resp" | jq -r '.weather[0].id // empty' 2>/dev/null) wind=$(echo "$resp" | jq -r '.wind.speed // 0' 2>/dev/null) else # Fallback parser if jq is missing — grabs the first "id":NNN it sees. code=$(echo "$resp" | grep -oE '"id":[0-9]+' | head -n1 | grep -oE '[0-9]+') wind=$(echo "$resp" | grep -oE '"speed":[0-9.]+' | head -n1 | grep -oE '[0-9.]+') fi [ -n "${code:-}" ] || exit 0 wind="${wind:-0}" # ── Map condition code → tag (vocabulary documented at top of file) ────────── # Group ranges follow OpenWeather's published condition-code table: # 2xx thunder / 3xx drizzle / 5xx rain / 6xx snow / 7xx atmosphere / 800 clear / 80x clouds tag="" case "$code" in 2*) tag="thunderstorm" ;; 3*) tag="rainshower" ;; # drizzle = light shower 500|520|521|522|531) tag="rainshower" ;; # light / shower variants 501|502|503|504|511) tag="rain" ;; # moderate → extreme + freezing 600|601|612|613|615|616|620|621) tag="snow" ;; 602|622) tag="snowstorm" ;; # heavy snow / heavy shower snow 611) tag="snow" ;; # sleet 701|711|721|741|762) tag="fog" ;; # mist / smoke / haze / fog / ash 731|751|761|771) tag="windy" ;; # dust whirls / sand / dust / squall 781) tag="thunderstorm" ;; # tornado → reuse strongest tag 800) tag="clear" ;; 801) tag="clear" ;; # few clouds — still mostly clear 802|803|804) tag="cloudy" ;; *) tag="" ;; # unknown code → no tag esac # Wind override: gusty conditions drown out other audio cues. If the base # tag is clear/cloudy and wind ≥ 8 m/s (~18 mph), promote to "windy". if [ "$tag" = "clear" ] || [ "$tag" = "cloudy" ]; then # bash can't compare floats — strip the decimal for an integer check. wind_int="${wind%.*}" [ "${wind_int:-0}" -ge 8 ] 2>/dev/null && tag="windy" fi [ -n "$tag" ] && printf '%s' "$tag" exit 0