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).
77 lines
3.5 KiB
Nix
77 lines
3.5 KiB
Nix
# Active power management. One concern, one file: this is the system
|
|
# side — the power daemon (power-profiles-daemon by default, or TLP),
|
|
# thermald, and the battery charge limit. The profile *switcher* and the
|
|
# Waybar *indicator* live home-side (rofi.nix / waybar.nix); they self-
|
|
# gate on powerprofilesctl being present, so there's no system→home wiring
|
|
# to keep in sync (the same way the Waybar battery widget auto-hides on
|
|
# desktops). See the roadmap in README.md.
|
|
{ config, lib, ... }:
|
|
|
|
let
|
|
cfg = config.nomarchy.system.power;
|
|
ppd = cfg.backend == "ppd";
|
|
tlp = cfg.backend == "tlp";
|
|
chargeLimit = cfg.laptop && cfg.batteryChargeLimit != null;
|
|
in
|
|
{
|
|
config = lib.mkIf cfg.enable {
|
|
# PPD and TLP both drive CPU/platform power — running both fights.
|
|
# `backend` selects exactly one; this assertion only fires if a
|
|
# downstream force-enables the other service directly.
|
|
assertions = [{
|
|
assertion = !(config.services.power-profiles-daemon.enable
|
|
&& config.services.tlp.enable);
|
|
message = ''
|
|
nomarchy: power-profiles-daemon and TLP are mutually exclusive.
|
|
Pick one with nomarchy.system.power.backend ("ppd" or "tlp").
|
|
'';
|
|
}];
|
|
|
|
# Unprivileged profile switching (the menu/Waybar) goes through
|
|
# polkit, already enabled distro-wide in default.nix.
|
|
services.power-profiles-daemon.enable = lib.mkDefault ppd;
|
|
services.tlp.enable = lib.mkDefault tlp;
|
|
|
|
# thermald is Intel-only, so off unless asked (the installer enables
|
|
# it on a GenuineIntel CPU). Sits happily next to either backend.
|
|
services.thermald.enable = lib.mkDefault cfg.thermal.enable;
|
|
|
|
# Battery charge limit via sysfs. PPD can't cap charge at all, and
|
|
# TLP's own knob only applies under TLP — so a tiny oneshot writes
|
|
# the threshold directly, independent of the backend. Re-applied on
|
|
# AC state changes by the udev rule below (some firmwares reset the
|
|
# threshold when the charger is unplugged). `[ -w ]` skips paths that
|
|
# don't exist, so this is a clean no-op on hardware without the control.
|
|
systemd.services.nomarchy-battery-charge-limit = lib.mkIf chargeLimit {
|
|
description = "Cap battery charging at ${toString cfg.batteryChargeLimit}%";
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
};
|
|
script = ''
|
|
# 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"
|
|
done
|
|
exit 0
|
|
'';
|
|
};
|
|
|
|
# Re-apply the threshold whenever the mains adapter changes state: the
|
|
# boot oneshot above runs once, but some firmwares clear the limit when
|
|
# the charger is unplugged, so it must be re-asserted on the event.
|
|
# Matched by ATTR{type}=="Mains" (vendor-neutral — the kernel name
|
|
# AC/AC0/ADP1/ACAD varies); --no-block so the RUN+= returns at once
|
|
# (systemd kills long-running udev workers); restart (not try-restart)
|
|
# so it re-applies even if the boot run was inactive.
|
|
services.udev.extraRules = lib.mkIf chargeLimit ''
|
|
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", RUN+="${config.systemd.package}/bin/systemctl --no-block restart nomarchy-battery-charge-limit.service"
|
|
'';
|
|
};
|
|
}
|