mirror of
https://github.com/anotherhadi/nixy.git
synced 2026-08-21 10:45:49 +02:00
@@ -3,3 +3,4 @@
|
||||
old/
|
||||
docs/superpowers/
|
||||
.superpowers/
|
||||
result/
|
||||
|
||||
@@ -17,6 +17,7 @@ in {
|
||||
"result"
|
||||
"result-*"
|
||||
".superpowers/"
|
||||
".claude/"
|
||||
"docs/superpowers/"
|
||||
];
|
||||
settings = {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# battery-monitor — low-battery alerts for waybar/swaync.
|
||||
# Sourced by the tests with BATTERY_MONITOR_TEST=1 (defines the functions, skips main).
|
||||
# Otherwise runs main once (triggered by the battery-monitor systemd timer).
|
||||
|
||||
POWER_SUPPLY_DIR="${POWER_SUPPLY_DIR:-/sys/class/power_supply}"
|
||||
STATE_FILE="${BATTERY_STATE_FILE:-/tmp/battery-alert-state}"
|
||||
CRIT_ID_FILE="${BATTERY_CRIT_ID_FILE:-/tmp/battery-crit-id}"
|
||||
|
||||
WARN_THRESHOLD=10
|
||||
CRIT_THRESHOLD=5
|
||||
|
||||
# Pure decision. Prints "<action>:<newstate>".
|
||||
# action ∈ noop | osd_warn | crit_show | crit_clear ; state ∈ none | warn10 | crit5
|
||||
battery_decide() {
|
||||
status=$1
|
||||
cap=$2
|
||||
prev=$3
|
||||
|
||||
if [ "$status" = "Discharging" ]; then
|
||||
if [ "$cap" -le "$CRIT_THRESHOLD" ]; then
|
||||
echo "crit_show:crit5"
|
||||
elif [ "$cap" -le "$WARN_THRESHOLD" ]; then
|
||||
if [ "$prev" = "crit5" ]; then
|
||||
echo "crit_clear:warn10"
|
||||
elif [ "$prev" = "warn10" ]; then
|
||||
echo "noop:warn10"
|
||||
else
|
||||
echo "osd_warn:warn10"
|
||||
fi
|
||||
else
|
||||
if [ "$prev" = "crit5" ]; then
|
||||
echo "crit_clear:none"
|
||||
else
|
||||
echo "noop:none"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ "$prev" = "crit5" ]; then
|
||||
echo "crit_clear:none"
|
||||
else
|
||||
echo "noop:none"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# First real battery (type == Battery, with status + capacity).
|
||||
resolve_battery() {
|
||||
for d in "$POWER_SUPPLY_DIR"/*; do
|
||||
[ -r "$d/type" ] || continue
|
||||
[ "$(cat "$d/type")" = "Battery" ] || continue
|
||||
[ -r "$d/status" ] && [ -r "$d/capacity" ] || continue
|
||||
printf '%s' "$d"
|
||||
return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
show_warn_osd() {
|
||||
waybar-osd " Low battery · $1%"
|
||||
}
|
||||
|
||||
show_crit_notification() {
|
||||
cap=$1
|
||||
id=$(cat "$CRIT_ID_FILE" 2>/dev/null || echo 0)
|
||||
newid=$(notify-send --print-id --replace-id="$id" \
|
||||
--urgency=critical --expire-time=0 \
|
||||
" Critical battery" "Battery at $cap% — plug in the charger")
|
||||
printf '%s' "$newid" > "$CRIT_ID_FILE"
|
||||
}
|
||||
|
||||
clear_crit_notification() {
|
||||
id=$(cat "$CRIT_ID_FILE" 2>/dev/null || echo 0)
|
||||
if [ "$id" != "0" ]; then
|
||||
gdbus call --session \
|
||||
--dest org.freedesktop.Notifications \
|
||||
--object-path /org/freedesktop/Notifications \
|
||||
--method org.freedesktop.Notifications.CloseNotification "$id" \
|
||||
>/dev/null 2>&1 || true
|
||||
fi
|
||||
rm -f "$CRIT_ID_FILE"
|
||||
}
|
||||
|
||||
main() {
|
||||
dir=$(resolve_battery) || exit 0
|
||||
|
||||
status=$(cat "$dir/status")
|
||||
cap=$(cat "$dir/capacity")
|
||||
prev=$(cat "$STATE_FILE" 2>/dev/null || echo none)
|
||||
|
||||
result=$(battery_decide "$status" "$cap" "$prev")
|
||||
action=${result%%:*}
|
||||
newstate=${result##*:}
|
||||
|
||||
case "$action" in
|
||||
osd_warn) show_warn_osd "$cap" ;;
|
||||
crit_show) show_crit_notification "$cap" ;;
|
||||
crit_clear) clear_crit_notification ;;
|
||||
noop) : ;;
|
||||
esac
|
||||
|
||||
printf '%s' "$newstate" > "$STATE_FILE"
|
||||
}
|
||||
|
||||
[ "${BATTERY_MONITOR_TEST:-}" = "1" ] || main "$@"
|
||||
@@ -20,7 +20,25 @@ in {
|
||||
iw
|
||||
hyprsunset
|
||||
]
|
||||
++ (with scripts; [vol-up vol-down vol-mute mic-mute bright-up bright-down nightshift-toggle focus-toggle waybar-toggle wifi-toggle bluetooth-toggle dnd-toggle output-cycle input-cycle color-pick screenshot-edit record-toggle power-cycle airplane-toggle clipboard-menu emoji-picker caffeine-toggle mic-status]);
|
||||
++ (with scripts; [waybar-osd battery-monitor vol-up vol-down vol-mute mic-mute bright-up bright-down nightshift-toggle focus-toggle waybar-toggle wifi-toggle bluetooth-toggle dnd-toggle output-cycle input-cycle color-pick screenshot-edit record-toggle power-cycle airplane-toggle clipboard-menu emoji-picker caffeine-toggle mic-status]);
|
||||
|
||||
# Poll battery level every 30s; battery-monitor no-ops when no battery exists.
|
||||
systemd.user.services.battery-monitor = {
|
||||
Unit.Description = "Low-battery OSD and critical notification monitor";
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${scripts.battery-monitor}/bin/battery-monitor";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.timers.battery-monitor = {
|
||||
Unit.Description = "Poll battery level for low-battery alerts";
|
||||
Timer = {
|
||||
OnBootSec = "1min";
|
||||
OnUnitActiveSec = "30s";
|
||||
};
|
||||
Install.WantedBy = ["timers.target"];
|
||||
};
|
||||
|
||||
wayland.windowManager.hyprland.settings.exec-once = [
|
||||
"waybar"
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
{pkgs}: let
|
||||
updateOsd = ''
|
||||
[ -f /tmp/waybar-osd-pid ] && kill "$(cat /tmp/waybar-osd-pid)" 2>/dev/null
|
||||
printf '%s' "$OSD_TEXT" > /tmp/waybar-osd
|
||||
pkill -RTMIN+8 waybar 2>/dev/null
|
||||
( sleep 2.5; rm -f /tmp/waybar-osd /tmp/waybar-osd-pid; pkill -RTMIN+8 waybar 2>/dev/null ) &
|
||||
# Canonical OSD popup: write the shared slot, signal waybar, expire after 2.5s.
|
||||
# Packaged (runtimeInputs) so it works outside a graphical session (systemd).
|
||||
waybar-osd = pkgs.writeShellApplication {
|
||||
name = "waybar-osd";
|
||||
runtimeInputs = with pkgs; [procps coreutils];
|
||||
text = ''
|
||||
if [ -f /tmp/waybar-osd-pid ]; then
|
||||
kill "$(cat /tmp/waybar-osd-pid)" 2>/dev/null || true
|
||||
fi
|
||||
printf '%s' "$1" > /tmp/waybar-osd
|
||||
pkill -RTMIN+8 waybar 2>/dev/null || true
|
||||
( sleep 2.5; rm -f /tmp/waybar-osd /tmp/waybar-osd-pid; pkill -RTMIN+8 waybar 2>/dev/null || true ) &
|
||||
printf '%s' "$!" > /tmp/waybar-osd-pid
|
||||
'';
|
||||
};
|
||||
|
||||
# battery-monitor: low-battery alerts, run by the systemd user timer.
|
||||
battery-monitor = pkgs.writeShellApplication {
|
||||
name = "battery-monitor";
|
||||
runtimeInputs = with pkgs; [waybar-osd libnotify glib coreutils];
|
||||
text = builtins.readFile ./battery-monitor.sh;
|
||||
};
|
||||
|
||||
updateOsd = ''
|
||||
${waybar-osd}/bin/waybar-osd "$OSD_TEXT"
|
||||
'';
|
||||
|
||||
volGetText = ''
|
||||
VOL_RAW=$(${pkgs.wireplumber}/bin/wpctl get-volume @DEFAULT_SINK@)
|
||||
@@ -46,10 +65,12 @@
|
||||
# launcher, unusable for a list, so override the geometry here.
|
||||
tofiMenu = "${pkgs.tofi}/bin/tofi --horizontal false --anchor center --width 700 --height 500 --margin-top 0 --margin-left 0 --margin-right 0 --num-results 10";
|
||||
in {
|
||||
inherit waybar-osd battery-monitor;
|
||||
|
||||
bluetoothScript = pkgs.writeShellScript "waybar-bluetooth" ''
|
||||
jq=${pkgs.jq}/bin/jq
|
||||
nl=$'\n'
|
||||
# bluetoothctl bloque sans contrôleur par défaut → toujours borné par timeout.
|
||||
# bluetoothctl hangs without a controller by default → always bounded by timeout.
|
||||
bt() { timeout 3 ${pkgs.bluez}/bin/bluetoothctl "$@" 2>/dev/null; }
|
||||
|
||||
powered=$(bt show | awk '/Powered:/ { print $2; exit }')
|
||||
@@ -331,7 +352,7 @@ in {
|
||||
|
||||
record-toggle = pkgs.writeShellScriptBin "record-toggle" ''
|
||||
if pgrep -x wf-recorder >/dev/null; then
|
||||
# -INT laisse wf-recorder finaliser le fichier proprement.
|
||||
# -INT lets wf-recorder finalize the file cleanly.
|
||||
pkill -INT -x wf-recorder
|
||||
OSD_TEXT=" Recording saved"
|
||||
else
|
||||
@@ -385,7 +406,7 @@ in {
|
||||
emoji-picker = pkgs.writeShellScriptBin "emoji-picker" ''
|
||||
export PATH="${pkgs.wtype}/bin:${pkgs.wl-clipboard}/bin:$PATH"
|
||||
export BEMOJI_PICKER_CMD="${tofiMenu} --prompt-text 'emoji: '"
|
||||
# -t tape l'emoji (wtype), -c le copie aussi.
|
||||
# -t types the emoji (wtype), -c also copies it.
|
||||
exec ${pkgs.bemoji}/bin/bemoji -t -c
|
||||
'';
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
}: let
|
||||
gaps-out = config.theme.gaps-out;
|
||||
c = config.lib.stylix.colors;
|
||||
hasBattery = config.var.hasBattery or false;
|
||||
in {
|
||||
programs.waybar.settings = [
|
||||
{
|
||||
@@ -16,9 +15,7 @@ in {
|
||||
position = "top";
|
||||
height = config.theme.bar-height;
|
||||
margin = "${toString gaps-out} ${toString gaps-out} 0";
|
||||
modules-center =
|
||||
["custom/osd" "custom/osd-sep" "clock" "tray" "hyprland/workspaces" "custom/network" "custom/bluetooth"]
|
||||
++ lib.optional hasBattery "battery";
|
||||
modules-center = ["custom/osd" "custom/osd-sep" "clock" "tray" "hyprland/workspaces" "custom/network" "custom/bluetooth" "battery"];
|
||||
|
||||
# ── Modules ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -34,7 +31,6 @@ in {
|
||||
};
|
||||
|
||||
battery = {
|
||||
bat = "BAT1";
|
||||
interval = 20;
|
||||
full-at = 100;
|
||||
tooltip = true;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# Unit tests for battery_decide. Run: bash home/system/waybar/tests/battery-decide.test.sh
|
||||
set -u
|
||||
|
||||
DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BATTERY_MONITOR_TEST=1 . "$DIR/battery-monitor.sh"
|
||||
|
||||
fail=0
|
||||
check() {
|
||||
desc=$1 status=$2 cap=$3 prev=$4 expected=$5
|
||||
got=$(battery_decide "$status" "$cap" "$prev")
|
||||
if [ "$got" = "$expected" ]; then
|
||||
printf 'ok - %s\n' "$desc"
|
||||
else
|
||||
printf 'FAIL - %s: expected %s, got %s\n' "$desc" "$expected" "$got"
|
||||
fail=1
|
||||
fi
|
||||
}
|
||||
|
||||
check "discharging 4% fresh -> crit" Discharging 4 none crit_show:crit5
|
||||
check "discharging 4% already crit -> refresh" Discharging 4 crit5 crit_show:crit5
|
||||
check "discharging 5% boundary -> crit" Discharging 5 none crit_show:crit5
|
||||
check "discharging 8% fresh -> warn osd" Discharging 8 none osd_warn:warn10
|
||||
check "discharging 8% already warned -> noop" Discharging 8 warn10 noop:warn10
|
||||
check "discharging 8% recovering from crit -> clear+warn" Discharging 8 crit5 crit_clear:warn10
|
||||
check "discharging 10% boundary -> warn" Discharging 10 none osd_warn:warn10
|
||||
check "discharging 11% -> noop none" Discharging 11 none noop:none
|
||||
check "discharging 12% rearm from warn -> none" Discharging 12 warn10 noop:none
|
||||
check "discharging 40% recover from crit -> clear" Discharging 40 crit5 crit_clear:none
|
||||
check "charging 4% from crit -> clear" Charging 4 crit5 crit_clear:none
|
||||
check "charging 8% from warn -> none" Charging 8 warn10 noop:none
|
||||
check "full 100% -> noop none" Full 100 none noop:none
|
||||
|
||||
exit $fail
|
||||
@@ -26,8 +26,6 @@
|
||||
|
||||
autoUpgrade = false;
|
||||
autoGarbageCollector = true;
|
||||
|
||||
hasBattery = true;
|
||||
};
|
||||
|
||||
# DON'T TOUCH THIS
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
|
||||
autoUpgrade = false;
|
||||
autoGarbageCollector = true;
|
||||
|
||||
hasBattery = false;
|
||||
};
|
||||
|
||||
# DON'T TOUCH THIS
|
||||
|
||||
Reference in New Issue
Block a user