-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathwm_status.sh
executable file
·255 lines (220 loc) · 6.75 KB
/
wm_status.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/env sh
# wm_status.sh v0.6
# non-blocking status line daemon
# prints formatted status information to stdout
# global option flags
for f in $(echo "${@#-}" | sed 's/./& /g'); do
case $f in
p) pad=' ';; # -p for extra padding
q) quote='"';; # -q to enquote output for xargs
m) invert=1;; # -m for reverse video glyphs
esac
done
# global delimiter
delim='・'
# generate sed script for final output formatting
IFS=''
script="s/${delim}$//" # strip trailing delimiter
for f in $pad $quote; do # surround output with special characters
script="$script;s/^/${f}&/;s/$/&${f}/"
done
unset IFS
# FIFO locations
prog="${0##*/}"
key="$(tr -cd 'a-z0-9' < /dev/urandom | dd bs=7 count=1 2> /dev/null)"
FIFO="${XDG_RUNTIME_DIR:-/tmp}/.${prog%.*}.$key"
# notification FIFO written to by notify-send
NFIFO="$HOME/.notify"
abort() {
rm -rf "$FIFO" "$NFIFO"
echo "[!] ${0##*/} terminated unexpectedly" | sed -e "$script"
kill -- -$$ > /dev/null 2>&1
}
trap abort 0 1 2 3 6 15
mkfifo "$FIFO" "$NFIFO"
# monitor and redirect notifications from notify-send
# prevent named pipe from closing
{ while :; do
while read -r msg < "$NFIFO"; do
echo "MSG $msg"
done
done
} > "$FIFO" &
# thread loop
launch() {
while :; do
sleep "$2" &
"$1"
wait
done > "$FIFO" &
}
# individual threads
ssh_agent() (
# list number of active keys stored in agent
num=$(ssh-add -l) && \
num=$(echo "$num" | wc -l) || unset num
echo "SSH ⚿×${num:-none}"
)
fan_speed() (
# express fan speed in RPM if supported
sensors -u | egrep 'fan[0-9]+_input' | head -n 1 | while read -r _ rpm; do
rpm="${rpm%.*}"
if [ ${#rpm} -gt 3 ]; then
rpm="$(echo "scale=1; $rpm / 1000" | bc)k"
fi
echo "FAN ${rpm}↻"
done
)
weather() (
# get geolocated current weather with wttr.in (very slow)
# bypass their caching which returns stale data when using en.wttr.in
fmt='%c%f'
locales="am ar af be bn ca da de el et fr fa gl hi hu ia id it lt mg \
nb nl oc pl pt-br ro ru ta tr th uk vi zh-cn zh-tw az bg bs cy cs eo \
es eu fi ga hi hr hy is ja jv ka kk ko ky lv mk ml mr nl fy nn pt pt-br \
sk sl sr sr-lat sv sw te uz zh zu he"
sel="$(echo "$locales" | tr -s '\t\n ' '\n' | shuf | head -n 1)"
wttr="$(wget --timeout=2 -q -O - "http://$sel.wttr.in/?u&format=$fmt" | tr -d '+ ')"
case "$wttr" in Unknown*) unset wttr;; esac # wttr.in API is down
echo "WTTR ${wttr:-none}"
)
temps() (
# randomly switch between expressing avg CPU and dGPU temps in ˚F if supported
data="$(sensors -u coretemp-isa-0000)" || return # not supported
ico='∿'
unset sum n
for f in $(echo "$data" | egrep 'temp[0-9]+_input' |
tr -d ' ' | tr -s ':' '\t' | cut -f2); do
n=$((n + 1))
sum=$((sum + ${f%.*}))
done
# GPU temps appear 1/3 of the time
# TODO: add AMD support with rocm-smi
if [ $(($(dd if=/dev/urandom count=1 bs=1 2> /dev/null \
| od -An -tu) % 3)) -eq 0 ]; then
if nvidia-smi > /dev/null; then # nvidia
sum="$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader)"
n=1
ico='◺' # cus GPUs are triangle accelerators
fi
fi
temp="$(echo "scale=1; (($sum / $n) * 1.8) + 32" | bc)"
echo "TEMP $ico${temp%*.0}°F"
)
cpu_speed() (
# express average CPU clock speed
unset sum n
for f in $(fgrep 'MHz' < '/proc/cpuinfo' | tr -d ' ' \
| tr -s ':' '\t' | cut -f2); do
n=$((n + 1))
sum=$((sum + ${f%.*}))
done
clk=$((sum / n))
[ ${#clk} -gt 3 ] && clk="$(echo "scale=2; $clk / 1000" | bc)G" \
|| clk="${clk}M"
echo "CPU ${clk}Hz"
)
public_ip() (
# get public IP address (very slow)
ip="$(dig @resolver1.opendns.com myip.opendns.com +short 2> /dev/null)"
echo "IP ☍${ip:-none}"
)
network() (
# show networking status for first active connection
net="$(nmcli -t device | grep '[^dis]connected' | head -n 1 | \
cut -d ':' -f2,4 | sed 's/:/& /')"
if [ -z "$net" ]; then # disconnected or networking disabled
net="$(nmcli -t networking)"
net="$(echo "${net%${net#?}}" | tr 'a-z' 'A-Z')${net#?}"
fi
ico='📶' # connected but no internet
ping -c 1 'google.com' > /dev/null 2>&1 || ico='❌'
echo "NET $ico $net"
)
power() (
# extract battery life / AC adapter status if supported
ico='↯' # normal discharging icon
acpi="$(acpi -b | tr -d ',' | head -n 1)" 2> /dev/null
[ ! -z "$acpi" ] || return # not supported
for f in $acpi; do case $f in
unavailable) return;; # not supported
*%) pct="$f";;
*:*:*) btime="$f";;
esac; done
# extract net energy-rate in W if supported
# reported as combined (-) battery draw and (+) AC power draw
watts="$(upower -i /org/freedesktop/UPower/devices/battery_BAT0 \
| grep 'energy-rate' | egrep -o '([0-9]+.?)+' | xargs printf '%.2fw')"
load="$(tr ' ' '\t' < /proc/loadavg | cut -f1)/$(grep -c '^proc' < /proc/cpuinfo)"
# rewrite approx. time remaining if available
if [ ! -z "$btime" ]; then
i=0; for f in h m; do
i=$((i + 1))
val="$(echo "$btime" | cut -d ':' -f$i | sed 's/^0//')"
[ ! $val -eq 0 ] && btime_v="$btime_v$val$f"
done
# randomly switch between net energy draw and charge time
if [ $(($(dd if=/dev/urandom count=1 bs=1 2> /dev/null \
| od -An -tu) % 2)) -eq 0 ]; then
for f in $acpi; do case $f in
charged) ico='🔌'; btime_v="$btime_v 'til charged";;
remaining) btime_v="$btime_v left";;
esac; done
else
for f in $acpi; do case $f in
charged) ico='🔌'; btime_v="$load${watts:+, +$watts}";;
remaining) btime_v="$load${watts:+, –$watts}";;
esac; done
fi
fi
echo "BAT $ico$pct${btime_v:+, $btime_v}"
)
sound() (
# sound mixer status
alsa="$(amixer get 'Master')"
lvl="$(echo "$alsa" | egrep -o '[0-9]+\%' | head -n 1)"
ico='🔉' # normal/muted icon
echo "$alsa" | fgrep -q 'off' && ico='🔇'
# headphone status
for f in $(pactl list sinks | tr 'A-Z' 'a-z' | fgrep 'active port') ; do
case $f in *headphones) aux=' ☊';; esac
done
echo "VOL $ico$lvl$aux"
)
current_date() (
# current date
echo "DATE $(moonphase-date ${invert+-i})"
)
current_time() (
# current time
echo "TIME $(date '+%-l:%M%P')"
)
# update every n seconds
launch ssh_agent 5
launch fan_speed 10
launch temps 15
launch weather 60
#launch cpu_speed 1
#launch public_ip 30
launch network 15
launch power 15
launch sound 5
launch current_date 60
launch current_time 10
while read -r module data; do
# receive module output and append delimiter
eval "$module=\"$data$delim\""
# stop everything to display notification
# expected format: '[n display sec] [message]'
[ ! -z "$MSG" ] &&
echo "${MSG#* }" | sed -e "$script" && sleep "${MSG%% *}" && unset MSG
# conditionally delete non-message modules
case "$data" in
0*|*none) eval "unset $module";; # fan spindown/no internet
*abled) unset IP;; # no internet
esac
# compose all available modules
bar="${SSH}${FAN:-$TEMP}${CPU}${NET}${IP}${BAT}${VOL}${WTTR}${DATE}${TIME}"
# output final formatted status line
echo "$bar" | sed -e "$script"
done < "$FIFO"