#!/usr/bin/env bash # Weather from met.no for the IP-geolocated position. The last result is # cached on disk, so re-opening the panel is instant and offline-safe; the # network is hit at most every 10 minutes. fallback='{"temp":0,"feelslike":0,"humidity":0,"wind":0,"desc":"Unavailable","icon":"󰖐","city":""}' cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}/eww loc_cache=$cache_dir/weather-location.json wx_cache=$cache_dir/weather.json interval=600 ua="eww-bar/1.0 cedric.hoelzl@gmail.com" mkdir -p "$cache_dir" fresh() { [[ -s $1 ]] && [[ -n $(find "$1" -mmin "-$2" 2>/dev/null) ]]; } # FILE MINUTES location() { fresh "$loc_cache" 60 && { cat "$loc_cache"; return; } local data if data=$(curl -sf --max-time 5 "http://ip-api.com/json?fields=lat,lon,city"); then printf '%s' "$data" | tee "$loc_cache" elif [[ -s $loc_cache ]]; then cat "$loc_cache" else return 1 fi } icon_for() { case $1 in *thunder*) echo "󰖔" ;; *snow* | *sleet*) echo "󰖘" ;; heavyrain*) echo "󰖖" ;; *rain* | *shower*) echo "󰖗" ;; fog*) echo "󰖑" ;; cloudy*) echo "󰖐" ;; partlycloudy*) echo "󰖕" ;; fair* | clearsky*) echo "󰖙" ;; *) echo "󰖐" ;; esac } desc_for() { local c=$1 c=${c%_day}; c=${c%_night}; c=${c%_polartwilight} case $c in clearsky) echo "Clear sky" ;; fair) echo "Fair" ;; partlycloudy) echo "Partly cloudy" ;; cloudy) echo "Cloudy" ;; fog) echo "Foggy" ;; lightrain) echo "Light rain" ;; rain) echo "Rain" ;; heavyrain) echo "Heavy rain" ;; lightrainshowers) echo "Light showers" ;; rainshowers) echo "Rain showers" ;; heavyrainshowers) echo "Heavy showers" ;; lightrainandthunder) echo "Light rain & thunder" ;; rainandthunder) echo "Rain & thunder" ;; heavyrainandthunder) echo "Heavy rain & thunder" ;; *showersandthunder) echo "Showers & thunder" ;; lightsleet | lightsleetshowers) echo "Light sleet" ;; sleet | sleetshowers) echo "Sleet" ;; heavysleet | heavysleetshowers) echo "Heavy sleet" ;; *sleetandthunder) echo "Sleet & thunder" ;; lightsnow | lightsnowshowers) echo "Light snow" ;; snow | snowshowers) echo "Snow" ;; heavysnow | heavysnowshowers) echo "Heavy snow" ;; *snowandthunder) echo "Snow & thunder" ;; *) echo "$c" ;; esac } fetch() { local loc lat lon city data temp humidity wind code feelslike loc=$(location) || return 1 read -r lat lon city < <(jq -r '"\(.lat) \(.lon) \(.city)"' <<<"$loc") data=$(curl -sf --max-time 8 -H "User-Agent: $ua" -H "Accept: application/json" \ "https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=$lat&lon=$lon") || return 1 read -r temp humidity wind code < <(jq -r ' .properties.timeseries[0].data | "\(.instant.details.air_temperature | round) \(.instant.details.relative_humidity | round) \(.instant.details.wind_speed * 3.6 | floor) \(.next_1_hours.summary.symbol_code // .next_6_hours.summary.symbol_code // "cloudy")"' <<<"$data") [[ -n $temp ]] || return 1 # Apparent temperature: wind chill below 10 °C, otherwise the air temperature. feelslike=$(awk -v t="$temp" -v v="$wind" 'BEGIN { if (v > 4.8 && t < 10) printf "%d", 13.12 + 0.6215 * t - 11.37 * (v ^ 0.16) + 0.3965 * t * (v ^ 0.16) else printf "%d", t }') printf '{"temp":%s,"feelslike":%s,"humidity":%s,"wind":%s,"desc":"%s","icon":"%s","city":"%s"}' \ "$temp" "$feelslike" "$humidity" "$wind" "$(desc_for "$code")" "$(icon_for "$code")" "$(jstr "$city")" } refresh() { local out if fresh "$wx_cache" $((interval / 60)); then emit "$(cat "$wx_cache")" elif out=$(fetch); then printf '%s\n' "$out" >"$wx_cache" emit "$out" elif [[ -s $wx_cache ]]; then emit "$(cat "$wx_cache")" else emit "$fallback" fi } refresh while sleep "$interval"; do refresh; done