feat(battery): low-battery notifications at the bar's 25/10% thresholds (item 13 slice)
All checks were successful
Check / eval (push) Successful in 3m4s

The bar colored the battery at 25/10% but nothing notified. New
shellcheck-gated watcher (pkgs/nomarchy-battery-notify, overlay) polls
the same sysfs the bar reads — type=Battery, scope!=Device — and fires
one toast per downward crossing (normal at 25%, critical at 10%; swaync
keeps critical up until dismissed), re-armed by charging. Self-gates on
battery presence (the powerprofile pattern), so desktops get a silent
no-op. HM user unit behind nomarchy.batteryNotify.enable (default on)
with libnotify on the unit PATH; notify-send resolves from PATH so the
VM check can shim it. poweralertd rejected: its UPower thresholds
(20/5) wouldn't match the bar, and it toasts every plug/unplug.

Verified: V0 (flake check, bash -n); V1 (template-home builds, unit in
the generation); V2 GREEN (new checks.battery-notify, test_power fake
battery: self-gate, silence at 80%, one toast per crossing, critical
urgency, charging re-arms). Remains: V3 — real swaync toast on a
draining laptop (HARDWARE-QUEUE).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-07-05 15:11:06 +01:00
parent 821032e81c
commit fb78c814cc
10 changed files with 195 additions and 5 deletions

View File

@@ -28,11 +28,8 @@ next, in what order*.
### 13. Small niceties batch (one slice per iteration)
Each is a small, self-contained polish item in the existing patterns
(idle-inhibit/caffeine slice shipped 2026-07-04 — generated bar +
summer-day parity + cheatsheet row):
- **Low-battery notifications:** the bar colors at 25/10% but nothing
*notifies*; gate on `power.laptop` (poweralertd, or a small upower
watcher consistent with how the bar reads state).
(shipped: idle-inhibit/caffeine 2026-07-04; low-battery notifications
2026-07-05 — sysfs watcher + checks.battery-notify):
- **Color picker:** `hyprpicker` → clipboard as a Tools entry +
`SUPER+CTRL` bind; pairs naturally with theme work.

View File

@@ -140,6 +140,12 @@ QA machine), the **T14s** (webcam case).
now reflects the real state (the `= true` comparison fix).
— 2026-07-04 Bernardo: PASS — theme changes committed and the
toggle self-committed.
- [ ] **Low-battery toasts (item 13 slice)** — on battery, drain past
25%: one "Battery low" toast; past 10%: a critical "Battery
critical" toast that stays up until dismissed (swaync); plug in,
drain again → it re-notifies; no repeat toasts while it just keeps
draining. (Crossing logic VM-verified — this checks the real
swaync rendering in a session.)
## AMD dev box only
- [ ] **AMD runtime bits** — VA-API (`vainfo` → radeonsi), amd-pstate EPP

View File

@@ -17,6 +17,28 @@ Template:
---
## 2026-07-05 — low-battery notifications (iteration #24, item 13 slice)
- **Task:** BACKLOG NEXT#13 second slice — the bar colors the battery
at 25/10% but nothing *notified*.
- **Did:** pkgs/nomarchy-battery-notify (shellcheck-gated watcher:
polls the same sysfs the bar reads — type=Battery, scope≠Device —
one toast per downward crossing: normal@25, critical@10; re-armed by
charging; self-gates on battery presence → desktop no-op) + overlay.
HM unit in modules/home/battery-notify.nix behind
nomarchy.batteryNotify.enable (default on), graphical-session-bound,
libnotify on the unit PATH (notify-send resolved from PATH so the
check can shim it). README row. Chose the tiny watcher over
poweralertd: its UPower thresholds (20/5) wouldn't match the bar,
and it toasts every plug/unplug.
- **Verified:** V0; V1 — template-home builds, unit present; V2 GREEN —
new checks.battery-notify (test_power fake battery): self-gate exits
0 without a battery, silence at 80%, exactly one low toast crossing
25, one critical-urgency toast crossing 10, charging re-arms → a
second drain toasts again.
- **Pending:** V3 queued (real toast through swaync while draining).
- **Next suggestion:** item 13 final slice — hyprpicker color picker
(Tools entry + SUPER+CTRL bind).
## 2026-07-04 — caffeine/idle-inhibit toggle (iteration #23, item 13 slice)
- **Task:** BACKLOG NEXT#13 first slice — idle_inhibitor in the
generated bar (summer-night had it; reverse parity gap).