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 "i8kctl temp"]
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
}
|