grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / hardware / thinkfan.nix
blob9733fbe5aa15ec5960b7265d6bcee437dfadefde
1 { config, lib, pkgs, ... }:
2 let
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;
11     let
12       tuple = ts: lib.mkOptionType {
13         name = "tuple";
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;
17       };
18       level = ints.unsigned;
19       special = enum [ "level auto" "level full-speed" "level disengaged" ];
20     in
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;
26     options = {
27       type = lib.mkOption {
28         type = lib.types.enum [ "hwmon" "atasmart" "tpacpi" "nvml" ];
29         description = ''
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.
39         '';
40       };
41       query = lib.mkOption {
42         type = lib.types.str;
43         description = ''
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).
48           ::: {.note}
49           When multiple ${name}s match, the query can be restricted using the
50           {option}`name` or {option}`indices` options.
51           :::
52         '';
53       };
54       indices = lib.mkOption {
55         type = with lib.types; nullOr (listOf ints.unsigned);
56         default = null;
57         description = ''
58           A list of ${name}s to pick in case multiple ${name}s match the query.
60           ::: {.note}
61           Indices start from 0.
62           :::
63         '';
64       };
65     } // lib.optionalAttrs (name == "sensor") {
66       correction = lib.mkOption {
67         type = with lib.types; nullOr (listOf int);
68         default = null;
69         description = ''
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.
72         '';
73       };
74     };
75   };
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; };
82   syntaxNote = name: ''
83     ::: {.note}
84     This section slightly departs from the thinkfan.conf syntax.
85     The type and path must be specified like this:
86     ```
87       type = "tpacpi";
88       query = "/proc/acpi/ibm/${name}";
89     ```
90     instead of a single declaration like:
91     ```
92       - tpacpi: /proc/acpi/ibm/${name}
93     ```
94     :::
95   '';
97 in {
99   options = {
101     services.thinkfan = {
103       enable = lib.mkOption {
104         type = lib.types.bool;
105         default = false;
106         description = ''
107           Whether to enable thinkfan, a fan control program.
109           ::: {.note}
110           This module targets IBM/Lenovo thinkpads by default, for
111           other hardware you will have configure it more carefully.
112           :::
113         '';
114         relatedPackages = [ "thinkfan" ];
115       };
117       smartSupport = lib.mkOption {
118         type = lib.types.bool;
119         default = false;
120         description = ''
121           Whether to build thinkfan with S.M.A.R.T. support to read temperatures
122           directly from hard disks.
123         '';
124       };
126       sensors = lib.mkOption {
127         type = lib.types.listOf (sensorType "sensor");
128         default = [
129           { type = "tpacpi";
130             query = "/proc/acpi/ibm/thermal";
131           }
132         ];
133         description = ''
134           List of temperature sensors thinkfan will monitor.
136           ${syntaxNote "thermal"}
137         '';
138       };
140       fans = lib.mkOption {
141         type = lib.types.listOf (sensorType "fan");
142         default = [
143           { type = "tpacpi";
144             query = "/proc/acpi/ibm/fan";
145           }
146         ];
147         description = ''
148           List of fans thinkfan will control.
150           ${syntaxNote "fan"}
151         '';
152       };
154       levels = lib.mkOption {
155         type = lib.types.listOf levelType;
156         default = [
157           [0  0   55]
158           [1  48  60]
159           [2  50  61]
160           [3  52  63]
161           [6  56  65]
162           [7  60  85]
163           ["level auto" 80 32767]
164         ];
165         description = ''
166           [LEVEL LOW HIGH]
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.
174         '';
175       };
177       extraArgs = lib.mkOption {
178         type = lib.types.listOf lib.types.str;
179         default = [ ];
180         example = [ "-b" "0" ];
181         description = ''
182           A list of extra command line arguments to pass to thinkfan.
183           Check the thinkfan(1) manpage for available arguments.
184         '';
185       };
187       settings = lib.mkOption {
188         type = lib.types.attrsOf settingsFormat.type;
189         default = { };
190         description = ''
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>
196         '';
197       };
199     };
201   };
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;
210       levels  = cfg.levels;
211     };
213     systemd.packages = [ thinkfan ];
215     systemd.services = {
216       thinkfan.environment.THINKFAN_ARGS = lib.escapeShellArgs ([ "-c" configFile ] ++ cfg.extraArgs);
217       thinkfan.serviceConfig = {
218         Restart = "on-failure";
219         RestartSec = "30s";
221         # Hardening
222         PrivateNetwork = true;
223       };
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" ];
229     };
231     boot.extraModprobeConfig = "options thinkpad_acpi experimental=1 fan_control=1";
233   };