#!/bin/bash # client version, calls server for actual API info usage() { cat <][-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 -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 -h, --help displays 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 # trimming state if [[ "$PRETTY" = true ]] ; then # print with NF icons icon=$(get_icon "$WEATHER_ICON") # checking units if [[ "$UNIT" == "C" ]] ; then temp_icon=$(echo -e '\ufa03') else temp_icon=$(echo -e '\ufa04') fi # printing printf '%s %.1f%s%d\ue373 in %s\n' $icon $TEMPERATURE "$temp_icon" "$HUMIDITY" "$CITY" else # default printing printf '%.1f*%s %d%% RH in %s\n' $TEMPERATURE $UNIT $HUMIDITY "$CITY" fi # 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") 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\n' \ "API_KEY=$API_KEY" \ "PARSER=$PARSER" \ "LAT=$LAT" \ "LON=$LON" \ "CITY=$CITY" \ "TEMPERATURE=$TEMPERATURE" \ "HUMIDITY=$HUMIDITY" \ "UNIT=$UNIT" \ "WEATHER_ICON=$WEATHER_ICON" \ "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 } 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' } # 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" ;; '--icon_test') set -- "$@" "-i" ;; '--help') set -- "$@" "-h" ;; *) set -- "$@" "$arg" ;; esac done # loading the info load_info while getopts "chpliz: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 ;; 'i' ) icon_test exit 0 ;; '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