--- /dev/null
+#! /bin/sh
+
+proc_stat_parse() {
+ proc_stat="$1"
+ n='[0-9]\+'
+ echo "$proc_stat" \
+ | grep "^cpu$n $n $n $n $n $n $n $n $n $n $n$" \
+ | awk '
+ {
+ cpu = $1;
+ user = $2;
+ sys = $4;
+ idle = $5;
+
+ total = user + sys + idle;
+ busy = user + sys;
+
+ if (NR > 1) {printf " "};
+
+ out = sprintf("%s %d %d", cpu, total, busy);
+ #print out >> "cpu_usage_debug.txt";
+ printf "%s", out;
+ }
+ END {
+ #print "" >> "cpu_usage_debug.txt";
+ print "";
+ }
+ '
+}
+
+calc_delta() {
+ for proc_stat in "$1" "$2"; do
+ proc_stat_parse "$proc_stat"
+ done \
+ | awk '
+ {
+ t = NR;
+ for (i = 1; i <= (NF - 2); i += 3) {
+ cpu_count[t]++;
+ cpu_id = $i; # For occasional debugging
+ total = $(i + 1);
+ busy = $(i + 2);
+ cpu[cpu_count[t], "total", t] = total;
+ cpu[cpu_count[t], "busy" , t] = busy;
+ }
+ }
+
+ END {
+ for (c=1; c<=cpu_count[2]; c++) {
+ total_1 = cpu[c, "total", 1];
+ total_2 = cpu[c, "total", 2];
+ busy_1 = cpu[c, "busy" , 1];
+ busy_2 = cpu[c, "busy" , 2];
+ total_d = total_2 - total_1;
+ busy_d = busy_2 - busy_1;
+ percent_busy = (busy_d / total_d) * 100;
+
+ #printf(\
+ # "c: %d, total_1: %f total_2: %f, total_d: %f\n",
+ # c, total_1, total_2, total_d \
+ #) >> "cpu_usage_debug.txt";
+ #printf(\
+ # "c: %d, busy_1: %f busy_2: %f, busy_d: %f\n",
+ # c, busy_1, busy_2, busy_d \
+ #) >> "cpu_usage_debug.txt";
+ #printf(\
+ # "c: %d, percent_busy: %f\n",
+ # c, percent_busy \
+ #) >> "cpu_usage_debug.txt";
+
+ if (c > 1) {printf " "};
+ out = sprintf("%3.0f%%", percent_busy)
+ #printf "c: %d, out: %s\n", c, out >> "cpu_usage_debug.txt";
+ printf "%s", out;
+ }
+ #print "" >> "cpu_usage_debug.txt";
+ print "";
+ }
+ '
+}
+
+main() {
+ last_proc_stat="$HOME/var/run/cpu_usage_from_proc_since_last_check/last_proc_stat"
+
+ if [ ! -f "$last_proc_stat" ]
+ then
+ mkdir -p `dirname "$last_proc_stat"`
+ cat /proc/stat > "$last_proc_stat"
+ sleep 0.1
+ fi
+
+ previous=`cat $last_proc_stat`;
+ cat /proc/stat > "$last_proc_stat"
+ current=`cat $last_proc_stat`;
+
+ calc_delta "$previous" "$current"
+}
+
+main $@
--- /dev/null
+#! /bin/bash
+
+set -e
+
+BIN=$HOME/bin
+STATUS_DIR=$HOME/var/run/status
+STATUS_FILE__WIFI=$STATUS_DIR/wifi
+STATUS_FILE__ENERGY_NOTIFIED_BELLOW_HALF=$STATUS_DIR/notified_energy_bellow_half
+while getopts ":b:d:s:i:w:" opt
+do
+ case "$opt" in
+ b)
+ SCREEN_BRIGHTNESS_DEVICE_NAME="$OPTARG"
+ ;;
+ d)
+ DISK_IO_DEVICE="$OPTARG"
+ ;;
+ s)
+ DISK_SPACE_DEVICE="$OPTARG"
+ ;;
+ i)
+ PREFIXES_OF_INTERFACES_TO_SHOW="$OPTARG"
+ ;;
+ w)
+ WEATHER_STATION_ID="$OPTARG"
+ ;;
+ \?)
+ echo "Unknown option: $OPTARG" >&2
+ exit 1
+ ;;
+ :)
+ echo "Missing argument for option: $OPTARG" >&2
+ exit 1
+ ;;
+ esac
+done
+if [ "$SCREEN_BRIGHTNESS_DEVICE_NAME" == '' ]; then echo 'Missing -b' >&2; exit 1; fi
+if [ "$DISK_IO_DEVICE" == '' ]; then echo 'Missing -d' >&2; exit 1; fi
+if [ "$DISK_SPACE_DEVICE" == '' ]; then echo 'Missing -s' >&2; exit 1; fi
+if [ "$WEATHER_STATION_ID" == '' ]; then echo 'Missing -w' >&2; exit 1; fi
+SCREEN_BRIGHTNESS_DEVICE_PATH="/sys/class/backlight/$SCREEN_BRIGHTNESS_DEVICE_NAME"
+
+
+load=$(awk '{printf("%4.2f", $1)}' /proc/loadavg)
+
+fan=$(awk '/^speed:/ {printf "%4d", $2}' /proc/acpi/ibm/fan)
+
+#cpu=$($BIN/khatus_cpu_usage_from_proc_since_last_check)
+
+memory=$(
+ free \
+ | awk '
+ function round(n) {return int(n + 0.5)}
+
+ $1 == "Mem:" {
+ total=$2;
+ used=$3;
+ cache=$6;
+ prev_file = ENVIRON["HOME"] "/var/run/status/memory_used_percentage";
+ curr = round(used / total * 100);
+ getline prev < prev_file;
+ print curr > prev_file;
+ if (curr > prev) {
+ direction = ">";
+ } else if (curr < prev) {
+ direction = "<";
+ } else {
+ direction = "=";
+ }
+ printf("%s%d%%", direction, curr);
+ }')
+
+temp=$(awk 'NR == 1 {print $1 / 1000}' /sys/class/thermal/thermal_zone0/temp)
+
+disk_io=$(
+ awk '
+ {
+ bytes_per_sector = 512
+ bytes_per_unit = 1024 * 1024
+
+ curr_sectors_read = $3
+ curr_sectors_write = $7
+
+ prev_file_prefix = ENVIRON["HOME"] "/var/run/status/disk_io"
+ prev_sectors_read_file = prev_file_prefix "_sectors_read"
+ prev_sectors_write_file = prev_file_prefix "_sectors_write"
+
+ getline prev_sectors_read < prev_sectors_read_file
+ getline prev_sectors_write < prev_sectors_write_file
+
+ diff_read_sectors = (curr_sectors_read - prev_sectors_read)
+ diff_write_sectors = (curr_sectors_write - prev_sectors_write)
+
+ diff_read_bytes = diff_read_sectors * bytes_per_sector
+ diff_write_bytes = diff_write_sectors * bytes_per_sector
+
+ diff_read = diff_read_bytes / bytes_per_unit
+ diff_write = diff_write_bytes / bytes_per_unit
+
+ print curr_sectors_read > prev_sectors_read_file
+ print curr_sectors_write > prev_sectors_write_file
+
+ printf("%0.3f▲ %0.3f▼\n", diff_write, diff_read);
+
+ }
+ ' "/sys/block/$DISK_IO_DEVICE/stat"
+)
+
+disk=$(
+ df \
+ | awk \
+ -v disk_io="$disk_io" \
+ -v device="$DISK_SPACE_DEVICE" \
+ '
+ function round(n) {return int(n + 0.5)}
+
+ $1 == device {
+ curr_perc = $5; sub("%$", "", curr_perc);
+ prev_perc_file = ENVIRON["HOME"] "/var/run/status/disk_space_used";
+ getline prev_perc < prev_perc_file;
+ print curr_perc > prev_perc_file;
+ if (curr_perc > prev_perc) {
+ direction = ">";
+ } else if (curr_perc < prev_perc) {
+ direction = "<";
+ } else {
+ direction = "=";
+ }
+ printf("%s[%d%% %s]", direction, curr_perc, disk_io);
+ }')
+
+# TODO: Wi-Fi status file should be a file per-wifi-device
+network=$(
+ ip -s addr \
+ | awk \
+ -v wifi_conn="$(cat $STATUS_FILE__WIFI)" \
+ -v prefixes_of_interfaces_to_show="$PREFIXES_OF_INTERFACES_TO_SHOW" \
+ '
+ BEGIN {
+ bytes_per_unit = 1024 * 1024
+ }
+
+ /^[0-9]+:/ {
+ sub(":$", "", $1)
+ sub(":$", "", $2)
+ sequence = $1
+ interface = $2
+ interfaces[sequence] = interface
+ }
+
+ /^ +inet [0-9]/ {
+ sub("/[0-9]+", "", $2)
+ addr = $2
+ addrs[interface] = addr
+ }
+
+ /^ +RX: / {transfer_direction = "r"}
+ /^ +TX: / {transfer_direction = "w"}
+
+ /^ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ *$/ {
+ io[interface, transfer_direction] = $1;
+ }
+
+ END {
+ for (seq=1; seq<=sequence; seq++) {
+ interface = interfaces[seq]
+ label = substr(interface, 1, 1)
+ if (addrs[interface]) {
+ curr_read = io[interface, "r"]
+ curr_write = io[interface, "w"]
+
+ prefix = ENVIRON["HOME"] "/var/run/status/io_net_" interface
+ prev_read_file = prefix "_read"
+ prev_write_file = prefix "_write"
+
+ getline prev_read < prev_read_file
+ getline prev_write < prev_write_file
+
+ diff_read = (curr_read - prev_read ) / bytes_per_unit
+ diff_written = (curr_write - prev_write) / bytes_per_unit
+
+ print curr_read > prev_read_file
+ print curr_write > prev_write_file
+
+ io_stat = sprintf("%0.3f▲ %0.3f▼", diff_written, diff_read)
+ if (interface ~ "^w") {
+ label = label ":" wifi_conn
+ }
+ } else {
+ io_stat = "--"
+ }
+ number_of_interfaces_to_show = \
+ split(\
+ prefixes_of_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]
+ if (interface ~ ("^" prefix)) {
+ if (++count_printed > 1) {
+ sep = " "
+ } else {
+ sep = ""
+ }
+ printf("%s%s:%s", sep, label, io_stat)
+ }
+ }
+ }
+ }'
+)
+
+energy=$(
+ upower -e \
+ | grep battery \
+ | xargs upower -i \
+ | awk '
+ /^ +percentage: +/ {percentage=$2}
+ /^ +state: +/ {state=$2}
+ END {
+ if (state == "discharging") {
+ direction_of_change = "<"
+ } else if (state == "charging") {
+ direction_of_change = ">"
+ } else {
+ direction_of_change = "="
+ };
+ printf("%s%s", direction_of_change, percentage)
+ }')
+
+datetime=$(date +'%a %b %d %H:%M:%S')
+#datetime=$(
+# date +'%a %u %b %d %H:%M:%S' \
+# | awk '
+# {
+# wday_name = $1
+# wday_seq = $2
+# month = $3
+# mday = $4
+# time = $5
+#
+# week = ""
+# for (i=1; i<=7; i++) {
+# if (i == 6 || i == 4) {
+# sep = " "
+# } else {
+# sep = ""
+# }
+#
+# if (i == wday_seq) {
+# #symbol = substr(wday_name, 1, 1)
+# symbol = "/"
+# } else if (i < wday_seq){
+# symbol = "X"
+# } else {
+# symbol = "_"
+# }
+# week = week sep symbol
+# }
+#
+# print "["week"]", month, mday, time;
+# }
+# '
+#)
+
+#volume_amixer=$(
+# amixer get Master \
+# | tail -1 \
+# | awk '
+# {
+# level = $4;
+# sub("^\\[", "", level);
+# sub("\\]$", "", level);
+# print level;
+# }' \
+# )
+
+#volume_amixer=$(
+# amixer get Master \
+# | tail -n 1 \
+# | awk '{print $4}' \
+# | tr -d '[]'
+#)
+
+volume_pactl=$(
+ pactl list sinks \
+ | awk '
+ /^\tMute:/ {
+ printf("%s,", $0);
+ }
+ /^\tVolume:/ {
+ for (i=2; i<=NF; i++) printf(" %s", $i);
+ }' \
+ | awk -v RS=',' '
+ /^[ \t]*Mute:/ {mute = $2}
+ /^[ \t]*front-left:/ {left = $4}
+ /^[ \t]*front-right:/ {right = $4}
+ END {
+ if (mute == "yes") {
+ printf("x")
+ } else {
+ printf("%s %s", left, right)
+ }
+ }
+ '
+)
+
+volume="($volume_pactl)"
+
+screen_brightness=$(
+ echo -n $((
+ $(cat $SCREEN_BRIGHTNESS_DEVICE_PATH/brightness)
+ / $(cat $SCREEN_BRIGHTNESS_DEVICE_PATH/max_brightness)
+ * 100
+ ))
+)
+
+#bluetooth_status=$(
+# grep '^status:' /proc/acpi/ibm/bluetooth \
+# | awk '
+# $2 == "disabled" {printf "off"}
+# $2 == "enabled" {printf "on"}
+# '
+#)
+
+bluetooth_power=$(
+ echo -e 'show \n quit' \
+ | bluetoothctl \
+ | awk '
+ /^Controller / {
+ controller = $2;
+ controllers[++ctrl_count] = controller;
+ }
+ /^\t[A-Z][A-Za-z]+:/ {
+ key = $1;
+ sub(":$", "", key);
+ val = $2;
+ for (i=3; i<=NF; i++) {
+ val = val " " $i};
+ data[controller, key] = val;
+ }
+ END {
+ # Using the 1st seen controller. Should we select specific instead?
+ power_status = data[controllers[1], "Powered"];
+ if (ctrl_count > 0) {
+ if (power_status == "no") {
+ power_status = "off"
+ } else if (power_status == "yes") {
+ power_status = "on"
+ } else {
+ printf("Unexpected bluetooth power status: %s\n", power_status)\
+ > "/dev/stderr";
+ power_status = "ERROR"
+ }
+ } else {
+ power_status = "off" # TODO: Perhaps use differentiated marker?
+ }
+ printf("%s", power_status);
+ }'
+)
+
+#touchpad_status=$(
+# xinput list-props 12 \
+# | awk '
+# /^\tDevice Enabled \([0-9]+\):/ {
+# status = $4;
+# printf("%s", status);
+# }'
+#)
+
+#color_off='\033[0m'
+#color_on_bg_gray='\033[m\033[40m'
+
+energy_direction=$(echo "$energy" | cut -b 1)
+energy_percentage=$(echo "$energy" | tr -d '<>=%')
+if [[ "$energy_direction" = '<' ]]
+then
+ if [[ $energy_percentage -le 5 ]]
+ then
+ DISPLAY=:0.0 notify-send \
+ -u critical \
+ "Energy CRITICALLY low: $energy" \
+ 'CHARGE NOW!!! GO GO GO!!!'
+ elif [[ $energy_percentage -le 10 ]]
+ then
+ DISPLAY=:0.0 notify-send \
+ -u critical \
+ "Energy VERY low: $energy" \
+ 'Plug it in ASAP.'
+ elif [[ $energy_percentage -le 15 ]]
+ then
+ DISPLAY=:0.0 notify-send \
+ -u critical \
+ "Energy low: $energy" \
+ 'Get the charger.'
+ elif [[ $energy_percentage -le 50 ]]
+ then
+ if [[ ! -a "$STATUS_FILE__ENERGY_NOTIFIED_BELLOW_HALF" ]]
+ then
+ DISPLAY=:0.0 notify-send \
+ -u normal \
+ "Energy bellow half: $energy" \
+ 'Where is the charger?'
+ touch "$STATUS_FILE__ENERGY_NOTIFIED_BELLOW_HALF"
+ fi
+ fi
+else
+ rm -f "$STATUS_FILE__ENERGY_NOTIFIED_BELLOW_HALF"
+fi
+
+weather=$(
+ awk \
+ 'NR == 1 {printf("%s°F", $1)}' \
+ "$HOME/var/run/metar-${WEATHER_STATION_ID}-decoded-temp-fahrenheit"
+)
+
+#signal_last_msg_age=$(
+# ls -lt --time-style=+%s $HOME/var/lib/signal/latest_message.json \
+# | awk -v now_seconds=$(date +%s) \
+# '{
+# mtime_seconds = $6;
+# seconds = now_seconds - mtime_seconds;
+# minutes = seconds / 60;
+# hours = minutes / 60;
+# days = hours / 24;
+# weeks = days / 7;
+# months = days / 30;
+# #fmt = "%.1f";
+# fmt = "%d";
+# printf(fmt " s\n", seconds);
+# printf(fmt " m\n", minutes);
+# printf(fmt " h\n", hours);
+# printf(fmt " d\n", days);
+# printf(fmt " w\n", weeks);
+# printf(fmt " mo\n", months);
+# }' \
+# | awk '$1 >= 1' \
+# | sort -n -k 1 \
+# | head -1 \
+# | tr -d ' '
+#)
+
+mpd_currentsong=$(
+ echo 'currentsong' \
+ | nc 127.0.0.1 6600 \
+ | awk -v max_chars=10 '
+ /^OK/ {
+ next
+ }
+
+ {
+ key = $1
+ val = $2
+ for (i=3; i<=NF; i++) {val = val " " $i}
+ data[key] = val
+ }
+
+ END {
+ name = data["Name:"]
+ title = data["Title:"]
+ file = data["file:"]
+
+ if (name) {
+ out = name
+ } else if (title) {
+ out = title
+ } else if (file) {
+ last = split(file, parts, "/")
+ out = parts[last]
+ } else {
+ out = ""
+ }
+
+ printf("%s", substr(out, 1, max_chars))
+ }
+ '
+)
+
+mpd_state=$(
+ echo 'status' \
+ | nc 127.0.0.1 6600 \
+ | awk \
+ -v current_song="$mpd_currentsong" \
+ '
+ {
+ status[$1] = $2
+ }
+
+ /^time: +[0-9]+:[0-9]+$/ {
+ split($2, 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 = "~"
+ }
+ }
+
+ function print_known_state(symbol) {
+ printf(\
+ "%s %s %s %s",
+ symbol, current_time, current_percentage, current_song \
+ )
+ }
+
+ function print_unknown_state(symbol) {
+ printf("%s", symbol)
+ }
+
+ END {
+ state = status["state:"]
+
+ if (state == "play") {
+ print_known_state("▶")
+ } else if (state == "pause") {
+ print_known_state("❚❚")
+ } else if (state == "stop") {
+ print_known_state("⬛")
+ } else {
+ print_unknown_state("--")
+ }
+ }
+ '
+)
+
+#graphics_card=$(
+ #nvidia-smi \
+ #--format=csv,noheader,nounits \
+ #--query-gpu=memory.total,memory.used,temperature.gpu \
+ #| awk -F ',' '
+ #{
+ #mem_total = $1;
+ #mem_used = $2;
+ #temp = $3;
+ #mem_used_percent = (100 * mem_used) / mem_total;
+ #printf("[%d%% %dC]", mem_used_percent, temp);
+ #}
+ #'
+#)
+
+echo \
+"\
+ E$energy\
+ \
+ M$memory\
+ \
+ C=[$load ${temp}°C ${fan}rpm]\
+ \
+ D$disk\
+ \
+ N:[$network]\
+ \
+ B:$bluetooth_power\
+ \
+ *$screen_brightness%\
+ \
+ $volume\
+ \
+ [$mpd_state]\
+ \
+ $weather\
+ \
+ $datetime \
+"