Skip to content

Modified the Weather segment script to be less complicated #468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,9 @@ Some segments have their own requirements. If you enable them in your theme, mak
* **tmux_mem_cpu_load.sh**: [tmux-mem-cpu-load](https://github.com/thewtex/tmux-mem-cpu-load)
* **wan_ip.sh**: `curl`
* **weather.sh**:
* Provider *yrno*: `jq`, `curl`, GNU `grep` with Perl regular expression enabled (FreeBSD specific)
* Provider *yrno*: `jq`, `curl`
* **xkb_layout.sh**: X11, XKB

## FreeBSD specific requirements
Preinstalled `grep` in FreeBSD doesn't support Perl regular expressions. Solution is rather simple -- you need to use `textproc/gnugrep` port instead. You also need to make sure, that it has support for PCRE and is compiled with `--enable-perl-regexp` flag.


# Installation
1. Install [tpm](https://github.com/tmux-plugins/tpm) and make sure it's working.
2. Install tmux-powerline as a plugin by adding a line to `tmux.conf`:
Expand Down
205 changes: 116 additions & 89 deletions segments/weather.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,64 @@
# To configure your location, set TMUX_POWERLINE_SEG_WEATHER_LOCATION in the tmux-powerline config file.

TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER_DEFAULT="yrno"
TMUX_POWERLINE_SEG_WEATHER_JSON_DEFAULT="jq"
TMUX_POWERLINE_SEG_WEATHER_UNIT_DEFAULT="c"
TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD_DEFAULT="600"

if shell_is_bsd && [ -f /user/local/bin/grep ]; then
TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT="/usr/local/bin/grep"
else
TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT="grep"
fi
TMUX_POWERLINE_SEG_WEATHER_LOCATION_UPDATE_PERIOD_DEFAULT="86400" # 24 hours
TMUX_POWERLINE_SEG_WEATHER_LAT_DEFAULT="auto"
TMUX_POWERLINE_SEG_WEATHER_LON_DEFAULT="auto"

generate_segmentrc() {
read -r -d '' rccontents <<EORC
# The data provider to use. Currently only "yahoo" is supported.
# The data provider to use. Currently only "yrno" is supported.
export TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER="${TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER_DEFAULT}"
# What unit to use. Can be any of {c,f,k}.
export TMUX_POWERLINE_SEG_WEATHER_UNIT="${TMUX_POWERLINE_SEG_WEATHER_UNIT_DEFAULT}"
# How often to update the weather in seconds.
export TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD_DEFAULT}"
# Name of GNU grep binary if in PATH, or path to it.
export TMUX_POWERLINE_SEG_WEATHER_GREP="${TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT}"
# Location of the JSON parser, jq
export TMUX_POWERLINE_SEG_WEATHER_JSON="${TMUX_POWERLINE_SEG_WEATHER_JSON_DEFAULT}"
# How often to update the weather location in seconds (this is only used when latitude and longitude settings are set to "auto")
export TMUX_POWERLINE_SEG_WEATHER_LOCATION_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_WEATHER_LOCATION_UPDATE_PERIOD_DEFAULT}"
# Your location
# Latitude and Longtitude for use with yr.no
# Set both to "auto" to detect automatically based on your IP address
TMUX_POWERLINE_SEG_WEATHER_LAT=""
TMUX_POWERLINE_SEG_WEATHER_LON=""
# Set both to "auto" to detect automatically based on your IP address, or set them manually
export TMUX_POWERLINE_SEG_WEATHER_LAT="${TMUX_POWERLINE_SEG_WEATHER_LAT_DEFAULT}"
export TMUX_POWERLINE_SEG_WEATHER_LON="${TMUX_POWERLINE_SEG_WEATHER_LON_DEFAULT}"
EORC
echo "$rccontents"
}

