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
 |