5 produce_temperature
() {
7 cat "/sys/class/thermal/thermal_zone${thermal_zone}/temp"
22 ' "/sys/block/$disk_io_device/stat"
25 produce_disk_space
() {
26 disk_space_device
="$1"
27 df
--output=pcent
"$disk_space_device" |
awk 'NR == 2 {print $1}'
30 produce_net_addr_io
() {
34 bytes_per_unit = 1024 * 1024
42 interfaces[sequence] = interface
46 sub("/[0-9]+", "", $2)
48 addrs[interface] = addr
51 /^ +RX: / {transfer_direction = "r"}
52 /^ +TX: / {transfer_direction = "w"}
54 /^ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ *$/ {
55 io[interface, transfer_direction] = $1;
59 for (seq=1; seq<=sequence; seq++) {
60 interface = interfaces[seq]
61 label = substr(interface, 1, 1)
62 if (addrs[interface]) {
63 curr_read = io[interface, "r"]
64 curr_write = io[interface, "w"]
65 print(interface, addrs[interface], curr_write, curr_read)
73 produce_net_wifi_status
() {
75 -f ACTIVE
,SSID
,SIGNAL \
81 BEGIN {wifi_status = "--"}
82 $1 == "yes" {wifi_status = $2 ":" $3 "%"}
83 END {print wifi_status}
87 produce_bluetooth_power
() {
88 echo -e 'show \n quit' \
93 controllers[++ctrl_count] = controller;
95 /^\t[A-Z][A-Za-z]+:/ {
99 for (i=3; i<=NF; i++) {
101 data[controller, key] = val;
104 # Using the 1st seen controller. Should we select specific instead?
105 power_status = data[controllers[1], "Powered"];
106 if (ctrl_count > 0) {
107 if (power_status == "no") {
109 } else if (power_status == "yes") {
112 printf("Unexpected bluetooth power status: %s\n", power_status)\
114 power_status = "ERROR"
117 power_status = "off" # TODO: Perhaps use differentiated marker?
119 printf("%s\n", power_status);
123 produce_screen_brightness
() {
124 screen_brightness_device_path
="$1"
126 $(cat $screen_brightness_device_path/max_brightness) \
127 $(cat $screen_brightness_device_path/brightness)\
138 for (i=2; i<=NF; i++) printf(" %s", $i);
141 /^[ \t]*Mute:/ {mute = $2}
142 /^[ \t]*front-left:/ {left = $4}
143 /^[ \t]*front-right:/ {right = $4}
148 print("%s %s\n", left, right)
154 produce_mpd_state
() {
156 | nc
127.0.0.1 6600 \
162 /^time: +[0-9]+:[0-9]+$/ {
164 seconds_current = time[1]
165 seconds_total = time[2]
167 hours = int(seconds_current / 60 / 60);
168 secs_beyond_hours = seconds_current - (hours * 60 * 60);
169 mins = int(secs_beyond_hours / 60);
170 secs = secs_beyond_hours - (mins * 60);
172 current_time = sprintf("%d:%.2d:%.2d", hours, mins, secs)
174 current_time = sprintf("%.2d:%.2d", mins, secs)
177 if (seconds_total > 0) {
178 time_percentage = (seconds_current / seconds_total) * 100
179 current_percentage = sprintf("%d%%", time_percentage)
181 current_percentage = "~"
186 state = status["state:"]
188 if (state == "play") {
190 } else if (state == "pause") {
192 } else if (state == "stop") {
200 status["state:"], current_time, current_percentage\
208 | nc
127.0.0.1 6600 \
216 sub("^" key " +", "")
223 title = data["Title:"]
231 last = split(file, parts, "/")
242 weather_station_id
="$1"
243 metar
-d "$weather_station_id" 2>&1 \
245 /METAR pattern not found in NOAA data/ {
251 fahrenheit = (celsius * (9 / 5)) + 32;
252 temperature = fahrenheit
259 print temperature "°F"
264 date +'%a %b %d %H:%M:%S'
270 prefixes_of_net_interfaces_to_show
="$3"
273 -v opt_debug
="$debug" \
274 -v opt_mpd_song_max_chars
=10 \
275 -v opt_prefixes_of_net_interfaces_to_show
="$prefixes_of_net_interfaces_to_show" \
280 db["temperature"] = $1
298 db["disk_space_used"] = msg_body
307 /^in:NET_WIFI_STATUS/\
310 db["net_wifi_status"] = msg_body
313 /^in:BLUETOOTH_POWER/\
316 db["bluetooth_power"] = msg_body
319 /^in:SCREEN_BRIGHTNESS/\
322 set_screen_brightness()
328 db["volume"] = msg_body
335 db["mpd_curr_song_time"] = $2
336 db["mpd_curr_song_percent"] = $3
342 db["mpd_curr_song_name"] = msg_body
348 db["weather_temperature"] = msg_body
354 db["datetime"] = msg_body
363 function set_load_avg( sched) {
364 split($4, sched, "/")
365 db["load_avg_1min"] = $1
366 db["load_avg_5min"] = $2
367 db["load_avg_15min"] = $3
368 db["kern_sched_queue_runnable"] = sched[1]
369 db["kern_sched_queue_total"] = sched[2]
370 db["kern_sched_latest_pid"] = $5
373 function set_disk_io( curr_w, curr_r, prev_w, prev_r) {
376 prev_w = db["disk_io_curr_w"]
377 prev_r = db["disk_io_curr_r"]
378 db["disk_io_curr_w"] = curr_w
379 db["disk_io_curr_r"] = curr_r
380 db["disk_io_diff_w"] = curr_w - prev_w
381 db["disk_io_diff_r"] = curr_r - prev_r
384 function set_net_addr_io( \
385 interface, address, io_curr_w, io_curr_r, io_prev_w, io_prev_r\
392 if (address && io_curr_w && io_curr_r) {
394 io_prev_w = net_io_curr_w[interface]
395 io_prev_r = net_io_curr_r[interface]
397 net_addr[interface] = address
398 net_io_curr_w[interface] = io_curr_w
399 net_io_curr_r[interface] = io_curr_r
400 net_io_diff_w[interface] = io_curr_w - io_prev_w
401 net_io_diff_r[interface] = io_curr_r - io_prev_r
404 net_addr[interface] = ""
405 net_io_curr_w[interface] = 0
406 net_io_curr_r[interface] = 0
407 net_io_diff_w[interface] = 0
408 net_io_diff_r[interface] = 0
413 function set_screen_brightness( max, cur) {
416 db["screen_brightness"] = (cur / max) * 100
419 function split_msg_parts() {
421 sub("^" msg_head " +", "")
423 debug(msg_head, msg_body)
426 function make_bar( position, bar, sep, i, j) {
427 position[++i] = make_status_cpu()
428 position[++i] = make_status_disk()
429 position[++i] = make_status_net()
430 position[++i] = sprintf("B=%s", db["bluetooth_power"])
431 position[++i] = sprintf("*%d%%", db["screen_brightness"])
432 position[++i] = sprintf("(%s)", db["volume"])
433 position[++i] = make_status_mpd()
434 position[++i] = db["weather_temperature"]
435 position[++i] = db["datetime"]
438 for (j = 1; j <= i; j++) {
439 bar = bar sep position[j]
445 function make_status_cpu( load, temp) {
446 load = db["load_avg_1min"]
447 temp = db["temperature"] / 1000
448 return sprintf("C=[%4.2f %d°C]", load, temp)
451 function make_status_disk( bytes_per_sector, bytes_per_mb, w, r) {
452 bytes_per_sector = 512
453 bytes_per_mb = 1024 * 1024
454 w = (db["disk_io_diff_w"] * bytes_per_sector) / bytes_per_mb
455 r = (db["disk_io_diff_r"] * bytes_per_sector) / bytes_per_mb
457 sprintf("D=[%s %0.3f▲ %0.3f▼]", db["disk_space_used"], w, r)
460 function make_status_net( \
462 number_of_interfaces_to_show,
464 array_of_prefixes_of_interfaces_to_show,
475 number_of_interfaces_to_show = \
477 opt_prefixes_of_net_interfaces_to_show,\
478 array_of_prefixes_of_interfaces_to_show,\
481 for (n = 1; n <= number_of_interfaces_to_show; n++) {
482 prefix = array_of_prefixes_of_interfaces_to_show[n]
483 for (interface in net_addr) {
484 if (interface ~ ("^" prefix)) {
485 label = substr(interface, 1, 1)
486 if (net_addr[interface]) {
487 bytes_per_mb = 1024 * 1024 # TODO: option
488 dw = net_io_diff_w[interface] / bytes_per_mb
489 dr = net_io_diff_r[interface] / bytes_per_mb
490 io_stat = sprintf("%0.3f▲ %0.3f▼", dw, dr)
494 if (interface ~ "^w") {
495 label = label ":" db["net_wifi_status"]
497 if (++count_printed > 1) {
502 out = out sep label ":" io_stat
506 return sprintf("N[%s]", out)
509 function make_status_mpd( state, status) {
510 state = db["mpd_state"]
512 if (state == "play") {
513 status = make_status_mpd_state_known("▶")
514 } else if (state == "pause") {
515 status = make_status_mpd_state_known("❚❚")
516 } else if (state == "stop") {
517 status = make_status_mpd_state_known("⬛")
519 status = make_status_mpd_state_unknown("--")
522 return sprintf("[%s]", status)
525 function make_status_mpd_state_known(symbol) {
529 db["mpd_curr_song_time"],
530 db["mpd_curr_song_percent"],
531 substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\
535 function make_status_mpd_state_unknown(symbol) {
536 return sprintf("%s", symbol)
539 function debug(location, msg) {
541 print_error(location, msg)
545 function print_error(location, msg) {
546 print(location " ==> " msg) > "/dev/stderr"
561 $cmd |
while read line
; do
562 echo "${msg_head} $line" > "$pipe"
571 dir_data
="$HOME/.khatus"
572 weather_station_id
='KJFK'
573 screen_brightness_device_name
='acpi_video0'
574 prefixes_of_net_interfaces_to_show
='w' # comma-separated
575 disk_space_device
='/'
581 long_options
+='debug'
582 long_options
+=',data-dir:'
583 long_options
+=',weather-station:'
584 long_options
+=',screen-device:'
585 long_options
+=',prefixes_of_net_interfaces_to_show:'
586 long_options
+=',disk_space_device:'
587 long_options
+=',thermal_zone:'
607 weather_station_id
="$2"
611 screen_brightness_device_name
="$2"
614 --prefixes_of_net_interfaces_to_show)
615 prefixes_of_net_interfaces_to_show
="$2"
619 disk_space_device
="$2"
637 pipe
="$dir_data/khatus_data_pipe"
638 screen_brightness_device_path
='/sys/class/backlight'
639 screen_brightness_device_path
+="/$screen_brightness_device_name"
641 ( echo "Khatus starting with the following parameters:"
642 ( echo " debug|= $debug"
643 echo " dir_data|= $dir_data"
645 echo " screen_brightness_device_name|= $screen_brightness_device_name"
646 echo " screen_brightness_device_path|= $screen_brightness_device_path"
647 echo " weather_station_id|= $weather_station_id"
648 echo " prefixes_of_net_interfaces_to_show|= $prefixes_of_net_interfaces_to_show"
649 echo " disk_space_device|= $disk_space_device"
650 echo " disk_io_device|= $disk_io_device"
651 echo " thermal_zone|= $thermal_zone"
660 cmd_produce_screen_brightness
='produce_screen_brightness'
661 cmd_produce_screen_brightness
+=" $screen_brightness_device_path"
663 cmd_produce_weather
="produce_weather $weather_station_id"
665 cmd_produce_disk_space
="produce_disk_space $disk_space_device"
667 cmd_produce_disk_io
="produce_disk_io $disk_io_device"
669 cmd_produce_temperature
="produce_temperature $thermal_zone"
671 # TODO: Redirect each worker's stderr to a dedicated log file
672 spawn produce_datetime
"$pipe" 'in:DATE_TIME' 1
673 spawn
"$cmd_produce_screen_brightness" "$pipe" 'in:SCREEN_BRIGHTNESS' 1
674 spawn
"$cmd_produce_weather" "$pipe" 'in:WEATHER' $
(( 30 * 60 ))
675 spawn produce_mpd_state
"$pipe" 'in:MPD_STATE' 1
676 spawn produce_mpd_song
"$pipe" 'in:MPD_SONG' 1
677 spawn produce_volume
"$pipe" 'in:VOLUME' 1
678 spawn produce_bluetooth_power
"$pipe" 'in:BLUETOOTH_POWER' 5
679 spawn produce_net_wifi_status
"$pipe" 'in:NET_WIFI_STATUS' 5
680 spawn produce_net_addr_io
"$pipe" 'in:NET_ADDR_IO' 1
681 spawn
"$cmd_produce_disk_space" "$pipe" 'in:DISK_SPACE' 1
682 spawn
"$cmd_produce_disk_io" "$pipe" 'in:DISK_IO' 1
683 spawn produce_loadavg
"$pipe" 'in:LOAD_AVG' 1
684 spawn
"$cmd_produce_temperature" "$pipe" 'in:TEMPERATURE' 1
685 spawn produce_bar_req
"$pipe" 'out:BAR' 1
690 "$prefixes_of_net_interfaces_to_show"