diff --git a/system/roles/laptop/power/auto-cpufreq/default.nix b/system/roles/laptop/power/auto-cpufreq/default.nix new file mode 100644 index 0000000..9f70597 --- /dev/null +++ b/system/roles/laptop/power/auto-cpufreq/default.nix @@ -0,0 +1,47 @@ +# This sets up power management using auto-cpufreq, +# alongside with upower and power-profiles-daemon. +# Together, this provides a complete alternative to TLP +{ pkgs, lib, config, ...}: let + inherit (lib) mkIf mkDefault; + deviceType = config.myOptions.device.roles.type; + acceptedTypes = ["laptop"]; +in { + imports = [ + ./power-profiles-daemon + ]; + + config = mkIf (builtins.elem deviceType acceptedTypes) { + services = { + # superior power management + auto-cpufreq = { + enable = true; + settings = let + MHz = x: x * 1000; + in { + battery = { + governor = "powersave"; + scaling_min_freq = mkDefault (MHz 1200); + scaling_max_freq = mkDefault (MHz 1800); + turbo = "never"; + }; + + charger = { + governor = "performance"; + scaling_min_freq = mkDefault (MHz 1800); + scaling_max_freq = mkDefault (MHz 3800); + turbo = "auto"; + }; + }; + }; + + # DBus service that provides power management support to applications + upower = { + enable = true; + percentageLow = 15; + percentageCritical = 5; + percentageAction = 3; + criticalPowerAction = "Hibernate"; + }; + }; + }; +} diff --git a/system/roles/laptop/power/auto-cpufreq/power-profiles-daemon/default.nix b/system/roles/laptop/power/auto-cpufreq/power-profiles-daemon/default.nix new file mode 100644 index 0000000..6ab2ece --- /dev/null +++ b/system/roles/laptop/power/auto-cpufreq/power-profiles-daemon/default.nix @@ -0,0 +1,40 @@ +# This sets up power management using auto-cpufreq, +# alongside with upower and power-profiles-daemon. +# Together, this provides a complete alternative to TLP +{ pkgs, lib, config, ...}: let + inherit (lib) mkIf; + inherit (lib.modules) mkForce; + inherit (lib.strings) makeBinPath; + deviceType = config.myOptions.device.roles.type; + acceptedTypes = ["laptop"]; +in { + config = mkIf (builtins.elem deviceType acceptedTypes) { + # allows changing system behavior based upon user-selected power profiles + # (with `powerprofilesctl` command) + services.power-profiles-daemon.enable = true; + + # Power state monitor. Switches power profiles based on charging state. + # Plugged in - performance (if available, falls back to balance) + # Unplugged - balanced, until below 50%, then power-saver + systemd.services."power-monitor" = let + dependencies = with pkgs; [ + coreutils + power-profiles-daemon + inotify-tools + jaq + ]; + in { + description = "Power Monitoring Service"; + environment.PATH = mkForce "/run/wrappers/bin:${makeBinPath dependencies}"; + script = builtins.readFile ./scripts/power_monitor.sh; + + serviceConfig = { + Type = "simple"; + Restart = "on-failure"; + }; + + wants = ["power-profiles-daemon.service"]; + wantedBy = ["default.target"]; + }; + }; +} diff --git a/system/roles/laptop/power/auto-cpufreq/power-profiles-daemon/scripts/power_monitor.sh b/system/roles/laptop/power/auto-cpufreq/power-profiles-daemon/scripts/power_monitor.sh new file mode 100644 index 0000000..7ecae89 --- /dev/null +++ b/system/roles/laptop/power/auto-cpufreq/power-profiles-daemon/scripts/power_monitor.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +BAT=$(echo /sys/class/power_supply/BAT*) # only supports single-battery systems +BAT_STATUS="$BAT/status" +BAT_CAP="$BAT/capacity" + +POWER_SAVE_PERCENT=50 # Enter power-save mode if on bat and below this capacity + +HAS_PERFORMANCE="$(powerprofilesctl list | grep performance)" + +# monitor loop +prev=0 +while true; do + # read the current state + status="$(cat "$BAT_STATUS")" + capacity="$(cat "$BAT_CAP")" + + if [[ $status == "Discharging" ]]; then + if [[ $capacity -le $POWER_SAVE_PERCENT ]]; then + profile="power-saver" + else + profile="balanced" + fi + else + if [[ -n $HAS_PERFORMANCE ]]; then + profile="performance" + else + profile="balanced" + fi + fi + + # Set the new profile + if [[ $profile != $prev ]]; then + echo -en "Setting power profile to ${profile}\n" + powerprofilesctl set $profile + prev=$profile + fi + + # wait for changes in status or capacity files + # i.e. for the next power change event + inotifywait -qq "$BAT_STATUS" "$BAT_CAP" +done diff --git a/system/roles/laptop/power/default.nix b/system/roles/laptop/power/default.nix index 7460c47..f122c22 100644 --- a/system/roles/laptop/power/default.nix +++ b/system/roles/laptop/power/default.nix @@ -3,6 +3,10 @@ deviceType = config.myOptions.device.roles.type; acceptedTypes = ["laptop"]; in { + imports = [ + ./auto-cpufreq + ]; + config = mkIf (builtins.elem deviceType acceptedTypes) { hardware.acpilight.enable = true; @@ -15,48 +19,11 @@ in { # handle ACPI events acpid.enable = true; - # allows changing system behavior based upon user-selected power profiles - power-profiles-daemon.enable = true; - # temperature target on battery undervolt = { tempBat = 65; # deg C package = pkgs.undervolt; }; - - # superior power management - auto-cpufreq = { - enable = true; - - # define the profiles - # (you can manually switch between profiles using `powerprofilesctl` cmd) - settings = let - MHz = x: x * 1000; - in { - battery = { - governor = "powersave"; - scaling_min_freq = mkDefault (MHz 1200); - scaling_max_freq = mkDefault (MHz 1800); - turbo = "never"; - }; - - charger = { - governor = "performance"; - scaling_min_freq = mkDefault (MHz 1800); - scaling_max_freq = mkDefault (MHz 3800); - turbo = "auto"; - }; - }; - }; - - # DBus service that provides power management support to applications - upower = { - enable = true; - percentageLow = 15; - percentageCritical = 5; - percentageAction = 3; - criticalPowerAction = "Hibernate"; - }; }; boot = {