2020-04-24 23:36:52 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
|
|
|
|
|
|
cfg = config.services.thinkfan;
|
2021-02-13 14:23:35 +00:00
|
|
|
settingsFormat = pkgs.formats.yaml { };
|
|
|
|
configFile = settingsFormat.generate "thinkfan.yaml" cfg.settings;
|
|
|
|
thinkfan = pkgs.thinkfan.override { inherit (cfg) smartSupport; };
|
|
|
|
|
|
|
|
# fan-speed and temperature levels
|
2024-09-19 14:19:46 +00:00
|
|
|
levelType = with lib.types;
|
2021-02-13 14:23:35 +00:00
|
|
|
let
|
2024-09-19 14:19:46 +00:00
|
|
|
tuple = ts: lib.mkOptionType {
|
2021-02-13 14:23:35 +00:00
|
|
|
name = "tuple";
|
2024-09-19 14:19:46 +00:00
|
|
|
merge = lib.mergeOneOption;
|
|
|
|
check = xs: lib.all lib.id (lib.zipListsWith (t: x: t.check x) ts xs);
|
|
|
|
description = "tuple of" + lib.concatMapStrings (t: " (${t.description})") ts;
|
2021-02-13 14:23:35 +00:00
|
|
|
};
|
|
|
|
level = ints.unsigned;
|
2021-12-30 13:39:12 +00:00
|
|
|
special = enum [ "level auto" "level full-speed" "level disengaged" ];
|
2021-02-13 14:23:35 +00:00
|
|
|
in
|
|
|
|
tuple [ (either level special) level level ];
|
|
|
|
|
|
|
|
# sensor or fan config
|
2024-09-19 14:19:46 +00:00
|
|
|
sensorType = name: lib.types.submodule {
|
|
|
|
freeformType = lib.types.attrsOf settingsFormat.type;
|
2021-02-13 14:23:35 +00:00
|
|
|
options = {
|
2024-09-19 14:19:46 +00:00
|
|
|
type = lib.mkOption {
|
|
|
|
type = lib.types.enum [ "hwmon" "atasmart" "tpacpi" "nvml" ];
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
The ${name} type, can be
|
2022-08-12 12:06:08 +00:00
|
|
|
`hwmon` for standard ${name}s,
|
2020-04-24 23:36:52 +00:00
|
|
|
|
2022-08-12 12:06:08 +00:00
|
|
|
`atasmart` to read the temperature via
|
2021-02-13 14:23:35 +00:00
|
|
|
S.M.A.R.T (requires smartSupport to be enabled),
|
|
|
|
|
2022-08-12 12:06:08 +00:00
|
|
|
`tpacpi` for the legacy thinkpac_acpi driver, or
|
2021-02-13 14:23:35 +00:00
|
|
|
|
2022-08-12 12:06:08 +00:00
|
|
|
`nvml` for the (proprietary) nVidia driver.
|
2021-02-13 14:23:35 +00:00
|
|
|
'';
|
|
|
|
};
|
2024-09-19 14:19:46 +00:00
|
|
|
query = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
The query string used to match one or more ${name}s: can be
|
|
|
|
a fullpath to the temperature file (single ${name}) or a fullpath
|
|
|
|
to a driver directory (multiple ${name}s).
|
|
|
|
|
2022-09-09 14:08:57 +00:00
|
|
|
::: {.note}
|
|
|
|
When multiple ${name}s match, the query can be restricted using the
|
|
|
|
{option}`name` or {option}`indices` options.
|
|
|
|
:::
|
2021-02-13 14:23:35 +00:00
|
|
|
'';
|
|
|
|
};
|
2024-09-19 14:19:46 +00:00
|
|
|
indices = lib.mkOption {
|
|
|
|
type = with lib.types; nullOr (listOf ints.unsigned);
|
2021-02-13 14:23:35 +00:00
|
|
|
default = null;
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
A list of ${name}s to pick in case multiple ${name}s match the query.
|
|
|
|
|
2022-09-09 14:08:57 +00:00
|
|
|
::: {.note}
|
|
|
|
Indices start from 0.
|
|
|
|
:::
|
2021-02-13 14:23:35 +00:00
|
|
|
'';
|
|
|
|
};
|
2024-09-19 14:19:46 +00:00
|
|
|
} // lib.optionalAttrs (name == "sensor") {
|
|
|
|
correction = lib.mkOption {
|
|
|
|
type = with lib.types; nullOr (listOf int);
|
2021-02-13 14:23:35 +00:00
|
|
|
default = null;
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
A list of values to be added to the temperature of each sensor,
|
|
|
|
can be used to equalize small discrepancies in temperature ratings.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
# removes NixOS special and unused attributes
|
|
|
|
sensorToConf = { type, query, ... }@args:
|
2024-09-19 14:19:46 +00:00
|
|
|
(lib.filterAttrs (k: v: v != null && !(lib.elem k ["type" "query"])) args)
|
2021-02-13 14:23:35 +00:00
|
|
|
// { "${type}" = query; };
|
|
|
|
|
|
|
|
syntaxNote = name: ''
|
2022-09-09 14:08:57 +00:00
|
|
|
::: {.note}
|
|
|
|
This section slightly departs from the thinkfan.conf syntax.
|
|
|
|
The type and path must be specified like this:
|
|
|
|
```
|
|
|
|
type = "tpacpi";
|
|
|
|
query = "/proc/acpi/ibm/${name}";
|
|
|
|
```
|
|
|
|
instead of a single declaration like:
|
|
|
|
```
|
|
|
|
- tpacpi: /proc/acpi/ibm/${name}
|
|
|
|
```
|
|
|
|
:::
|
2021-02-13 14:23:35 +00:00
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
|
|
|
|
in {
|
|
|
|
|
|
|
|
options = {
|
|
|
|
|
|
|
|
services.thinkfan = {
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
enable = lib.mkOption {
|
|
|
|
type = lib.types.bool;
|
2020-04-24 23:36:52 +00:00
|
|
|
default = false;
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
Whether to enable thinkfan, a fan control program.
|
|
|
|
|
2022-09-09 14:08:57 +00:00
|
|
|
::: {.note}
|
|
|
|
This module targets IBM/Lenovo thinkpads by default, for
|
|
|
|
other hardware you will have configure it more carefully.
|
|
|
|
:::
|
2020-04-24 23:36:52 +00:00
|
|
|
'';
|
2021-02-13 14:23:35 +00:00
|
|
|
relatedPackages = [ "thinkfan" ];
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
smartSupport = lib.mkOption {
|
|
|
|
type = lib.types.bool;
|
2020-04-24 23:36:52 +00:00
|
|
|
default = false;
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
Whether to build thinkfan with S.M.A.R.T. support to read temperatures
|
2020-04-24 23:36:52 +00:00
|
|
|
directly from hard disks.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
sensors = lib.mkOption {
|
|
|
|
type = lib.types.listOf (sensorType "sensor");
|
2021-02-13 14:23:35 +00:00
|
|
|
default = [
|
|
|
|
{ type = "tpacpi";
|
|
|
|
query = "/proc/acpi/ibm/thermal";
|
|
|
|
}
|
|
|
|
];
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
List of temperature sensors thinkfan will monitor.
|
2022-09-09 14:08:57 +00:00
|
|
|
|
|
|
|
${syntaxNote "thermal"}
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
fans = lib.mkOption {
|
|
|
|
type = lib.types.listOf (sensorType "fan");
|
2021-02-13 14:23:35 +00:00
|
|
|
default = [
|
|
|
|
{ type = "tpacpi";
|
|
|
|
query = "/proc/acpi/ibm/fan";
|
|
|
|
}
|
|
|
|
];
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
List of fans thinkfan will control.
|
2022-09-09 14:08:57 +00:00
|
|
|
|
|
|
|
${syntaxNote "fan"}
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
levels = lib.mkOption {
|
|
|
|
type = lib.types.listOf levelType;
|
2021-02-13 14:23:35 +00:00
|
|
|
default = [
|
|
|
|
[0 0 55]
|
|
|
|
[1 48 60]
|
|
|
|
[2 50 61]
|
|
|
|
[3 52 63]
|
|
|
|
[6 56 65]
|
|
|
|
[7 60 85]
|
|
|
|
["level auto" 80 32767]
|
|
|
|
];
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
[LEVEL LOW HIGH]
|
|
|
|
|
|
|
|
LEVEL is the fan level to use: it can be an integer (0-7 with thinkpad_acpi),
|
|
|
|
"level auto" (to keep the default firmware behavior), "level full-speed" or
|
2021-12-30 13:39:12 +00:00
|
|
|
"level disengaged" (to run the fan as fast as possible).
|
2020-04-24 23:36:52 +00:00
|
|
|
LOW is the temperature at which to step down to the previous level.
|
|
|
|
HIGH is the temperature at which to step up to the next level.
|
|
|
|
All numbers are integers.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
extraArgs = lib.mkOption {
|
|
|
|
type = lib.types.listOf lib.types.str;
|
2021-02-13 14:23:35 +00:00
|
|
|
default = [ ];
|
|
|
|
example = [ "-b" "0" ];
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
A list of extra command line arguments to pass to thinkfan.
|
|
|
|
Check the thinkfan(1) manpage for available arguments.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
settings = lib.mkOption {
|
|
|
|
type = lib.types.attrsOf settingsFormat.type;
|
2021-02-13 14:23:35 +00:00
|
|
|
default = { };
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2021-02-13 14:23:35 +00:00
|
|
|
Thinkfan settings. Use this option to configure thinkfan
|
|
|
|
settings not exposed in a NixOS option or to bypass one.
|
2022-08-12 12:06:08 +00:00
|
|
|
Before changing this, read the `thinkfan.conf(5)`
|
2021-02-13 14:23:35 +00:00
|
|
|
manpage and take a look at the example config file at
|
2022-08-12 12:06:08 +00:00
|
|
|
<https://github.com/vmatare/thinkfan/blob/master/examples/thinkfan.yaml>
|
2021-02-13 14:23:35 +00:00
|
|
|
'';
|
|
|
|
};
|
2020-04-24 23:36:52 +00:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
config = lib.mkIf cfg.enable {
|
2020-04-24 23:36:52 +00:00
|
|
|
|
|
|
|
environment.systemPackages = [ thinkfan ];
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
services.thinkfan.settings = lib.mapAttrs (k: v: lib.mkDefault v) {
|
2021-02-13 14:23:35 +00:00
|
|
|
sensors = map sensorToConf cfg.sensors;
|
|
|
|
fans = map sensorToConf cfg.fans;
|
|
|
|
levels = cfg.levels;
|
|
|
|
};
|
|
|
|
|
|
|
|
systemd.packages = [ thinkfan ];
|
|
|
|
|
|
|
|
systemd.services = {
|
2024-09-19 14:19:46 +00:00
|
|
|
thinkfan.environment.THINKFAN_ARGS = lib.escapeShellArgs ([ "-c" configFile ] ++ cfg.extraArgs);
|
2024-02-29 20:09:43 +00:00
|
|
|
thinkfan.serviceConfig = {
|
|
|
|
Restart = "on-failure";
|
|
|
|
RestartSec = "30s";
|
|
|
|
|
|
|
|
# Hardening
|
|
|
|
PrivateNetwork = true;
|
|
|
|
};
|
2021-02-13 14:23:35 +00:00
|
|
|
|
|
|
|
# must be added manually, see issue #81138
|
|
|
|
thinkfan.wantedBy = [ "multi-user.target" ];
|
|
|
|
thinkfan-wakeup.wantedBy = [ "sleep.target" ];
|
|
|
|
thinkfan-sleep.wantedBy = [ "sleep.target" ];
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
boot.extraModprobeConfig = "options thinkpad_acpi experimental=1 fan_control=1";
|
|
|
|
|
|
|
|
};
|
|
|
|
}
|