feat(power): laptop power management (power-profiles-daemon + TLP)

Adds a `nomarchy.system.power.*` surface (modules/nixos/power.nix),
replacing the bare `services.upower` (Waybar reporting only) baseline:

- power-profiles-daemon ships by default; switched through polkit (no
  root prompt). TLP is the opt-in `backend = "tlp"` for deeper battery
  tuning — the two are mutually exclusive (asserted).
- thermald behind `power.thermal.enable` (Intel-only).
- `power.batteryChargeLimit` (e.g. 80): a backend-independent sysfs
  oneshot writes charge_control_end_threshold; gated on `power.laptop`.

Switcher + indicator live home-side and self-gate on a battery being
present + powerprofilesctl running, so they hide on desktops and under
TLP — no system->home wiring (like the Waybar battery widget):

- rofi.nix: `nomarchy-menu power-profile` module + a picker entry shown
  only on laptops with PPD.
- waybar.nix: first `custom/*` module — click cycles the profile.

Installer writes `power.laptop = true` (battery probe) and
`power.thermal.enable` (GenuineIntel) into the generated system.nix, and
scaffolds the charge limit commented-out.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 17:35:42 +01:00
parent 8853f6ae49
commit 00f5311499
7 changed files with 233 additions and 37 deletions

View File

@@ -412,23 +412,28 @@ close · `SUPER+1..9` workspaces · `Print` region screenshot.
comments/docs for drift against the code; and re-check that every file
still earns its place under the `modules`/`hosts`/`themes`/`pkgs`/`tools`
rule of thumb.
- **Laptop power / battery management:** today the distro does *no* active
power management — only `services.upower` is on, and that's just battery
*reporting* for the Waybar widget; CPU/thermal/charge behaviour is whatever
the kernel and the nixos-hardware profile default to. Add a real power
story behind a `nomarchy.system.power.*` surface, gated to laptops (the
installer already detects chassis):
- **profiles:** ship `power-profiles-daemon` by default (the upstream-
aligned choice — power-saver/balanced/performance via `powerprofilesctl`),
with a switcher in the rofi menu and a Waybar indicator. Offer **TLP** as
an opt-in for maximum battery life (note: mutually exclusive with PPD, and
more aggressive/opaque). **Decision to make:** PPD-default vs TLP-default.
- **thermal:** `thermald` on Intel (the Latitude is Intel) to avoid
throttling.
- **longevity:** battery charge thresholds (e.g. stop charging at ~80%)
where the hardware supports it.
- **cohere** with `modules/home/idle.nix` (idle lock / display-off /
suspend) and review logind lid handling so the power behaviours line up.
- **Laptop power / battery management:** a real power story behind a
`nomarchy.system.power.*` surface (`modules/nixos/power.nix`), replacing the
old "only `services.upower` for Waybar reporting" baseline:
-**profiles:** `power-profiles-daemon` ships by default (the upstream-
aligned choice — power-saver/balanced/performance via `powerprofilesctl`,
switched through polkit so no root prompt). A `power-profile` menu module
(in the SUPER+M picker) and the first Waybar `custom/*` indicator (click to
cycle) both **self-gate** on a battery being present + PPD running, so they
hide on desktops and under TLP — no system→home wiring. **TLP** is the
opt-in (`backend = "tlp"`) for deeper battery tuning; the two are mutually
exclusive (asserted).
- **thermal:** `thermald` behind `power.thermal.enable`; the installer
turns it on for a `GenuineIntel` CPU.
- **longevity:** `power.batteryChargeLimit` (e.g. 80) — a backend-
independent sysfs oneshot writes `charge_control_end_threshold`. Off by
default; the installer scaffolds it commented-out on laptops.
-**installer:** writes `power.laptop = true` (battery probe) and
`power.thermal.enable` (Intel) into the generated `system.nix`.
- Remaining: **cohere** with `modules/home/idle.nix` (AC-vs-battery idle
timeouts / no auto-suspend on AC) and review logind lid handling, plus a
boot-only→event-driven charge-limit re-apply (udev) if a firmware resets
the threshold on unplug.
- **Opt-in services & integrations:** the counterpart to the opt-*out*
application suite above — heavier or more personal integrations shipped
**off by default**, each a `nomarchy.services.<name>.enable` toggle a