Redesign component interfaces
authorSiraaj Khandkar <siraaj@khandkar.net>
Thu, 16 Aug 2018 00:47:00 +0000 (20:47 -0400)
committerSiraaj Khandkar <siraaj@khandkar.net>
Thu, 16 Aug 2018 00:47:00 +0000 (20:47 -0400)
to be consistent src/key/val triples

32 files changed:
bin/khatus
bin/khatus_actuate_alert_to_notify_send
bin/khatus_actuate_status_bar_to_xsetroot_name
bin/khatus_bar [new file with mode: 0755]
bin/khatus_controller [deleted file]
bin/khatus_monitor_energy [new file with mode: 0755]
bin/khatus_monitor_errors [new file with mode: 0755]
bin/khatus_parse_bluetoothctl_show
bin/khatus_parse_df_pcent [new file with mode: 0755]
bin/khatus_parse_fan_file [new file with mode: 0755]
bin/khatus_parse_free [new file with mode: 0755]
bin/khatus_parse_ip_addr
bin/khatus_parse_iwconfig
bin/khatus_parse_loadavg_file [new file with mode: 0755]
bin/khatus_parse_metar_d_output
bin/khatus_parse_mpd_status [deleted file]
bin/khatus_parse_mpd_status_currentsong [new file with mode: 0755]
bin/khatus_parse_pactl_list_sinks
bin/khatus_parse_sys_block_stat [new file with mode: 0755]
bin/khatus_parse_upower
bin/khatus_sensor_datetime
bin/khatus_sensor_disk_io
bin/khatus_sensor_disk_space
bin/khatus_sensor_fan
bin/khatus_sensor_loadavg
bin/khatus_sensor_memory
bin/khatus_sensor_mpd [new file with mode: 0755]
bin/khatus_sensor_mpd_song [deleted file]
bin/khatus_sensor_mpd_state [deleted file]
bin/khatus_sensor_screen_brightness
bin/khatus_sensor_temperature
bin/khatus_transform_total_to_diff [new file with mode: 0755]

index bbc7ab8..371b15c 100755 (executable)
@@ -1,24 +1,18 @@
 #! /bin/bash
 
+MSG_FS='|'
+
 set -e
 