run_segment() {
__process_settings
local tmp_file="${TMUX_POWERLINE_DIR_TEMPORARY}/temp_weather_file.txt"
local weather
case "$TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER" in
"yrno") weather=$(__yrno) ;;
*)
echo "Unknown weather provider [$TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER]"
return 1
;;
esac
if [ -n "$weather" ]; then
echo "$weather"
local cache_weather_file="${TMUX_POWERLINE_DIR_TEMPORARY}/weather_cache_data.txt"
local cache_location_file="${TMUX_POWERLINE_DIR_TEMPORARY}/weather_cache_location.txt"
local weather=""

# Check if the weather data is still a valid cache hit
if [ -f "$cache_weather_file" ]; then
last_update=$(__read_file_last_update "$cache_weather_file")
time_now=$(date +%s)
up_to_date=$(echo "(${time_now}-${last_update}) < ${TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD}" | bc)
if [ "$up_to_date" -eq 1 ]; then
weather=$(__read_file_content "$cache_weather_file")
fi
fi

# Fetch from provider if empty
# If a new provider is implemented, please set the $weather variable!
if [ -z "$weather" ]; then
__process_settings
case "$TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER" in
"yrno")
weather=$(__yrno)
;;
*)
# Just read the stale cache, default to empty/blank
# Better not overwriting the previous healthy content
weather=$(__read_file_content "$cache_weather_file")
;;
esac
echo "$weather" > "$cache_weather_file"
fi

echo "$weather"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondrful 👏 🥳 🎆 , now this is the only echo for all code paths. This is a fantastic simplication of this old messy segment. Well don @onlyuuuuu !

}

__process_settings() {
Expand All @@ -60,66 +73,52 @@ __process_settings() {
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD" ]; then
export TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD_DEFAULT}"
fi
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_GREP" ]; then
export TMUX_POWERLINE_SEG_WEATHER_GREP="${TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT}"
fi
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_JSON" ]; then
export TMUX_POWERLINE_SEG_WEATHER_JSON="${TMUX_POWERLINE_SEG_WEATHER_JSON_DEFAULT}"
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_LOCATION_UPDATE_PERIOD" ]; then
export TMUX_POWERLINE_SEG_WEATHER_LOCATION_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_WEATHER_LOCATION_UPDATE_PERIOD_DEFAULT}"
fi
if [ "$TMUX_POWERLINE_SEG_WEATHER_LAT" = "auto" ] || [ "$TMUX_POWERLINE_SEG_WEATHER_LON" = "auto" ]; then
if [ "$TMUX_POWERLINE_SEG_WEATHER_LAT" = "auto" ] || [ "$TMUX_POWERLINE_SEG_WEATHER_LON" = "auto" ] || [ -z "$TMUX_POWERLINE_SEG_WEATHER_LON" ] || [ -z "$TMUX_POWERLINE_SEG_WEATHER_LAT" ]; then
if ! get_auto_location; then
exit 8
fi
elif [ -z "$TMUX_POWERLINE_SEG_WEATHER_LON" ] || [ -z "$TMUX_POWERLINE_SEG_WEATHER_LAT" ]; then
echo "No location defined." >&2
exit 8
fi
}

# An implementation of a weather provider, just need to echo the result, run_segment() will take care of the rest
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to add a note. Thanks!

