1 { config, lib, pkgs, ... }:
4 cfg = config.services.thinkfan;
5 settingsFormat = pkgs.formats.yaml { };
6 configFile = settingsFormat.generate "thinkfan.yaml" cfg.settings;
7 thinkfan = pkgs.thinkfan.override { inherit (cfg) smartSupport; };
9 # fan-speed and temperature levels
10 levelType = with lib.types;
12 tuple = ts: lib.mkOptionType {
14 merge = lib.mergeOneOption;
15 check = xs: lib.all lib.id (lib.zipListsWith (t: x: t.check x) ts xs);
16 description = "tuple of" + lib.concatMapStrings (t: " (${t.description})") ts;
18 level = ints.unsigned;
19 special = enum [ "level auto" "level full-speed" "level disengaged" ];
21 tuple [ (either level special) level level ];
23 # sensor or fan config
24 sensorType = name: lib.types.submodule {
25 freeformType = lib.types.attrsOf settingsFormat.type;
28 type = lib.types.enum [ "hwmon" "atasmart" "tpacpi" "nvml" ];
30 The ${name} type, can be
31 `hwmon` for standard ${name}s,
33 `atasmart` to read the temperature via
34 S.M.A.R.T (requires smartSupport to be enabled),
36 `tpacpi` for the legacy thinkpac_acpi driver, or
38 `nvml` for the (proprietary) nVidia driver.
41 query = lib.mkOption {
44 The query string used to match one or more ${name}s: can be
45 a fullpath to the temperature file (single ${name}) or a fullpath
46 to a driver directory (multiple ${name}s).
49 When multiple ${name}s match, the query can be restricted using the
50 {option}`name` or {option}`indices` options.
54 indices = lib.mkOption {
55 type = with lib.types; nullOr (listOf ints.unsigned);
58 A list of ${name}s to pick in case multiple ${name}s match the query.
65 } // lib.optionalAttrs (name == "sensor") {
66 correction = lib.mkOption {
67 type = with lib.types; nullOr (listOf int);
70 A list of values to be added to the temperature of each sensor,
71 can be used to equalize small discrepancies in temperature ratings.
77 # removes NixOS special and unused attributes
78 sensorToConf = { type, query, ... }@args:
79 (lib.filterAttrs (k: v: v != null && !(lib.elem k ["type" "query"])) args)
80 // { "${type}" = query; };
84 This section slightly departs from the thinkfan.conf syntax.
85 The type and path must be specified like this:
88 query = "/proc/acpi/ibm/${name}";
90 instead of a single declaration like:
92 - tpacpi: /proc/acpi/ibm/${name}
101 services.thinkfan = {
103 enable = lib.mkOption {
104 type = lib.types.bool;
107 Whether to enable thinkfan, a fan control program.
110 This module targets IBM/Lenovo thinkpads by default, for
111 other hardware you will have configure it more carefully.
114 relatedPackages = [ "thinkfan" ];
117 smartSupport = lib.mkOption {
118 type = lib.types.bool;
121 Whether to build thinkfan with S.M.A.R.T. support to read temperatures
122 directly from hard disks.
126 sensors = lib.mkOption {
127 type = lib.types.listOf (sensorType "sensor");
130 query = "/proc/acpi/ibm/thermal";
134 List of temperature sensors thinkfan will monitor.
136 ${syntaxNote "thermal"}
140 fans = lib.mkOption {
141 type = lib.types.listOf (sensorType "fan");
144 query = "/proc/acpi/ibm/fan";
148 List of fans thinkfan will control.
154 levels = lib.mkOption {
155 type = lib.types.listOf levelType;
163 ["level auto" 80 32767]
168 LEVEL is the fan level to use: it can be an integer (0-7 with thinkpad_acpi),
169 "level auto" (to keep the default firmware behavior), "level full-speed" or
170 "level disengaged" (to run the fan as fast as possible).
171 LOW is the temperature at which to step down to the previous level.
172 HIGH is the temperature at which to step up to the next level.
173 All numbers are integers.
177 extraArgs = lib.mkOption {
178 type = lib.types.listOf lib.types.str;
180 example = [ "-b" "0" ];
182 A list of extra command line arguments to pass to thinkfan.
183 Check the thinkfan(1) manpage for available arguments.
187 settings = lib.mkOption {
188 type = lib.types.attrsOf settingsFormat.type;
191 Thinkfan settings. Use this option to configure thinkfan
192 settings not exposed in a NixOS option or to bypass one.
193 Before changing this, read the `thinkfan.conf(5)`
194 manpage and take a look at the example config file at
195 <https://github.com/vmatare/thinkfan/blob/master/examples/thinkfan.yaml>
203 config = lib.mkIf cfg.enable {
205 environment.systemPackages = [ thinkfan ];
207 services.thinkfan.settings = lib.mapAttrs (k: v: lib.mkDefault v) {
208 sensors = map sensorToConf cfg.sensors;
209 fans = map sensorToConf cfg.fans;
213 systemd.packages = [ thinkfan ];
216 thinkfan.environment.THINKFAN_ARGS = lib.escapeShellArgs ([ "-c" configFile ] ++ cfg.extraArgs);
217 thinkfan.serviceConfig = {
218 Restart = "on-failure";
222 PrivateNetwork = true;
225 # must be added manually, see issue #81138
226 thinkfan.wantedBy = [ "multi-user.target" ];
227 thinkfan-wakeup.wantedBy = [ "sleep.target" ];
228 thinkfan-sleep.wantedBy = [ "sleep.target" ];
231 boot.extraModprobeConfig = "options thinkpad_acpi experimental=1 fan_control=1";