1# Upower daemon.
2{
3 config,
4 lib,
5 pkgs,
6 ...
7}:
8let
9
10 cfg = config.services.upower;
11
12in
13
14{
15
16 ###### interface
17
18 options = {
19
20 services.upower = {
21
22 enable = lib.mkOption {
23 type = lib.types.bool;
24 default = false;
25 description = ''
26 Whether to enable Upower, a DBus service that provides power
27 management support to applications.
28 '';
29 };
30
31 package = lib.mkPackageOption pkgs "upower" { };
32
33 enableWattsUpPro = lib.mkOption {
34 type = lib.types.bool;
35 default = false;
36 description = ''
37 Enable the Watts Up Pro device.
38
39 The Watts Up Pro contains a generic FTDI USB device without a specific
40 vendor and product ID. When we probe for WUP devices, we can cause
41 the user to get a perplexing "Device or resource busy" error when
42 attempting to use their non-WUP device.
43
44 The generic FTDI device is known to also be used on:
45
46 - Sparkfun FT232 breakout board
47 - Parallax Propeller
48 '';
49 };
50
51 noPollBatteries = lib.mkOption {
52 type = lib.types.bool;
53 default = false;
54 description = ''
55 Don't poll the kernel for battery level changes.
56
57 Some hardware will send us battery level changes through
58 events, rather than us having to poll for it. This option
59 allows disabling polling for hardware that sends out events.
60 '';
61 };
62
63 ignoreLid = lib.mkOption {
64 type = lib.types.bool;
65 default = false;
66 description = ''
67 Do we ignore the lid state
68
69 Some laptops are broken. The lid state is either inverted, or stuck
70 on or off. We can't do much to fix these problems, but this is a way
71 for users to make the laptop panel vanish, a state that might be used
72 by a couple of user-space daemons. On Linux systems, see also
73 {manpage}`logind.conf(5)`.
74 '';
75 };
76
77 usePercentageForPolicy = lib.mkOption {
78 type = lib.types.bool;
79 default = true;
80 description = ''
81 Policy for warnings and action based on battery levels
82
83 Whether battery percentage based policy should be used. The default
84 is to use the percentage, which
85 should work around broken firmwares. It is also more reliable than
86 the time left (frantically saving all your files is going to use more
87 battery than letting it rest for example).
88 '';
89 };
90
91 percentageLow = lib.mkOption {
92 type = lib.types.ints.unsigned;
93 default = 20;
94 description = ''
95 When `usePercentageForPolicy` is
96 `true`, the levels at which UPower will consider the
97 battery low.
98
99 This will also be used for batteries which don't have time information
100 such as that of peripherals.
101
102 If any value (of `percentageLow`,
103 `percentageCritical` and
104 `percentageAction`) is invalid, or not in descending
105 order, the defaults will be used.
106 '';
107 };
108
109 percentageCritical = lib.mkOption {
110 type = lib.types.ints.unsigned;
111 default = 5;
112 description = ''
113 When `usePercentageForPolicy` is
114 `true`, the levels at which UPower will consider the
115 battery critical.
116
117 This will also be used for batteries which don't have time information
118 such as that of peripherals.
119
120 If any value (of `percentageLow`,
121 `percentageCritical` and
122 `percentageAction`) is invalid, or not in descending
123 order, the defaults will be used.
124 '';
125 };
126
127 percentageAction = lib.mkOption {
128 type = lib.types.ints.unsigned;
129 default = 2;
130 description = ''
131 When `usePercentageForPolicy` is
132 `true`, the levels at which UPower will take action
133 for the critical battery level.
134
135 This will also be used for batteries which don't have time information
136 such as that of peripherals.
137
138 If any value (of `percentageLow`,
139 `percentageCritical` and
140 `percentageAction`) is invalid, or not in descending
141 order, the defaults will be used.
142 '';
143 };
144
145 timeLow = lib.mkOption {
146 type = lib.types.ints.unsigned;
147 default = 1200;
148 description = ''
149 When `usePercentageForPolicy` is
150 `false`, the time remaining in seconds at which
151 UPower will consider the battery low.
152
153 If any value (of `timeLow`,
154 `timeCritical` and `timeAction`) is
155 invalid, or not in descending order, the defaults will be used.
156 '';
157 };
158
159 timeCritical = lib.mkOption {
160 type = lib.types.ints.unsigned;
161 default = 300;
162 description = ''
163 When `usePercentageForPolicy` is
164 `false`, the time remaining in seconds at which
165 UPower will consider the battery critical.
166
167 If any value (of `timeLow`,
168 `timeCritical` and `timeAction`) is
169 invalid, or not in descending order, the defaults will be used.
170 '';
171 };
172
173 timeAction = lib.mkOption {
174 type = lib.types.ints.unsigned;
175 default = 120;
176 description = ''
177 When `usePercentageForPolicy` is
178 `false`, the time remaining in seconds at which
179 UPower will take action for the critical battery level.
180
181 If any value (of `timeLow`,
182 `timeCritical` and `timeAction`) is
183 invalid, or not in descending order, the defaults will be used.
184 '';
185 };
186
187 allowRiskyCriticalPowerAction = lib.mkOption {
188 type = lib.types.bool;
189 default = false;
190 description = ''
191 Enable the risky critical power actions "Suspend" and "Ignore".
192 '';
193 };
194
195 criticalPowerAction = lib.mkOption {
196 type = lib.types.enum [
197 "PowerOff"
198 "Hibernate"
199 "HybridSleep"
200 "Suspend"
201 "Ignore"
202 ];
203 default = "HybridSleep";
204 description = ''
205 The action to take when `timeAction` or
206 `percentageAction` has been reached for the batteries
207 (UPS or laptop batteries) supplying the computer.
208
209 When set to `Suspend` or `Ignore`,
210 {option}`services.upower.allowRiskyCriticalPowerAction` must be set
211 to `true`.
212 '';
213 };
214
215 };
216
217 };
218
219 ###### implementation
220
221 config = lib.mkIf cfg.enable {
222 assertions = [
223 {
224 assertion =
225 let
226 inherit (builtins) elem;
227 riskyActions = [
228 "Suspend"
229 "Ignore"
230 ];
231 riskyActionEnabled = elem cfg.criticalPowerAction riskyActions;
232 in
233 riskyActionEnabled -> cfg.allowRiskyCriticalPowerAction;
234 message = ''
235 services.upower.allowRiskyCriticalPowerAction must be true if
236 services.upower.criticalPowerAction is set to
237 '${cfg.criticalPowerAction}'.
238 '';
239 }
240 ];
241
242 environment.systemPackages = [ cfg.package ];
243
244 services.dbus.packages = [ cfg.package ];
245
246 services.udev.packages = [ cfg.package ];
247
248 systemd.packages = [ cfg.package ];
249
250 environment.etc."UPower/UPower.conf".text = lib.generators.toINI { } {
251 UPower = {
252 EnableWattsUpPro = cfg.enableWattsUpPro;
253 NoPollBatteries = cfg.noPollBatteries;
254 IgnoreLid = cfg.ignoreLid;
255 UsePercentageForPolicy = cfg.usePercentageForPolicy;
256 PercentageLow = cfg.percentageLow;
257 PercentageCritical = cfg.percentageCritical;
258 PercentageAction = cfg.percentageAction;
259 TimeLow = cfg.timeLow;
260 TimeCritical = cfg.timeCritical;
261 TimeAction = cfg.timeAction;
262 AllowRiskyCriticalPowerAction = cfg.allowRiskyCriticalPowerAction;
263 CriticalPowerAction = cfg.criticalPowerAction;
264 };
265 };
266 };
267
268}