__yrno() {
degree=""
if [ -f "$tmp_file" ]; then
if shell_is_macos || shell_is_bsd; then
last_update=$(stat -f "%m" "${tmp_file}")
elif shell_is_linux; then
last_update=$(stat -c "%Y" "${tmp_file}")
fi
time_now=$(date +%s)
local degree=""

up_to_date=$(echo "(${time_now}-${last_update}) < ${TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD}" | bc)
if [ "$up_to_date" -eq 1 ]; then
__read_tmp_file
# There's a chance that you will get rate limited or both location APIs are not working
# Then long and lat will be "null", as literal string
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_LAT" ] || [ -z "$TMUX_POWERLINE_SEG_WEATHER_LON" ]; then
echo "Err: Unable to auto-detect your location"
return 1
fi

if weather_data=$(curl --max-time 4 -s "https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=${TMUX_POWERLINE_SEG_WEATHER_LAT}&lon=${TMUX_POWERLINE_SEG_WEATHER_LON}"); then
error=$(echo "$weather_data" | grep -i "error")
if [ -n "$error" ]; then
echo "error"
return 1
fi
degree=$(echo "$weather_data" | jq -r '.properties.timeseries | .[0].data.instant.details.air_temperature')
condition=$(echo "$weather_data" | jq -r '.properties.timeseries | .[0].data.next_1_hours.summary.symbol_code')
fi

if [ -z "$degree" ]; then
if weather_data=$(curl --max-time 4 -s "https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=${TMUX_POWERLINE_SEG_WEATHER_LAT}&lon=${TMUX_POWERLINE_SEG_WEATHER_LON}"); then
grep=$TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT
error=$(echo "$weather_data" | $grep -i "error")
if [ -n "$error" ]; then
echo "error"
exit 1
fi

jsonparser="${TMUX_POWERLINE_SEG_WEATHER_JSON}"
degree=$(echo "$weather_data" | $jsonparser -r '.properties.timeseries | .[0].data.instant.details.air_temperature')
condition=$(echo "$weather_data" | $jsonparser -r '.properties.timeseries | .[0].data.next_1_hours.summary.symbol_code')
elif [ -f "${tmp_file}" ]; then
__read_tmp_file
fi
echo "yr.no err: unable to fetch weather data"
return 1
fi

if [ -n "$degree" ]; then
if [ "$TMUX_POWERLINE_SEG_WEATHER_UNIT" == "k" ]; then
degree=$(echo "${degree} + 273.15" | bc)
fi
if [ "$TMUX_POWERLINE_SEG_WEATHER_UNIT" == "f" ]; then
degree=$(echo "${degree} * 9 / 5 + 32" | bc)
fi
# condition_symbol=$(__get_yrno_condition_symbol "$condition" "$sunrise" "$sunset")
condition_symbol=$(__get_yrno_condition_symbol "$condition")
echo "${condition_symbol} ${degree}°$(echo "$TMUX_POWERLINE_SEG_WEATHER_UNIT" | tr '[:lower:]' '[:upper:]')" | tee "${tmp_file}"
if [ "$TMUX_POWERLINE_SEG_WEATHER_UNIT" == "k" ]; then
degree=$(echo "${degree} + 273.15" | bc)
fi
if [ "$TMUX_POWERLINE_SEG_WEATHER_UNIT" == "f" ]; then
degree=$(echo "${degree} * 9 / 5 + 32" | bc)
fi
# condition_symbol=$(__get_yrno_condition_symbol "$condition" "$sunrise" "$sunset")
condition_symbol=$(__get_yrno_condition_symbol "$condition")
# Write the <content@date>, separated by a @ character, so we can fetch it later on without having to call 'stat'
echo "${condition_symbol} ${degree}°$(echo "$TMUX_POWERLINE_SEG_WEATHER_UNIT" | tr '[:lower:]' '[:upper:]')@$(date +%s)"
}

# Get symbol for condition. Available symbol names: https://api.met.no/weatherapi/weathericon/2.0/documentation#List_of_symbols
Expand Down Expand Up @@ -177,23 +176,43 @@ __get_yrno_condition_symbol() {
esac
}

