1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 configFormat = pkgs.formats.json { };
9 cfg = config.hardware.fw-fanctrl;
10in
11{
12 options.hardware.fw-fanctrl = {
13 enable = lib.mkEnableOption "the fw-fanctrl systemd service and install the needed packages";
14
15 package = lib.mkPackageOption pkgs "fw-fanctrl" { };
16
17 ectoolPackage = lib.mkPackageOption pkgs "fw-ectool" { };
18
19 disableBatteryTempCheck = lib.mkOption {
20 type = lib.types.bool;
21 default = false;
22 description = ''
23 Disable checking battery temperature sensor
24 '';
25 };
26
27 config = lib.mkOption {
28 default = { };
29 description = ''
30 Additional config entries for the fw-fanctrl service (documentation: <https://github.com/TamtamHero/fw-fanctrl/blob/main/doc/configuration.md>)
31 '';
32 type = lib.types.submodule {
33 freeformType = lib.types.attrsOf configFormat.type;
34 options = {
35 defaultStrategy = lib.mkOption {
36 type = lib.types.str;
37 default = "lazy";
38 description = "Default strategy to use";
39 };
40
41 strategyOnDischarging = lib.mkOption {
42 type = lib.types.str;
43 default = "";
44 description = "Default strategy on discharging";
45 };
46
47 strategies = lib.mkOption {
48 default = { };
49 description = ''
50 Additional strategies which can be used by fw-fanctrl
51 '';
52 type = lib.types.attrsOf (
53 lib.types.submodule {
54 options = {
55 fanSpeedUpdateFrequency = lib.mkOption {
56 type = lib.types.ints.unsigned;
57 default = 5;
58 description = "How often the fan speed should be updated in seconds";
59 };
60
61 movingAverageInterval = lib.mkOption {
62 type = lib.types.ints.unsigned;
63 default = 25;
64 description = "Interval (seconds) of the last temperatures to use to calculate the average temperature";
65 };
66
67 speedCurve = lib.mkOption {
68 default = [ ];
69 description = "How should the speed curve look like";
70 type = lib.types.listOf (
71 lib.types.submodule {
72 options = {
73 temp = lib.mkOption {
74 type = lib.types.int;
75 default = 0;
76 description = "Temperature in °C at which the fan speed should be changed";
77 };
78
79 speed = lib.mkOption {
80 type = lib.types.ints.between 0 100;
81 default = 0;
82 description = "Percent how fast the fan should run at";
83 };
84
85 };
86 }
87 );
88 };
89 };
90 }
91 );
92 };
93 };
94 };
95 };
96 };
97
98 config =
99 let
100 defaultConfig = builtins.fromJSON (builtins.readFile "${cfg.package}/share/fw-fanctrl/config.json");
101 finalConfig = lib.attrsets.recursiveUpdate defaultConfig cfg.config;
102 configFile = configFormat.generate "custom.json" finalConfig;
103 in
104 lib.mkIf cfg.enable {
105 environment.systemPackages = [
106 cfg.package
107 cfg.ectoolPackage
108 ];
109
110 systemd.services.fw-fanctrl = {
111 description = "Framework Fan Controller";
112 after = [ "multi-user.target" ];
113 serviceConfig = {
114 Type = "simple";
115 Restart = "always";
116 ExecStart = "${lib.getExe cfg.package} --output-format JSON run --config ${configFile} --silent ${lib.optionalString cfg.disableBatteryTempCheck "--no-battery-sensors"}";
117 ExecStopPost = "${lib.getExe cfg.ectoolPackage} autofanctrl";
118 };
119 wantedBy = [ "multi-user.target" ];
120 };
121
122 # Create suspend config
123 environment.etc."systemd/system-sleep/fw-fanctrl-suspend.sh".source =
124 "${cfg.package}/share/fw-fanctrl/fw-fanctrl-suspend";
125 };
126
127 meta = {
128 maintainers = [ lib.maintainers.Svenum ];
129 };
130}