Grab wifi status from iwconfig instead of nmcli
[khatus.git] / bin / khatus_controller
index e6954a2..c67e640 100755 (executable)
 #! /usr/bin/awk -f
 
-/^in:ENERGY/\
+/^OK/ { debug("OK line", $0) }
+
+/^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/\
 {
-    split_msg_parts()
-    db["energy_state"]      = $1
-    db["energy_percentage"] = $2
+    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()
 }
 
-/^in:MEMORY/\
+/^OK in:ENERGY line_power/\
 {
-    split_msg_parts()
+    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
 }
 
-/^in:FAN +status:/\
+/^OK in:FAN +status:/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     db["fan_status"] = $2
 }
 
-/^in:FAN +speed:/\
+/^OK in:FAN +speed:/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     db["fan_speed"] = $2
 }
 
-/^in:FAN +level:/\
+/^OK in:FAN +level:/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     db["fan_level"] = $2
 }
 
-/^in:TEMPERATURE/\
+/^OK in:TEMPERATURE/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     db["temperature"] = $1
 }
 
-/^in:LOAD_AVG/\
+/^OK in:LOAD_AVG/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     set_load_avg()
 }
 
-/^in:DISK_IO/\
+/^OK in:DISK_IO/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     set_disk_io()
 }
 
-/^in:DISK_SPACE/\
+/^OK in:DISK_SPACE/\
 {
-    split_msg_parts()
-    db["disk_space_used"] = msg_body
+    shift()
+    shift()
+    db["disk_space_used"] = $0
 }
 
-/^in:NET_ADDR_IO/\
+/^OK in:NET_ADDR_IO/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     set_net_addr_io()
 }
 
-/^in:NET_WIFI_STATUS/\
+/^OK in:NET_WIFI_STATUS/\
 {
-    split_msg_parts()
-    db["net_wifi_status"] = msg_body
+    shift()
+    shift()
+    set_net_wifi_status()
 }
 
-/^in:BLUETOOTH_POWER/\
+/^OK in:BLUETOOTH_POWER/\
 {
-    split_msg_parts()
-    db["bluetooth_power"] = msg_body
+    shift()
+    shift()
+    db["bluetooth_power"] = $0
 }
 
-/^in:SCREEN_BRIGHTNESS/\
+/^OK in:SCREEN_BRIGHTNESS/\
 {
-    split_msg_parts()
+    shift()
+    shift()
     set_screen_brightness()
 }
 
-/^in:VOLUME/\
+/^OK in:VOLUME/\
 {
-    split_msg_parts()
-    db["volume"] = msg_body
+    shift()
+    set_volume()
 }
 
-/^in:MPD_STATE/\
+/^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 /\
 {
-    split_msg_parts()
-    db["mpd_state"]             = $1
-    db["mpd_curr_song_time"]    = $2
-    db["mpd_curr_song_percent"] = $3
+    shift()
+    shift()
+    db["mpd_status_state"]   = $1
+    db["mpd_status_time"]    = $2
+    db["mpd_status_percent"] = $3
 }
 
-/^in:MPD_SONG/\
+/^OK in:WEATHER/\
 {
-    split_msg_parts()
-    db["mpd_curr_song_name"] = msg_body
+    shift()
+    shift()
+    db["weather_temperature"] = $0
 }
 
-/^in:WEATHER/\
+/^OK in:DATE_TIME/\
 {
-    split_msg_parts()
-    db["weather_temperature"] = msg_body
+    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)
+    }
 }
 
-/^in:DATE_TIME/\
-{
-    split_msg_parts()
-    db["datetime"] = msg_body
+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)
 }
 
-/^out:BAR/\
-{
-    split_msg_parts()
-    print make_bar()
+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) {
@@ -139,6 +338,12 @@ function set_disk_io(    curr_w, curr_r, prev_w, prev_r) {
     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\
 ) {
@@ -174,14 +379,12 @@ function set_screen_brightness(    max, cur) {
     db["screen_brightness"] = (cur / max) * 100
 }
 
-function split_msg_parts() {
-    msg_head = $1
-    sub("^" msg_head " +", "")
-    msg_body = $0
-    debug(msg_head, msg_body)
+# TODO: Revise overuse of shift() where it is not really needed
+function shift() {
+    sub("^" $1 " +", "")
 }
 
-function make_bar(    position, bar, sep, i, j) {
+function make_status_bar(    position, bar, sep, i, j) {
     position[++i] = make_status_energy()
     position[++i] = make_status_mem()
     position[++i] = make_status_cpu()
@@ -203,7 +406,7 @@ function make_bar(    position, bar, sep, i, j) {
 }
 
 function make_status_energy(    state, direction_of_change) {
-    state = db["energy_state"]
+    state = db["energy_state_curr"]
     if (state == "discharging") {
         direction_of_change = "<"
     } else if (state == "charging") {
@@ -211,7 +414,7 @@ function make_status_energy(    state, direction_of_change) {
     } else {
         direction_of_change = "="
     };
-    printf("E%s%s", direction_of_change, db["energy_percentage"])
+    return sprintf("E%s%d%%", direction_of_change, db["energy_percentage"])
 }
 
 function make_status_mem(    total, used, percent, status) {
@@ -278,7 +481,7 @@ function make_status_net(    \
                     io_stat = "--"
                 }
                 if (interface ~ "^w") {
-                    label = label ":" db["net_wifi_status"]
+                    label = label ":" db["net_wifi_status", interface]
                 }
                 if (++count_printed > 1) {
                     sep = "  "
@@ -293,7 +496,7 @@ function make_status_net(    \
 }
 
 function make_status_mpd(    state, status) {
-    state = db["mpd_state"]
+    state = db["mpd_status_state"]
 
     if (state == "play") {
         status = make_status_mpd_state_known("▶")
@@ -312,9 +515,9 @@ function make_status_mpd_state_known(symbol) {
     return sprintf(\
         "%s %s %s %s",
         symbol,
-        db["mpd_curr_song_time"],
-        db["mpd_curr_song_percent"],
-        substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\
+        db["mpd_status_time"],
+        db["mpd_status_percent"],
+        substr(db["mpd_playing_curr"], 1, opt_mpd_song_max_chars)\
     )
 }
 
@@ -326,12 +529,56 @@ function round(n) {
     return int(n + 0.5)
 }
 
-function debug(location, msg) {
+function debug(location, msg, values,    sep, vals, key, payload) {
     if (opt_debug) {
-        print_error(location, msg)
+        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 print_error(location, msg) {
-    print(location " ==> " msg) > "/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
This page took 0.032192 seconds and 4 git commands to generate.