13 ' "/sys/block/$disk_io_device/stat"
16 produce_disk_space
() {
17 disk_space_device
="$1"
18 df
--output=pcent
"$disk_space_device" |
awk 'NR == 2 {print $1}'
21 produce_net_addr_io
() {
25 bytes_per_unit = 1024 * 1024
33 interfaces[sequence] = interface
37 sub("/[0-9]+", "", $2)
39 addrs[interface] = addr
42 /^ +RX: / {transfer_direction = "r"}
43 /^ +TX: / {transfer_direction = "w"}
45 /^ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ *$/ {
46 io[interface, transfer_direction] = $1;
50 for (seq=1; seq<=sequence; seq++) {
51 interface = interfaces[seq]
52 label = substr(interface, 1, 1)
53 if (addrs[interface]) {
54 curr_read = io[interface, "r"]
55 curr_write = io[interface, "w"]
56 print(interface, addrs[interface], curr_write, curr_read)
64 produce_net_wifi_status
() {
66 -f ACTIVE
,SSID
,SIGNAL \
72 BEGIN {wifi_status = "--"}
73 $1 == "yes" {wifi_status = $2 ":" $3 "%"}
74 END {print wifi_status}
78 produce_bluetooth_power
() {
79 echo -e 'show \n quit' \
84 controllers[++ctrl_count] = controller;
86 /^\t[A-Z][A-Za-z]+:/ {
90 for (i=3; i<=NF; i++) {
92 data[controller, key] = val;
95 # Using the 1st seen controller. Should we select specific instead?
96 power_status = data[controllers[1], "Powered"];
98 if (power_status == "no") {
100 } else if (power_status == "yes") {
103 printf("Unexpected bluetooth power status: %s\n", power_status)\
105 power_status = "ERROR"
108 power_status = "off" # TODO: Perhaps use differentiated marker?
110 printf("%s\n", power_status);
114 produce_screen_brightness
() {
115 screen_brightness_device_path
="$1"
117 $(cat $screen_brightness_device_path/max_brightness) \
118 $(cat $screen_brightness_device_path/brightness)\
129 for (i=2; i<=NF; i++) printf(" %s", $i);
132 /^[ \t]*Mute:/ {mute = $2}
133 /^[ \t]*front-left:/ {left = $4}
134 /^[ \t]*front-right:/ {right = $4}
139 print("%s %s\n", left, right)
145 produce_mpd_state
() {
147 | nc
127.0.0.1 6600 \
153 /^time: +[0-9]+:[0-9]+$/ {
155 seconds_current = time[1]
156 seconds_total = time[2]
158 hours = int(seconds_current / 60 / 60);
159 secs_beyond_hours = seconds_current - (hours * 60 * 60);
160 mins = int(secs_beyond_hours / 60);
161 secs = secs_beyond_hours - (mins * 60);
163 current_time = sprintf("%d:%.2d:%.2d", hours, mins, secs)
165 current_time = sprintf("%.2d:%.2d", mins, secs)
168 if (seconds_total > 0) {
169 time_percentage = (seconds_current / seconds_total) * 100
170 current_percentage = sprintf("%d%%", time_percentage)
172 current_percentage = "~"
177 state = status["state:"]
179 if (state == "play") {
181 } else if (state == "pause") {
183 } else if (state == "stop") {
191 status["state:"], current_time, current_percentage\
199 | nc
127.0.0.1 6600 \
207 sub("^" key " +", "")
214 title = data["Title:"]
222 last = split(file, parts, "/")
233 weather_station_id
="$1"
234 metar
-d "$weather_station_id" 2>&1 \
236 /METAR pattern not found in NOAA data/ {
242 fahrenheit = (celsius * (9 / 5)) + 32;
243 temperature = fahrenheit
250 print temperature "°F"
255 date +'%a %b %d %H:%M:%S'
261 prefixes_of_net_interfaces_to_show
="$3"
264 -v opt_debug
="$debug" \
265 -v opt_mpd_song_max_chars
=10 \
266 -v opt_prefixes_of_net_interfaces_to_show
="$prefixes_of_net_interfaces_to_show" \
277 db["disk_space_used"] = msg_body
286 /^in:NET_WIFI_STATUS/\
289 db["net_wifi_status"] = msg_body
292 /^in:BLUETOOTH_POWER/\
295 db["bluetooth_power"] = msg_body
298 /^in:SCREEN_BRIGHTNESS/\
301 set_screen_brightness()
307 db["volume"] = msg_body
314 db["mpd_curr_song_time"] = $2
315 db["mpd_curr_song_percent"] = $3
321 db["mpd_curr_song_name"] = msg_body
327 db["weather_temperature"] = msg_body
333 db["datetime"] = msg_body
343 function set_disk_io( curr_w, curr_r, prev_w, prev_r) {
346 prev_w = db["disk_io_curr_w"]
347 prev_r = db["disk_io_curr_r"]
348 db["disk_io_curr_w"] = curr_w
349 db["disk_io_curr_r"] = curr_r
350 db["disk_io_diff_w"] = curr_w - prev_w
351 db["disk_io_diff_r"] = curr_r - prev_r
354 function set_net_addr_io( \
355 interface, address, io_curr_w, io_curr_r, io_prev_w, io_prev_r\
362 if (address && io_curr_w && io_curr_r) {
364 io_prev_w = net_io_curr_w[interface]
365 io_prev_r = net_io_curr_r[interface]
367 net_addr[interface] = address
368 net_io_curr_w[interface] = io_curr_w
369 net_io_curr_r[interface] = io_curr_r
370 net_io_diff_w[interface] = io_curr_w - io_prev_w
371 net_io_diff_r[interface] = io_curr_r - io_prev_r
374 net_addr[interface] = ""
375 net_io_curr_w[interface] = 0
376 net_io_curr_r[interface] = 0
377 net_io_diff_w[interface] = 0
378 net_io_diff_r[interface] = 0
383 function set_screen_brightness( max, cur) {
386 db["screen_brightness"] = (cur / max) * 100
389 function split_msg_parts() {
391 sub("^" msg_head " +", "")
393 debug(msg_head, msg_body)
396 function make_bar( position, bar, sep, i, j) {
397 position[++i] = make_status_disk()
398 position[++i] = make_status_net()
399 position[++i] = sprintf("B=%s", db["bluetooth_power"])
400 position[++i] = sprintf("*%d%%", db["screen_brightness"])
401 position[++i] = sprintf("(%s)", db["volume"])
402 position[++i] = make_status_mpd()
403 position[++i] = db["weather_temperature"]
404 position[++i] = db["datetime"]
407 for (j = 1; j <= i; j++) {
408 bar = bar sep position[j]
414 function make_status_disk( bytes_per_sector, bytes_per_mb, w, r) {
415 bytes_per_sector = 512
416 bytes_per_mb = 1024 * 1024
417 w = (db["disk_io_diff_w"] * bytes_per_sector) / bytes_per_mb
418 r = (db["disk_io_diff_r"] * bytes_per_sector) / bytes_per_mb
420 sprintf("D=[%s %0.3f▲ %0.3f▼]", db["disk_space_used"], w, r)
423 function make_status_net( \
425 number_of_interfaces_to_show,
427 array_of_prefixes_of_interfaces_to_show,
438 number_of_interfaces_to_show = \
440 opt_prefixes_of_net_interfaces_to_show,\
441 array_of_prefixes_of_interfaces_to_show,\
444 for (n = 1; n <= number_of_interfaces_to_show; n++) {
445 prefix = array_of_prefixes_of_interfaces_to_show[n]
446 for (interface in net_addr) {
447 if (interface ~ ("^" prefix)) {
448 label = substr(interface, 1, 1)
449 if (net_addr[interface]) {
450 bytes_per_mb = 1024 * 1024 # TODO: option
451 dw = net_io_diff_w[interface] / bytes_per_mb
452 dr = net_io_diff_r[interface] / bytes_per_mb
453 io_stat = sprintf("%0.3f▲ %0.3f▼", dw, dr)
457 if (interface ~ "^w") {
458 label = label ":" db["net_wifi_status"]
460 if (++count_printed > 1) {
465 out = out sep label ":" io_stat
469 return sprintf("N[%s]", out)
472 function make_status_mpd( state, status) {
473 state = db["mpd_state"]
475 if (state == "play") {
476 status = make_status_mpd_state_known("▶")
477 } else if (state == "pause") {
478 status = make_status_mpd_state_known("❚❚")
479 } else if (state == "stop") {
480 status = make_status_mpd_state_known("⬛")
482 status = make_status_mpd_state_unknown("--")
485 return sprintf("[%s]", status)
488 function make_status_mpd_state_known(symbol) {
492 db["mpd_curr_song_time"],
493 db["mpd_curr_song_percent"],
494 substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\
498 function make_status_mpd_state_unknown(symbol) {
499 return sprintf("%s", symbol)
502 function debug(location, msg) {
504 print_error(location, msg)
508 function print_error(location, msg) {
509 print(location " ==> " msg) > "/dev/stderr"
524 $cmd |
while read line
; do
525 echo "${msg_head} $line" > "$pipe"
534 dir_data
="$HOME/.khatus"
535 weather_station_id
='KJFK'
536 screen_brightness_device_name
='acpi_video0'
537 prefixes_of_net_interfaces_to_show
='w' # comma-separated
538 disk_space_device
='/'
543 long_options
+='debug'
544 long_options
+=',data-dir:'
545 long_options
+=',weather-station:'
546 long_options
+=',screen-device:'
547 long_options
+=',prefixes_of_net_interfaces_to_show:'
548 long_options
+=',disk_space_device:'
568 weather_station_id
="$2"
572 screen_brightness_device_name
="$2"
575 --prefixes_of_net_interfaces_to_show)
576 prefixes_of_net_interfaces_to_show
="$2"
580 disk_space_device
="$2"
594 pipe
="$dir_data/khatus_data_pipe"
595 screen_brightness_device_path
='/sys/class/backlight'
596 screen_brightness_device_path
+="/$screen_brightness_device_name"
598 ( echo "Khatus starting with the following parameters:"
599 ( echo " debug|= $debug"
600 echo " dir_data|= $dir_data"
602 echo " screen_brightness_device_name|= $screen_brightness_device_name"
603 echo " screen_brightness_device_path|= $screen_brightness_device_path"
604 echo " weather_station_id|= $weather_station_id"
605 echo " prefixes_of_net_interfaces_to_show|= $prefixes_of_net_interfaces_to_show"
606 echo " disk_space_device|= $disk_space_device"
607 echo " disk_io_device|= $disk_io_device"
616 cmd_produce_screen_brightness
='produce_screen_brightness'
617 cmd_produce_screen_brightness
+=" $screen_brightness_device_path"
619 cmd_produce_weather
="produce_weather $weather_station_id"
621 cmd_produce_disk_space
="produce_disk_space $disk_space_device"
623 cmd_produce_disk_io
="produce_disk_io $disk_io_device"
625 # TODO: Redirect each worker's stderr to a dedicated log file
626 spawn produce_datetime
"$pipe" 'in:DATE_TIME' 1
627 spawn
"$cmd_produce_screen_brightness" "$pipe" 'in:SCREEN_BRIGHTNESS' 1
628 spawn
"$cmd_produce_weather" "$pipe" 'in:WEATHER' $
(( 30 * 60 ))
629 spawn produce_mpd_state
"$pipe" 'in:MPD_STATE' 1
630 spawn produce_mpd_song
"$pipe" 'in:MPD_SONG' 1
631 spawn produce_volume
"$pipe" 'in:VOLUME' 1
632 spawn produce_bluetooth_power
"$pipe" 'in:BLUETOOTH_POWER' 5
633 spawn produce_net_wifi_status
"$pipe" 'in:NET_WIFI_STATUS' 5
634 spawn produce_net_addr_io
"$pipe" 'in:NET_ADDR_IO' 1
635 spawn
"$cmd_produce_disk_space" "$pipe" 'in:DISK_SPACE' 1
636 spawn
"$cmd_produce_disk_io" "$pipe" 'in:DISK_IO' 1
637 spawn produce_bar_req
"$pipe" 'out:BAR' 1
642 "$prefixes_of_net_interfaces_to_show"