···
cfg = config.services.thinkfan;
8
-
configFile = pkgs.writeText "thinkfan.conf" ''
9
-
# ATTENTION: There is only very basic sanity checking on the configuration.
10
-
# That means you can set your temperature limits as insane as you like. You
11
-
# can do anything stupid, e.g. turn off your fan when your CPU reaches 70°C.
13
-
# That's why this program is called THINKfan: You gotta think for yourself.
15
-
######################################################################
17
-
# IBM/Lenovo Thinkpads (thinkpad_acpi, /proc/acpi/ibm)
18
-
# ====================================================
22
-
# To keep your HD from overheating, you have to specify a correction value for
23
-
# the sensor that has the HD's temperature. You need to do this because
24
-
# thinkfan uses only the highest temperature it can find in the system, and
25
-
# that'll most likely never be your HD, as most HDs are already out of spec
26
-
# when they reach 55 °C.
27
-
# Correction values are applied from left to right in the same order as the
28
-
# temperatures are read from the file.
31
-
# tp_thermal /proc/acpi/ibm/thermal (0, 0, 10)
32
-
# will add a fixed value of 10 °C the 3rd value read from that file. Check out
33
-
# http://www.thinkwiki.org/wiki/Thermal_Sensors to find out how much you may
34
-
# want to add to certain temperatures.
8
+
settingsFormat = pkgs.formats.yaml { };
9
+
configFile = settingsFormat.generate "thinkfan.yaml" cfg.settings;
10
+
thinkfan = pkgs.thinkfan.override { inherit (cfg) smartSupport; };
12
+
# fan-speed and temperature levels
13
+
levelType = with types;
15
+
tuple = ts: mkOptionType {
17
+
merge = mergeOneOption;
18
+
check = xs: all id (zipListsWith (t: x: t.check x) ts xs);
19
+
description = "tuple of" + concatMapStrings (t: " (${t.description})") ts;
21
+
level = ints.unsigned;
22
+
special = enum [ "level auto" "level full-speed" "level disengage" ];
24
+
tuple [ (either level special) level level ];
26
+
# sensor or fan config
27
+
sensorType = name: types.submodule {
28
+
freeformType = types.attrsOf settingsFormat.type;
31
+
type = types.enum [ "hwmon" "atasmart" "tpacpi" "nvml" ];
33
+
The ${name} type, can be
34
+
<literal>hwmon</literal> for standard ${name}s,
36
+
<literal>atasmart</literal> to read the temperature via
37
+
S.M.A.R.T (requires smartSupport to be enabled),
39
+
<literal>tpacpi</literal> for the legacy thinkpac_acpi driver, or
41
+
<literal>nvml</literal> for the (proprietary) nVidia driver.
47
+
The query string used to match one or more ${name}s: can be
48
+
a fullpath to the temperature file (single ${name}) or a fullpath
49
+
to a driver directory (multiple ${name}s).
52
+
When multiple ${name}s match, the query can be restricted using the
53
+
<option>name</option> or <option>indices</option> options.
57
+
indices = mkOption {
58
+
type = with types; nullOr (listOf ints.unsigned);
61
+
A list of ${name}s to pick in case multiple ${name}s match the query.
63
+
<note><para>Indices start from 0.</para></note>
66
+
} // optionalAttrs (name == "sensor") {
67
+
correction = mkOption {
68
+
type = with types; nullOr (listOf int);
71
+
A list of values to be added to the temperature of each sensor,
72
+
can be used to equalize small discrepancies in temperature ratings.
40
-
# (LEVEL, LOW, HIGH)
41
-
# LEVEL is the fan level to use (0-7 with thinkpad_acpi)
42
-
# LOW is the temperature at which to step down to the previous level
43
-
# HIGH is the temperature at which to step up to the next level
44
-
# All numbers are integers.
78
+
# removes NixOS special and unused attributes
79
+
sensorToConf = { type, query, ... }@args:
80
+
(filterAttrs (k: v: v != null && !(elem k ["type" "query"])) args)
81
+
// { "${type}" = query; };
83
+
syntaxNote = name: ''
85
+
This section slightly departs from the thinkfan.conf syntax.
86
+
The type and path must be specified like this:
89
+
query = "/proc/acpi/ibm/${name}";
91
+
instead of a single declaration like:
93
+
- tpacpi: /proc/acpi/ibm/${name}
50
-
thinkfan = pkgs.thinkfan.override { smartSupport = cfg.smartSupport; };
···
62
-
Whether to enable thinkfan, fan controller for IBM/Lenovo ThinkPads.
108
+
Whether to enable thinkfan, a fan control program.
111
+
This module targets IBM/Lenovo thinkpads by default, for
112
+
other hardware you will have configure it more carefully.
115
+
relatedPackages = [ "thinkfan" ];
smartSupport = mkOption {
70
-
Whether to build thinkfan with SMART support to read temperatures
122
+
Whether to build thinkfan with S.M.A.R.T. support to read temperatures
directly from hard disks.
78
-
tp_thermal /proc/acpi/ibm/thermal (0,0,10)
81
-
thinkfan can read temperatures from three possible sources:
83
-
/proc/acpi/ibm/thermal
84
-
Which is provided by the thinkpad_acpi kernel
85
-
module (keyword tp_thermal)
128
+
type = types.listOf (sensorType "sensor");
131
+
query = "/proc/acpi/ibm/thermal";
135
+
List of temperature sensors thinkfan will monitor.
136
+
'' + syntaxNote "thermal";
87
-
/sys/class/hwmon/*/temp*_input
88
-
Which may be provided by any hwmon drivers (keyword
140
+
type = types.listOf (sensorType "fan");
143
+
query = "/proc/acpi/ibm/fan";
147
+
List of fans thinkfan will control.
148
+
'' + syntaxNote "fan";
91
-
S.M.A.R.T. (requires smartSupport to be enabled)
92
-
Which reads the temperature directly from the hard
93
-
disk using libatasmart (keyword atasmart)
151
+
levels = mkOption {
152
+
type = types.listOf levelType;
160
+
["level auto" 80 32767]
95
-
Multiple sensors may be added, in which case they will be
96
-
numbered in their order of appearance.
165
+
LEVEL is the fan level to use: it can be an integer (0-7 with thinkpad_acpi),
166
+
"level auto" (to keep the default firmware behavior), "level full-speed" or
167
+
"level disengage" (to run the fan as fast as possible).
168
+
LOW is the temperature at which to step down to the previous level.
169
+
HIGH is the temperature at which to step up to the next level.
170
+
All numbers are integers.
102
-
default = "tp_fan /proc/acpi/ibm/fan";
104
-
Specifies the fan we want to use.
105
-
On anything other than a Thinkpad you'll probably
106
-
use some PWM control file in /sys/class/hwmon.
107
-
A sysfs fan would be specified like this:
108
-
pwm_fan /sys/class/hwmon/hwmon2/device/pwm1
174
+
extraArgs = mkOption {
175
+
type = types.listOf types.str;
177
+
example = [ "-b" "0" ];
179
+
A list of extra command line arguments to pass to thinkfan.
180
+
Check the thinkfan(1) manpage for available arguments.
112
-
levels = mkOption {
113
-
type = types.lines;
184
+
settings = mkOption {
185
+
type = types.attrsOf settingsFormat.type;
125
-
LEVEL is the fan level to use (0-7 with thinkpad_acpi).
126
-
LOW is the temperature at which to step down to the previous level.
127
-
HIGH is the temperature at which to step up to the next level.
128
-
All numbers are integers.
188
+
Thinkfan settings. Use this option to configure thinkfan
189
+
settings not exposed in a NixOS option or to bypass one.
190
+
Before changing this, read the <literal>thinkfan.conf(5)</literal>
191
+
manpage and take a look at the example config file at
192
+
<link xlink:href="https://github.com/vmatare/thinkfan/blob/master/examples/thinkfan.yaml"/>
···
environment.systemPackages = [ thinkfan ];
141
-
systemd.services.thinkfan = {
142
-
description = "Thinkfan";
143
-
after = [ "basic.target" ];
144
-
wantedBy = [ "multi-user.target" ];
145
-
path = [ thinkfan ];
146
-
serviceConfig.ExecStart = "${thinkfan}/bin/thinkfan -n -c ${configFile}";
204
+
services.thinkfan.settings = mapAttrs (k: v: mkDefault v) {
205
+
sensors = map sensorToConf cfg.sensors;
206
+
fans = map sensorToConf cfg.fans;
207
+
levels = cfg.levels;
210
+
systemd.packages = [ thinkfan ];
212
+
systemd.services = {
213
+
thinkfan.environment.THINKFAN_ARGS = escapeShellArgs ([ "-c" configFile ] ++ cfg.extraArgs);
215
+
# must be added manually, see issue #81138
216
+
thinkfan.wantedBy = [ "multi-user.target" ];
217
+
thinkfan-wakeup.wantedBy = [ "sleep.target" ];
218
+
thinkfan-sleep.wantedBy = [ "sleep.target" ];
boot.extraModprobeConfig = "options thinkpad_acpi experimental=1 fan_control=1";