__read_tmp_file() {
if [ ! -f "$tmp_file" ]; then
__read_file_split() {
file_to_read="$1"
lookup_index="$2"
fallback_value="$3"
if [ ! -f "$file_to_read" ]; then
echo "$fallback_value"
return
fi
local -a file_arr
IFS='@' read -ra file_arr <<< "$(cat "$file_to_read")"
if [ -z "${file_arr[$lookup_index]}" ]; then
echo "$fallback_value"
return
fi
cat "${tmp_file}"
exit
echo "${file_arr[$lookup_index]}"
}

# Default to empty/blank
__read_file_content() {
__read_file_split "$1" 0 ""
}

# Default to 0
__read_file_last_update() {
__read_file_split "$1" 1 0
}

get_auto_location() {
local cache_file="${TMUX_POWERLINE_DIR_TEMPORARY}/weather_location_cache.txt"
local max_cache_age=86400 # 24 hours
local max_cache_age=$TMUX_POWERLINE_SEG_WEATHER_LOCATION_UPDATE_PERIOD
local -a lat_lon_arr

if [[ -f "$cache_file" ]]; then
local cache_age=$(($(date +%s) - $(stat -c %Y "$cache_file" 2>/dev/null || echo 0)))
if [[ -f "$cache_location_file" ]]; then
local cache_age=$(($(date +%s) - $(__read_file_last_update "$cache_location_file")))
if (( cache_age < max_cache_age )); then
TMUX_POWERLINE_SEG_WEATHER_LAT=$(grep -oP "(?<=TMUX_POWERLINE_SEG_WEATHER_LAT=')[^']*" "$cache_file")
TMUX_POWERLINE_SEG_WEATHER_LON=$(grep -oP "(?<=TMUX_POWERLINE_SEG_WEATHER_LON=')[^']*" "$cache_file")
IFS=' ' read -ra lat_lon_arr <<< "$(__read_file_content "$cache_location_file")"
TMUX_POWERLINE_SEG_WEATHER_LAT=${lat_lon_arr[0]}
TMUX_POWERLINE_SEG_WEATHER_LON=${lat_lon_arr[1]}
if [[ -n "$TMUX_POWERLINE_SEG_WEATHER_LAT" && -n "$TMUX_POWERLINE_SEG_WEATHER_LON" ]]; then
return 0
fi
Expand All @@ -214,18 +233,26 @@ get_auto_location() {
TMUX_POWERLINE_SEG_WEATHER_LON="${loc[1]}"
;;
esac
if [[ -n "$TMUX_POWERLINE_SEG_WEATHER_LAT" && -n "$TMUX_POWERLINE_SEG_WEATHER_LON" ]]; then
mkdir -p "$(dirname "$cache_file")"
echo "TMUX_POWERLINE_SEG_WEATHER_LAT='$TMUX_POWERLINE_SEG_WEATHER_LAT'" > "$cache_file"
echo "TMUX_POWERLINE_SEG_WEATHER_LON='$TMUX_POWERLINE_SEG_WEATHER_LON'" >> "$cache_file"
return 0

# There's no data, move on to the next API, just don't overwrite the previous location
# Also, there's a case where lat/lon was set to "null" as a string, gotta handle it
if [[ -z "$TMUX_POWERLINE_SEG_WEATHER_LAT" ||
-z "$TMUX_POWERLINE_SEG_WEATHER_LON" ||
"$TMUX_POWERLINE_SEG_WEATHER_LAT" == "null" ||
"$TMUX_POWERLINE_SEG_WEATHER_LON" == "null" ]]; then
continue
fi

echo "$TMUX_POWERLINE_SEG_WEATHER_LAT $TMUX_POWERLINE_SEG_WEATHER_LON@$(date +%s)" > "$cache_location_file"
return 0
fi
done
if [[ -f "$cache_file" ]]; then

if [[ -f "$cache_location_file" ]]; then
echo "Warning: Using stale location data (failed to refresh)" >&2
TMUX_POWERLINE_SEG_WEATHER_LAT=$(grep -oP "(?<=TMUX_POWERLINE_SEG_WEATHER_LAT=')[^']*" "$cache_file")
TMUX_POWERLINE_SEG_WEATHER_LON=$(grep -oP "(?<=TMUX_POWERLINE_SEG_WEATHER_LON=')[^']*" "$cache_file")
IFS=' ' read -ra lat_lon_arr <<< "$(__read_file_content "$cache_location_file")"
TMUX_POWERLINE_SEG_WEATHER_LAT=${lat_lon_arr[0]}
TMUX_POWERLINE_SEG_WEATHER_LON=${lat_lon_arr[1]}
if [[ -n "$TMUX_POWERLINE_SEG_WEATHER_LAT" && -n "$TMUX_POWERLINE_SEG_WEATHER_LON" ]]; then
return 0
fi
Expand Down