From 756b9d5a05cbd36f97b1cb817137148517e14abf Mon Sep 17 00:00:00 2001 From: Siraaj Khandkar Date: Tue, 12 Jun 2018 14:17:24 -0400 Subject: [PATCH] Import --- README.md | 6 + ...hatus_cpu_usage_from_proc_since_last_check | 99 +++++++ bin/khatus_show | 271 ++++++++++++++++++ bin/khatus_update_network | 37 +++ bin/khatus_update_weather | 28 ++ install | 15 + screenshot.jpg | Bin 0 -> 7092 bytes 7 files changed, 456 insertions(+) create mode 100644 README.md create mode 100755 bin/khatus_cpu_usage_from_proc_since_last_check create mode 100755 bin/khatus_show create mode 100755 bin/khatus_update_network create mode 100755 bin/khatus_update_weather create mode 100755 install create mode 100644 screenshot.jpg diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb7f3e3 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +khatus +====== + +My ad-hoc, text-only status bar scripts I use with `dwm` on GNU/Linux. + +![screenshot](screenshot.jpg) diff --git a/bin/khatus_cpu_usage_from_proc_since_last_check b/bin/khatus_cpu_usage_from_proc_since_last_check new file mode 100755 index 0000000..e76d21d --- /dev/null +++ b/bin/khatus_cpu_usage_from_proc_since_last_check @@ -0,0 +1,99 @@ +#! /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 $@ diff --git a/bin/khatus_show b/bin/khatus_show new file mode 100755 index 0000000..19004be --- /dev/null +++ b/bin/khatus_show @@ -0,0 +1,271 @@ +#! /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 + +#load=$(cat /proc/loadavg | awk '{printf "%4.2f %4.2f %4.2f", $1, $2, $3}') + +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; + file = "/home/siraaj/var/run/status/memory_used_percentage"; + curr = round(used / total * 100); + getline prev < file; + print curr > 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=$( + df \ + | awk ' + function round(n) {return int(n + 0.5)} + + $1 == "/dev/mapper/kubuntu--vg-root" { + total = $2; + used = $3; + file = "/home/siraaj/var/run/status/disk_space_used_percentage"; + curr = round(used / total * 100); + getline prev < file; + print curr > file; + if (curr > prev) { + direction = ">"; + } else if (curr < prev) { + direction = "<"; + } else { + direction = "="; + } + printf("%s%d%%", direction, curr); + }') + +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') + +#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]" + +wifi=$(cat $STATUS_FILE__WIFI) + +screen_brightness=$( + max=$(cat /sys/class/backlight/acpi_video0/max_brightness) + cur=$(cat /sys/class/backlight/acpi_video0/brightness) + awk -v max=$max -v cur=$cur 'BEGIN {printf("%d%%", cur/max*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="$(cat ~/var/run/metar-KJFK-decoded-temp-fahrenheit)°F" + +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 ' ' +) + +echo \ +"\ + E$energy\ + M$memory\ + D$disk\ + C=[$cpu ${temp}°C ${fan}rpm]\ + |\ + S=$screen_brightness\ + V=$volume\ + B:$bluetooth_power\ + W:$wifi\ + |\ + $weather\ + \ + $datetime \ +" diff --git a/bin/khatus_update_network b/bin/khatus_update_network new file mode 100755 index 0000000..f3b562f --- /dev/null +++ b/bin/khatus_update_network @@ -0,0 +1,37 @@ +#! /bin/bash + +set -e + +#TERM=xterm-256color # To keep unicode charcters from BARS + +STATUS_DIR=$HOME/var/run/status +STATUS_FILE_WIFI=$STATUS_DIR/wifi +STATUS_FILE_ETH=$STATUS_DIR/eth + +mkdir -p $STATUS_DIR + +# nmcli d \ +# | awk \ +# -v file_wifi="$STATUS_FILE_WIFI" \ +# -v file_eth="$STATUS_FILE_ETH" \ +# ' +# $2 == "wifi" {wifi = $4} +# $2 == "ethernet" {eth=$4} +# END { +# print(wifi) > file_wifi; +# print(eth) > file_eth; +# } +# ' + +nmcli \ + -f ACTIVE,SSID,SIGNAL \ + -t \ + d wifi \ +| awk \ + -F ':' \ + -v file_wifi="$STATUS_FILE_WIFI" \ + ' + BEGIN {wifi_status = "--"} + $1 == "yes" {wifi_status = $2 ":" $3 "%"} + END {print wifi_status > file_wifi} + ' diff --git a/bin/khatus_update_weather b/bin/khatus_update_weather new file mode 100755 index 0000000..1127141 --- /dev/null +++ b/bin/khatus_update_weather @@ -0,0 +1,28 @@ +#! /bin/bash + +set -e + +FILE_METAR_DECODED="$HOME/var/run/metar-KJFK-decoded" +FILE_TEMP_FAHRENHEIT="${FILE_METAR_DECODED}-temp-fahrenheit" + +(metar -d KJFK 2>&1) > $FILE_METAR_DECODED # TODO: Better error handling + +awk ' + /METAR pattern not found in NOAA data/ { + failures++ + } + + /^Temperature/ { + celsius = $3; + fahrenheit = (celsius * (9 / 5)) + 32; + } + + END { + if (failures > 0) { + print "--"; + } else { + print fahrenheit; + } + }' \ + $FILE_METAR_DECODED \ +> $FILE_TEMP_FAHRENHEIT diff --git a/install b/install new file mode 100755 index 0000000..44fa3fe --- /dev/null +++ b/install @@ -0,0 +1,15 @@ +#! /bin/sh + +PREFIX="$HOME/bin" + +if [ $1 ] +then + PREFIX=$1 +fi + +cd bin + +for filename in * +do + ln -s "`pwd`/$filename" "$PREFIX/$filename" +done diff --git a/screenshot.jpg b/screenshot.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e3ce04209177000eb35c93c3a50938342ac4bdc5 GIT binary patch literal 7092 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 H-_!pDD~TFT literal 0 HcmV?d00001 -- 2.20.1