dotfiles/.local/bin/monitors

316 lines
7 KiB
Bash
Executable file

#!/bin/sh
DEFAULT_COLOR="#cccccc"
STATUS_WELL_COLOR="#31f53e"
STATUS_OK_COLOR="#f58c31"
STATUS_UNWELL_COLOR="#f53131"
BATTERY_NAME="BAT1"
BATTERY_EMPTY_THRESHOLD=20
BATTERY_FULL_THRESHOLD=80
WIFI_ADAPTER="wlan0"
DISK_ICON_COLOR="#48a3e8"
NOW_PLAYING_COLOR="#e647a1"
NOW_PLAYING_STATE_COLOR="#ccca4e"
readable_kib() {
data="${1:-}"
decimals="${2:-}"
[ -z "$data" ] || [ -z "$decimals" ] && exit
if [ "$data" -lt 1024 ]; then
amt="$1"
letter="K"
elif [ "$data" -lt 1048576 ]; then
amt="$(($1 / 1024))"
letter="M"
else
amt="$(($1 / 1048576))"
letter="G"
fi
printf "%.*f%s\n" "$decimals" "$amt" "$letter"
}
replace_substring() {
original="${1:-}"
pattern="${2:-}"
replacement="${3:-}"
[ -z "$original" ] || [ -z "$pattern" ] && exit
new="$original"
while :; do
case "$original" in
*"$pattern"*)
before_pattern="${original%%"$pattern"*}"
after_pattern="${original#*"$pattern"}"
new="$before_pattern$replacement$after_pattern"
;;
*) break ;;
esac
done
printf '%s\n' "$new"
}
monitor_battery() {
read -r capacity </sys/class/power_supply/$BATTERY_NAME/capacity
read -r status </sys/class/power_supply/$BATTERY_NAME/status
if [ "$capacity" -ge "$BATTERY_FULL_THRESHOLD" ] && [ "$status" = "Not charging" ]; then
status="Full"
fi
# TODO(frosty): Add more icons based on other statuses.
case $status in
"Full" | "Charging")
icon=""
color="$STATUS_WELL_COLOR"
;;
"Not charging")
icon=""
color="$STATUS_OK_COLOR"
;;
*)
icon=""
color="$DEFAULT_COLOR"
;;
esac
if [ "$capacity" -le "$BATTERY_EMPTY_THRESHOLD" ] && [ "$status" != "Charging" ]; then
color="$STATUS_UNWELL_COLOR"
fi
printf '%s <fc=%s>%s%%</fc> (%s)\n' "$icon" "$color" "$capacity" "$status"
}
monitor_cpu() {
while read -r _ user _ system idle _; do
initial_user_sys="$((user + system))"
initial_total="$((user + system + idle))"
break
done </proc/stat
# TODO(frosty): Allow the user to specify how long to wait via an argument.
sleep 1
while read -r _ user _ system idle _; do
current_user_sys="$((user + system))"
current_total="$((user + system + idle))"
break
done </proc/stat
while read -r line; do
case "$line" in
*"Package id 0:"*) temp="$line" ;;
esac
done <<EOF
$(sensors)
EOF
if [ -n "$temp" ]; then
temp="${temp#Package id 0:*+}"
temp="${temp%%°*}"
temp="${temp%.*}"
fi
usage_percent="$(printf '%i\n' $(((current_user_sys - initial_user_sys) * 100 / (current_total - initial_total))))"
case "$usage_percent" in
[0-9] | [0-3][0-9]) color="$DEFAULT_COLOR" ;;
[4-5][0-9]) color="$STATUS_OK_COLOR" ;;
*) color="$STATUS_UNWELL_COLOR" ;;
esac
if [ -n "$temp" ]; then
printf '%s°C (<fc=%s>%i%%</fc>)\n' "$temp" "$color" "$usage_percent"
else
printf '<fc=%s>%i%%</fc>\n' "$color" "$usage_percent"
fi
}
monitor_load() {
while read -r one five fifteen _; do
load_one="$one"
load_five="$five"
load_fifteen="$fifteen"
done </proc/loadavg
# TODO(frosty): Find a less cursed way to achieve this.
load_one_color=
load_five_color=
load_fifteen_color=
for load in load_one load_five load_fifteen; do
case "$(eval 'printf "%i\n" "\$$load"')" in
[0-3]) eval "${load}_color=#cccccc" ;;
[4-7]) eval "${load}_color=$STATUS_OK_COLOR" ;;
*) eval "${load}_color=$STATUS_UNWELL_COLOR" ;;
esac
done
printf '<fc=%s>%s</fc> <fc=%s>%s</fc> <fc=%s>%s</fc>\n' "$load_one_color" "$load_one" "$load_five_color" "$load_five" "$load_fifteen_color" "$load_fifteen"
}
monitor_memory() {
while read -r label size _; do
case "$label" in
"MemTotal:") total_mem="$size" ;;
"MemAvailable:") free_mem="$size" ;;
esac
done </proc/meminfo
used_mem="$((total_mem - free_mem))"
usage_percent="$((used_mem * 100 / total_mem))"
case "$usage_percent" in
[0-9] | [1-3][0-9]) color="$DEFAULT_COLOR" ;;
[4-6][0-9]) color="$STATUS_OK_COLOR" ;;
*) color="$STATUS_UNWELL_COLOR" ;;
esac
printf '%s (<fc=%s>%s%%</fc>)\n' "$(readable_kib "$used_mem" 1)" "$color" "$usage_percent"
}
monitor_ssid() {
while read -r line; do
case $line in
*"Connected network"*) ssid="$line" ;;
esac
done <<EOF
$(iwctl station "$WIFI_ADAPTER" show)
EOF
[ -z "$ssid" ] && exit
ssid="${ssid##*Connected network}"
ssid="${ssid#"${ssid%%[![:space:]]*}"}"
# TODO(frosty): Add other icons based on the connection status.
icon=""
printf '%s %s\n' "$icon" "$ssid"
}
monitor_local_ip() {
# TODO(frosty): Add a fallback value when offline.
address="$(ip route get 1)"
address="${address#* via * dev * src }"
address="${address% uid *}"
printf '%s\n' "$address"
}
monitor_volume() {
volume="$(wpctl get-volume @DEFAULT_AUDIO_SINK@)"
volume="${volume% \[MUTED\]}"
volume="${volume#Volume: }"
volume="${volume%.*}${volume#*.}"
volume="${volume#0}"
# TODO(frosty): Add other icons based on the current device (headphones, speakers, etc.).
icon=""
printf '%s %s%%\n' "$icon" "$volume"
}
monitor_uptime() {
# TODO(frosty): Find the uptime without calling another command.
up="$(uptime -p)"
up="$(replace_substring "$up" 'up ' '')"
up="$(replace_substring "$up" ',' '')"
up="$(replace_substring "$up" ' days' 'd')"
up="$(replace_substring "$up" ' day' 'd')"
up="$(replace_substring "$up" ' hours' 'h')"
up="$(replace_substring "$up" ' hour' 'h')"
up="$(replace_substring "$up" ' minutes' 'm')"
up="$(replace_substring "$up" ' minute' 'm')"
printf '%s\n' "$up"
}
monitor_date() {
date '+%-I:%M:%S %p'
}
monitor_disk() {
disk="${2:-}"
use_icon="${3:-0}"
[ -z "$disk" ] || [ ! -d "$disk" ] && exit 1
# TODO(frosty): Allow more icons to be added using an rc file.
if [ "$use_icon" -eq 1 ]; then
case "$disk" in
"/") icon="" ;;
"/home") icon="" ;;
*) icon="" ;;
esac
else
case "$disk" in
"/home") icon="~" ;;
*) icon="$disk" ;;
esac
fi
i=0
while read -r _ size used _; do
case "$i" in
0) ;;
1)
size_amt="$size"
used_amt="$used"
;;
*) break ;;
esac
i="$((i + 1))"
done <<EOF
$(df "$disk")
EOF
[ -z "$size_amt" ] || [ -z "$used_amt" ] && exit 1
printf '<fc=%s>%s</fc> %s/%s (<fc=%s>%s%%</fc>)\n' "$DISK_ICON_COLOR" "$icon" "$(readable_kib "$used_amt" 0)" "$(readable_kib "$size_amt" 0)" "$STATUS_WELL_COLOR" "$((used_amt * 100 / size_amt))"
}
monitor_now_playing() {
mpc_output="$(mpc)"
while read -r line; do
now_playing="$line"
break
done <<EOF
$mpc_output
EOF
i=0
while read -r state _; do
case "$i" in
0) ;;
1)
case "$state" in
\[*\]) play_state="$state" ;;
esac
;;
*) break ;;
esac
i="$((i + 1))"
done <<EOF
$mpc_output
EOF
if [ -z "$now_playing" ] || [ -z "$play_state" ]; then
printf 'N/A\n'
exit
fi
play_state="${play_state#\[}"
play_state="${play_state%\]}"
# TODO(frosty): Split the artist and track names, and truncate each one separately.
if [ "${#now_playing}" -gt 47 ]; then
now_playing="${now_playing%"${now_playing#??????????????????????????????????????????????}"}..."
fi
printf '<fc=%s>%s</fc> <fc=%s>[%s]</fc>\n' "$NOW_PLAYING_COLOR" "$now_playing" "$NOW_PLAYING_STATE_COLOR" "$play_state"
}
if command -v monitor_"$1" >/dev/null 2>&1; then
"monitor_$1" "$@"
else
exit 1
fi