forked from vitorafsr/i8kutils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
i8kmon
executable file
·365 lines (303 loc) · 9.47 KB
/
i8kmon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#!/usr/bin/env tclsh
#
# i8kmon -- Monitor the temperature on Dell laptops.
#
# Copyright (C) 2013-2017 Vitor Augusto <vitorafsr@gmail.com>
# Copyright (C) 2001-2005 Massimo Dal Zotto <dz@debian.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
array set config {
sysconfig /etc/i8kmon.conf
userconfig ~/.i8kmon
i8kfan /usr/bin/i8kfan
acpi "acpi"
use_conf 1
verbose 0
timeout 2
t_high 80
num_configs 5
0 {{0 0} -1 60 -1 65}
1 {{1 0} 50 70 55 75}
2 {{1 1} 60 80 65 85}
3 {{2 1} 65 85 70 90}
4 {{2 2} 70 128 75 128}
}
array set status {
leftspeed {}
rightspeed {}
nfans 2
acpi_timer 0
state 0
temp 0
lstate -2
rstate -2
ac 0
t_low 0
t_high 0
}
proc read_config {} {
global config
global status
# read system config file
if {[file exists $config(sysconfig)]} {
source $config(sysconfig)
if {$config(verbose) > 0} {
puts "reading system config file"
}
}
# read user config file
# as it is read last, the options here have precedence
if {$config(use_conf) != 0} {
if {[file exists $config(userconfig)]} {
source $config(userconfig)
if {$config(verbose) > 0} {
puts "reading user config file"
}
}
}
for {set key 0} {$key < $config(num_configs)} {incr key} {
set fans [lindex $config($key) 0]
set lo_ac [lindex $config($key) 1]
set hi_ac [lindex $config($key) 2]
set lo_bt [lindex $config($key) 3]
set hi_bt [lindex $config($key) 4]
# check that for each key hi temp > lo temp
if {$lo_ac > $hi_ac} {set hi_ac [expr $lo_ac + 5]}
if {$lo_bt > $hi_bt} {set hi_bt [expr $lo_bt + 5]}
# set temperature to -1 to lo_ac and lo_bt at config(0)
if {$key == 0} {
set lo_ac -1
set lo_bt -1
}
# set temperature to 128 to hi_ac and hi_bt at config(3)
if {$key == [expr $config(num_configs)-1]} {
set hi_ac 128
set hi_bt 128
}
set config($key) [list $fans $lo_ac $hi_ac $lo_bt $hi_bt]
}
}
proc status_timer {} {
global config
global status
# Reschedule status timer
after [expr $config(timeout)*1000] {status_timer}
check_status
}
proc check_status {} {
global config
global status
if {![read_i8k_status]} {return}
fan_control
}
proc read_i8k_status {} {
global config
global status
set status(temp) [eval exec "i8ktemp"]
set speed [eval exec "i8kctl speed"]
set lspeed [lindex $speed 0]
set rspeed [lindex $speed 1]
# Calculate fan state from probed fans speeds.
# It improve performance as it does not requires reading values all the time from the system,
# since reading fan speeds is a slow call that freezes the computer in some Dell laptops models.
set status(lstate) 0
set status(rstate) 0
set ldiff_speed "+inf"
set rdiff_speed "+inf"
# Search for nearest value
for {set idx_speed 0} {$idx_speed < 4} {incr idx_speed} {
if {[expr abs($lspeed - [lindex $status(leftspeed) $idx_speed]) < $ldiff_speed]} {
set ldiff_speed [expr abs($lspeed - [lindex $status(leftspeed) $idx_speed])]
set status(lstate) $idx_speed
}
if {[expr abs($rspeed - [lindex $status(rightspeed) $idx_speed]) < $rdiff_speed]} {
set rdiff_speed [expr abs($rspeed - [lindex $status(rightspeed) $idx_speed])]
set status(rstate) $idx_speed
}
}
# If AC status is not available read it from procfs
set ac [eval exec "i8kctl ac"]
if {$ac < 0} {
read_ac_status
}
if {$config(verbose) > 0} {
puts "temp, \[fan state\], \[fan speed\], ac state: $status(temp) \[$status(lstate) $status(rstate)\] \[$lspeed $rspeed\] $status(ac)"
}
return 1
}
proc read_ac_status {} {
global config
global status
# Read ac status once per minute
if {[incr status(acpi_timer) -1] > 0} {
return 1
}
set status(acpi_timer) [expr 60 / $config(timeout)]
set acpi_ac [exec {*}$config(acpi)]
if {[string match *on-line* $acpi_ac] || [string match *online* $acpi_ac]} {
set status(ac) 1
} else {
set status(ac) 0
}
if {$config(verbose) > 0} {
puts "[clock seconds] acpi: $acpi_ac"
}
return 0
}
proc fan_control {} {
global config
global status
set index [expr $status(ac) ? 1 : 3]
set state $status(state)
set temp $status(temp)
set status(t_low) [lindex $config($state) $index]
set status(t_high) [lindex $config($state) [expr $index+1]]
while {$temp < 128 && $temp >= $status(t_high)} {
if {$config(verbose) > 0} {
puts -nonewline "# ($temp>=$status(t_high)), "
}
incr state
set status(t_low) [lindex $config($state) $index]
set status(t_high) [lindex $config($state) [expr $index+1]]
if {$config(verbose) > 0} {
puts "state=$state, low=$status(t_low), high=$status(t_high)"
}
}
while {$temp > 0 && $temp <= $status(t_low)} {
if {$config(verbose) > 0} {
puts -nonewline "# ($temp<=$status(t_low)), "
}
incr state -1
set status(t_low) [lindex $config($state) $index]
set status(t_high) [lindex $config($state) [expr $index+1]]
if {$config(verbose) > 0} {
puts "state=$state, low=$status(t_low), high=$status(t_high)"
}
}
set_fan $state
}
proc set_fan {state} {
global config
global status
set status(state) $state
set args [lindex $config($state) 0]
set left [lindex $args 0]
set right [lindex $args 1]
if {$status(lstate) == -1 || $left == $status(lstate)} {
set left "-"
} else {
set status(lstate) $left
}
if {$status(rstate) == -1 || $right == $status(rstate)} {
set right "-"
} else {
set status(rstate) $right
}
if {$status(nfans) < 2} { set right "-" }
if {$left == "-" && $right == "-"} {return}
if {$config(verbose) > 0} {
puts "$config(i8kfan) $left $right"
}
exec $config(i8kfan) $left $right
}
proc usage {} {
global argv0
regsub -all {^.*/} $argv0 {} progname
puts "Usage: $progname \[<options>...]\n"
puts "Options:\n"
puts " -nc|--nouserconfig don\x27t read \$HOME/.i8kmon"
puts " -v|--verbose report log to stdout"
puts " -t|--timeout <secs> set poll timeout\n"
}
proc parse_options {} {
global config
global status
global argv
for {set i 0} {$i < [llength $argv]} {incr i} {
set arg [lindex $argv $i]
switch -- $arg {
-\? - -h - -help - --help {
usage
exit
}
--nouserconfig - -nc {
set config(use_conf) 0
}
--verbose - -v {
set config(verbose) 1
}
--timeout - -t {
set config(timeout) [lindex $argv [incr i]]
}
-- {
continue
}
default {
puts stderr "invalid option: $arg"
exit 1
}
}
}
if {$config(verbose) > 0} {
puts "i8kmon"
parray config
parray status
}
}
proc probe_fan_speed {} {
global config
global status
if {$status(leftspeed) != {} || $status(rightspeed) != {}} {return}
exec $config(i8kfan) 1 1
after 5000
set speeds [eval exec "i8kctl speed"]
set lspeed1 [lindex $speeds 0]
set rspeed1 [lindex $speeds 1]
exec $config(i8kfan) 2 2
after 5000
set speeds [eval exec "i8kctl speed"]
set lspeed2 [lindex $speeds 0]
set rspeed2 [lindex $speeds 1]
exec $config(i8kfan) 3 3
after 5000
set speeds [eval exec "i8kctl speed"]
set lspeed3 [lindex $speeds 0]
set rspeed3 [lindex $speeds 1]
set status(leftspeed) "0 $lspeed1 $lspeed2 $lspeed3"
set status(rightspeed) "0 $rspeed1 $rspeed2 $rspeed3"
if {$config(verbose) > 0} {
puts "Probed fan speeds are left=$status(leftspeed), right=status(rightspeed)"
}
}
# Probe external tools
proc probe_tools {} {
# The possibility of choosing 'acpi' or 'acpitool' is for compatibility between different architectures: amd64, i386, kFreeBSD.
# This code below is strictly related on package dependency stated at keyword 'Depends:' on file 'debian/control'.
if {![catch {exec acpi}]} {
set config(acpi) "acpi"
} elseif {[catch {exec acpitool}]} {
set config(acpi) "acpitool"
} else {
puts stderr "Package dependency problem: neither 'acpi' nor 'acpitool' package is installed."
}
}
proc main {} {
global status
read_config
probe_fan_speed
probe_tools
parse_options
status_timer
}
if {$tcl_interactive == 0} {
main
vwait forever
}