10 produce_temperature
() {
12 cat "/sys/class/thermal/thermal_zone${thermal_zone}/temp"
27 ' "/sys/block/$disk_io_device/stat"
30 produce_disk_space
() {
31 disk_space_device
="$1"
32 df
--output=pcent
"$disk_space_device" |
awk 'NR == 2 {print $1}'
35 produce_net_addr_io
() {
39 bytes_per_unit = 1024 * 1024
47 interfaces[sequence] = interface
51 sub("/[0-9]+", "", $2)
53 addrs[interface] = addr
56 /^ +RX: / {transfer_direction = "r"}
57 /^ +TX: / {transfer_direction = "w"}
59 /^ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ *$/ {
60 io[interface, transfer_direction] = $1;
64 for (seq=1; seq<=sequence; seq++) {
65 interface = interfaces[seq]
66 label = substr(interface, 1, 1)
67 if (addrs[interface]) {
68 curr_read = io[interface, "r"]
69 curr_write = io[interface, "w"]
70 print(interface, addrs[interface], curr_write, curr_read)
78 produce_net_wifi_status
() {
80 -f ACTIVE
,SSID
,SIGNAL \
86 BEGIN {wifi_status = "--"}
87 $1 == "yes" {wifi_status = $2 ":" $3 "%"}
88 END {print wifi_status}
92 produce_bluetooth_power
() {
93 echo -e 'show \n quit' \
98 controllers[++ctrl_count] = controller;
100 /^\t[A-Z][A-Za-z]+:/ {
104 for (i=3; i<=NF; i++) {
106 data[controller, key] = val;
109 # Using the 1st seen controller. Should we select specific instead?
110 power_status = data[controllers[1], "Powered"];
111 if (ctrl_count > 0) {
112 if (power_status == "no") {
114 } else if (power_status == "yes") {
117 printf("Unexpected bluetooth power status: %s\n", power_status)\
119 power_status = "ERROR"
122 power_status = "off" # TODO: Perhaps use differentiated marker?
124 printf("%s\n", power_status);
128 produce_screen_brightness
() {
129 screen_brightness_device_path
="$1"
131 $(cat $screen_brightness_device_path/max_brightness) \
132 $(cat $screen_brightness_device_path/brightness)\
143 for (i=2; i<=NF; i++) printf(" %s", $i);
146 /^[ \t]*Mute:/ {mute = $2}
147 /^[ \t]*front-left:/ {left = $4}
148 /^[ \t]*front-right:/ {right = $4}
153 print("%s %s\n", left, right)
159 produce_mpd_state
() {
161 | nc
127.0.0.1 6600 \
167 /^time: +[0-9]+:[0-9]+$/ {
169 seconds_current = time[1]
170 seconds_total = time[2]
172 hours = int(seconds_current / 60 / 60);
173 secs_beyond_hours = seconds_current - (hours * 60 * 60);
174 mins = int(secs_beyond_hours / 60);
175 secs = secs_beyond_hours - (mins * 60);
177 current_time = sprintf("%d:%.2d:%.2d", hours, mins, secs)
179 current_time = sprintf("%.2d:%.2d", mins, secs)
182 if (seconds_total > 0) {
183 time_percentage = (seconds_current / seconds_total) * 100
184 current_percentage = sprintf("%d%%", time_percentage)
186 current_percentage = "~"
191 state = status["state:"]
193 if (state == "play") {
195 } else if (state == "pause") {
197 } else if (state == "stop") {
205 status["state:"], current_time, current_percentage\
213 | nc
127.0.0.1 6600 \
221 sub("^" key " +", "")
228 title = data["Title:"]
236 last = split(file, parts, "/")
247 weather_station_id
="$1"
248 metar
-d "$weather_station_id" 2>&1 \
250 /METAR pattern not found in NOAA data/ {
256 fahrenheit = (celsius * (9 / 5)) + 32;
257 temperature = fahrenheit
264 print temperature "°F"
269 date +'%a %b %d %H:%M:%S'
275 prefixes_of_net_interfaces_to_show
="$3"
278 -v opt_debug
="$debug" \
279 -v opt_mpd_song_max_chars
=10 \
280 -v opt_prefixes_of_net_interfaces_to_show
="$prefixes_of_net_interfaces_to_show" \
285 db["fan_status"] = $2
303 db["temperature"] = $1
321 db["disk_space_used"] = msg_body
330 /^in:NET_WIFI_STATUS/\
333 db["net_wifi_status"] = msg_body
336 /^in:BLUETOOTH_POWER/\
339 db["bluetooth_power"] = msg_body
342 /^in:SCREEN_BRIGHTNESS/\
345 set_screen_brightness()
351 db["volume"] = msg_body
358 db["mpd_curr_song_time"] = $2
359 db["mpd_curr_song_percent"] = $3
365 db["mpd_curr_song_name"] = msg_body
371 db["weather_temperature"] = msg_body
377 db["datetime"] = msg_body
386 function set_load_avg( sched) {
387 split($4, sched, "/")
388 db["load_avg_1min"] = $1
389 db["load_avg_5min"] = $2
390 db["load_avg_15min"] = $3
391 db["kern_sched_queue_runnable"] = sched[1]
392 db["kern_sched_queue_total"] = sched[2]
393 db["kern_sched_latest_pid"] = $5
396 function set_disk_io( curr_w, curr_r, prev_w, prev_r) {
399 prev_w = db["disk_io_curr_w"]
400 prev_r = db["disk_io_curr_r"]
401 db["disk_io_curr_w"] = curr_w
402 db["disk_io_curr_r"] = curr_r
403 db["disk_io_diff_w"] = curr_w - prev_w
404 db["disk_io_diff_r"] = curr_r - prev_r
407 function set_net_addr_io( \
408 interface, address, io_curr_w, io_curr_r, io_prev_w, io_prev_r\
415 if (address && io_curr_w && io_curr_r) {
417 io_prev_w = net_io_curr_w[interface]
418 io_prev_r = net_io_curr_r[interface]
420 net_addr[interface] = address
421 net_io_curr_w[interface] = io_curr_w
422 net_io_curr_r[interface] = io_curr_r
423 net_io_diff_w[interface] = io_curr_w - io_prev_w
424 net_io_diff_r[interface] = io_curr_r - io_prev_r
427 net_addr[interface] = ""
428 net_io_curr_w[interface] = 0
429 net_io_curr_r[interface] = 0
430 net_io_diff_w[interface] = 0
431 net_io_diff_r[interface] = 0
436 function set_screen_brightness( max, cur) {
439 db["screen_brightness"] = (cur / max) * 100
442 function split_msg_parts() {
444 sub("^" msg_head " +", "")
446 debug(msg_head, msg_body)
449 function make_bar( position, bar, sep, i, j) {
450 position[++i] = make_status_cpu()
451 position[++i] = make_status_disk()
452 position[++i] = make_status_net()
453 position[++i] = sprintf("B=%s", db["bluetooth_power"])
454 position[++i] = sprintf("*%d%%", db["screen_brightness"])
455 position[++i] = sprintf("(%s)", db["volume"])
456 position[++i] = make_status_mpd()
457 position[++i] = db["weather_temperature"]
458 position[++i] = db["datetime"]
461 for (j = 1; j <= i; j++) {
462 bar = bar sep position[j]
468 function make_status_cpu( load, temp, fan) {
469 load = db["load_avg_1min"]
470 temp = db["temperature"] / 1000
471 fan = db["fan_speed"]
472 return sprintf("C=[%4.2f %d°C %4drpm]", load, temp, fan)
475 function make_status_disk( bytes_per_sector, bytes_per_mb, w, r) {
476 bytes_per_sector = 512
477 bytes_per_mb = 1024 * 1024
478 w = (db["disk_io_diff_w"] * bytes_per_sector) / bytes_per_mb
479 r = (db["disk_io_diff_r"] * bytes_per_sector) / bytes_per_mb
481 sprintf("D=[%s %0.3f▲ %0.3f▼]", db["disk_space_used"], w, r)
484 function make_status_net( \
486 number_of_interfaces_to_show,
488 array_of_prefixes_of_interfaces_to_show,
499 number_of_interfaces_to_show = \
501 opt_prefixes_of_net_interfaces_to_show,\
502 array_of_prefixes_of_interfaces_to_show,\
505 for (n = 1; n <= number_of_interfaces_to_show; n++) {
506 prefix = array_of_prefixes_of_interfaces_to_show[n]
507 for (interface in net_addr) {
508 if (interface ~ ("^" prefix)) {
509 label = substr(interface, 1, 1)
510 if (net_addr[interface]) {
511 bytes_per_mb = 1024 * 1024 # TODO: option
512 dw = net_io_diff_w[interface] / bytes_per_mb
513 dr = net_io_diff_r[interface] / bytes_per_mb
514 io_stat = sprintf("%0.3f▲ %0.3f▼", dw, dr)
518 if (interface ~ "^w") {
519 label = label ":" db["net_wifi_status"]
521 if (++count_printed > 1) {
526 out = out sep label ":" io_stat
530 return sprintf("N[%s]", out)
533 function make_status_mpd( state, status) {
534 state = db["mpd_state"]
536 if (state == "play") {
537 status = make_status_mpd_state_known("▶")
538 } else if (state == "pause") {
539 status = make_status_mpd_state_known("❚❚")
540 } else if (state == "stop") {
541 status = make_status_mpd_state_known("⬛")
543 status = make_status_mpd_state_unknown("--")
546 return sprintf("[%s]", status)
549 function make_status_mpd_state_known(symbol) {
553 db["mpd_curr_song_time"],
554 db["mpd_curr_song_percent"],
555 substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\
559 function make_status_mpd_state_unknown(symbol) {
560 return sprintf("%s", symbol)
563 function debug(location, msg) {
565 print_error(location, msg)
569 function print_error(location, msg) {
570 print(location " ==> " msg) > "/dev/stderr"
585 $cmd |
while read line
; do
586 echo "${msg_head} $line" > "$pipe"
595 dir_data
="$HOME/.khatus"
596 weather_station_id
='KJFK'
597 screen_brightness_device_name
='acpi_video0'
598 prefixes_of_net_interfaces_to_show
='w' # comma-separated
599 disk_space_device
='/'
602 fan_path
='/proc/acpi/ibm/fan'
606 long_options
+='debug'
607 long_options
+=',data-dir:'
608 long_options
+=',weather-station:'
609 long_options
+=',screen-device:'
610 long_options
+=',prefixes_of_net_interfaces_to_show:'
611 long_options
+=',disk_space_device:'
612 long_options
+=',thermal_zone:'
613 long_options
+=',fan_path:'
633 weather_station_id
="$2"
637 screen_brightness_device_name
="$2"
640 --prefixes_of_net_interfaces_to_show)
641 prefixes_of_net_interfaces_to_show
="$2"
645 disk_space_device
="$2"
667 pipe
="$dir_data/khatus_data_pipe"
668 screen_brightness_device_path
='/sys/class/backlight'
669 screen_brightness_device_path
+="/$screen_brightness_device_name"
671 ( echo "Khatus starting with the following parameters:"
672 ( echo " debug|= $debug"
673 echo " dir_data|= $dir_data"
675 echo " screen_brightness_device_name|= $screen_brightness_device_name"
676 echo " screen_brightness_device_path|= $screen_brightness_device_path"
677 echo " weather_station_id|= $weather_station_id"
678 echo " prefixes_of_net_interfaces_to_show|= $prefixes_of_net_interfaces_to_show"
679 echo " disk_space_device|= $disk_space_device"
680 echo " disk_io_device|= $disk_io_device"
681 echo " thermal_zone|= $thermal_zone"
682 echo " fan_path|= $fan_path"
691 cmd_produce_screen_brightness
='produce_screen_brightness'
692 cmd_produce_screen_brightness
+=" $screen_brightness_device_path"
694 cmd_produce_weather
="produce_weather $weather_station_id"
696 cmd_produce_disk_space
="produce_disk_space $disk_space_device"
698 cmd_produce_disk_io
="produce_disk_io $disk_io_device"
700 cmd_produce_temperature
="produce_temperature $thermal_zone"
702 cmd_produce_fan
="produce_fan $fan_path"
704 # TODO: Redirect each worker's stderr to a dedicated log file
705 spawn produce_datetime
"$pipe" 'in:DATE_TIME' 1
706 spawn
"$cmd_produce_screen_brightness" "$pipe" 'in:SCREEN_BRIGHTNESS' 1
707 spawn
"$cmd_produce_weather" "$pipe" 'in:WEATHER' $
(( 30 * 60 ))
708 spawn produce_mpd_state
"$pipe" 'in:MPD_STATE' 1
709 spawn produce_mpd_song
"$pipe" 'in:MPD_SONG' 1
710 spawn produce_volume
"$pipe" 'in:VOLUME' 1
711 spawn produce_bluetooth_power
"$pipe" 'in:BLUETOOTH_POWER' 5
712 spawn produce_net_wifi_status
"$pipe" 'in:NET_WIFI_STATUS' 5
713 spawn produce_net_addr_io
"$pipe" 'in:NET_ADDR_IO' 1
714 spawn
"$cmd_produce_disk_space" "$pipe" 'in:DISK_SPACE' 1
715 spawn
"$cmd_produce_disk_io" "$pipe" 'in:DISK_IO' 1
716 spawn produce_loadavg
"$pipe" 'in:LOAD_AVG' 1
717 spawn
"$cmd_produce_temperature" "$pipe" 'in:TEMPERATURE' 1
718 spawn
"$cmd_produce_fan" "$pipe" 'in:FAN' 1
719 spawn produce_bar_req
"$pipe" 'out:BAR' 1
724 "$prefixes_of_net_interfaces_to_show"