Add the waybar scripts.

This commit is contained in:
Tom Alexander 2024-12-21 16:25:40 -05:00
parent a0f9f4baa4
commit 14e6e78aee
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
7 changed files with 471 additions and 0 deletions

View File

@ -4,14 +4,76 @@
pkgs,
...
}:
let
waybar_available_memory =
(pkgs.writeScriptBin "waybar_custom_available_memory" (
builtins.readFile ./files/waybar_scripts/waybar_available_memory_linux.bash
)).overrideAttrs
(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
waybar_battery =
(pkgs.writeScriptBin "waybar_custom_battery" (
builtins.readFile ./files/waybar_scripts/waybar_battery_linux.bash
)).overrideAttrs
(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
waybar_clock =
(pkgs.writeScriptBin "waybar_custom_clock" (
builtins.readFile ./files/waybar_scripts/waybar_custom_clock.py
)).overrideAttrs
(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
waybar_night_mode =
(pkgs.writeScriptBin "waybar_night_mode" (
builtins.readFile ./files/waybar_scripts/waybar_night_mode.bash
)).overrideAttrs
(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
waybar_sound =
(pkgs.writeScriptBin "waybar_custom_sound" (
builtins.readFile ./files/waybar_scripts/waybar_sound_linux.bash
)).overrideAttrs
(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
waybar_temperature =
(pkgs.writeScriptBin "waybar_custom_temperature" (
builtins.readFile ./files/waybar_scripts/waybar_temperature_linux.bash
)).overrideAttrs
(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
in
{
imports = [ ];
environment.systemPackages = with pkgs; [
waybar
waybar_available_memory
waybar_battery
waybar_clock
waybar_night_mode
waybar_sound
waybar_temperature
python3 # for clock TODO python should not be in the system packages, maybe switch to a venv? ref https://nixos.wiki/wiki/Python
bc # for temperature and sound
jq # for memory, battery, sound, night mode, and temperature
upower # for battery
wlsunset # for night mode
];
services.upower.enable = true; # for battery
home-manager.users.talexander =
{ pkgs, ... }:
{

View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
#
# Read memory usage in Linux
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SLEEP_INTERVAL=${SLEEP_INTERVAL:-30}
while true; do
memory_usage=$(free --bytes --wide)
total_bytes=$(grep 'Mem:' <<<"$memory_usage" | awk '{print $2}')
free_bytes=$(grep 'Mem:' <<<"$memory_usage" | awk '{print $4}')
free_percent=$((100 * free_bytes / total_bytes))
text=""
if [ $free_bytes -ge $((1024 * 1024 * 1024)) ]; then
text="$((free_bytes / 1024 / 1024 / 1024)) GiB"
fi
tooltip="${free_percent}%"
jq --unbuffered --compact-output <<EOF
{
"text":"${text}",
"tooltip":"${tooltip}",
"percentage":${free_percent}
}
EOF
sleep $SLEEP_INTERVAL
done

View File

@ -0,0 +1,80 @@
#!/usr/bin/env bash
#
# Read battery status in Linux
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
batteries=$(upower --enumerate | grep BAT)
function generate_battery_update {
local battery_path=$1
local battery_status=$(upower --show-info "$battery_path")
local battery_state=$(grep 'state:' <<<"$battery_status" | awk '{print $2}')
local battery_percentage=$(grep 'percentage:' <<<"$battery_status" | awk '{print $2}')
battery_percentage=${battery_percentage%\%}
set +e
local time_remaining=$(grep 'time to empty:' <<<"$battery_status" | sed 's/\W*time to empty:\W*//g')
local tooltip="$time_remaining until empty"
if [ -z "$time_remaining" ]; then
time_remaining=$(grep 'time to full:' <<<"$battery_status" | sed 's/\W*time to full:\W*//g')
if [ -z "$time_remaining" ]; then
tooltip="fully charged"
else
tooltip="$time_remaining until full"
fi
fi
set -e
local battery_icon=""
local class=""
if [ $battery_state = "charging" ]; then
if [ $battery_percentage -eq 100 ]; then
battery_icon="⚡"
else
battery_icon="⚡"
fi
elif [ $battery_percentage -le 12 ]; then
battery_icon="⚠"
elif [ $battery_percentage -le 25 ]; then
battery_icon="▁"
elif [ $battery_percentage -le 37 ]; then
battery_icon="▂"
elif [ $battery_percentage -le 50 ]; then
battery_icon="▃"
elif [ $battery_percentage -le 62 ]; then
battery_icon="▄"
elif [ $battery_percentage -le 75 ]; then
battery_icon="▅"
elif [ $battery_percentage -le 87 ]; then
battery_icon="▆"
elif [ $battery_percentage -lt 100 ]; then
battery_icon="▇"
else
battery_icon="█"
fi
if [ $battery_percentage -le 15 ]; then
class="critical"
elif [ $battery_percentage -le 30 ]; then
class="warning"
fi
jq --unbuffered --compact-output <<EOF
{
"text":"${battery_percentage}% ${battery_icon}",
"tooltip":"$tooltip",
"percentage":${battery_percentage},
"class":"${class}"
}
EOF
}
while read bat; do
generate_battery_update "$bat"
done<<<"$batteries"
upower --monitor | grep --line-buffered 'device changed:' | while read l; do
while read bat; do
generate_battery_update "$bat"
done<<<"$batteries"
done

View File

@ -0,0 +1,107 @@
#!/usr/bin/env python
#
# Custom waybar module for clocks. Implemented because the official clock module was downloading timezone definitions on every boot.
import datetime
import json
import sys
import time
from dataclasses import dataclass
from functools import lru_cache
from typing import Final, List, Optional
from zoneinfo import ZoneInfo
LOCAL_TIMEZONE = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
@dataclass
class Update:
text: Optional[str]
alt: Optional[str]
tooltip: Optional[str]
css_class: Optional[List[str]]
percentage: Optional[str]
def dump(self) -> str:
# Dump a dict because we can't name our member variable "class"
return json.dumps(
{
k: v
for k, v in {
"text": self.text,
"alt": self.alt,
"tooltip": self.tooltip,
"class": self.css_class,
"percentage": self.percentage,
}.items()
if v is not None
}
)
@lru_cache(maxsize=1)
def make_calendar(today: datetime.date, tz: datetime.tzinfo):
width: Final[int] = 20
start_of_month = today.replace(day=1)
month_header = today.strftime("%B %Y")
padding = width - len(month_header)
right_padding = " " * (padding // 2)
left_padding = right_padding + (" " if padding % 2 == 1 else "")
header = f"{left_padding}{month_header}{right_padding}"
days_of_week = "Su Mo Tu We Th Fr Sa"
timezone_str = str(tz)
# Make the grid
first_day_padding = " " * (
0 if start_of_month.weekday() == 6 else (3 * (1 + start_of_month.weekday())) - 1
)
output = f"{header}\n{days_of_week}\n{first_day_padding}"
print_day = start_of_month
while print_day.month == today.month:
if print_day.weekday() == 6:
output += "\n"
else:
output += " "
if print_day == today:
output += '<span foreground="#323232" background="#CCCCCC">'
output += f"{print_day.day: >2}"
if print_day == today:
output += "</span>"
print_day += datetime.timedelta(days=1)
output += f'\n<span size="small">{timezone_str}</span>'
return output
def main():
tz: Optional[datetime.tzinfo] = (
ZoneInfo(sys.argv[1]) if len(sys.argv) >= 2 else LOCAL_TIMEZONE
)
if tz is None:
raise Exception("Failed to detect timezone.")
next_update = time.time()
while True:
time_before_next_update = next_update - time.time()
if time_before_next_update > 0:
time.sleep(time_before_next_update)
next_update = 60 * (1 + int(time.time()) // 60)
now = datetime.datetime.now(tz=tz)
text = now.strftime("%Y-%m-%d %H:%M:%S")
tooltip = make_calendar(now.date(), tz)
out = Update(
text=text,
alt="foo",
tooltip=f"<tt>{tooltip}</tt>",
css_class=["foo"],
percentage="100",
)
print(out.dump(), flush=True)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,93 @@
#!/usr/bin/env bash
#
# Power an icon for tinting the screen at night.
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SLEEP_INTERVAL=${SLEEP_INTERVAL:-30}
# ◓◒●◌◎
# 🔴🔵🟠🟡🟢🟣🟤
# 🟥🟦🟧🟨🟩🟪🟫
# ☀☯⭐🌝🌞⏾
# 🌑🌓🌗🌕
# 👓
############## Setup #########################
function cleanup {
log "Killing child process $wlsunset_pid"
kill "$wlsunset_pid"
true
}
for sig in EXIT; do
trap "set +e; cleanup" "$sig"
done
function die {
local status_code="$1"
shift
(>&2 echo "${@}")
exit "$status_code"
}
function log {
(>&2 echo "${@}")
}
############## Program #########################
function main {
local night_mode_icon night_mode_text night_mode_class
night_mode_mode="auto"
night_mode_class=""
wlsunset -S 07:00 -s 22:00 &
wlsunset_pid=$!
while true; do
if [ "$night_mode_mode" == "auto" ]; then
night_mode_icon="🌗"
night_mode_text="auto"
elif [ "$night_mode_mode" == "on" ]; then
night_mode_icon="🌑"
night_mode_text="night"
elif [ "$night_mode_mode" == "off" ]; then
night_mode_icon="🌕"
night_mode_text="day"
fi
render
sleep "$SLEEP_INTERVAL" &
wait $! || true
done
}
function render {
jq --unbuffered --compact-output <<EOF
{
"text":"${night_mode_icon}",
"tooltip":"${night_mode_text}",
"percentage":100,
"class":"${night_mode_class}"
}
EOF
}
function handle_click {
if [ "$night_mode_mode" == "auto" ]; then
log "Setting night mode to force-day."
night_mode_mode="off"
elif [ "$night_mode_mode" == "on" ]; then
log "Setting night mode to auto."
night_mode_mode="auto"
elif [ "$night_mode_mode" == "off" ]; then
log "Setting night mode to force-night."
night_mode_mode="on"
fi
kill -s SIGUSR1 "$wlsunset_pid"
}
trap "handle_click" SIGUSR1
main "${@}"

View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
#
# Read volume status in Linux
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SLEEP_INTERVAL=${SLEEP_INTERVAL:-30}
while true; do
current_vol_decimal=$(wpctl get-volume @DEFAULT_AUDIO_SINK@ | sed 's/Volume: //g')
if [[ "$current_vol_decimal" = *MUTED* ]]; then
current_vol_percent=0
else
current_vol_percent=$(bc -s <<<"$current_vol_decimal * 100")
current_vol_percent=${current_vol_percent%.*} # Remove decimal
fi
tooltip="<tt>$(wpctl status)</tt>"
tooltip=${tooltip//$'\n'/\\n}
tooltip=${tooltip//$'\t'/\\t}
class=""
icon=""
if [ $current_vol_percent -eq 0 ]; then
icon="🔇"
elif [ $current_vol_percent -le 50 ]; then
icon="🔉"
else
icon="🔊"
fi
jq --unbuffered --compact-output <<EOF
{
"text":"${current_vol_percent}% ${icon}",
"tooltip":"${tooltip}",
"percentage":${current_vol_percent},
"class":"${class}"
}
EOF
sleep $SLEEP_INTERVAL
done

View File

@ -0,0 +1,56 @@
#!/usr/bin/env bash
#
# Read temperature status in Linux
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SLEEP_INTERVAL=${SLEEP_INTERVAL:-30}
function print_all_x86_pkg_temps {
for thermal_zone in /sys/class/thermal/thermal_zone*; do
local thermal_zone_type=$(cat "$thermal_zone/type")
if [ "$thermal_zone_type" != "x86_pkg_temp" ] && [ "$thermal_zone_type" != "acpitz" ]; then
continue
fi
local thermal_zone_temperature=$(cat "$thermal_zone/temp")
echo "$thermal_zone_temperature"
done
}
while true; do
sum_temperature=0
num_temperature=0
max_temperature=0
while read temp; do
numeric_temp=$(bc -s <<<"$temp / 1000.0")
sum_temperature=$(bc -s <<<"$sum_temperature + $numeric_temp")
num_temperature=$((num_temperature + 1))
max_temperature=$(bc <<EOF
define max(a,b){
if(a>b)
{
return(a)
}else{
return(b)
}
}
max($max_temperature,$numeric_temp)
EOF
)
done<<<$(print_all_x86_pkg_temps)
avg_temperature=$(bc -s <<<"$sum_temperature / $num_temperature")
tooltip=""
class=""
icon="🌡"
jq --unbuffered --compact-output <<EOF
{
"text":"${max_temperature}${icon}",
"tooltip":"${tooltip//$'\n'/\\n}",
"percentage":50,
"class":"${class}"
}
EOF
sleep $SLEEP_INTERVAL
done