-consume() {
-    pipe="$1"
-    debug="$2"
-    bin="$3"
-    prefixes_of_net_interfaces_to_show="$4"
-    tail -f "$pipe" \
-    | stdbuf -o L "$bin"/khatus_controller \
-        -v opt_debug="$debug" \
-        -v opt_mpd_song_max_chars=10 \
-        -v opt_prefixes_of_net_interfaces_to_show="$prefixes_of_net_interfaces_to_show"
+executable_name_of_cmd() {
+    basename "$(echo $1 | awk '{print $1; exit}')"
 }
 
 run_producer() {
     pipe="$1"
     bin="$2"
     cmd="$3"
-    msg_head="$4"
+    executable_name="$4"
     perf_log="$5"
 
     if [ ! "$perf_log" = '' ]
@@ -42,40 +36,51 @@ run_producer() {
     2> >(
         while read line
         do
-            echo "ERROR ${msg_head} $line" > "$pipe"
+            echo "ERROR${MSG_FS}${executable_name}${MSG_FS}$line" > "$pipe"
         done \
     ) \
     | while read line
         do
-            echo "OK ${msg_head} $line" > "$pipe"
+            echo "OK${MSG_FS}${executable_name}${MSG_FS}$line" > "$pipe"
         done
     cmd_exit_code=${PIPESTATUS[0]}
     if [ "$cmd_exit_code" -ne 0 ]
     then
-        echo "ERROR ${msg_head} NON_ZERO_EXIT_CODE $cmd_exit_code" > "$pipe"
+        echo
+            "ERROR${MSG_FS}${executable_name}${MSG_FS}NON_ZERO_EXIT_CODE${MSG_FS}$cmd_exit_code" \
+            > "$pipe"
     fi
 }
 
 fork_watcher() {
-    run_producer "$@" &
+    pipe="$1"
+    bin="$2"
+    cmd="$3"
+    executable_name=$(executable_name_of_cmd "$cmd")
+    run_producer "$pipe" "$bin" "$cmd" "$executable_name" &
 }
 
 fork_poller() {
     interval="$1"
     perf_log_dir="$2"
     shift 2
+    pipe="$1"
+    bin="$2"
+    cmd="$3"
+
+    executable_name=$(basename "$(echo $cmd | awk '{print $1; exit}')")
 
     if [ ! "$perf_log_dir" = '' ]
     then
         cmd="$3"
-        perf_log_file=$(basename "$(echo $cmd | awk '{print $1; exit}')").log
+        perf_log_file=${executable_name}.log
         mkdir -p "$perf_log_dir"
         perf_log_path="$perf_log_dir/$perf_log_file"
     fi
 
     while :
     do
-        run_producer "$@" "$perf_log_path"
+        run_producer "$pipe" "$bin" "$cmd" "$executable_name" "$perf_log_path"
         sleep "$interval"
     done &
 }
@@ -88,22 +93,22 @@ main() {
         ["--file_pipe"]=$(mktemp)
         ["--weather_station_id"]='KJFK'
         ["--screen_brightness_device_name"]='acpi_video0'
-        ["--prefixes_of_net_interfaces_to_show"]='w'  # comma-separated
+        ["--net_interfaces_to_show"]=''  # comma-separated
         ["--wifi_interface"]=''
         ["--disk_space_device"]='/'
         ["--disk_io_device"]='sda'
         ["--thermal_zone"]=0
         ["--fan_path"]='/proc/acpi/ibm/fan'
+        ["--pulseaudio_sink"]='0'
         ["--interval_inp_datetime"]=1
         ["--interval_inp_brightness"]=1
         ["--interval_inp_weather"]=$(( 30 * 60))  # 30 minutes
-        ["--interval_inp_mpd_state"]=1
-        ["--interval_inp_mpd_song"]=1
+        ["--interval_inp_mpd"]=1
         ["--interval_inp_volume"]=1
-        ["--interval_inp_bluetooth"]=5
-        ["--interval_inp_net_wifi"]=5
+        ["--interval_inp_bluetooth"]=1
+        ["--interval_inp_net_wifi"]=1
         ["--interval_inp_net_io"]=1
-        ["--interval_inp_disk_space"]=5
+        ["--interval_inp_disk_space"]=1
         ["--interval_inp_disk_io"]=1
         ["--interval_inp_loadavg"]=1
         ["--interval_inp_temp"]=1
@@ -175,38 +180,67 @@ main() {
     cmd_sens_screen_brightness+=" $screen_brightness_device_path"
 
     cmd_sens_weather="khatus_sensor_weather $bin ${opts['--weather_station_id']}"
-    cmd_sens_disk_space="khatus_sensor_disk_space ${opts['--disk_space_device']}"
-    cmd_sens_disk_io="khatus_sensor_disk_io ${opts['--disk_io_device']}"
+    cmd_sens_disk_space="khatus_sensor_disk_space $bin ${opts['--disk_space_device']}"
+    cmd_sens_disk_io="khatus_sensor_disk_io $bin ${opts['--disk_io_device']}"
     cmd_sens_temperature="khatus_sensor_temperature ${opts['--thermal_zone']}"
-    cmd_sens_fan="khatus_sensor_fan ${opts['--fan_path']}"
+    cmd_sens_fan="khatus_sensor_fan $bin ${opts['--fan_path']}"
     cmd_sens_bluetooth="khatus_sensor_bluetooth_power $bin"
-    cmd_sens_mpd_state="khatus_sensor_mpd_state $bin"
+    cmd_sens_mpd="khatus_sensor_mpd $bin"
     cmd_sens_net_addr_io="khatus_sensor_net_addr_io $bin"
     cmd_sens_volume="khatus_sensor_volume $bin"
     cmd_sens_wifi="khatus_sensor_net_wifi_status $bin ${opts['--wifi_interface']}"
+    cmd_sens_loadavg="khatus_sensor_loadavg $bin"
+    cmd_sens_memory="khatus_sensor_memory $bin"
+
+    fork_watcher                                               "$pipe" "$bin" "khatus_sensor_energy $bin"
+    fork_poller "${opts['--interval_inp_datetime']}"   "$perf" "$pipe" "$bin" khatus_sensor_datetime
+    fork_poller "${opts['--interval_inp_brightness']}" "$perf" "$pipe" "$bin" "$cmd_sens_screen_brightness"
+    fork_poller "${opts['--interval_inp_weather']}"    "$perf" "$pipe" "$bin" "$cmd_sens_weather"
+    fork_poller "${opts['--interval_inp_mpd']}"        "$perf" "$pipe" "$bin" "$cmd_sens_mpd"
+    fork_poller "${opts['--interval_inp_volume']}"     "$perf" "$pipe" "$bin" "$cmd_sens_volume"
+    fork_poller "${opts['--interval_inp_bluetooth']}"  "$perf" "$pipe" "$bin" "$cmd_sens_bluetooth"
+    fork_poller "${opts['--interval_inp_net_wifi']}"   "$perf" "$pipe" "$bin" "$cmd_sens_wifi"
+    fork_poller "${opts['--interval_inp_net_io']}"     "$perf" "$pipe" "$bin" "$cmd_sens_net_addr_io"
+    fork_poller "${opts['--interval_inp_disk_space']}" "$perf" "$pipe" "$bin" "$cmd_sens_disk_space"
+    fork_poller "${opts['--interval_inp_disk_io']}"    "$perf" "$pipe" "$bin" "$cmd_sens_disk_io"
+    fork_poller "${opts['--interval_inp_loadavg']}"    "$perf" "$pipe" "$bin" "$cmd_sens_loadavg"
+    fork_poller "${opts['--interval_inp_temp']}"       "$perf" "$pipe" "$bin" "$cmd_sens_temperature"
+    fork_poller "${opts['--interval_inp_fan']}"        "$perf" "$pipe" "$bin" "$cmd_sens_fan"
+    fork_poller "${opts['--interval_inp_mem']}"        "$perf" "$pipe" "$bin" "$cmd_sens_memory"
+
+    stdbuf -o L tail -f "$pipe" \
+    | stdbuf -o L "$bin"/khatus_transform_total_to_diff \
+        -F "$MSG_FS" \
+        -v status='OK' \
+        -v src='khatus_sensor_net_addr_io' \
+        -v key_prefix='bytes_read' \
+    | stdbuf -o L "$bin"/khatus_transform_total_to_diff \
+        -F "$MSG_FS" \
+        -v status='OK' \
+        -v src='khatus_sensor_net_addr_io' \
+        -v key_prefix='bytes_written' \
+    | stdbuf -o L "$bin"/khatus_transform_total_to_diff \
+        -F "$MSG_FS" \
+        -v status='OK' \
+        -v src='khatus_sensor_disk_io' \
+        -v key_prefix='sectors_read' \
+    | stdbuf -o L "$bin"/khatus_transform_total_to_diff \
+        -F "$MSG_FS" \
+        -v status='OK' \
+        -v src='khatus_sensor_disk_io' \
+        -v key_prefix='sectors_written' \
+    | stdbuf -o L "$bin"/khatus_bar \
+        -F "$MSG_FS" \
+        -v opt_debug=""${opts['--debug']}"" \
+        -v opt_mpd_song_max_chars=10 \
+        -v opt_net_interfaces_to_show="${opts['--net_interfaces_to_show']}" \
+        -v opt_pulseaudio_sink="${opts['--pulseaudio_sink']}" \
+    | stdbuf -o L tee >("$bin"/khatus_actuate_status_bar_to_xsetroot_name) \
+    | stdbuf -o L "$bin"/khatus_monitor_energy \
+    | stdbuf -o L "$bin"/khatus_monitor_errors \
+    | stdbuf -o L tee >("$bin"/khatus_actuate_alert_to_notify_send) \
+    > /dev/null
 
-    fork_watcher                                               "$pipe" "$bin" "khatus_sensor_energy $bin"     'in:ENERGY'
-    fork_poller "${opts['--interval_inp_datetime']}"   "$perf" "$pipe" "$bin" khatus_sensor_datetime          'in:DATE_TIME'
-    fork_poller "${opts['--interval_inp_brightness']}" "$perf" "$pipe" "$bin" "$cmd_sens_screen_brightness"   'in:SCREEN_BRIGHTNESS'
-    fork_poller "${opts['--interval_inp_weather']}"    "$perf" "$pipe" "$bin" "$cmd_sens_weather"             'in:WEATHER'
-    fork_poller "${opts['--interval_inp_mpd_state']}"  "$perf" "$pipe" "$bin" "$cmd_sens_mpd_state"           'in:MPD_STATE'
-    fork_poller "${opts['--interval_inp_mpd_song']}"   "$perf" "$pipe" "$bin" khatus_sensor_mpd_song          'in:MPD_SONG'
-    fork_poller "${opts['--interval_inp_volume']}"     "$perf" "$pipe" "$bin" "$cmd_sens_volume"              'in:VOLUME'
-    fork_poller "${opts['--interval_inp_bluetooth']}"  "$perf" "$pipe" "$bin" "$cmd_sens_bluetooth"           'in:BLUETOOTH_POWER'
-    fork_poller "${opts['--interval_inp_net_wifi']}"   "$perf" "$pipe" "$bin" "$cmd_sens_wifi"                'in:NET_WIFI_STATUS'
-    fork_poller "${opts['--interval_inp_net_io']}"     "$perf" "$pipe" "$bin" "$cmd_sens_net_addr_io"         'in:NET_ADDR_IO'
-    fork_poller "${opts['--interval_inp_disk_space']}" "$perf" "$pipe" "$bin" "$cmd_sens_disk_space"          'in:DISK_SPACE'
-    fork_poller "${opts['--interval_inp_disk_io']}"    "$perf" "$pipe" "$bin" "$cmd_sens_disk_io"             'in:DISK_IO'
-    fork_poller "${opts['--interval_inp_loadavg']}"    "$perf" "$pipe" "$bin" khatus_sensor_loadavg           'in:LOAD_AVG'
-    fork_poller "${opts['--interval_inp_temp']}"       "$perf" "$pipe" "$bin" "$cmd_sens_temperature"         'in:TEMPERATURE'
-    fork_poller "${opts['--interval_inp_fan']}"        "$perf" "$pipe" "$bin" "$cmd_sens_fan"                 'in:FAN'
-    fork_poller "${opts['--interval_inp_mem']}"        "$perf" "$pipe" "$bin" khatus_sensor_memory            'in:MEMORY'
-
-    consume \
-      "$pipe" \
-      "${opts['--debug']}" \
-      "$bin" \
-      "${opts['--prefixes_of_net_interfaces_to_show']}"
 }
 
 main $@
index b1c695a..e383917 100755 (executable)
@@ -6,14 +6,23 @@ BEGIN {
     #   khatus_actuate_alert_to_notify_send -v display="$CORRECT_DISPLAY"
     #
     display = ":0"
+     FS = msg_fs ? msg_fs : "|"
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
 }
 
-/^ALERT / {
+$1 == "OK" && \
+$3 == "alert" {
     src      = $2
-    priority = $3
-    subject  = $4
-    sub("^" $1 " +" $2 " +" $3 " +" $4 " +", "")
-    body     = $0
+    priority = $4
+    subject  = $5
+
+    # Not just using $6 for body - because body might contain a character
+    # identical to FS
+    len_line = length($0)
+    len_head = length($1 FS $2 FS $3 FS $4 FS $5 FS)
+    len_body = len_line - len_head
+    body = substr($0, len_head + 1, len_body)
 
     sep = body ? "\n" : ""
     body = body sep "--" src
index a7463e2..f41cbd1 100755 (executable)
@@ -1,8 +1,21 @@
 #! /usr/bin/awk -f
 
-/^STATUS_BAR / {
-    sub("^" $1 " +", "")
-    # TODO: Move padding back to controller, now that we no-longer use readline
-    system("xsetroot -name \" " $0 "\" ")
+BEGIN {
+     FS = msg_fs ? msg_fs : "|"
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+$1 == "OK" && \
+$2 == "khatus_bar" && \
+$3 == "status_bar" {
+    # Not just using $4 for val - because val might contain a character
+    # identical to FS
+    len_line = length($0)
+    len_head = length($1 FS $2 FS $3 FS)
+    len_val  = len_line - len_head
+    val = substr($0, len_head + 1, len_val)
+
+    system("xsetroot -name \"" val "\"")
     next
 }
diff --git a/bin/khatus_bar b/bin/khatus_bar
new file mode 100755 (executable)
index 0000000..025b968
--- /dev/null
@@ -0,0 +1,310 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+     FS = msg_fs ? msg_fs : "|"
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+# -----------------------------------------------------------------------------
+# Input
+# -----------------------------------------------------------------------------
+$1 == "OK" {
+    Data_update()
+}
+
+$1 == "OK" && \
+$2 == "khatus_sensor_datetime" {
+    print_msg_ok("status_bar", make_status_bar())
+}
+
+# Let everything else through
+//
+
+# -----------------------------------------------------------------------------
+# Data
+# -----------------------------------------------------------------------------
+
+function Data_update(    src, key, val, len_line, len_head, len_val, time) {
+    src = $2
+    key = $3
+
+    # Not just using $4 for val - because an unstructured value (like name of a
+    # song) might contain a character identical to FS
+    len_line = length($0)
+    len_head = length($1 FS $2 FS $3 FS)
+    len_val  = len_line - len_head
+    val = substr($0, len_head + 1, len_val)
+
+    Data[src, key] = val
+    time = Data_get_time()
+    M_time[src, key] = time
+
+    if (time % 3600 == 0) {
+        Data_gc()
+    }
+}
+
+function Data_get(src, key, age_max,    time, age, is_expired) {
+    time = Data_get_time()
+    A_time[src, key] = time
+    age = time - M_time[src, key]
+    is_expired = age_max && age > age_max
+    return is_expired ? "" : Data[src, key]
+}
+
+function Data_get_time(    src, key, time) {
+    src = "khatus_sensor_datetime"
+    key = "epoch"
+    time = Data[src, key]
+    A_time[src, key] = time
+    return time
+}
+
+function Data_gc(    src_and_key, unused_for) {
+    for (src_and_key in Data) {
+        unused_for = Data_get_time() - A_time[src_and_key]
+        if (unused_for > 3600) {
+            print_msg_error(\
+                "Data_gc", "Deleting unused src_and_key: " src_and_key \
+            )
+            delete Data[src_and_key]
+        }
+    }
+}
+
+# -----------------------------------------------------------------------------
+# Status bar
+# -----------------------------------------------------------------------------
+
+function make_status_bar(    position, bar, sep, i, j) {
+    position[++i] = ""
+    position[++i] = make_status_energy()
+    position[++i] = make_status_mem()
+    position[++i] = make_status_cpu()
+    position[++i] = make_status_disk()
+    position[++i] = make_status_net()
+    position[++i] = make_status_bluetooth()
+    position[++i] = make_status_screen_brightness()
+    position[++i] = make_status_volume()
+    position[++i] = make_status_mpd()
+    position[++i] = make_status_weather()
+    position[++i] = make_status_datetime()
+    position[++i] = ""
+    bar = ""
+    sep = ""
+    for (j = 1; j <= i; j++) {
+        bar = bar sep position[j]
+        sep = " "
+    }
+    return bar
+}
+
+function make_status_energy(    state, charge, direction_of_change) {
+    state  = Data_get("khatus_sensor_energy", "battery_state")
+    charge = Data_get("khatus_sensor_energy", "battery_percentage")
+
+    if (state == "discharging") {
+        direction_of_change = "<"
+    } else if (state == "charging") {
+        direction_of_change = ">"
+    } else {
+        direction_of_change = "="
+    }
+
+    return sprintf("E%s%d%%", direction_of_change, charge)
+}
+
+function make_status_mem(    total, used, percent, status) {
+    total = Data_get("khatus_sensor_memory", "total", 2)
+    used  = Data_get("khatus_sensor_memory", "used" , 2)
+    # To avoid division by zero when data is missing
+    if (total && used) {
+        percent = round((used / total) * 100)
+        status = sprintf("%d%%", percent)
+    } else {
+        status = "__"
+    }
+    return sprintf("M=%s", status)
+}
+
+function make_status_cpu(    load, temp, fan) {
+    load = Data_get("khatus_sensor_loadavg"    , "load_avg_1min", 2)
+    temp = Data_get("khatus_sensor_temperature", "temp_c"       , 2)
+    fan  = Data_get("khatus_sensor_fan"        , "speed"        , 2)
+
+    load = load ? sprintf("%4.2f", load) : "--"
+    temp = temp ? sprintf("%d"   , temp) : "--"
+    fan  = fan  ? sprintf("%4d"  , fan)  : "--"
+
+    return sprintf("C=[%s %s°C %srpm]", load, temp, fan)
+}
+
+function make_status_disk(    bytes_per_sector, bytes_per_mb, w, r, u) {
+    bytes_per_sector = 512
+    bytes_per_mb     = 1024 * 1024
+
+    w = Data_get("khatus_sensor_disk_io"   , "sectors_written"      , 2)
+    r = Data_get("khatus_sensor_disk_io"   , "sectors_read"         , 2)
+    u = Data_get("khatus_sensor_disk_space", "disk_usage_percentage", 10)
+
+    w = w ? sprintf("%0.3f", (w * bytes_per_sector) / bytes_per_mb) : "--"
+    r = r ? sprintf("%0.3f", (r * bytes_per_sector) / bytes_per_mb) : "--"
+    u = u ?                                                       u : "--"
+
+    return sprintf("D=[%s%% %s▲ %s▼]", u, w, r)
+}
+
+function make_status_net(    \
+    number_of_net_interfaces_to_show, \
+    net_interfaces_to_show, \
+    sensor_io, \
+    sensor_wi, \
+    out, \
+    sep, \
+    i, \
+    interface, \
+    label, \
+    addr, \
+    w, \
+    r, \
+    bytes_per_mb, \
+    io_stat, \
+    wifi \
+) {
+    number_of_net_interfaces_to_show = \
+        split(opt_net_interfaces_to_show, net_interfaces_to_show, ",")
+
+    sensor_io = "khatus_sensor_net_addr_io"
+    sensor_wi = "khatus_sensor_net_wifi_status"
+
+    out = ""
+    sep = ""
+    for (i = number_of_net_interfaces_to_show; i > 0; i--) {
+        interface = net_interfaces_to_show[i]
+        label = substr(interface, 1, 1)
+
+        addr = Data_get(sensor_io, "addr"          Kfs interface, 2)
+        w    = Data_get(sensor_io, "bytes_written" Kfs interface, 2)
+        r    = Data_get(sensor_io, "bytes_read"    Kfs interface, 2)
+
+        if (addr) {
+            bytes_per_mb = 1024 * 1024
+            w = w ? sprintf("%0.3f", w / bytes_per_mb) : "--"
+            r = r ? sprintf("%0.3f", r / bytes_per_mb) : "--"
+            io_stat = sprintf("%s▲ %s▼", w, r)
+        } else {
+            io_stat = "--"
+        }
+
+        if (interface ~ "^w") {
+            wifi = Data_get(sensor_wi, "status" Kfs interface, 5)
+            label = label ":" (wifi ? wifi : "--")
+        }
+
+        out = out sep label ":" io_stat
+        sep = " "
+    }
+
+    return sprintf("N[%s]", out)
+}
+
+function make_status_bluetooth(    status) {
+    status = Data_get("khatus_sensor_bluetooth_power", "power_status", 5)
+    return sprintf("B=%s", status ? status : "--")
+}
+
+function make_status_screen_brightness(    percentage) {
+    percentage = Data_get("khatus_sensor_screen_brightness", "percentage", 5)
+    percentage = percentage ? sprintf("%d", percentage) : "--"
+    return sprintf("*%s%%", percentage)
+}
+
+function make_status_volume(    sink, mute, vol_l, vol_r, status) {
+    sink = opt_pulseaudio_sink
+    mute  = Data_get("khatus_sensor_volume", "mute"      Kfs sink, 2)
+    vol_l = Data_get("khatus_sensor_volume", "vol_left"  Kfs sink, 2)
+    vol_r = Data_get("khatus_sensor_volume", "vol_right" Kfs sink, 2)
+
+    if (mute && vol_l && vol_r) {
+             if (mute == "yes") {status = "X"}
+        else if (mute == "no")  {status = sprintf("%s %s", vol_l, vol_r)}
+        else {
+            print_msg_error(\
+                "make_status_volume", \
+                "Unexpected value for 'mute' field: " mute \
+            )
+        }
+    } else {
+        status = "--"
+    }
+
+    return sprintf("(%s)", status)
+}
+
+function make_status_mpd(    state, status) {
+    if (state = Data_get("khatus_sensor_mpd", "state", 2)) {
+        if (state == "play") {
+            status = make_status_mpd_state_known("▶")
+        } else if (state == "pause") {
+            status = make_status_mpd_state_known("❚❚")
+        } else if (state == "stop") {
+            status = make_status_mpd_state_known("⬛")
+        } else {
+            print_msg_error(\
+                "make_status_mpd", \
+                "Unexpected value for 'state' field: " state \
+            )
+            status = "--"
+        }
+    } else {
+        status = "--"
+    }
+
+    return sprintf("[%s]", status)
+}
+
+function make_status_mpd_state_known(symbol,    s, song, time, percentage) {
+    s = "khatus_sensor_mpd"
+    song    = Data_get(s, "song"                   , 2)
+    time    = Data_get(s, "play_time_minimal_units", 2)
+    percent = Data_get(s, "play_time_percentage"   , 2)
+    song    = substr(song, 1, opt_mpd_song_max_chars)
+    return sprintf("%s %s %s %s", symbol, time, percent, song)
+}
+
+function make_status_weather(    hour, t_f) {
+    hour = 60 * 60
+    t_f = Data_get("khatus_sensor_weather", "temperature_f", 3 * hour)
+    t_f = t_f ? sprintf("%d", t_f) : "--"
+    return sprintf("%s°F", t_f)
+}
+
+function make_status_datetime(    dt) {
+    dt = Data_get("khatus_sensor_datetime", "datetime", 2)
+    return dt ? dt : "--"
+}
+
+# -----------------------------------------------------------------------------
+# Output
+# -----------------------------------------------------------------------------
+
+function print_msg_ok(key, val) {
+    print_msg("OK", key, val, "/dev/stdout")
+}
+
+function print_msg_error(location, msg) {
+    print_msg("ERROR", location, msg, "/dev/stderr")
+}
+
+function print_msg(status, key, val, channel) {
+    print(status, "khatus_bar", key, val) > channel
+}
+
+# -----------------------------------------------------------------------------
+# Numbers
+# -----------------------------------------------------------------------------
+
+function round(n) {
+    return int(n + 0.5)
+}
diff --git a/bin/khatus_controller b/bin/khatus_controller
deleted file mode 100755 (executable)
index 72136d0..0000000
+++ /dev/null
@@ -1,602 +0,0 @@
-#! /usr/bin/awk -f
-
-/^OK/ { debug("OK line", $0) }
-
-/^ERROR in:MPD.*NON_ZERO_EXIT_CODE/ {
-    for (mpd_key in db) {
-        if (mpd_key ~ "^mpd_") {
-            delete db[mpd_key]
-        }
-    }
-    next
-}
-
-/^ERROR/ {
-    debug("ERROR line", $0)
-    shift()
-    msg_head = $1
-    shift()
-    msg_body = $0
-    alert_trigger_hi(msg_head, "KhatusSensorError", msg_body)
-}
-
-/^OK in:ENERGY battery/\
-{
-    debug("ENERGY battery", $0)
-    sub("%$", "", $5)
-    db["energy_state_prev"] = db["energy_state_curr"]
-    db["energy_state_curr"] = $4
-    db["energy_percentage"] = ensure_numeric($5)
-    alert_check_energy_battery()
-}
-
-/^OK in:ENERGY line_power/\
-{
-    debug("ENERGY line_power", $0)
-    db["energy_line_power_prev"] = db["energy_line_power_curr"]
-    db["energy_line_power_curr"] = $4
-    alert_check_energy_line_power()
-}
-
-/^OK in:MEMORY/\
-{
-    shift()
-    shift()
-    db["memory_total"] = $1
-    db["memory_used"]  = $2
-}
-
-/^OK in:FAN +status:/\
-{
-    shift()
-    shift()
-    db["fan_status"] = $2
-}
-
-/^OK in:FAN +speed:/\
-{
-    shift()
-    shift()
-    db["fan_speed"] = $2
-}
-
-/^OK in:FAN +level:/\
-{
-    shift()
-    shift()
-    db["fan_level"] = $2
-}
-
-/^OK in:TEMPERATURE/\
-{
-    shift()
-    shift()
-    db["temperature"] = $1
-}
-
-/^OK in:LOAD_AVG/\
-{
-    shift()
-    shift()
-    set_load_avg()
-}
-
-/^OK in:DISK_IO/\
-{
-    shift()
-    shift()
-    set_disk_io()
-}
-
-/^OK in:DISK_SPACE/\
-{
-    shift()
-    shift()
-    db["disk_space_used"] = $0
-}
-
-/^OK in:NET_ADDR_IO/\
-{
-    shift()
-    shift()
-    set_net_addr_io()
-}
-
-/^OK in:NET_WIFI_STATUS/\
-{
-    shift()
-    shift()
-    set_net_wifi_status()
-}
-
-/^OK in:BLUETOOTH_POWER/\
-{
-    shift()
-    shift()
-    db["bluetooth_power"] = $0
-}
-
-/^OK in:SCREEN_BRIGHTNESS/\
-{
-    shift()
-    shift()
-    set_screen_brightness()
-}
-
-/^OK in:VOLUME/\
-{
-    shift()
-    set_volume()
-}
-
-/^OK in:MPD_SONG OK +MPD/ { delete db_mpd_song; next }
-/^OK in:MPD_SONG OK$/     { set_mpd_playing() ; next }
-/^OK in:MPD_SONG /        { set_mpd_song()    ; next }
-
-/^OK in:MPD_STATE /\
-{
-    shift()
-    shift()
-    db["mpd_status_state"]   = $1
-    db["mpd_status_time"]    = $2
-    db["mpd_status_percent"] = $3
-}
-
-/^OK in:WEATHER temperature/\
-{
-    shift()
-    shift()
-    shift()
-    db["weather_temperature"] = $0
-}
-
-/^OK in:WEATHER phenomenon/\
-{
-    shift()
-    shift()
-    shift()
-    alert_trigger_low("weather_phenomenon", "WeatherPhenomenon", $0)
-}
-
-/^OK in:DATE_TIME/\
-{
-    shift()
-    shift()
-    db["datetime"] = $0
-    output_msg_status_bar(make_status_bar())
-}
-
-function set_volume(    mute, left, right) {
-    # 0 RUNNING no 75% 75%
-    #msg_head = $1
-    #sink     = $2
-    #state    = $3
-    mute      = $4
-    left      = $5
-    right     = $6
-
-    if (mute == "no") {
-        db["volume"] = sprintf("%s %s", left, right)
-    } else if (mute == "yes") {
-        db["volume"] = "X"
-    } else {
-        error("set_volume", "Unexpected value for 'mute' field: " mute)
-    }
-}
-
-function set_mpd_song(    key, val) {
-    shift()
-    key = $2
-    shift()
-    shift()
-    val = $0
-    db_mpd_song[key] = val
-    debug("set_mpd_song", "", db_mpd_song)
-}
-
-function set_mpd_playing(    \
-    currently_playing, name, title, file, last, parts\
-) {
-    debug("set_mpd_playing", "", db_mpd_song)
-    name  = db_mpd_song["Name:"]
-    title = db_mpd_song["Title:"]
-    file  = db_mpd_song["file:"]
-
-    if (name) {
-        currently_playing = name
-    } else if (title) {
-        currently_playing = title
-    } else if (file) {
-        last = split(file, parts, "/")
-        currently_playing = parts[last]
-    } else {
-        currently_playing = ""
-    }
-    db["mpd_playing_prev"] = db["mpd_playing_curr"]
-    db["mpd_playing_curr"] = currently_playing
-
-    alert_check_mpd()
-}
-
-function alert_check_mpd(    curr, prev, name, body) {
-    prev = db["mpd_playing_prev"]
-    curr = db["mpd_playing_curr"]
-    if (curr && curr != prev) {
-        name = db_mpd_song["Name:"]
-        if (name) {
-            body = name
-        } else {
-            body = \
-                      db_mpd_song["Artist:"] \
-                " - " db_mpd_song["Album:"] \
-                " - " db_mpd_song["Title:"]
-        }
-        alert_trigger_low("alert_check_mpd", "MpdNowPlaying", body)
-    }
-}
-
-# TODO: Generalize alert spec lang
-#       - trigger threshold
-#       - above/bellow/equal to threshold value
-#       - priority
-#       - snooze time (if already alerted, when to re-alert?)
-#       - text: subject/body
-function alert_check_energy_battery(    \
-    from, dbg, state_curr, state_prev, remaining, subj, body\
-) {
-    from = "alert_check_energy_battery"
-
-    state_curr = db["energy_state_curr"]
-    state_prev = db["energy_state_prev"]
-    remaining  = db["energy_percentage"]
-
-    dbg["state_curr"] = state_curr
-    dbg["remaining"] = remaining
-    debug(from, "", dbg)
-
-    if (state_curr == "discharging") {
-        if (remaining < 5) {
-            subj = "Energy_CRITICALLY_Low"
-            body = sprintf("%d%% CHARGE NOW!!! GO GO GO!!!", remaining)
-            alert_trigger_hi(from, subj, body)
-        } else if (remaining < 10) {
-            subj = "Energy_Very_Low"
-            body = sprintf("%d%% Plug it in ASAP.", remaining)
-            alert_trigger_hi(from, subj, body)
-        } else if (remaining < 15) {
-            subj = "Energy_Low"
-            body = sprintf("%d%% Get the charger.", remaining)
-            alert_trigger_hi(from, subj, body)
-        } else if (remaining < 20) {
-            subj = "Energy_Low"
-            body = sprintf("%d%% Get the charger.", remaining)
-            alert_trigger_med(from, subj, body)
-        } else if (remaining < 50) {
-            if (!state__alerts__energy__notified_bellow_half) {
-                state__alerts__energy__notified_bellow_half = 1
-                subj = "Energy_Bellow_Half"
-                body = sprintf("%d%% Where is the charger?", remaining)
-                alert_trigger_med(from, subj, body)
-            }
-        }
-    } else {
-        # TODO: Reconsider the competing global-state organizing-conventions
-        state__alerts__energy__notified_bellow_half = 0
-    }
-}
-
-function alert_check_energy_line_power(    \
-    from, dbg, line_power_curr, line_power_prev, subj, body \
-) {
-    from = "alert_check_energy_line_power"
-
-    dbg["energy_line_power_prev"] = db["energy_line_power_prev"]
-    dbg["energy_line_power_curr"] = db["energy_line_power_curr"]
-    debug(from, "", dbg)
-
-    line_power_curr = db["energy_line_power_curr"]
-    line_power_prev = db["energy_line_power_prev"]
-
-    if (line_power_curr == "no" && line_power_prev != "no") {
-        alert_trigger_low(from, "PowerUnplugged", "")
-    }
-}
-
-function alert_trigger_low(from, subject, body) {
-    alert_trigger("low", from, subject, body)
-}
-
-function alert_trigger_med(from, subject, body) {
-    alert_trigger("med", from, subject, body)
-}
-
-function alert_trigger_hi(from, subject, body) {
-    alert_trigger("hi", from, subject, body)
-}
-
-function alert_trigger(priority, from, subject, body,    msg) {
-    # priority : "low" | "med" | "hi"
-    # subject  : no spaces
-    # body     : anything
-    msg = sprintf("khatus_%s %s %s %s", from, priority, subject, body)
-    output_msg_alert(msg)
-}
-
-function output_msg_alert(msg) {
-    # TODO: Should alerts go into a dedicated channel?
-    output_msg("ALERT", msg, "/dev/stdout")
-}
-
-function output_msg_status_bar(msg) {
-    output_msg("STATUS_BAR", msg, "/dev/stdout")
-}
-
-function output_msg(type, content, channel) {
-    print(type, content) > channel
-}
-
-function set_load_avg(    sched) {
-    split($4, sched, "/")
-    db["load_avg_1min"]             = $1
-    db["load_avg_5min"]             = $2
-    db["load_avg_15min"]            = $3
-    db["kern_sched_queue_runnable"] = sched[1]
-    db["kern_sched_queue_total"]    = sched[2]
-    db["kern_sched_latest_pid"]     = $5
-}
-
-function set_disk_io(    curr_w, curr_r, prev_w, prev_r) {
-    curr_w = $1
-    curr_r = $2
-    prev_w = db["disk_io_curr_w"]
-    prev_r = db["disk_io_curr_r"]
-    db["disk_io_curr_w"] = curr_w
-    db["disk_io_curr_r"] = curr_r
-    db["disk_io_diff_w"] = curr_w - prev_w
-    db["disk_io_diff_r"] = curr_r - prev_r
-}
-
-function set_net_wifi_status(    interface) {
-    interface = $1
-    shift()
-    db["net_wifi_status", interface] = $0
-}
-
-function set_net_addr_io(    \
-    interface, address, io_curr_w, io_curr_r, io_prev_w, io_prev_r\
-) {
-    interface = $1
-    address   = $2
-    io_curr_w = $3
-    io_curr_r = $4
-    if (interface) {
-        if (address && io_curr_w && io_curr_r) {
-            # recalculate
-            io_prev_w = net_io_curr_w[interface]
-            io_prev_r = net_io_curr_r[interface]
-
-            net_addr[interface]      = address
-            net_io_curr_w[interface] = io_curr_w
-            net_io_curr_r[interface] = io_curr_r
-            net_io_diff_w[interface] = io_curr_w - io_prev_w
-            net_io_diff_r[interface] = io_curr_r - io_prev_r
-        } else {
-            # clear
-            net_addr[interface]      = ""
-            net_io_curr_w[interface] = 0
-            net_io_curr_r[interface] = 0
-            net_io_diff_w[interface] = 0
-            net_io_diff_r[interface] = 0
-        }
-    }
-}
-
-function set_screen_brightness(    max, cur) {
-    max = $1
-    cur = $2
-    db["screen_brightness"] = (cur / max) * 100
-}
-
-# TODO: Revise overuse of shift() where it is not really needed
-function shift() {
-    sub("^" $1 " +", "")
-}
-
-function make_status_bar(    position, bar, sep, i, j) {
-    position[++i] = make_status_energy()
-    position[++i] = make_status_mem()
-    position[++i] = make_status_cpu()
-    position[++i] = make_status_disk()
-    position[++i] = make_status_net()
-    position[++i] = sprintf("B=%s", db["bluetooth_power"])
-    position[++i] = sprintf("*%d%%", db["screen_brightness"])
-    position[++i] = sprintf("(%s)", db["volume"])
-    position[++i] = make_status_mpd()
-    position[++i] = db["weather_temperature"]
-    position[++i] = db["datetime"]
-    bar = ""
-    sep = ""
-    for (j = 1; j <= i; j++) {
-        bar = bar sep position[j]
-        sep = " "
-    }
-    return bar
-}
-
-function make_status_energy(    state, direction_of_change) {
-    state = db["energy_state_curr"]
-    if (state == "discharging") {
-        direction_of_change = "<"
-    } else if (state == "charging") {
-        direction_of_change = ">"
-    } else {
-        direction_of_change = "="
-    };
-    return sprintf("E%s%d%%", direction_of_change, db["energy_percentage"])
-}
-
-function make_status_mem(    total, used, percent, status) {
-    total = db["memory_total"]
-    used  = db["memory_used"]
-    # To avoid division by zero when data is missing
-    if (total && used) {
-        percent = round((used / total) * 100)
-        status = sprintf("%d%%", percent)
-    } else {
-        status = "__"
-    }
-    return sprintf("M=%s", status)
-}
-
-function make_status_cpu(    load, temp, fan) {
-    load = db["load_avg_1min"]
-    temp = db["temperature"] / 1000
-    fan  = db["fan_speed"]
-    return sprintf("C=[%4.2f %d°C %4drpm]", load, temp, fan)
-}
-
-function make_status_disk(    bytes_per_sector, bytes_per_mb, w, r) {
-    bytes_per_sector = 512
-    bytes_per_mb     = 1024 * 1024
-    w = (db["disk_io_diff_w"] * bytes_per_sector) / bytes_per_mb
-    r = (db["disk_io_diff_r"] * bytes_per_sector) / bytes_per_mb
-    return \
-        sprintf("D=[%s %0.3f▲ %0.3f▼]", db["disk_space_used"], w, r)
-}
-
-function make_status_net(    \
-    out,
-    number_of_interfaces_to_show,
-    n,
-    array_of_prefixes_of_interfaces_to_show,
-    prefix,
-    interface,
-    label,
-    count_printed,
-    sep,
-    io_stat,
-    dw, dr,
-    bytes_per_unit\
-) {
-    out = ""
-    number_of_interfaces_to_show = \
-        split(\
-            opt_prefixes_of_net_interfaces_to_show,\
-            array_of_prefixes_of_interfaces_to_show,\
-            ","\
-        )
-    for (n = 1; n <= number_of_interfaces_to_show; n++) {
-        prefix = array_of_prefixes_of_interfaces_to_show[n]
-        for (interface in net_addr) {
-            if (interface ~ ("^" prefix)) {
-                label = substr(interface, 1, 1)
-                if (net_addr[interface]) {
-                    bytes_per_mb = 1024 * 1024  # TODO: option
-                    dw = net_io_diff_w[interface] / bytes_per_mb
-                    dr = net_io_diff_r[interface] / bytes_per_mb
-                    io_stat = sprintf("%0.3f▲ %0.3f▼", dw, dr)
-                } else {
-                    io_stat = "--"
-                }
-                if (interface ~ "^w") {
-                    label = label ":" db["net_wifi_status", interface]
-                }
-                if (++count_printed > 1) {
-                    sep = "  "
-                } else {
-                    sep = ""
-                }
-                out = out sep label ":" io_stat
-            }
-        }
-    }
-    return sprintf("N[%s]", out)
-}
-
-function make_status_mpd(    state, status) {
-    state = db["mpd_status_state"]
-
-    if (state == "play") {
-        status = make_status_mpd_state_known("▶")
-    } else if (state == "pause") {
-        status = make_status_mpd_state_known("❚❚")
-    } else if (state == "stop") {
-        status = make_status_mpd_state_known("⬛")
-    } else {
-        status = make_status_mpd_state_unknown("--")
-    }
-
-    return sprintf("[%s]", status)
-}
-
-function make_status_mpd_state_known(symbol) {
-    return sprintf(\
-        "%s %s %s %s",
-        symbol,
-        db["mpd_status_time"],
-        db["mpd_status_percent"],
-        substr(db["mpd_playing_curr"], 1, opt_mpd_song_max_chars)\
-    )
-}
-
-function make_status_mpd_state_unknown(symbol) {
-    return sprintf("%s", symbol)
-}
-
-function round(n) {
-    return int(n + 0.5)
-}
-
-function debug(location, msg, values,    sep, vals, key, payload) {
-    if (opt_debug) {
-        sep = ""
-        vals = ""
-        for (key in values) {
-            vals = sprintf("%s%s%s: %s", vals, sep, key, values[key])
-            sep = ", "
-        }
-        payload = \
-            sprintf("LOCATION[%s] MSG[%s] DATA[%s]", location, msg, vals)
-        output_msg("DEBUG", payload, "/dev/stderr")
-    }
-}
-
-function error(location, msg) {
-    # TODO: Reconsider classifying internal errors as alerts
-    #       Maybe better to keep the error class distinct and provide a
-    #       an optional transformation from error to alert
-    alert_trigger_hi(location, "KhatusControllerError", msg)
-}
-
-function ensure_numeric(n) {
-    return n + 0
-}
-#-------------------------------
-# Why do we need ensure_numeric?
-#-------------------------------
-# awk appears to be guessing the type of an inputted scalar based on usage, so
-# if we read-in a number, but did not use it in any numeric operations, but did
-# use as a string (even in just a format string!) - it will be treated as a
-# string and can lead to REALLY SURPRISING behavior in conditional statements,
-# where smaller number may compare as greater than the bigger ones, such as.
-#
-# Demo:
-#
-# $ awk 'BEGIN {x = "75"; y = "100"; sprintf("x: %d, y: %d\n", x, y); if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
-# 75 < 100
-# $ awk 'BEGIN {x = "75"; y = "100"; sprintf("x: %s, y: %d\n", x, y); if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
-# 75 > 100
-
-# However, once used as a number, seems to stay that way even after being
-# used as string:
-#
-# $ awk 'BEGIN {x = "75"; y = "100"; x + y; sprintf("x: %s, y: %d\n", x, y); if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
-# 75 < 100
-# 
-# $ awk 'BEGIN {x = "75"; y = "100"; x + y; sprintf("x: %s, y: %d\n", x, y); z = x y; if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
-# 75 < 100
-# 
-# $ awk 'BEGIN {x = "75"; y = "100"; x + y; z = x y; if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
-# 75 < 100
-# $ awk 'BEGIN {x = "75"; y = "100"; z = x y; if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
-# 75 > 100
diff --git a/bin/khatus_monitor_energy b/bin/khatus_monitor_energy
new file mode 100755 (executable)
index 0000000..a295e3e
--- /dev/null
@@ -0,0 +1,98 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+     FS = msg_fs ? msg_fs : "|"
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+
+    bat_alert_spec[100] = "low|Energy_Bellow_Full|Must have perfection!"
+    bat_alert_spec[50] = "low|Energy_Bellow_Half|Where is the charger?"
+    bat_alert_spec[20] = "med|Energy_Low|Get the charger."
+    bat_alert_spec[15] = "med|Energy_Low|Get the charger!"
+    bat_alert_spec[10] = "hi|Energy_Low|Plug it in, ASAP!"
+    bat_alert_spec[5]  = "hi|Energy_CRITICALLY_Low|CHARGE NOW!!! GO GO GO!!!"
+}
+
+$1 == "OK" && \
+$2 == "khatus_sensor_energy" && \
+$3 == "line_power" {
+    line_power_prev = line_power_curr
+    line_power_curr = $4
+    if (line_power_curr == "no" && line_power_prev != "no") {
+        alert("low", "PowerUnplugged", "")
+    }
+}
+
+$1 == "OK" && \
+$2 == "khatus_sensor_energy" && \
+$3 == "battery_state" {
+    battery_state_prev = battery_state_curr
+    battery_state_curr = $4
+    printf("BATTERY_STATE prev:%s  curr:%s\n", battery_state_prev, battery_state_curr)
+}
+
+$1 == "OK" && \
+$2 == "khatus_sensor_energy" && \
+$3 == "battery_percentage" {
+    battery_percentage = ensure_numeric($4)
+    printf("BATTERY_PERCENTAGE %s\n", battery_percentage)
+    if (battery_state_curr == "discharging") {
+        for (threshold in bat_alert_spec) {
+            threshold = ensure_numeric(threshold)
+            if (battery_percentage <= threshold && !alerted[threshold]) {
+                split(bat_alert_spec[threshold], msg, "|")
+                priority = msg[1]
+                subject = msg[2]
+                body = sprintf("%d%% %s", battery_percentage, msg[3])
+                alert(priority, subject, body)
+                alerted[threshold]++
+            }
+        }
+    } else {
+        delete alerted
+    }
+}
+
+# After peeking, let everything pass through!
+//
+
+function alert(priority, subject, body) {
+    # priority : "low" | "med" | "hi"
+    # subject  : no spaces
+    # body     : anything
+    print("OK", "khatus_monitor_energy", "alert", priority, subject, body)
+}
+
+function ensure_numeric(n) {
+    return n + 0
+}
+
+#-------------------------------
+# Why do we need ensure_numeric?
+#-------------------------------
+# awk appears to be guessing the type of an inputted scalar based on usage, so
+# if we read-in a number, but did not use it in any numeric operations, but did
+# use as a string (even in just a format string!) - it will be treated as a
+# string and can lead to REALLY SURPRISING behavior in conditional statements,
+# where smaller number may compare as greater than the bigger ones, such as.
+#
+# Demo:
+#
+# $ awk 'BEGIN {x = "75"; y = "100"; sprintf("x: %d, y: %d\n", x, y); if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
+# 75 < 100
+# $ awk 'BEGIN {x = "75"; y = "100"; sprintf("x: %s, y: %d\n", x, y); if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
+# 75 > 100
+
+# However, once used as a number, seems to stay that way even after being
+# used as string:
+#
+# $ awk 'BEGIN {x = "75"; y = "100"; x + y; sprintf("x: %s, y: %d\n", x, y); if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
+# 75 < 100
+# 
+# $ awk 'BEGIN {x = "75"; y = "100"; x + y; sprintf("x: %s, y: %d\n", x, y); z = x y; if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
+# 75 < 100
+# 
+# $ awk 'BEGIN {x = "75"; y = "100"; x + y; z = x y; if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
+# 75 < 100
+# $ awk 'BEGIN {x = "75"; y = "100"; z = x y; if (x > y) {print "75 > 100"} else if (x < y) {print "75 < 100"}}'
+# 75 > 100
diff --git a/bin/khatus_monitor_errors b/bin/khatus_monitor_errors
new file mode 100755 (executable)
index 0000000..459b0ea
--- /dev/null
@@ -0,0 +1,28 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+     FS = msg_fs ? msg_fs : "|"
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+/^ERROR/ {
+    src = $2
+    # Not just using $6 for body - because body might contain a character
+    # identical to FS
+    len_line = length($0)
+    len_head = length($1 FS $2 FS)
+    len_body = len_line - len_head
+    body = substr($0, len_head + 1, len_body)
+    alert("hi", "Error_in_" src, body)
+}
+
+# After peeking, let everything pass through!
+//
+
+function alert(priority, subject, body) {
+    # priority : "low" | "med" | "hi"
+    # subject  : no spaces
+    # body     : anything
+    print("OK", "khatus_monitor_errors", "alert", priority, subject, body)
+}
index ed1e228..0d56853 100755 (executable)
@@ -1,5 +1,10 @@
 #! /usr/bin/awk -f
 
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
 /^Controller / {
     controller = $2
     controllers[++ctrl_count] = controller
@@ -28,7 +33,7 @@ END {
     } else {
         show = "n/a"
     }
-    print(show)
+    print("power_status", show)
 }
 
 function print_error(msg) {
diff --git a/bin/khatus_parse_df_pcent b/bin/khatus_parse_df_pcent
new file mode 100755 (executable)
index 0000000..7e4a534
--- /dev/null
@@ -0,0 +1,12 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+NR == 2 {
+       percentage = $1
+       sub("%$", "", percentage)
+    print("disk_usage_percentage", percentage)
+}
diff --git a/bin/khatus_parse_fan_file b/bin/khatus_parse_fan_file
new file mode 100755 (executable)
index 0000000..120e150
--- /dev/null
@@ -0,0 +1,13 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+{
+    key = $1
+    sub(":$", "", key)
+    val = $2
+    print(key, val)
+}
diff --git a/bin/khatus_parse_free b/bin/khatus_parse_free
new file mode 100755 (executable)
index 0000000..452f44d
--- /dev/null
@@ -0,0 +1,11 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+$1 == "Mem:" {
+    print("total", $2)
+    print("used" , $3)
+}
index bc833b2..b1b2ecd 100755 (executable)
@@ -1,17 +1,22 @@
 #! /usr/bin/awk -f
 
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
 /^[0-9]+:/ {
     sub(":$", "", $1)
     sub(":$", "", $2)
     sequence = $1
     interface = $2
-    interfaces[sequence] = interface
+    Interfaces[sequence] = interface
 }
 
 /^ +inet [0-9]/ {
     sub("/[0-9]+", "", $2)
     addr = $2
-    addrs[interface] = addr
+    Addrs[interface] = addr
 }
 
 /^ +RX: / {transfer_direction = "r"}
 
 END {
     for (seq=1; seq<=sequence; seq++) {
-        interface = interfaces[seq]
+        interface = Interfaces[seq]
         label = substr(interface, 1, 1)
-        addr = addrs[interface]
+        addr = Addrs[interface]
         if (addr) {
-            curr_read  = io[interface, "r"]
-            curr_write = io[interface, "w"]
-            print(interface, addr, curr_write, curr_read)
+            bytes_read    = io[interface, "r"]
+            bytes_written = io[interface, "w"]
         } else {
-            print(interface)
+            bytes_read    = ""
+            bytes_written = ""
         }
+        output["addr"          Kfs interface] = addr
+        output["bytes_read"    Kfs interface] = bytes_read
+        output["bytes_written" Kfs interface] = bytes_written
+    }
+    for (key in output) {
+        print(key, output[key])
     }
 }
index e502731..a33b50f 100755 (executable)
 #
 # USAGE: khatus_parse_iwconfig -v requested_interface="$wifi_interface"
 
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
 /^[a-z0-9]+ +IEEE 802\.11 +ESSID:/ {
     interface = $1
     split($4, essid_parts, ":")
@@ -32,6 +37,6 @@
 
 END {
     i = requested_interface
-    status = link[i] ? sprintf("%s %s:%d%%", i, essid[i], link[i]) : i " --"
-    print(status)
+    status = link[i] ? sprintf("%s:%d%%", essid[i], link[i]) : "--:--%"
+    print("status" Kfs i, status)
 }
diff --git a/bin/khatus_parse_loadavg_file b/bin/khatus_parse_loadavg_file
new file mode 100755 (executable)
index 0000000..f7665fa
--- /dev/null
@@ -0,0 +1,17 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+# 0.71 1.04 1.12 1/325 2409
+{
+    split($4, sched, "/")
+    print("load_avg_1min"             , $1)
+    print("load_avg_5min"             , $2)
+    print("load_avg_15min"            , $3)
+    print("kern_sched_queue_runnable" , sched[1])
+    print("kern_sched_queue_total"    , sched[2])
+    print("kern_sched_latest_pid"     , $5)
+}
index b228390..1dd2778 100755 (executable)
@@ -3,10 +3,9 @@
 # Qualifying the name as "_d_output" lest it be mistaken for parser of actual
 # metar format.
 
-function strip(s) {
-    sub("^ *", "", s)
-    sub(" *$", "", s)
-    return s
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
 }
 
 /METAR pattern not found in NOAA data/ {
@@ -36,12 +35,19 @@ END {
         split(temp_string, temp_parts, " +")
         temp_celsius = temp_parts[1]
         temp_fahrenheit = (temp_celsius * (9 / 5)) + 32
-        print "temperature " temp_fahrenheit "°F"
+        print("temperature_c", temp_celsius)     # °C
+        print("temperature_f", temp_fahrenheit)  # °F
         for (i=first["Phenomena"]; i<=last["Phenomena"]; i++) {
             phenomenon = values[i]
             if (phenomenon) {
-                print "phenomenon " phenomenon
+                print("phenomenon" Kfs i, phenomenon)
             }
         }
     }
 }
+
+function strip(s) {
+    sub("^ *", "", s)
+    sub(" *$", "", s)
+    return s
+}
diff --git a/bin/khatus_parse_mpd_status b/bin/khatus_parse_mpd_status
deleted file mode 100755 (executable)
index 1088b4e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#! /usr/bin/awk -f
-
-# Msg BEGINs
-/^OK MPD / { delete status; next}
-
-# Msg ENDs
-/^OK$/ {
-    split(status["time"], time, ":")
-    seconds_current = time[1]
-    seconds_total   = time[2]
-
-    hours = int(seconds_current / 60 / 60);
-    secs_beyond_hours = seconds_current - (hours * 60 * 60);
-    mins = int(secs_beyond_hours / 60);
-    secs = secs_beyond_hours - (mins * 60);
-
-    if (hours > 0) {
-        current_time = sprintf("%d:%.2d:%.2d", hours, mins, secs)
-    } else {
-        current_time = sprintf("%.2d:%.2d", mins, secs)
-    }
-
-    if (seconds_total > 0) {
-        time_percentage = (seconds_current / seconds_total) * 100
-        current_percentage = sprintf("%d%%", time_percentage)
-    } else {
-        current_percentage = "~"
-    }
-
-    printf("%s %s %s\n", status["state"], current_time, current_percentage)
-    next
-}
-
-# Msg content
-/^[a-z]+: / {
-    sub(":$", "", $1)
-    status[$1] = $2
-    next
-}
diff --git a/bin/khatus_parse_mpd_status_currentsong b/bin/khatus_parse_mpd_status_currentsong
new file mode 100755 (executable)
index 0000000..c045c0f
--- /dev/null
@@ -0,0 +1,88 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+# Msg separator
+/^OK/ {msg_count++; next}
+
+# Msg content
+/^[a-zA-Z-]+: / {
+    key = $1
+    val = $0
+    sub(".*" key " *", "", val)
+    sub(":$", "", key)
+    key = tolower(key)
+    # Note that we expect a particular order of response messages (also
+    # reflected in the name of this script file): "status" THEN "currentsong"
+         if (msg_count == 1) {status[key]      = val}
+    else if (msg_count == 2) {currentsong[key] = val}
+    else {
+        printf("Unexpected msg_count in mpd response: %d\n", msg_count) \
+            > "/dev/stderr"
+        exit 1
+    }
+    next
+}
+
+END {
+    name  = currentsong["name"]
+    title = currentsong["title"]
+    file  = currentsong["file"]
+
+    if (name) {
+        song = name
+    } else if (title) {
+        song = title
+    } else if (file) {
+        last = split(file, parts, "/")
+        song = parts[last]
+    } else {
+        song = "?"
+    }
+
+    format_time(status["time"], time)
+    output["play_time_minimal_units"] = time["minimal_units"]
+    output["play_time_percentage"]    = time["percentage"]
+    output["state"]                   = status["state"]
+    output["song"]                    = song
+    for (key in output) {
+        print(key, output[key])
+    }
+}
+
+function format_time(time_str, time_arr,    \
+    \
+    time_str_parts,
+    seconds_current,
+    seconds_total,
+    hours,
+    secs_beyond_hours,
+    mins,
+    secs,
+    time_percentage \
+) {
+    split(time_str, time_str_parts, ":")
+    seconds_current = time_str_parts[1]
+    seconds_total   = time_str_parts[2]
+
+    hours = int(seconds_current / 60 / 60);
+    secs_beyond_hours = seconds_current - (hours * 60 * 60);
+    mins = int(secs_beyond_hours / 60);
+    secs = secs_beyond_hours - (mins * 60);
+
+    if (hours > 0) {
+        time_arr["minimal_units"] = sprintf("%d:%.2d:%.2d", hours, mins, secs)
+    } else {
+        time_arr["minimal_units"] = sprintf("%.2d:%.2d", mins, secs)
+    }
+
+    if (seconds_total > 0) {
+        time_percentage = (seconds_current / seconds_total) * 100
+        time_arr["percentage"] = sprintf("%d%%", time_percentage)
+    } else {
+        time_arr["percentage"] = "~"
+    }
+}
index 2de73e8..7ed10e6 100755 (executable)
@@ -1,5 +1,10 @@
 #! /usr/bin/awk -f
 
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
 /^Sink \#[0-9]+$/ {
     sub("^#", "", $2)
     sink = $2
@@ -34,9 +39,9 @@
 
 END {
     for (sink in state) {
-        printf(\
-            "%s %s %s %s %s\n",
-            sink, state[sink], mute[sink], vol_left[sink], vol_right[sink] \
-        )
+        print("state"     Kfs sink, state[sink])
+        print("mute"      Kfs sink, mute[sink])
+        print("vol_left"  Kfs sink, vol_left[sink])
+        print("vol_right" Kfs sink, vol_right[sink])
     }
 }
diff --git a/bin/khatus_parse_sys_block_stat b/bin/khatus_parse_sys_block_stat
new file mode 100755 (executable)
index 0000000..923e3ee
--- /dev/null
@@ -0,0 +1,11 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+{
+    print("sectors_read"   , $3)
+    print("sectors_written", $7)
+}
index 6761508..af73e19 100755 (executable)
@@ -1,5 +1,10 @@
 #! /usr/bin/awk -f
 
+BEGIN {
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
 # When parsing 'upower --dump'
 /^Device:[ \t]+/ {
     device["path"] = $2
 
 /    percentage:/ && device["is_battery"] {
     device["battery_percentage"] = $2
+    sub("%$", "", device["battery_percentage"])
     next
 }
 
 /^$/ && device["is_battery"] {
-    printf("battery %s %s\n", device["battery_state"], device["battery_percentage"])
+    print("battery_state"     , device["battery_state"])
+    print("battery_percentage", device["battery_percentage"])
 }
 # END battery
 
@@ -45,7 +52,7 @@
 }
 
 /^$/ && device["is_line_power"] {
-    printf("line_power %s\n", device["line_power_online"])
+    print("line_power", device["line_power_online"])
 }
 # END line-power
 
index ee1da65..effcb1a 100755 (executable)
@@ -1,3 +1,19 @@
 #! /bin/sh
 
-date +'%a %b %d %H:%M:%S'
+set -e
+
+date +'%s %a %b %d %H:%M:%S' \
+| awk '
+    BEGIN {
+        OFS = msg_fs ? msg_fs : "|"
+        Kfs = key_fs ? key_fs : ":"
+    }
+
+       {
+               epoch = $1
+               datetime = $0
+               sub("^" epoch " +", "", datetime)
+               print("epoch"   , epoch)
+               print("datetime", datetime)
+       }
+       '
index 379bcb3..840aed3 100755 (executable)
@@ -2,13 +2,7 @@
 
 set -e
 
-disk_io_device="$1"
+dir_bin="$1"
+disk_io_device="$2"
 
-awk '
-    {
-        r = $3
-        w = $7
-        print w, r
-    }
-    ' \
-    "/sys/block/$disk_io_device/stat"
+"$dir_bin"/khatus_parse_sys_block_stat "/sys/block/$disk_io_device/stat"
index b0f00e6..218d050 100755 (executable)
@@ -2,6 +2,7 @@
 
 set -e
 
-disk_space_device="$1"
+dir_bin="$1"
+disk_space_device="$2"
 
-df --output=pcent "$disk_space_device" | awk 'NR == 2 {print $1}'
+df --output=pcent "$disk_space_device" | "$dir_bin"/khatus_parse_df_pcent
index 12db7b6..40c580b 100755 (executable)
@@ -2,6 +2,7 @@
 
 set -e
 
-fan_path="$1"
+dir_bin="$1"
+fan_path="$2"
 
-cat "$fan_path"
+"$dir_bin"/khatus_parse_fan_file "$fan_path"
index 283d032..04f386f 100755 (executable)
@@ -2,4 +2,6 @@
 
 set -e
 
-cat /proc/loadavg
+dir_bin="$1"
+
+"$dir_bin"/khatus_parse_loadavg_file /proc/loadavg
index 0b79722..10dd058 100755 (executable)
@@ -2,4 +2,6 @@
 
 set -e
 
-free | awk '$1 == "Mem:" {print $2, $3}'
+dir_bin="$1"
+
+free | "$dir_bin"/khatus_parse_free
diff --git a/bin/khatus_sensor_mpd b/bin/khatus_sensor_mpd
new file mode 100755 (executable)
index 0000000..058cecd
--- /dev/null
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+set -e
+
+dir_bin="$1"
+
+echo 'status\ncurrentsong' \
+| nc 127.0.0.1 6600 \
+| "$dir_bin"/khatus_parse_mpd_status_currentsong
diff --git a/bin/khatus_sensor_mpd_song b/bin/khatus_sensor_mpd_song
deleted file mode 100755 (executable)
index 0d119d1..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /bin/sh
-
-set -e
-
-echo 'currentsong' | nc 127.0.0.1 6600
diff --git a/bin/khatus_sensor_mpd_state b/bin/khatus_sensor_mpd_state
deleted file mode 100755 (executable)
index 20cc49a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#! /bin/sh
-
-set -e
-
-dir_bin="$1"
-
-echo 'status' \
-| nc 127.0.0.1 6600 \
-| "$dir_bin"/khatus_parse_mpd_status
index 08a8dfc..9b075d3 100755 (executable)
@@ -4,7 +4,15 @@ set -e
 
 screen_brightness_device_path="$1"
 
-echo "\
-    $(cat $screen_brightness_device_path/max_brightness) \
-    $(cat $screen_brightness_device_path/brightness)\
-"
+awk '
+    BEGIN {
+        OFS = msg_fs ? msg_fs : "|"
+        Kfs = key_fs ? key_fs : ":"
+    }
+
+    FILENAME ~ "/max_brightness$" {max = $1; next}
+    FILENAME ~     "/brightness$" {cur = $1; next}
+    END                           {print("percentage", (cur / max) * 100)}
+' \
+"$screen_brightness_device_path/max_brightness" \
+"$screen_brightness_device_path/brightness"
index 6f9bd23..321c8d8 100755 (executable)
@@ -4,4 +4,12 @@ set -e
 
 thermal_zone="$1"
 
-cat "/sys/class/thermal/thermal_zone${thermal_zone}/temp"
+awk '
+    BEGIN {
+        OFS = msg_fs ? msg_fs : "|"
+        Kfs = key_fs ? key_fs : ":"
+    }
+
+    {print("temp_c", $1 / 1000)}
+' \
+"/sys/class/thermal/thermal_zone${thermal_zone}/temp"
diff --git a/bin/khatus_transform_total_to_diff b/bin/khatus_transform_total_to_diff
new file mode 100755 (executable)
index 0000000..59894c0
--- /dev/null
@@ -0,0 +1,21 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+     FS = msg_fs ? msg_fs : "|"
+    OFS = msg_fs ? msg_fs : "|"
+    Kfs = key_fs ? key_fs : ":"
+}
+
+# Modify the record we're interested in
+$1 == status && $2 == src && ($3 ~ ("^" key_prefix)) {
+    key = $3
+    val = $4
+    prev[key] = curr[key]
+    curr[key] = val
+    diff[key] = curr[key] - prev[key]
+    print($1, $2, $3, diff[key])
+    next
+}
+
+# Let everything else through untouched
+//
This page took 0.098842 seconds and 4 git commands to generate.