fix(power): #60 name-agnostic system-battery scan
All checks were successful
Check / eval (push) Successful in 2m53s
All checks were successful
Check / eval (push) Successful in 2m53s
Replace BAT* globs with type=Battery + !scope=Device (same filter as nomarchy-battery-notify) in charge-limit oneshot, menu gates, Waybar power-profile self-gate, doctor, and install chassis detection. CMB0- style names get charge-cap and laptop modules consistently. V3 pending: confirm on non-BAT* hardware (HARDWARE-QUEUE). Close #60. Verified: V1 (option-docs, hardware-db-modules, battery-charge-limit VM).
This commit is contained in:
@@ -95,15 +95,6 @@ the Wi‑Fi card.” Does **not** rewrite the flake unless a later `--apply`
|
|||||||
is explicitly designed. Cost: extract script + package + man/README
|
is explicitly designed. Cost: extract script + package + man/README
|
||||||
pointer; V0–V1.
|
pointer; V0–V1.
|
||||||
|
|
||||||
### 60. Battery / powerprofile: name-agnostic battery scan
|
|
||||||
`power.nix` oneshot and menu/waybar globs use
|
|
||||||
`/sys/class/power_supply/BAT*`. `nomarchy-battery-notify` correctly
|
|
||||||
treats any system battery (`type=Battery`, not `scope=Device`). Machines
|
|
||||||
with `CMB0` (and similar) get notify but not charge-cap / inconsistent
|
|
||||||
gating. Fix: name-agnostic scan aligned with battery-notify. Cost:
|
|
||||||
small multi-file; `[blocked:hw]` only to confirm on non-BAT* hardware —
|
|
||||||
logic still ships at V1.
|
|
||||||
|
|
||||||
### 63. Generated Waybar: identity-bar affordances (powermenu + logo)
|
### 63. Generated Waybar: identity-bar affordances (powermenu + logo)
|
||||||
Whole-swap bars ship `custom/powermenu` and a left logo → main menu; the
|
Whole-swap bars ship `custom/powermenu` and a left logo → main menu; the
|
||||||
generated `waybar.nix` bar has neither. Optional parity add for
|
generated `waybar.nix` bar has neither. Optional parity add for
|
||||||
@@ -151,7 +142,7 @@ Still PROPOSED for later triage: first-boot tips (partially shipped via
|
|||||||
Custom…) writing `settings.power.batteryChargeLimit` — but that's a
|
Custom…) writing `settings.power.batteryChargeLimit` — but that's a
|
||||||
baked NixOS option, so it only lands on the next `sys-rebuild`. To make
|
baked NixOS option, so it only lands on the next `sys-rebuild`. To make
|
||||||
it instant (no rebuild): add a udev rule making
|
it instant (no rebuild): add a udev rule making
|
||||||
`/sys/class/power_supply/BAT*/charge_control_end_threshold`
|
system-battery `charge_control_end_threshold` nodes
|
||||||
group-writable (`MODE`/`GROUP`) so the menu can `echo N >` it live
|
group-writable (`MODE`/`GROUP`) so the menu can `echo N >` it live
|
||||||
alongside persisting state (the sysfs oneshot in power.nix still owns
|
alongside persisting state (the sysfs oneshot in power.nix still owns
|
||||||
boot + AC-replug re-apply), and/or surface a rofi-menu quick row or a
|
boot + AC-replug re-apply), and/or surface a rofi-menu quick row or a
|
||||||
@@ -181,7 +172,8 @@ Still PROPOSED for later triage: first-boot tips (partially shipped via
|
|||||||
_(Waybar doctor indicator, Control Center appearance no-ops, summer-day
|
_(Waybar doctor indicator, Control Center appearance no-ops, summer-day
|
||||||
scroll → #51; neon-glass broken waybar → **fixed** 2026-07-09 by quarantine
|
scroll → #51; neon-glass broken waybar → **fixed** 2026-07-09 by quarantine
|
||||||
(removed `waybar.css` → generated bar; render-confirmed).)_
|
(removed `waybar.css` → generated bar; render-confirmed).)_
|
||||||
_(Battery charge-limit / powerprofile BAT*-only → **#60**.)_
|
_(Battery charge-limit / powerprofile BAT*-only → **#60** ✓ 2026-07-10;
|
||||||
|
V3 pending on non-BAT* hardware.)_
|
||||||
|
|
||||||
### Docs / option-surface drift (codebase exam 2026-07-09)
|
### Docs / option-surface drift (codebase exam 2026-07-09)
|
||||||
|
|
||||||
@@ -456,7 +448,7 @@ NVIDIA depth) is still mostly CLI. Items below are product work on top
|
|||||||
of that doc — design notes live there (§4–§10).*
|
of that doc — design notes live there (§4–§10).*
|
||||||
|
|
||||||
_(`nomarchy-detect-hw` CLI → **#58**; Fingerprint menu → **#55**;
|
_(`nomarchy-detect-hw` CLI → **#58**; Fingerprint menu → **#55**;
|
||||||
Installer NVIDIA commented guidance → **#59**.)_
|
Installer NVIDIA commented guidance → **#59** ✓ 2026-07-10.)_
|
||||||
|
|
||||||
_(hardware-db ∈ nixos-hardware → #49; usbutils on install PATH → #50;
|
_(hardware-db ∈ nixos-hardware → #49; usbutils on install PATH → #50;
|
||||||
i2c README table → #48.)_
|
i2c README table → #48.)_
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ the failure as a NOW bug in BACKLOG.md. Machines: the **AMD dev box**
|
|||||||
QA machine), the **T14s** (webcam case).
|
QA machine), the **T14s** (webcam case).
|
||||||
|
|
||||||
## Any machine (dev box is fine)
|
## Any machine (dev box is fine)
|
||||||
|
- [ ] **#60 non-BAT* battery name (if available)** — on a machine whose
|
||||||
|
system battery is **not** named `BAT*` (e.g. `CMB0`): confirm
|
||||||
|
charge-limit oneshot writes the threshold, System › Battery limit
|
||||||
|
and Power profile rows appear, Waybar power-profile module shows,
|
||||||
|
doctor charge-limit section runs. On `BAT0`-only machines this is
|
||||||
|
a no-op (already covered by existing charge-limit re-apply item).
|
||||||
- [ ] **btop theme fidelity (#52 residual)** — softGL theme-shot cannot
|
- [ ] **btop theme fidelity (#52 residual)** — softGL theme-shot cannot
|
||||||
open Ghostty, so the hand `btop.theme` assets were only guest-file
|
open Ghostty, so the hand `btop.theme` assets were only guest-file
|
||||||
asserted (main_bg / inactive_fg keys) + desktop/bar rendered for
|
asserted (main_bg / inactive_fg keys) + desktop/bar rendered for
|
||||||
|
|||||||
@@ -17,6 +17,19 @@ Template:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-10 — #60 name-agnostic system-battery scan
|
||||||
|
- **Task:** NEXT #60 — align charge-limit / menu / waybar / doctor /
|
||||||
|
installer chassis with battery-notify’s type=Battery + !scope=Device
|
||||||
|
filter (BAT* globs miss CMB0).
|
||||||
|
- **Did:** power.nix oneshot, rofi helpers (`has_system_battery` /
|
||||||
|
`has_charge_threshold`), waybar powerprofile gate, doctor loop,
|
||||||
|
hardware-db chassis, options.nix description. HARDWARE-QUEUE entry
|
||||||
|
for non-BAT* confirm. Closed #60.
|
||||||
|
- **Verified:** **V1** — `checks.option-docs`, `hardware-db-modules`,
|
||||||
|
`battery-charge-limit` (VM) green; no remaining `BAT*` globs in code
|
||||||
|
paths. **V3 pending:** non-BAT* name on real hardware (queue entry).
|
||||||
|
- **Next suggestion:** #63 generated Waybar powermenu + logo.
|
||||||
|
|
||||||
## 2026-07-10 — #59 NVIDIA commented system.nix guidance
|
## 2026-07-10 — #59 NVIDIA commented system.nix guidance
|
||||||
- **Task:** NEXT #59 — when `common-gpu-nvidia` is selected, emit
|
- **Task:** NEXT #59 — when `common-gpu-nvidia` is selected, emit
|
||||||
commented PRIME/power/open-module guidance in installer-generated
|
commented PRIME/power/open-module guidance in installer-generated
|
||||||
|
|||||||
@@ -174,6 +174,28 @@ let
|
|||||||
# icon set (Papirus) via rofi's per-row icon protocol — see row() below.
|
# icon set (Papirus) via rofi's per-row icon protocol — see row() below.
|
||||||
# `nomarchy-menu` with no argument shows the module picker.
|
# `nomarchy-menu` with no argument shows the module picker.
|
||||||
|
|
||||||
|
# System batteries: type=Battery, not scope=Device — same filter as
|
||||||
|
# nomarchy-battery-notify. Name-agnostic (BAT0, CMB0, test_battery)
|
||||||
|
# so charge-limit / power-profile gates match notify (BACKLOG #60).
|
||||||
|
has_system_battery() {
|
||||||
|
local d
|
||||||
|
for d in /sys/class/power_supply/*/; do
|
||||||
|
[ "$(cat "$d/type" 2>/dev/null)" = Battery ] || continue
|
||||||
|
[ "$(cat "$d/scope" 2>/dev/null || echo System)" = Device ] && continue
|
||||||
|
return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
has_charge_threshold() {
|
||||||
|
local d
|
||||||
|
for d in /sys/class/power_supply/*/; do
|
||||||
|
[ "$(cat "$d/type" 2>/dev/null)" = Battery ] || continue
|
||||||
|
[ "$(cat "$d/scope" 2>/dev/null || echo System)" = Device ] && continue
|
||||||
|
[ -e "$d/charge_control_end_threshold" ] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
urlencode() {
|
urlencode() {
|
||||||
local s="$1" out="" ch i
|
local s="$1" out="" ch i
|
||||||
for ((i = 0; i < ''${#s}; i++)); do
|
for ((i = 0; i < ''${#s}; i++)); do
|
||||||
@@ -264,9 +286,8 @@ let
|
|||||||
# rows drive powerprofilesctl like the power-profile picker, charge
|
# rows drive powerprofilesctl like the power-profile picker, charge
|
||||||
# rows drive the same theme-sync writer as batterylimit. Each half
|
# rows drive the same theme-sync writer as batterylimit. Each half
|
||||||
# self-gates on its hardware, so a desktop with neither just toasts.
|
# self-gates on its hardware, so a desktop with neither just toasts.
|
||||||
bats=(/sys/class/power_supply/BAT*)
|
|
||||||
haveppd=false; command -v powerprofilesctl >/dev/null 2>&1 && haveppd=true
|
haveppd=false; command -v powerprofilesctl >/dev/null 2>&1 && haveppd=true
|
||||||
havecharge=false; [ -e "''${bats[0]}/charge_control_end_threshold" ] && havecharge=true
|
havecharge=false; has_charge_threshold && havecharge=true
|
||||||
if ! $haveppd && ! $havecharge; then
|
if ! $haveppd && ! $havecharge; then
|
||||||
notify-send "Power" "No power-profile or charge-limit control on this machine."
|
notify-send "Power" "No power-profile or charge-limit control on this machine."
|
||||||
exit 0
|
exit 0
|
||||||
@@ -755,7 +776,6 @@ ${themeRows}
|
|||||||
# System submenu — connectivity and machine state. Snapshots and the
|
# System submenu — connectivity and machine state. Snapshots and the
|
||||||
# power-profile picker self-gate (BTRFS+snapper / laptop on ppd), so
|
# power-profile picker self-gate (BTRFS+snapper / laptop on ppd), so
|
||||||
# the submenu shrinks to match the hardware, like the Waybar modules.
|
# the submenu shrinks to match the hardware, like the Waybar modules.
|
||||||
bats=(/sys/class/power_supply/BAT*)
|
|
||||||
choice=$( {
|
choice=$( {
|
||||||
row "Network${menuHint "network"}" network-wireless
|
row "Network${menuHint "network"}" network-wireless
|
||||||
row "VPN" network-vpn
|
row "VPN" network-vpn
|
||||||
@@ -783,13 +803,12 @@ ${themeRows}
|
|||||||
# on VMs/headless via services.fwupd.enable=false), like Printers.
|
# on VMs/headless via services.fwupd.enable=false), like Printers.
|
||||||
command -v fwupdmgr >/dev/null 2>&1 && row "Firmware" firmware-manager
|
command -v fwupdmgr >/dev/null 2>&1 && row "Firmware" firmware-manager
|
||||||
command -v nomarchy-control-center >/dev/null 2>&1 && row "Control Center" preferences-desktop
|
command -v nomarchy-control-center >/dev/null 2>&1 && row "Control Center" preferences-desktop
|
||||||
if [ -e "''${bats[0]}" ] && command -v powerprofilesctl >/dev/null 2>&1; then
|
if has_system_battery && command -v powerprofilesctl >/dev/null 2>&1; then
|
||||||
row "Power profile" preferences-system-power
|
row "Power profile" preferences-system-power
|
||||||
fi
|
fi
|
||||||
# Self-gated on the charge-threshold sysfs node (laptops that
|
# Self-gated on the charge-threshold sysfs node (laptops that
|
||||||
# expose it), like Power profile above.
|
# expose it), like Power profile above.
|
||||||
[ -e "''${bats[0]}/charge_control_end_threshold" ] \
|
has_charge_threshold && row "Battery limit" battery-080
|
||||||
&& row "Battery limit" battery-080
|
|
||||||
back
|
back
|
||||||
} | rofi -dmenu -show-icons -markup-rows -p System) || exit 0
|
} | rofi -dmenu -show-icons -markup-rows -p System) || exit 0
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
@@ -820,8 +839,7 @@ ${themeRows}
|
|||||||
# it lands on the next sys-rebuild — the sysfs oneshot in power.nix
|
# it lands on the next sys-rebuild — the sysfs oneshot in power.nix
|
||||||
# owns applying it (boot + AC-replug). Self-gated in System; guard
|
# owns applying it (boot + AC-replug). Self-gated in System; guard
|
||||||
# here too in case it's invoked directly.
|
# here too in case it's invoked directly.
|
||||||
bats=(/sys/class/power_supply/BAT*)
|
has_charge_threshold \
|
||||||
[ -e "''${bats[0]}/charge_control_end_threshold" ] \
|
|
||||||
|| { notify-send "Battery limit" "No charge-threshold control on this machine."; exit 0; }
|
|| { notify-send "Battery limit" "No charge-threshold control on this machine."; exit 0; }
|
||||||
cur=$(nomarchy-theme-sync get settings.power.batteryChargeLimit 2>/dev/null)
|
cur=$(nomarchy-theme-sync get settings.power.batteryChargeLimit 2>/dev/null)
|
||||||
sel=$( {
|
sel=$( {
|
||||||
|
|||||||
@@ -60,7 +60,14 @@ let
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
powerProfileStatus = pkgs.writeShellScriptBin "nomarchy-powerprofile-status" ''
|
powerProfileStatus = pkgs.writeShellScriptBin "nomarchy-powerprofile-status" ''
|
||||||
case "$(ls /sys/class/power_supply/ 2>/dev/null)" in *BAT*) ;; *) exit 0 ;; esac
|
# Name-agnostic system battery (BAT0, CMB0, …) — BACKLOG #60.
|
||||||
|
has_bat=
|
||||||
|
for d in /sys/class/power_supply/*/; do
|
||||||
|
[ "$(cat "$d/type" 2>/dev/null)" = Battery ] || continue
|
||||||
|
[ "$(cat "$d/scope" 2>/dev/null || echo System)" = Device ] && continue
|
||||||
|
has_bat=1; break
|
||||||
|
done
|
||||||
|
[ -n "$has_bat" ] || exit 0
|
||||||
command -v powerprofilesctl >/dev/null 2>&1 || exit 0
|
command -v powerprofilesctl >/dev/null 2>&1 || exit 0
|
||||||
prof=$(powerprofilesctl get 2>/dev/null) || exit 0
|
prof=$(powerprofilesctl get 2>/dev/null) || exit 0
|
||||||
case "$prof" in
|
case "$prof" in
|
||||||
|
|||||||
@@ -106,8 +106,9 @@
|
|||||||
example = 80;
|
example = 80;
|
||||||
description = ''
|
description = ''
|
||||||
Stop charging at this percentage to extend battery lifespan,
|
Stop charging at this percentage to extend battery lifespan,
|
||||||
where the hardware exposes a charge threshold
|
where the hardware exposes charge_control_end_threshold on a
|
||||||
(/sys/class/power_supply/BAT*/charge_control_end_threshold).
|
system battery (type=Battery under /sys/class/power_supply;
|
||||||
|
name-agnostic — BAT0, CMB0, …).
|
||||||
null leaves charging at the firmware default. Backend-independent
|
null leaves charging at the firmware default. Backend-independent
|
||||||
(a small systemd unit writes the sysfs knob, re-applied on AC
|
(a small systemd unit writes the sysfs knob, re-applied on AC
|
||||||
state changes), so it works under PPD too; needs
|
state changes), so it works under PPD too; needs
|
||||||
|
|||||||
@@ -50,7 +50,12 @@ in
|
|||||||
RemainAfterExit = true;
|
RemainAfterExit = true;
|
||||||
};
|
};
|
||||||
script = ''
|
script = ''
|
||||||
for thresh in /sys/class/power_supply/BAT*/charge_control_end_threshold; do
|
# Name-agnostic (BAT0, CMB0, …): same type/scope filter as
|
||||||
|
# nomarchy-battery-notify — BACKLOG #60.
|
||||||
|
for d in /sys/class/power_supply/*/; do
|
||||||
|
[ "$(cat "$d/type" 2>/dev/null)" = Battery ] || continue
|
||||||
|
[ "$(cat "$d/scope" 2>/dev/null || echo System)" = Device ] && continue
|
||||||
|
thresh="$d/charge_control_end_threshold"
|
||||||
[ -w "$thresh" ] && echo ${toString cfg.batteryChargeLimit} > "$thresh"
|
[ -w "$thresh" ] && echo ${toString cfg.batteryChargeLimit} > "$thresh"
|
||||||
done
|
done
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -232,8 +232,11 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── laptop battery charge threshold (only when a limit is set) ───────
|
# ── laptop battery charge threshold (only when a limit is set) ───────
|
||||||
|
# Name-agnostic (BAT0, CMB0, …) — same type/scope filter as notify (#60).
|
||||||
bat_seen=0; bat_limited=0
|
bat_seen=0; bat_limited=0
|
||||||
for bat in /sys/class/power_supply/BAT*; do
|
for bat in /sys/class/power_supply/*/; do
|
||||||
|
[ "$(cat "$bat/type" 2>/dev/null)" = Battery ] || continue
|
||||||
|
[ "$(cat "$bat/scope" 2>/dev/null || echo System)" = Device ] && continue
|
||||||
f="$bat/charge_control_end_threshold"
|
f="$bat/charge_control_end_threshold"
|
||||||
[ -r "$f" ] || continue
|
[ -r "$f" ] || continue
|
||||||
bat_seen=1
|
bat_seen=1
|
||||||
|
|||||||
@@ -227,10 +227,16 @@ nomarchy_detect_hw() {
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Chassis (glob test, not compgen — nixpkgs' non-interactive bash
|
# Chassis: any system battery (type=Battery, not scope=Device) —
|
||||||
# is built without the completion builtins)
|
# name-agnostic BAT0/CMB0/… (BACKLOG #60). Not a bare BAT* glob.
|
||||||
local bats=(/sys/class/power_supply/BAT*)
|
local has_bat=0 d
|
||||||
if [[ -e "${bats[0]}" ]]; then
|
for d in /sys/class/power_supply/*/; do
|
||||||
|
[[ "$(cat "$d/type" 2>/dev/null)" == Battery ]] || continue
|
||||||
|
[[ "$(cat "$d/scope" 2>/dev/null || echo System)" == Device ]] && continue
|
||||||
|
has_bat=1
|
||||||
|
break
|
||||||
|
done
|
||||||
|
if (( has_bat )); then
|
||||||
echo "MODULE common-pc-laptop"
|
echo "MODULE common-pc-laptop"
|
||||||
echo "DETAIL chassis: laptop (battery present)"
|
echo "DETAIL chassis: laptop (battery present)"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user