From 579a9484491af1930054b0877706a59e135e4211 Mon Sep 17 00:00:00 2001 From: nevaforget Date: Wed, 8 Apr 2026 09:55:46 +0200 Subject: [PATCH] feat: add battery conservation mode with Waybar toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Laptops with charge_control_end_threshold support get a click-to-toggle on the battery module (80% ↔ 100%). A ♥ icon appears when conservation is active, hidden when inactive. State persists across reboots via systemd oneshot service. udev rule grants wheel group write access so no sudo is needed for toggling. --- CLAUDE.md | 11 +++++- DECISIONS.md | 6 ++++ README.md | 4 +++ defaults/bin/moonarch-batsaver-toggle | 28 +++++++++++++++ defaults/bin/moonarch-waybar-batsaver | 19 +++++++++++ .../systemd/system/moonarch-batsaver.service | 15 ++++++++ .../udev/rules.d/90-moonarch-battery.rules | 4 +++ defaults/xdg/waybar/config | 34 +++++++++++++++---- 8 files changed, 113 insertions(+), 8 deletions(-) create mode 100755 defaults/bin/moonarch-batsaver-toggle create mode 100755 defaults/bin/moonarch-waybar-batsaver create mode 100644 defaults/etc/systemd/system/moonarch-batsaver.service create mode 100644 defaults/etc/udev/rules.d/90-moonarch-battery.rules diff --git a/CLAUDE.md b/CLAUDE.md index d1dac06..621f972 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,16 @@ Reproduzierbares Arch-Linux-Setup basierend auf archinstall + Post-Install-Autom - `config/` — archinstall-Konfiguration (inkl. custom-commands die das Repo nach /opt/moonarch klonen, root-owned) - `scripts/` — Post-Install- und Helper-Scripts - `packages/` — Paketlisten (offiziell + AUR), getrennt gepflegt -- `defaults/` — XDG-Configs, Shell-Config, Helper-Binaries, systemd User-Services, greetd/moongreet-Config, Wallpaper +- `defaults/` — XDG-Configs, Shell-Config, Helper-Binaries, systemd Services, udev-Regeln, greetd/moongreet-Config, Wallpaper + +## Battery Conservation Mode + +Laptops mit `charge_control_end_threshold`-Support (ThinkPad, Framework, etc.) erhalten einen Waybar-Toggle: +- Klick auf das Battery-Modul schaltet zwischen 80% und 100% Ladegrenze um +- Bei aktiver Conservation erscheint ein ♥-Icon neben der Battery-Anzeige +- Zustand wird in `/var/lib/moonarch/batsaver-threshold` persistiert und beim Boot via systemd-Service wiederhergestellt +- udev-Regel gibt Gruppe `wheel` Schreibzugriff auf den Threshold (kein sudo nötig) +- Auf Desktops ohne Battery-Support versteckt sich das Feature komplett ## Konventionen diff --git a/DECISIONS.md b/DECISIONS.md index 79f1602..386b456 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1,5 +1,11 @@ # Decisions +## 2026-04-08 – Battery conservation mode: udev + sysfs + Waybar toggle +- **Who**: Dominik, ClaudeCode +- **Why**: Laptops with `charge_control_end_threshold` support benefit from limiting charge to 80% to extend battery lifespan. Needed a user-friendly toggle without requiring sudo. +- **Tradeoffs**: udev RUN approach for permissions (group wheel gets write access) vs polkit/pkexec (password prompt on every toggle). Chose udev for seamless UX. State persisted in `/var/lib/moonarch/` (system-wide, not per-user) — acceptable since charge threshold is a hardware setting, not a user preference. Fixed 80% threshold instead of configurable — KISS, matches industry standard. +- **How**: udev rule grants wheel group write on `charge_control_end_threshold`. Toggle script writes sysfs + state file. systemd oneshot service restores on boot. Waybar shows ♥ icon when active, hidden when inactive. Click on battery module toggles. + ## 2026-04-07 – Walker theme: gtk-inherit → moonarch with fixed colors - **Who**: Dominik, ClaudeCode - **Why**: gtk-inherit theme relied on GTK4 color inheritance which works but doesn't update live when switching GTK themes (Walker service needs restart). Explicit color definitions make the theme self-contained and predictable. diff --git a/README.md b/README.md index 54cf740..f1c7460 100644 --- a/README.md +++ b/README.md @@ -131,11 +131,15 @@ defaults/ moonarch-waybar-cpugov Waybar module: CPU governor status moonarch-waybar-gpustat Waybar module: GPU utilization moonarch-waybar-hidpp Waybar module: Logitech HID++ device battery + moonarch-waybar-batsaver Waybar module: battery conservation mode indicator + moonarch-batsaver-toggle Toggle battery charge limit (80% ↔ 100%) shell/zshrc Zsh config: prompt, aliases, FZF, completion etc/greetd/ greetd daemon + greeter Niri config etc/moongreet/ moongreet configuration etc/systemd/user/ Systemd user services (cliphist, kanshi, walker, nautilus) + etc/systemd/system/ System service (battery conservation restore on boot) + etc/udev/rules.d/ udev rules (battery threshold permissions) backgrounds/wallpaper.jpg Default wallpaper (shared by desktop, greeter, lock screen) ``` diff --git a/defaults/bin/moonarch-batsaver-toggle b/defaults/bin/moonarch-batsaver-toggle new file mode 100755 index 0000000..beb483d --- /dev/null +++ b/defaults/bin/moonarch-batsaver-toggle @@ -0,0 +1,28 @@ +#!/usr/bin/bash +# ABOUTME: Toggles battery conservation mode between 80% and 100% charge limit. +# ABOUTME: Writes to sysfs (immediate) and state file (persistence across reboots). + +THRESHOLD_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold" +STATE_DIR="/var/lib/moonarch" +STATE_FILE="${STATE_DIR}/batsaver-threshold" +CONSERVATION_LIMIT=80 + +[[ -f "$THRESHOLD_FILE" ]] || exit 1 + +CURRENT=$(cat "$THRESHOLD_FILE") + +if [[ "$CURRENT" -le "$CONSERVATION_LIMIT" ]]; then + NEW=100 +else + NEW="$CONSERVATION_LIMIT" +fi + +# Apply immediately +echo "$NEW" > "$THRESHOLD_FILE" || exit 1 + +# Persist for next boot +mkdir -p "$STATE_DIR" +echo "$NEW" > "$STATE_FILE" + +# Signal Waybar to refresh the batsaver module (SIGRTMIN+9) +pkill -RTMIN+9 waybar diff --git a/defaults/bin/moonarch-waybar-batsaver b/defaults/bin/moonarch-waybar-batsaver new file mode 100755 index 0000000..6d50a26 --- /dev/null +++ b/defaults/bin/moonarch-waybar-batsaver @@ -0,0 +1,19 @@ +#!/usr/bin/bash +# ABOUTME: Waybar module showing battery conservation mode status. +# ABOUTME: Outputs heart icon when active (threshold ≤80), empty when inactive. + +THRESHOLD_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold" + +# No battery threshold support → no output → Waybar hides module +[[ -f "$THRESHOLD_FILE" ]] || exit 0 + +THRESHOLD=$(cat "$THRESHOLD_FILE") + +if [[ "$THRESHOLD" -le 80 ]]; then + jq --compact-output -n \ + --arg text "♥" \ + --arg tooltip "Battery Conservation: ON (limit ${THRESHOLD}%)" \ + --arg class "on" \ + '{text: $text, tooltip: $tooltip, class: $class}' +fi +# Threshold > 80 → no output → Waybar hides module diff --git a/defaults/etc/systemd/system/moonarch-batsaver.service b/defaults/etc/systemd/system/moonarch-batsaver.service new file mode 100644 index 0000000..8f1a2b7 --- /dev/null +++ b/defaults/etc/systemd/system/moonarch-batsaver.service @@ -0,0 +1,15 @@ +# ABOUTME: Restores battery charge threshold from saved state on boot. +# ABOUTME: Only runs on laptops with threshold support and a saved state file. + +[Unit] +Description=Restore battery conservation mode threshold +After=sysinit.target +ConditionPathExists=/sys/class/power_supply/BAT0/charge_control_end_threshold +ConditionPathExists=/var/lib/moonarch/batsaver-threshold + +[Service] +Type=oneshot +ExecStart=/bin/sh -c 'cat /var/lib/moonarch/batsaver-threshold > /sys/class/power_supply/BAT0/charge_control_end_threshold' + +[Install] +WantedBy=multi-user.target diff --git a/defaults/etc/udev/rules.d/90-moonarch-battery.rules b/defaults/etc/udev/rules.d/90-moonarch-battery.rules new file mode 100644 index 0000000..1baa703 --- /dev/null +++ b/defaults/etc/udev/rules.d/90-moonarch-battery.rules @@ -0,0 +1,4 @@ +# ABOUTME: udev rule granting wheel group write access to battery charge threshold. +# ABOUTME: Enables unprivileged toggling of conservation mode via moonarch-batsaver-toggle. + +SUBSYSTEM=="power_supply", ATTR{type}=="Battery", RUN+="/bin/sh -c 'chgrp wheel /sys%p/charge_control_end_threshold 2>/dev/null; chmod g+w /sys%p/charge_control_end_threshold 2>/dev/null'" diff --git a/defaults/xdg/waybar/config b/defaults/xdg/waybar/config index e07475f..952ad75 100644 --- a/defaults/xdg/waybar/config +++ b/defaults/xdg/waybar/config @@ -21,7 +21,7 @@ "bluetooth", "group/sound", "backlight", - "battery", + "group/battery", "group/indicators" ], "group/indicators": { @@ -58,6 +58,13 @@ "transition-left-to-right": true } }, + "group/battery": { + "orientation": "inherit", + "modules": [ + "battery", + "custom/batsaver" + ] + }, "group/sys": { "orientation": "inherit", "modules": [ @@ -306,13 +313,26 @@ }, "format": "{capacity}% {icon}", "format-icons": [ - "", - "", - "", - "", - "" + "󰂎", + "󰁺", + "󰁻", + "󰁼", + "󰁽", + "󰁾", + "󰁿", + "󰂀", + "󰂁", + "󰂂", + "󰁹" ], - "max-length": 25 + "max-length": 25, + "on-click": "moonarch-batsaver-toggle" + }, + "custom/batsaver": { + "exec": "moonarch-waybar-batsaver", + "return-type": "json", + "interval": 30, + "signal": 9 }, "bluetooth": { // "controller": "controller1", // specify the alias of the controller if there are more than 1 on the system