feat(system): add laptop power preset module

New `nomarchy.system.laptop.{enable,thermald}` options. `enable`
defaults to `formFactor == "laptop"`, so the installer's existing
formFactor write auto-flips the preset on without installer changes.

The module wires TLP (governors + 75/80 charge thresholds),
force-disables power-profiles-daemon (mutually exclusive with TLP),
enables upower and thermald (x86_64), adds the brightnessctl udev
rule so the existing brightness scripts work without root, and sets
a logind lid-switch policy that resolves to suspend-then-hibernate
when `hibernation.enable` is on, plain suspend otherwise.

Closes the "Form-factor → laptop preset auto-enable" Now item and
the "Laptop preset module" Next item from docs/ROADMAP.md in one
change.
This commit is contained in:
Bernardo Magri
2026-04-26 08:31:19 +01:00
parent 7086a6f29c
commit 034da701a3
5 changed files with 82 additions and 5 deletions

42
core/system/laptop.nix Normal file
View File

@@ -0,0 +1,42 @@
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system.laptop;
hib = config.nomarchy.system.hibernation;
lidAction = if hib.enable then "suspend-then-hibernate" else "suspend";
in
{
config = lib.mkIf cfg.enable {
services.tlp = {
enable = lib.mkDefault true;
settings = {
CPU_SCALING_GOVERNOR_ON_AC = lib.mkDefault "performance";
CPU_SCALING_GOVERNOR_ON_BAT = lib.mkDefault "powersave";
CPU_BOOST_ON_BAT = lib.mkDefault 0;
PLATFORM_PROFILE_ON_AC = lib.mkDefault "balanced";
PLATFORM_PROFILE_ON_BAT = lib.mkDefault "low-power";
# Charge thresholds only honored on supported hardware (ThinkPad,
# some ASUS); a harmless warning is logged elsewhere.
START_CHARGE_THRESH_BAT0 = lib.mkDefault 75;
STOP_CHARGE_THRESH_BAT0 = lib.mkDefault 80;
};
};
# TLP and power-profiles-daemon both arbitrate CPU/EPP — NixOS asserts
# mutual exclusion. Opt out of the preset entirely to use PPD instead.
services.power-profiles-daemon.enable = lib.mkForce false;
services.upower.enable = lib.mkDefault true;
services.thermald.enable = lib.mkDefault cfg.thermald;
# Backlight write access for the `video` group, so the existing
# nomarchy-brightness-{display,keyboard} scripts run without root.
services.udev.packages = [ pkgs.brightnessctl ];
services.logind.settings.Login = {
HandleLidSwitch = lib.mkDefault lidAction;
HandleLidSwitchExternalPower = lib.mkDefault "suspend";
HandleLidSwitchDocked = lib.mkDefault "ignore";
};
};
}