# SPDX-FileCopyrightText: 2023 Simon Bruder # # SPDX-License-Identifier: AGPL-3.0-or-later { config, lib, pkgs, ... }: let cfg = config.sbruder.fancontrol; in { options.sbruder.fancontrol = { enable = lib.mkEnableOption "afancontrol based fancontrol"; package = lib.mkOption { type = lib.types.package; default = pkgs.afancontrol; description = "The afancontrol package to use."; }; interval = lib.mkOption { type = lib.types.int; default = 1; description = "Interval in seconds the fan speeds should be calculated and set."; }; enableDefaultMapping = lib.mkEnableOption "the default mapping, mapping all sensors to all fans"; fans = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule { options = { pwmFile = lib.mkOption { type = lib.types.path; description = "File to write the PWM duty cycle to."; }; pwmLineStart = lib.mkOption { type = lib.types.int; default = 0; description = "PWM value at which the linear correlation of PWM/RPM starts."; }; pwmLineEnd = lib.mkOption { type = lib.types.int; default = 255; description = "PWM value at which the linear correlation of PWM/RPM ends."; }; rpmFile = lib.mkOption { type = lib.types.path; description = "File to read the fan RPM from."; }; neverStop = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to stop the fan on speed 0% (otherwise runs with pwmLineStart)."; }; }; }); default = { }; }; sensors = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule { options = { file = lib.mkOption { type = lib.types.path; description = "File to read the temperature (in m°C) from."; }; min = lib.mkOption { type = lib.types.int; description = "Temperature at which the fan speed is lowest."; }; max = lib.mkOption { type = lib.types.int; description = "Temperature at which the fan speed is highest."; }; }; }); default = { }; }; mappings = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule { options = { fans = lib.mkOption { type = lib.types.listOf (lib.types.str); description = "Fans to map."; }; sensors = lib.mkOption { type = lib.types.listOf (lib.types.str); description = "Sensors to map."; }; }; }); default = if cfg.enableDefaultMapping then { all = { fans = lib.attrNames cfg.fans; sensors = lib.attrNames cfg.sensors; }; } else { }; }; }; config = lib.mkIf cfg.enable { # makes some assumptions #systemd.packages = [ cfg.package ]; systemd.services.afancontrol = { wantedBy = [ "multi-user.target" ]; serviceConfig = { LimitNOFILE = 8192; ExecStartPre = "${cfg.package}/bin/afancontrol daemon --test"; ExecStart = "${cfg.package}/bin/afancontrol daemon --pidfile /run/afancontrol.pid"; PIDFile = "/run/afancontrol.pid"; Restart = "always"; }; }; environment.etc."afancontrol/afancontrol.conf".text = lib.generators.toINI { mkKeyValue = lib.generators.mkKeyValueDefault { mkValueString = v: if lib.isList v then lib.concatMapStringsSep ", " toString v else lib.generators.mkValueStringDefault { } v; } ":"; } ({ daemon = { interval = 1; }; actions = { report_cmd = "true"; }; } // (lib.mapAttrs' (name: config: lib.nameValuePair "fan: ${name}" { type = "linux"; pwm = config.pwmFile; fan_input = config.rpmFile; pwm_line_start = config.pwmLineStart; pwm_line_end = config.pwmLineEnd; never_stop = config.neverStop; }) cfg.fans ) // (lib.mapAttrs' (name: config: lib.nameValuePair "temp: ${name}" { type = "file"; path = config.file; inherit (config) min max; }) cfg.sensors ) // (lib.mapAttrs' (name: config: lib.nameValuePair "mapping: ${name}" { inherit (config) fans; temps = config.sensors; }) cfg.mappings )); }; }