Remove no-longer-used variable
[khatus.git] / bin / khatus_loop
CommitLineData
438d0d5f
SK
1#! /bin/bash
2
4d314e0f
SK
3set -e
4
c0e8473e
SK
5produce_disk_space() {
6 disk_space_device="$1"
7 df --output=pcent "$disk_space_device" | awk 'NR == 2 {print $1}'
8}
9
2b221665
SK
10produce_net_addr_io() {
11 ip -s addr \
e70e90e1 12 | awk '
2b221665
SK
13 BEGIN {
14 bytes_per_unit = 1024 * 1024
15 }
16
17 /^[0-9]+:/ {
18 sub(":$", "", $1)
19 sub(":$", "", $2)
20 sequence = $1
21 interface = $2
22 interfaces[sequence] = interface
23 }
24
25 /^ +inet [0-9]/ {
26 sub("/[0-9]+", "", $2)
27 addr = $2
28 addrs[interface] = addr
29 }
30
31 /^ +RX: / {transfer_direction = "r"}
32 /^ +TX: / {transfer_direction = "w"}
33
34 /^ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ *$/ {
35 io[interface, transfer_direction] = $1;
36 }
37
38 END {
39 for (seq=1; seq<=sequence; seq++) {
40 interface = interfaces[seq]
41 label = substr(interface, 1, 1)
42 if (addrs[interface]) {
43 curr_read = io[interface, "r"]
44 curr_write = io[interface, "w"]
45 print(interface, addrs[interface], curr_write, curr_read)
46 } else {
47 print(interface)
48 }
49 }
50 }'
51}
52
0d116e88
SK
53produce_net_wifi_status() {
54 nmcli \
55 -f ACTIVE,SSID,SIGNAL \
56 -t \
57 d wifi \
58 | awk \
59 -F ':' \
60 '
61 BEGIN {wifi_status = "--"}
62 $1 == "yes" {wifi_status = $2 ":" $3 "%"}
63 END {print wifi_status}
64 '
65}
66
ad0b3a5b
SK
67produce_bluetooth_power() {
68 echo -e 'show \n quit' \
69 | bluetoothctl \
70 | awk '
71 /^Controller / {
72 controller = $2;
73 controllers[++ctrl_count] = controller;
74 }
75 /^\t[A-Z][A-Za-z]+:/ {
76 key = $1;
77 sub(":$", "", key);
78 val = $2;
79 for (i=3; i<=NF; i++) {
80 val = val " " $i};
81 data[controller, key] = val;
82 }
83 END {
84 # Using the 1st seen controller. Should we select specific instead?
85 power_status = data[controllers[1], "Powered"];
86 if (ctrl_count > 0) {
87 if (power_status == "no") {
88 power_status = "off"
89 } else if (power_status == "yes") {
90 power_status = "on"
91 } else {
92 printf("Unexpected bluetooth power status: %s\n", power_status)\
93 > "/dev/stderr";
94 power_status = "ERROR"
95 }
96 } else {
97 power_status = "off" # TODO: Perhaps use differentiated marker?
98 }
62aa4063 99 printf("%s\n", power_status);
ad0b3a5b
SK
100 }'
101}
102
bbb42518
SK
103produce_screen_brightness() {
104 screen_brightness_device_path="$1"
105 echo "\
106 $(cat $screen_brightness_device_path/max_brightness) \
107 $(cat $screen_brightness_device_path/brightness)\
108 "
109}
110
61e5a351
SK
111produce_volume() {
112 pactl list sinks \
113 | awk '
114 /^\tMute:/ {
115 printf("%s,", $0);
116 }
117 /^\tVolume:/ {
118 for (i=2; i<=NF; i++) printf(" %s", $i);
119 }' \
120 | awk -v RS=',' '
121 /^[ \t]*Mute:/ {mute = $2}
122 /^[ \t]*front-left:/ {left = $4}
123 /^[ \t]*front-right:/ {right = $4}
124 END {
125 if (mute == "yes") {
62aa4063 126 print("x")
61e5a351 127 } else {
62aa4063 128 print("%s %s\n", left, right)
61e5a351
SK
129 }
130 }
131 '
132}
133
365549a9
SK
134produce_mpd_state() {
135 echo 'status' \
136 | nc 127.0.0.1 6600 \
137 | awk '
138 {
139 status[$1] = $2
140 }
141
142 /^time: +[0-9]+:[0-9]+$/ {
143 split($2, time, ":")
144 seconds_current = time[1]
145 seconds_total = time[2]
146
147 hours = int(seconds_current / 60 / 60);
148 secs_beyond_hours = seconds_current - (hours * 60 * 60);
149 mins = int(secs_beyond_hours / 60);
150 secs = secs_beyond_hours - (mins * 60);
151 if (hours > 0) {
152 current_time = sprintf("%d:%.2d:%.2d", hours, mins, secs)
153 } else {
154 current_time = sprintf("%.2d:%.2d", mins, secs)
155 }
156
157 if (seconds_total > 0) {
158 time_percentage = (seconds_current / seconds_total) * 100
159 current_percentage = sprintf("%d%%", time_percentage)
160 } else {
161 current_percentage = "~"
162 }
163 }
164
165 END {
166 state = status["state:"]
167
168 if (state == "play") {
169 symbol = "▶"
170 } else if (state == "pause") {
171 symbol = "❚❚"
172 } else if (state == "stop") {
173 symbol = "⬛"
174 } else {
175 symbol = "--"
176 }
177
178 printf(\
62aa4063 179 "%s %s %s\n",
365549a9
SK
180 status["state:"], current_time, current_percentage\
181 )
182 }
183 '
184}
185
186produce_mpd_song() {
187 echo 'currentsong' \
188 | nc 127.0.0.1 6600 \
189 | awk '
190 /^OK/ {
191 next
192 }
193
194 {
195 key = $1
196 sub("^" key " +", "")
197 val = $0
198 data[key] = val
199 }
200
201 END {
202 name = data["Name:"]
203 title = data["Title:"]
204 file = data["file:"]
205
206 if (name) {
207 out = name
208 } else if (title) {
209 out = title
210 } else if (file) {
211 last = split(file, parts, "/")
212 out = parts[last]
213 } else {
214 out = ""
215 }
216 print out
217 }
218 '
219}
438d0d5f 220
6339a4f8 221produce_weather() {
4d314e0f
SK
222 weather_station_id="$1"
223 metar -d "$weather_station_id" 2>&1 \
438d0d5f
SK
224 | awk '
225 /METAR pattern not found in NOAA data/ {
226 failures++
227 }
228
229 /^Temperature/ {
230 celsius = $3;
231 fahrenheit = (celsius * (9 / 5)) + 32;
232 temperature = fahrenheit
233 }
234
235 END {
236 if (failures > 0) {
237 temperature = "--"
238 }
239 print temperature "°F"
240 }'
241}
242
6339a4f8 243produce_datetime() {
438d0d5f
SK
244 date +'%a %b %d %H:%M:%S'
245}
246
6339a4f8 247consume() {
438d0d5f 248 pipe="$1"
2b221665
SK
249 debug="$2"
250 prefixes_of_net_interfaces_to_show="$3"
438d0d5f
SK
251 tail -f "$pipe" \
252 | stdbuf -o L awk \
2b221665 253 -v opt_debug="$debug" \
365549a9 254 -v opt_mpd_song_max_chars=10 \
2b221665 255 -v opt_prefixes_of_net_interfaces_to_show="$prefixes_of_net_interfaces_to_show" \
438d0d5f 256 '
c0e8473e
SK
257 /^in:DISK_SPACE/\
258 {
259 split_msg_parts()
260 db["disk_space_used"] = msg_body
261 }
262
2b221665
SK
263 /^in:NET_ADDR_IO/\
264 {
265 split_msg_parts()
266 set_net_addr_io()
267 }
268
0d116e88
SK
269 /^in:NET_WIFI_STATUS/\
270 {
271 split_msg_parts()
272 db["net_wifi_status"] = msg_body
273 }
274
ad0b3a5b
SK
275 /^in:BLUETOOTH_POWER/\
276 {
277 split_msg_parts()
278 db["bluetooth_power"] = msg_body
279 }
280
bbb42518
SK
281 /^in:SCREEN_BRIGHTNESS/\
282 {
283 split_msg_parts()
284 set_screen_brightness()
285 }
286
61e5a351
SK
287 /^in:VOLUME/\
288 {
289 split_msg_parts()
290 db["volume"] = msg_body
291 }
292
365549a9
SK
293 /^in:MPD_STATE/\
294 {
295 split_msg_parts()
296 db["mpd_state"] = $1
297 db["mpd_curr_song_time"] = $2
298 db["mpd_curr_song_percent"] = $3
299 }
300
301 /^in:MPD_SONG/\
438d0d5f 302 {
365549a9
SK
303 split_msg_parts()
304 db["mpd_curr_song_name"] = msg_body
438d0d5f
SK
305 }
306
365549a9 307 /^in:WEATHER/\
438d0d5f 308 {
365549a9
SK
309 split_msg_parts()
310 db["weather_temperature"] = msg_body
438d0d5f
SK
311 }
312
365549a9 313 /^in:DATE_TIME/\
438d0d5f 314 {
365549a9
SK
315 split_msg_parts()
316 db["datetime"] = msg_body
317 }
318
319 /^out:BAR/\
320 {
321 split_msg_parts()
438d0d5f
SK
322 print make_bar()
323 }
324
365549a9 325
2b221665
SK
326 function set_net_addr_io( \
327 interface, address, io_curr_w, io_curr_r, io_prev_w, io_prev_r\
328 ) {
329 interface = $1
330 address = $2
331 io_curr_w = $3
332 io_curr_r = $4
333 if (interface) {
334 if (address && io_curr_w && io_curr_r) {
335 # recalculate
336 io_prev_w = net_io_curr_w[interface]
337 io_prev_r = net_io_curr_r[interface]
338
339 net_addr[interface] = address
340 net_io_curr_w[interface] = io_curr_w
341 net_io_curr_r[interface] = io_curr_r
342 net_io_diff_w[interface] = io_curr_w - io_prev_w
343 net_io_diff_r[interface] = io_curr_r - io_prev_r
344 } else {
345 # clear
346 net_addr[interface] = ""
347 net_io_curr_w[interface] = 0
348 net_io_curr_r[interface] = 0
349 net_io_diff_w[interface] = 0
350 net_io_diff_r[interface] = 0
351 }
352 }
353 }
354
bbb42518
SK
355 function set_screen_brightness( max, cur) {
356 max = $1
357 cur = $2
358 db["screen_brightness"] = (cur / max) * 100
359 }
360
365549a9
SK
361 function split_msg_parts() {
362 msg_head = $1
363 sub("^" msg_head " +", "")
364 msg_body = $0
365 debug(msg_head, msg_body)
438d0d5f
SK
366 }
367
365549a9 368 function make_bar( position, bar, sep, i, j) {
c0e8473e 369 position[++i] = sprintf("D=[%s]", db["disk_space_used"])
0d116e88 370 position[++i] = make_status_net()
ad0b3a5b 371 position[++i] = sprintf("B=%s", db["bluetooth_power"])
bbb42518 372 position[++i] = sprintf("*%d%%", db["screen_brightness"])
61e5a351 373 position[++i] = sprintf("(%s)", db["volume"])
365549a9 374 position[++i] = make_status_mpd()
438d0d5f
SK
375 position[++i] = db["weather_temperature"]
376 position[++i] = db["datetime"]
377 bar = ""
378 sep = ""
379 for (j = 1; j <= i; j++) {
380 bar = bar sep position[j]
381 sep = " "
382 }
383 return bar
384 }
365549a9 385
2b221665
SK
386 function make_status_net( \
387 out,
388 number_of_interfaces_to_show,
389 n,
390 array_of_prefixes_of_interfaces_to_show,
391 prefix,
392 interface,
393 label,
394 count_printed,
395 sep,
396 io_stat,
397 dw, dr,
398 bytes_per_unit\
399 ) {
400 out = ""
401 number_of_interfaces_to_show = \
402 split(\
403 opt_prefixes_of_net_interfaces_to_show,\
404 array_of_prefixes_of_interfaces_to_show,\
405 ","\
406 )
407 for (n = 1; n <= number_of_interfaces_to_show; n++) {
408 prefix = array_of_prefixes_of_interfaces_to_show[n]
409 for (interface in net_addr) {
410 if (interface ~ ("^" prefix)) {
411 label = substr(interface, 1, 1)
412 if (net_addr[interface]) {
413 bytes_per_mb = 1024 * 1024 # TODO: option
414 dw = net_io_diff_w[interface] / bytes_per_mb
415 dr = net_io_diff_r[interface] / bytes_per_mb
416 io_stat = sprintf("%0.3f▲ %0.3f▼", dw, dr)
417 } else {
418 io_stat = "--"
419 }
420 if (interface ~ "^w") {
421 label = label ":" db["net_wifi_status"]
422 }
423 if (++count_printed > 1) {
424 sep = " "
425 } else {
426 sep = ""
427 }
428 out = out sep label ":" io_stat
429 }
430 }
431 }
432 return sprintf("N[%s]", out)
0d116e88
SK
433 }
434
365549a9
SK
435 function make_status_mpd( state, status) {
436 state = db["mpd_state"]
437
438 if (state == "play") {
439 status = make_status_mpd_state_known("▶")
440 } else if (state == "pause") {
441 status = make_status_mpd_state_known("❚❚")
442 } else if (state == "stop") {
443 status = make_status_mpd_state_known("⬛")
444 } else {
445 status = make_status_mpd_state_unknown("--")
446 }
447
448 return sprintf("[%s]", status)
449 }
450
451 function make_status_mpd_state_known(symbol) {
452 return sprintf(\
453 "%s %s %s %s",
454 symbol,
455 db["mpd_curr_song_time"],
456 db["mpd_curr_song_percent"],
457 substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\
458 )
459 }
460
461 function make_status_mpd_state_unknown(symbol) {
462 return sprintf("%s", symbol)
463 }
464
465 function debug(location, msg) {
466 if (opt_debug) {
467 print_error(location, msg)
468 }
469 }
470
471 function print_error(location, msg) {
472 print(location " ==> " msg) > "/dev/stderr"
473 }
438d0d5f
SK
474 '
475}
476
6339a4f8 477produce_bar_req() {
438d0d5f
SK
478 echo ''
479}
480
481spawn() {
482 cmd="$1"
483 pipe="$2"
365549a9 484 msg_head="$3"
438d0d5f
SK
485 interval="$4"
486 while true; do
2b221665
SK
487 $cmd | while read line; do
488 echo "${msg_head} $line" > "$pipe"
489 done
490 sleep "$interval"
438d0d5f
SK
491 done &
492}
493
494main() {
4d314e0f 495 # Defaults
2b221665 496 debug=0
4d314e0f
SK
497 dir_data="$HOME/.khatus"
498 weather_station_id='KJFK'
bbb42518 499 screen_brightness_device_name='acpi_video0'
2b221665 500 prefixes_of_net_interfaces_to_show='w' # comma-separated
c0e8473e 501 disk_space_device='/'
4d314e0f
SK
502
503 # User-overrides
2b221665
SK
504 long_options=''
505 long_options+='debug'
506 long_options+=',data-dir:'
507 long_options+=',weather-station:'
508 long_options+=',screen-device:'
509 long_options+=',prefixes_of_net_interfaces_to_show:'
c0e8473e 510 long_options+=',disk_space_device:'
4d314e0f
SK
511 OPTS=$(
512 getopt \
2b221665
SK
513 -o 'd' \
514 -l $long_options \
4d314e0f
SK
515 -- "$@"
516 )
517 eval set -- "$OPTS"
518 while true
519 do
520 case "$1" in
2b221665
SK
521 -d|--debug)
522 debug=1
523 shift
524 ;;
4d314e0f
SK
525 --data-dir)
526 dir_data="$2"
527 shift 2
528 ;;
529 --weather-station)
530 weather_station_id="$2"
531 shift 2
532 ;;
bbb42518
SK
533 --screen-device)
534 screen_brightness_device_name="$2"
535 shift 2
536 ;;
2b221665
SK
537 --prefixes_of_net_interfaces_to_show)
538 prefixes_of_net_interfaces_to_show="$2"
539 shift 2
540 ;;
c0e8473e
SK
541 --disk_space_device)
542 disk_space_device="$2"
543 shift 2
544 ;;
4d314e0f
SK
545 --)
546 shift
547 break
548 ;;
549 esac
550 done
551
0c0ebbe1 552 pipe="$dir_data/khatus_data_pipe"
bbb42518
SK
553 screen_brightness_device_path='/sys/class/backlight'
554 screen_brightness_device_path+="/$screen_brightness_device_name"
0c0ebbe1 555
4d314e0f 556 ( echo "Khatus starting with the following parameters:"
2b221665
SK
557 ( echo " debug|= $debug"
558 echo " dir_data|= $dir_data"
0c0ebbe1 559 echo " pipe|= $pipe"
2b221665
SK
560 echo " screen_brightness_device_name|= $screen_brightness_device_name"
561 echo " screen_brightness_device_path|= $screen_brightness_device_path"
4d314e0f 562 echo " weather_station_id|= $weather_station_id"
c0e8473e
SK
563 echo " prefixes_of_net_interfaces_to_show|= $prefixes_of_net_interfaces_to_show"
564 echo " disk_space_device|= $disk_space_device"
4d314e0f
SK
565 ) | column -ts\|
566 echo ''
567 ) >&2
568
4d314e0f 569 mkdir -p "$dir_data"
438d0d5f
SK
570 rm -f "$pipe"
571 mkfifo "$pipe"
572
bbb42518
SK
573 cmd_produce_screen_brightness='produce_screen_brightness'
574 cmd_produce_screen_brightness+=" $screen_brightness_device_path"
2b221665 575
0c0ebbe1
SK
576 cmd_produce_weather="produce_weather $weather_station_id"
577
c0e8473e
SK
578 cmd_produce_disk_space="produce_disk_space $disk_space_device"
579
4d314e0f 580 # TODO: Redirect each worker's stderr to a dedicated log file
0c0ebbe1 581 spawn produce_datetime "$pipe" 'in:DATE_TIME' 1
bbb42518 582 spawn "$cmd_produce_screen_brightness" "$pipe" 'in:SCREEN_BRIGHTNESS' 1
0c0ebbe1
SK
583 spawn "$cmd_produce_weather" "$pipe" 'in:WEATHER' $(( 30 * 60 ))
584 spawn produce_mpd_state "$pipe" 'in:MPD_STATE' 1
585 spawn produce_mpd_song "$pipe" 'in:MPD_SONG' 1
586 spawn produce_volume "$pipe" 'in:VOLUME' 1
ad0b3a5b 587 spawn produce_bluetooth_power "$pipe" 'in:BLUETOOTH_POWER' 5
0d116e88 588 spawn produce_net_wifi_status "$pipe" 'in:NET_WIFI_STATUS' 5
2b221665 589 spawn produce_net_addr_io "$pipe" 'in:NET_ADDR_IO' 1
c0e8473e 590 spawn "$cmd_produce_disk_space" "$pipe" 'in:DISK_SPACE' 1
0c0ebbe1 591 spawn produce_bar_req "$pipe" 'out:BAR' 1
2b221665
SK
592
593 consume \
594 "$pipe" \
595 "$debug" \
596 "$prefixes_of_net_interfaces_to_show"
438d0d5f
SK
597}
598
599main $@
This page took 0.100759 seconds and 4 git commands to generate.