463 lines
13 KiB
Bash
Executable File
463 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# client version, calls server for actual API info
|
|
usage() {
|
|
cat <<EOF
|
|
usage: $0 [-a KEY][-s QUERY | -z ZIPCODE][-u <c|f>][-p][-i][-l][-h]
|
|
|
|
Queries OpenWeatherMapAPI for weather or lat/long
|
|
options:
|
|
-a, --apikey set the api key to use to KEY
|
|
-l, --location print the location being used
|
|
-p, --pretty enables nerdfont symbols
|
|
-d, --desc prints the conditions text as well
|
|
-z, --zipcode returns the weather for ZIPCODE
|
|
-s, --search returns the weather for QUERY
|
|
-u, --units sets the temperature units
|
|
-i, --icon_test tests the icons used
|
|
-r, --radar displays the radar for a given location
|
|
-f, --forecast displays the forecast a given location
|
|
-h, --help show this message
|
|
|
|
ZIPCODE sets the location to the given zipcode
|
|
to narrow results, include the ISO country code
|
|
ex) -z "02139,US"
|
|
|
|
QUERY sets the location to the search
|
|
QUERY format is "CITY,REGION,COUNTRY" where
|
|
EOF
|
|
exit $1
|
|
}
|
|
|
|
WORKING_DIR="$HOME/.local/share/weather"
|
|
WEATHER_FILE=".env"
|
|
|
|
TIMEOUT=60 # timeout for info
|
|
TIME=$(date +%s)
|
|
|
|
|
|
if [[ ! -d "$WORKING_DIR" ]] ; then
|
|
mkdir -p "$WORKING_DIR"
|
|
fi
|
|
|
|
# going to weather directory
|
|
cd "$WORKING_DIR"
|
|
|
|
print_weather() {
|
|
# prints the weather
|
|
|
|
# check to see if it is expired
|
|
check_expiration
|
|
|
|
# checking if description enabled
|
|
if [[ "$DESCRIPTION" = true ]] ; then
|
|
DESC=$(echo "$CONDITIONS" | awk '{for(i=1;i<=NF;i++){ $i=toupper(substr($i,1,1)) substr($i,2) }}1')
|
|
DESC=$(printf '%s ' "$DESC")
|
|
fi
|
|
|
|
if [[ "$PRETTY" = true ]] ; then
|
|
# print with NF icons
|
|
icon=$(get_icon "$WEATHER_ICON")
|
|
humidity_icon=$(echo -e '\ue373')
|
|
|
|
# checking units
|
|
if [[ "$UNIT" == "C" ]] ; then
|
|
temp_icon=$(echo -e '\ufa03')
|
|
else
|
|
temp_icon=$(echo -e '\ufa04')
|
|
fi
|
|
else
|
|
# default printing
|
|
humidity_icon="% RH"
|
|
if [[ "$UNIT" == "C" ]] ; then
|
|
temp_icon="*C"
|
|
else
|
|
temp_icon="*F"
|
|
fi
|
|
fi
|
|
|
|
# printing
|
|
printf '%s%s %.1f%s %d%s in %s\n' "$DESC" "$icon" $TEMPERATURE "$temp_icon" $HUMIDITY "$humidity_icon" "$CITY"
|
|
|
|
# save before exiting
|
|
save_info
|
|
}
|
|
|
|
print_location() {
|
|
# prints the location being used for the weather
|
|
|
|
# uses reverse geocoding
|
|
LOCATION_URL="https://api.openweathermap.org/geo/1.0/reverse?lat=$LAT&lon=$LON&appid=$API_KEY"
|
|
LOCATION=$(curl --silent -fL "$LOCATION_URL" | gojq '.[0] | del(.local_names)')
|
|
|
|
name=$(echo "$LOCATION" | gojq '.name' | tr -d '"')
|
|
state=$(echo "$LOCATION" | gojq '.state' | tr -d '"')
|
|
country=$(echo "$LOCATION" | gojq '.country' | tr -d '"')
|
|
|
|
printf 'Location is set to %s in %s, %s\n' "$name" "$state" "$country"
|
|
}
|
|
|
|
check_expiration() {
|
|
# checks if the info is expired
|
|
if [[ $TIME -gt $EXPIRATION ]] ; then
|
|
# expired
|
|
get_weather
|
|
fi
|
|
}
|
|
|
|
get_location() {
|
|
# searching for user location
|
|
if [[ -n "$SEARCH" ]] ; then
|
|
# search
|
|
LOCATION_URL="https://api.openweathermap.org/geo/1.0/$SEARCH&appid=$API_KEY&limit=5"
|
|
RESULTS=$(curl --silent -fL "$LOCATION_URL")
|
|
# check if its an array
|
|
is_array=$(echo "$RESULTS" | gojq 'if type=="array" then 1 else 0 end')
|
|
|
|
if [[ $is_array -eq 1 ]] ; then
|
|
# returned result is an array
|
|
|
|
NUM_RESULTS=$(echo "$RESULTS" | gojq '. | length')
|
|
if [[ $NUM_RESULTS -gt 1 ]] ; then
|
|
# provide menu for multiple results
|
|
for ((i=0; i < $NUM_RESULTS; i++)); do
|
|
# adding options
|
|
res=$(echo "$RESULTS" | gojq ".[$i]")
|
|
name=$(echo "$res" | gojq '.name' | tr -d '"')
|
|
state=$(echo "$res" | gojq '.state' | tr -d '"')
|
|
country=$(echo "$res" | gojq '.country' | tr -d '"')
|
|
opt=$(printf '%s %s, %s' "$name" "$state" "$country")
|
|
OPTIONS+=($(($i+1)) "$opt")
|
|
done
|
|
|
|
# executing dialog menu
|
|
exec 3>&1
|
|
SELECTION=$(dialog \
|
|
--title 'Multiple Locations Found!' \
|
|
--clear \
|
|
--cancel-label 'Exit' \
|
|
--ok-label 'Select' \
|
|
--menu 'Please select the desired location. If none of the options look correct, try making refining your search by including a country code and/or region' 0 0 4 \
|
|
"${OPTIONS[@]}" \
|
|
2>&1 1>&3)
|
|
exit_status=$?
|
|
exec 3>&-
|
|
clear
|
|
|
|
if [[ $exit_status -eq 1 || $exit_satus -eq 255 ]] ; then
|
|
# canceled or escaped
|
|
echo "Location not changed"
|
|
exit 0
|
|
fi
|
|
|
|
# fix index
|
|
SELECTION=$(($SELECTION - 1))
|
|
fi
|
|
# updating result
|
|
RESULTS=$(echo "$RESULTS" | gojq ".[$SELECTION]")
|
|
fi
|
|
|
|
# Update info
|
|
LAT=$(echo "$RESULTS" | gojq ".lat")
|
|
LON=$(echo "$RESULTS" | gojq ".lon")
|
|
CITY=$(echo "$RESULTS" | gojq ".name" | tr -d '"')
|
|
else
|
|
# no search, default to user IP
|
|
|
|
url="http://ip-api.com/csv/?fields=252"
|
|
res=$(curl --silent -fL "$url")
|
|
LAT=$(awk -F , '{print $5}' <<<"$res")
|
|
LON=$(awk -F , '{print $6}' <<<"$res")
|
|
CITY=$(awk -F , '{print $3}' <<<"$res")
|
|
fi
|
|
|
|
# check
|
|
if [[ -z "$LAT" || -z "$LON" ]] ; then
|
|
echo "No location found!" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# force refresh
|
|
EXPIRED=0
|
|
}
|
|
|
|
get_weather() {
|
|
# calls api for weather based on $LAT, $LONG
|
|
|
|
if [[ -z "$LAT" || -z "$LON" ]] ; then
|
|
# no lat or lon
|
|
get_location
|
|
fi
|
|
|
|
if [[ "$UNIT" =~ ^[Cc]$ ]] ; then
|
|
# units set to metric
|
|
UNIT="C"
|
|
UNITS="metric"
|
|
elif [[ "$UNIT" =~ ^[Ff]$ || -z "$UNIT" ]] ; then
|
|
# imperial (default)
|
|
UNIT="F"
|
|
UNITS="imperial"
|
|
else
|
|
printf 'Unit %s unrecognized\n' "$UNIT" >&2
|
|
exit 1
|
|
fi
|
|
|
|
WEATHER_URL="https://api.openweathermap.org/data/2.5/weather?lat=$LAT&lon=$LON&appid=$API_KEY&units=$UNITS"
|
|
|
|
WEATHER=$(curl --silent -fL "$WEATHER_URL")
|
|
|
|
CONDITIONS=$(echo $WEATHER | gojq -r '.weather[0].description')
|
|
TEMPERATURE=$(echo $WEATHER | gojq -r '.main.temp')
|
|
HUMIDITY=$(echo $WEATHER | gojq -r '.main.humidity')
|
|
WEATHER_ICON=$(echo $WEATHER | gojq -r '.weather[0].icon')
|
|
EXPIRATION=$(($TIME+$TIMEOUT))
|
|
}
|
|
|
|
update_key() {
|
|
# updates the API key used
|
|
|
|
printf 'Testing API key %s... ' "$API_KEY"
|
|
WEATHER_URL="https://api.openweathermap.org/data/2.5/weather?lat=42.3736&lon=71.1097&appid=$API_KEY"
|
|
CODE=$(curl --silent "$WEATHER_URL" | gojq '.cod' | tr -d '"')
|
|
|
|
if [[ $CODE -eq 401 ]] ; then
|
|
# API_KEY error
|
|
printf 'Error: Invalid API Key "%s"!\n' "$API_KEY" >&2
|
|
exit 1
|
|
fi
|
|
printf 'Success: Key Updated!\n'
|
|
|
|
# saving new key
|
|
save_info
|
|
}
|
|
|
|
save_info() {
|
|
# saves set env vars to file
|
|
printf '%s="%s"\n' \
|
|
"API_KEY" "$API_KEY" \
|
|
"PARSER" "$PARSER" \
|
|
"LAT" "$LAT" \
|
|
"LON" "$LON" \
|
|
"CITY" "$CITY" \
|
|
"TEMPERATURE" "$TEMPERATURE" \
|
|
"HUMIDITY" "$HUMIDITY" \
|
|
"UNIT" "$UNIT" \
|
|
"WEATHER_ICON" "$WEATHER_ICON" \
|
|
"CONDITIONS" "$CONDITIONS" \
|
|
"EXPIRATION" "$EXPIRATION" > "$WEATHER_FILE"
|
|
}
|
|
|
|
load_info() {
|
|
# loads env vars
|
|
if [[ ! -e "$WEATHER_FILE" ]] ; then
|
|
# generates blank info on fresh installs
|
|
|
|
# test for gojq
|
|
PARSER=gojq
|
|
if ! command -v $PARSER ; then
|
|
# test for jq
|
|
PARSER=jq
|
|
if ! command -v $PARSER ; then
|
|
echo "$1 depends on jq or gojq"
|
|
exit 1
|
|
fi
|
|
fi
|
|
save_info
|
|
fi
|
|
source $WEATHER_FILE
|
|
|
|
}
|
|
|
|
display_forecast() {
|
|
# two step process, gets weather station from NWS, then mpv to play radar
|
|
if [[ -z "$LAT" || -z "$LON" ]] ; then
|
|
echo "Location not found!"
|
|
exit 1
|
|
fi
|
|
echo "Loading weather..."
|
|
if [[ -n "$PRETTY" ]] ; then
|
|
# nerdfont
|
|
curl -fsSL "https://v2d.wttr.in/${LAT},${LON}?F" | less -R
|
|
else
|
|
curl -fsSL "https://wttr.in/${LAT},${LON}?F" | less -R
|
|
fi
|
|
|
|
exit 0
|
|
}
|
|
|
|
display_radar() {
|
|
# two step process, gets weather station from NWS, then mpv to play radar
|
|
if [[ -z "$LAT" || -z "$LON" ]] ; then
|
|
echo "Location not found!"
|
|
exit 1
|
|
fi
|
|
# grabbing nws station
|
|
NWS=$(curl -fsSL "https://api.weather.gov/points/$LAT,$LON")
|
|
STATION=$(echo "$NWS" | gojq '.properties.radarStation' | tr -d '"')
|
|
|
|
# validation
|
|
if [[ -z "$STATION" ]] ; then
|
|
echo "NWS Station error!"
|
|
exit 1
|
|
fi
|
|
# mpv to play weather in fullscreen
|
|
echo "Fetching weather for NWS Station $STATION..."
|
|
mpv --fs --loop-file "https://radar.weather.gov/ridge/standard/${STATION}_loop.gif" >/dev/null 2>&1
|
|
|
|
exit 0
|
|
}
|
|
|
|
get_icon() {
|
|
# sets NF symbols
|
|
tod=$(echo "$1" | sed --expression='s/[0-9]//g')
|
|
conditions=$(echo "$1" | sed --expression='s/[^0-9]//g')
|
|
|
|
# getting icon
|
|
if [[ "$tod" == "d" ]] ; then
|
|
# day icons
|
|
case "$conditions" in
|
|
"01") echo -e '\ue30d' ;; # clear
|
|
"02") echo -e '\ue30c' ;; # scattered clouds
|
|
"03") echo -e '\ue302' ;; # broken clouds
|
|
"04") echo -e '\ue312' ;; # cloudy
|
|
"09") echo -e '\ue309' ;; # showers
|
|
"10") echo -e '\ue308' ;; # rain
|
|
"11") echo -e '\ue30f' ;; # thunderstorm
|
|
"13") echo -e '\uf2dc' ;; # snow
|
|
"50") echo -e '\ue303' ;; # mist
|
|
* ) echo -e '\ue374' ;; # unknown
|
|
esac
|
|
elif [[ "$tod" == "n" ]] ; then
|
|
# night icons
|
|
case "$conditions" in
|
|
"01") echo -e '\ue32b' ;; # clear
|
|
"02") echo -e '\ue379' ;; # scattered clouds
|
|
"03") echo -e '\ue37e' ;; # broken clouds
|
|
"04") echo -e '\ue312' ;; # cloudy
|
|
"09") echo -e '\ue326' ;; # showers
|
|
"10") echo -e '\ue325' ;; # rain
|
|
"11") echo -e '\ue32a' ;; # thunderstorm
|
|
"13") echo -e '\uf2dc' ;; # snow
|
|
"50") echo -e '\ue346' ;; # mist
|
|
* ) echo -e '\ue374' ;; # unknown
|
|
esac
|
|
else
|
|
echo "TOD not recognized"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
icon_test() {
|
|
#tests icons
|
|
printf 'Testing weather icons\nIf any look broken, check that NerdFont is installed\n'
|
|
|
|
printf '\nDay:\n'
|
|
printf 'Clear: \ue30d\n'
|
|
printf 'Partly Cloudy: \ue30c\n'
|
|
printf 'Cloudy: \ue302\n'
|
|
printf 'Very Cloudy: \ue312\n'
|
|
printf 'Showers: \ue309\n'
|
|
printf 'Rain: \ue308\n'
|
|
printf 'Thunderstorm: \ue30f\n'
|
|
printf 'Snow: \uf2dc\n'
|
|
printf 'Fog: \ue303\n'
|
|
# night icons
|
|
printf '\nNight:\n'
|
|
printf 'Clear: \ue32b\n'
|
|
printf 'Partly Cloudy: \ue379\n'
|
|
printf 'Cloudy: \ue37e\n'
|
|
printf 'Very Cloudy: \ue312\n'
|
|
printf 'Showers: \uf2dc\n'
|
|
printf 'Rain: \ue325\n'
|
|
printf 'Thunderstorm: \ue32a\n'
|
|
printf 'Snow: \uf2dc\n'
|
|
printf 'Fog: \ue346\n'
|
|
|
|
printf '\nAssorted:\n'
|
|
printf 'Degrees (F) \ufa04\n'
|
|
printf 'Degrees (C) \ufa03\n'
|
|
printf '%% Humidity \ue373\n'
|
|
exit 0
|
|
}
|
|
|
|
# shifting longform
|
|
for arg in "$@"; do
|
|
shift
|
|
case "$arg" in
|
|
'--zipcode') set -- "$@" "-z" ;;
|
|
'--location') set -- "$@" "-l" ;;
|
|
'--apikey') set -- "$@" "-a" ;;
|
|
'--coords') set -- "$@" "-c" ;;
|
|
'--search') set -- "$@" "-s" ;;
|
|
'--units') set -- "$@" "-u" ;;
|
|
'--pretty') set -- "$@" "-p" ;;
|
|
'--pretty') set -- "$@" "-p" ;;
|
|
'--icon_test') set -- "$@" "-i" ;;
|
|
'--radar') set -- "$@" "-r" ;;
|
|
'--forecast') set -- "$@" "-f" ;;
|
|
'--help') set -- "$@" "-h" ;;
|
|
*) set -- "$@" "$arg" ;;
|
|
esac
|
|
done
|
|
|
|
# loading the info
|
|
load_info
|
|
|
|
while getopts "chpldrfiz:s:u:a:" opt; do
|
|
case "$opt" in
|
|
'a' )
|
|
API_KEY="$OPTARG"
|
|
update_key
|
|
;;
|
|
'u' )
|
|
UNIT="$OPTARG"
|
|
;;
|
|
'l' )
|
|
print_location
|
|
exit 0
|
|
;;
|
|
'z' )
|
|
[ -n "$SEARCH" ] && usage 1 || SEARCH="/zip?zip=$OPTARG"
|
|
;;
|
|
's' )
|
|
[ -n "$SEARCH" ] && usage 1 || SEARCH="/direct?q=$OPTARG"
|
|
;;
|
|
'p' )
|
|
PRETTY=true
|
|
;;
|
|
'd' )
|
|
DESCRIPTION=true
|
|
;;
|
|
'i' )
|
|
icon_test
|
|
;;
|
|
'r' )
|
|
display_radar
|
|
;;
|
|
'f' )
|
|
display_forecast
|
|
;;
|
|
'h' )
|
|
usage 0
|
|
;;
|
|
'?' )
|
|
usage 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# test for APIKEY
|
|
if [[ -z "$API_KEY" ]] ; then
|
|
echo "No API Key found!"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$SEARCH" ]] ; then
|
|
# perform search
|
|
get_location
|
|
fi
|
|
|
|
if [[ -z "$QUIET" ]] ; then
|
|
print_weather
|
|
fi
|