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

@@ -51,5 +51,58 @@
`nixos-rebuild-snap` pre-rebuild-snapshot helper. No-op unless the
root filesystem is BTRFS with a /.snapshots subvolume (the installer
creates one)'';
power = {
enable = lib.mkEnableOption ''
active power management. By default ships power-profiles-daemon
the upstream-aligned power-saver/balanced/performance model,
switchable from the menu (nomarchy-menu power-profile) and shown
in Waybar on laptops plus thermald and a battery charge limit
where applicable. Harmless on desktops (the profile daemon just
offers balanced/performance)'' // { default = true; };
backend = lib.mkOption {
type = lib.types.enum [ "ppd" "tlp" ];
default = "ppd";
description = ''
Which daemon governs CPU/platform power. "ppd"
(power-profiles-daemon) is the default: a clean three-profile
model with the menu switcher and Waybar indicator. "tlp" trades
that for TLP's deeper, more aggressive battery tuning, at the
cost of the profile switcher (TLP has no profile concept). The
two manage the same knobs and are mutually exclusive.
'';
};
laptop = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Marks this machine as a laptop, gating battery-only features
(the charge limit below). The installer sets it to true when it
detects a battery at install time.
'';
};
thermal.enable = lib.mkEnableOption ''
thermald, Intel's thermal-management daemon, to avoid aggressive
throttling under sustained load. Intel-only the installer
enables it when it detects a GenuineIntel CPU. Harmless alongside
either backend'';
batteryChargeLimit = lib.mkOption {
type = lib.types.nullOr (lib.types.ints.between 50 100);
default = null;
example = 80;
description = ''
Stop charging at this percentage to extend battery lifespan,
where the hardware exposes a charge threshold
(/sys/class/power_supply/BAT*/charge_control_end_threshold).
null leaves charging at the firmware default. Backend-independent
(a small systemd unit writes the sysfs knob at boot), so it
works under PPD too; needs nomarchy.system.power.laptop.
'';
};
};
};
}