1 -- statusd_laptopstatus.lua v0.0.2 (last modified 2005-06-13)
3 -- Copyright (C) 2005 Jari Eskelinen <jari.eskelinen@iki.fi>
4 -- modified by René van Bevern <rvb@pro-linux.de> for error handling
6 -- Permission to copy, redistirbute, modify etc. is granted under the terms
7 -- of GNU GENERAL PUBLIC LICENSE Version 2.
9 -- This is script for displaying some interesting information about your
10 -- laptops power saving in Ion's status monitor. Script is very Linux
11 -- specific (uses /proc interface) and needs ACPI -support new enough (don't
12 -- know exactly but 2.6.x kernels should be fine). Also if you have some
13 -- kind of exotic hardware (multiple processors, multiple batteries etc.)
14 -- this script probably will fail or show incorrect information.
16 -- Just throw this script under ~/.ion3 and add following keywords to your
17 -- cfg_statusbar.lua's template-line with your own taste:
19 -- %laptopstatus_cpuspeed
20 -- %laptopstatus_temperature
21 -- %laptopstatus_batterypercent
22 -- %laptopstatus_batterytimeleft
23 -- %laptopstatus_batterydrain
25 -- Template example: template="[ %date || load:% %>load || CPU: %laptopstatus_cpuspeed %laptopstatus_temperature || BATT: %laptopstatus_batterypercent %laptopstatus_batterytimeleft %laptopstatus_batterydrain ]"
27 -- You can also run this script with lua interpreter and see if you get
30 -- NOTICE: This is my first ion/lua-script, so probably this can be done better.
31 -- Feel free to improve this script.
33 -- TODO: * Is it stupid to use file:read("*all"), can this cause infinite
34 -- loops in some weird situations?
35 -- * Do not poll for information not defined in template to be used
36 -- * Auto-detect right acpi devices instead of hardcoded BATT0 etc.
42 if not statusd_laptopstatus
then
43 statusd_laptopstatus
= {
44 interval
= 10, -- Polling interval in seconds
45 temperature_important
= 66, -- Temperature which will cause important hint
46 temperature_critical
= 71, -- Temperature which will cause critical hint
47 batterypercent_important
= 10, -- Battery percent which will cause important hint
48 batterypercent_critical
= 5, -- Battery percent which will cause critical hint
49 batterytimeleft_important
= 600, -- Battery time left (in secs) which will cause important hint
50 batterytimeleft_critical
= 300, -- Battery time left (in secs) which will cause critical hint
51 ac_state
= {"/proc/acpi/ac_adapter/AC/state",
52 "/proc/acpi/ac_adapter/ACAD/state",
53 "/proc/acpi/ac_adapter/ADP0/state",
54 "/proc/acpi/ac_adapter/ADP1/state"},
55 temp_info
= {"/proc/acpi/thermal_zone/THRM/temperature",
56 "/proc/acpi/thermal_zone/THM/temperature",
57 "/proc/acpi/thermal_zone/THM0/temperature",
58 "/proc/acpi/thermal_zone/THM1/temperature"},
59 bat_info
= {"/proc/acpi/battery/BAT0/info",
60 "/proc/acpi/battery/BAT1/info"},
61 bat_state
= {"/proc/acpi/battery/BAT0/state",
62 "/proc/acpi/battery/BAT1/state"}
66 if statusd
== nil then
69 statusd_laptopstatus
=table.join(statusd
.get_config("laptopstatus"), statusd_laptopstatus
)
74 local laptopstatus_timer
76 function try_open(files
, mode
)
77 for _
, file
in pairs(files
) do
78 local fd
= io
.open(file
, mode
)
79 if fd
then return fd
, file
end
83 local function get_cpu()
87 local file
= io
.open("/proc/cpuinfo", "r")
88 status
, _
, mhz
= string.find(file
:read("*all"),
90 if not status
then error("could not get MHz") end
93 then return {speed
=string.format("%4dMHz", math
.ceil(mhz
/5)*5),
95 else return {speed
="n/a", hint
=hint
} end
99 local function get_ac()
100 local file
= try_open(statusd_laptopstatus
.ac_state
, "r")
101 if not string.find(file
:read("*all"), "state:%s+on.line") then return 0
107 local function get_thermal()
108 local temp
, hint
= nil, "normal"
112 local file
=try_open(statusd_laptopstatus
.temp_info
, "r")
113 status
, _
, temp
= string.find(file
:read("*all"),
114 "temperature:%s+(%d+).*")
115 if not status
then error("could not get temperature") end
116 temp
= tonumber(temp
)
119 then if temp
>= statusd_laptopstatus
.temperature_important
then
120 hint
= "important" end
121 if temp
>= statusd_laptopstatus
.temperature_critical
then
122 hint
= "critical" end
123 return {temperature
=string.format("%02dC", temp
), hint
=hint
}
124 else return {temperature
="n/a", hint
= "hint"} end
128 local function get_battery()
129 local percenthint
= "normal"
130 local timelefthint
= "normal"
131 local lastfull
, rate
, rateunit
, remaining
135 local file
=try_open(statusd_laptopstatus
.bat_info
, "r")
136 local infocontents
= file
:read("*all")
139 local file
=try_open(statusd_laptopstatus
.bat_state
, "r")
140 local statecontents
= file
:read("*all")
143 status
, _
, lastfull
= string.find(infocontents
, "last full capacity:%s+(%d+).*")
144 if not status
then error("could not get full battery capacity") end
145 lastfull
= tonumber(lastfull
)
146 if string.find(statecontents
, "present rate:%s+unknown.*") then
149 status
, _
, rate
, rateunit
= string.find(statecontents
, "present rate:%s+(%d+)(.*)")
150 if not status
then error("could not get battery draining-rate") end
151 rate
= tonumber(rate
)
153 status
, _
, remaining
= string.find(statecontents
, "remaining capacity:%s+(%d+).*")
154 if not status
then error("could not get remaining capacity") end
155 remaining
= tonumber(remaining
)
157 local percent
= math
.floor(remaining
/ lastfull
* 100 + 5/10)
159 local hours
, secs
, mins
160 if get_ac() == 1 then
162 elseif rate
<= 0 then
165 secs
= 3600 * (remaining
/ rate
)
167 hours
= math
.floor(mins
/ 60)
168 mins
= math
.floor(mins
- (hours
* 60))
169 timeleft
= string.format("%02d:%02d", hours
, mins
)
172 if secs
~= nil and secs
<= statusd_laptopstatus
.batterytimeleft_important
then timelefthint
= "important" end
173 if secs
~= nil and secs
<= statusd_laptopstatus
.batterytimeleft_critical
then timelefthint
= "critical" end
174 if percent
<= statusd_laptopstatus
.batterypercent_important
then percenthint
= "important" end
175 if percent
<= statusd_laptopstatus
.batterypercent_critical
then percenthint
= "critical" end
177 return { percent
=string.format("%02d%%", percent
), timeleft
=timeleft
, drain
=tostring(rate
)..rateunit
, percenthint
=percenthint
, timelefthint
=timelefthint
}
178 else return { percent
="n/a", timeleft
="n/a", drain
="n/a", percenthint
=percenthint
, timelefthint
=timelefthint
} end
181 local last_timeleft
= nil
183 local function update_laptopstatus ()
185 thermal
= get_thermal()
186 battery
= get_battery()
188 -- Informing statusd OR printing to stdout if statusd not present
189 if statusd
~= nil then
190 statusd
.inform("laptopstatus_cpuspeed", cpu
.speed
)
191 statusd
.inform("laptopstatus_cpuspeed_template", "xxxxMHz")
192 statusd
.inform("laptopstatus_cpuspeed_hint", cpu
.hint
)
193 statusd
.inform("laptopstatus_temperature", thermal
.temperature
)
194 statusd
.inform("laptopstatus_temperature_template", "xxxC")
195 statusd
.inform("laptopstatus_temperature_hint", thermal
.hint
)
196 statusd
.inform("laptopstatus_batterypercent", battery
.percent
)
197 statusd
.inform("laptopstatus_batterypercent_template", "xxx%")
198 statusd
.inform("laptopstatus_batterypercent_hint", battery
.percenthint
)
199 if battery
.timeleft
~= "n/a" or last_timeleft
== " AC" then
200 statusd
.inform("laptopstatus_batterytimeleft", battery
.timeleft
)
201 last_timeleft
= battery
.timeleft
203 statusd
.inform("laptopstatus_batterytimeleft_template", "hh:mm")
204 statusd
.inform("laptopstatus_batterytimeleft_hint", battery
.timelefthint
)
205 statusd
.inform("laptopstatus_batterydrain", battery
.drain
)
206 laptopstatus_timer
:set(statusd_laptopstatus
.interval
*1000, update_laptopstatus
)
208 io
.stdout
:write("CPU: "..cpu
.speed
.." "..thermal
.temperature
.." || BATT: "..battery
.percent
.." "..battery
.timeleft
.."\n")
209 laptopstatus_timer
:set(statusd_laptopstatus
.interval
*2*1000, update_laptopstatus
)
214 if statusd
~= nil then
215 laptopstatus_timer
= statusd
.create_timer()
217 update_laptopstatus()