]> git.madduck.net Git - etc/awesome.git/blob - widget/bat.lua

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge pull request #344 from 2009/patch-5
[etc/awesome.git] / widget / bat.lua
1
2 --[[
3                                                                                                                         
4          Licensed under GNU General Public License v2 
5           * (c) 2013,      Luke Bonham                
6           * (c) 2010-2012, Peter Hofmann              
7                                                                                                                         
8 --]]
9
10 local first_line = require("lain.helpers").first_line
11 local newtimer   = require("lain.helpers").newtimer
12 local naughty    = require("naughty")
13 local wibox      = require("wibox")
14 local math       = { abs    = math.abs,
15                      floor  = math.floor,
16                      log10  = math.log10,
17                      min    = math.min }
18 local string     = { format = string.format }
19 local ipairs     = ipairs
20 local tonumber   = tonumber
21
22 -- Battery infos
23 -- lain.widget.bat
24
25 local function factory(args)
26     local bat       = { widget = wibox.widget.textbox() }
27     local args      = args or {}
28     local timeout   = args.timeout or 30
29     local batteries = args.batteries or (args.battery and {args.battery}) or {"BAT0"}
30     local ac        = args.ac or "AC0"
31     local notify    = args.notify or "on"
32     local n_perc    = args.n_perc or { 5, 15 }
33     local settings  = args.settings or function() end
34
35     bat_notification_critical_preset = {
36         title   = "Battery exhausted",
37         text    = "Shutdown imminent",
38         timeout = 15,
39         fg      = "#000000",
40         bg      = "#FFFFFF"
41     }
42
43     bat_notification_low_preset = {
44         title   = "Battery low",
45         text    = "Plug the cable!",
46         timeout = 15,
47         fg      = "#202020",
48         bg      = "#CDCDCD"
49     }
50
51     bat_now = {
52         status    = "N/A",
53         ac_status = "N/A",
54         perc      = "N/A",
55         time      = "N/A",
56         watt      = "N/A"
57     }
58
59     bat_now.n_status = {}
60     bat_now.n_perc   = {}
61     for i = 1, #batteries do
62         bat_now.n_status[i] = "N/A"
63         bat_now.n_perc[i] = 0
64     end
65
66     function bat.update()
67         local sum_rate_current = 0
68         local sum_rate_voltage = 0
69         local sum_rate_power   = 0
70         local sum_rate_energy  = 0
71         local sum_energy_now   = 0
72         local sum_energy_full  = 0
73         local pspath           = "/sys/class/power_supply/"
74
75         for i, battery in ipairs(batteries) do
76             local bstr    = pspath .. battery
77             local present = first_line(bstr .. "/present")
78
79             if tonumber(present) == 1 then
80                 -- current_now(I)[uA], voltage_now(U)[uV], power_now(P)[uW]
81                 local rate_current = tonumber(first_line(bstr .. "/current_now"))
82                 local rate_voltage = tonumber(first_line(bstr .. "/voltage_now"))
83                 local rate_power   = tonumber(first_line(bstr .. "/power_now"))
84
85                 -- energy_now(P)[uWh], charge_now(I)[uAh]
86                 local energy_now        = tonumber(first_line(bstr .. "/energy_now") or
87                                           first_line(bstr .. "/charge_now"))
88
89                 -- energy_full(P)[uWh], charge_full(I)[uAh]
90                 local energy_full       = tonumber(first_line(bstr .. "/energy_full") or
91                                           first_line(bstr .. "/charge_full"))
92
93                 local energy_percentage = tonumber(first_line(bstr .. "/capacity")) or
94                                           math.floor((energy_now / energy_full) * 100)
95
96                 bat_now.n_status[i] = first_line(bstr .. "/status") or "N/A"
97                 bat_now.n_perc[i]   = energy_percentage or bat_now.n_perc[i]
98
99                 sum_rate_current = sum_rate_current + (rate_current or 0)
100                 sum_rate_voltage = sum_rate_voltage + (rate_voltage or 0)
101                 sum_rate_power   = sum_rate_power + (rate_power or 0)
102                 sum_rate_energy  = sum_rate_energy + (rate_power or (((rate_voltage or 0) * (rate_current or 0)) / 1e6))
103                 sum_energy_now   = sum_energy_now + (energy_now or 0)
104                 sum_energy_full  = sum_energy_full + (energy_full or 0)
105             end
106         end
107
108         -- When one of the battery is charging, others' status are either
109         -- "Full", "Unknown" or "Charging". When the laptop is not plugged in,
110         -- one or more of the batteries may be full, but only one battery
111         -- discharging suffices to set global status to "Discharging".
112         bat_now.status = bat_now.n_status[1]
113         for _,status in ipairs(bat_now.n_status) do
114             if status == "Discharging" or status == "Charging" then
115                 bat_now.status = status
116             end
117         end
118         bat_now.ac_status = tonumber(first_line(string.format("%s%s/online", pspath, ac))) or "N/A"
119
120         if bat_now.status ~= "N/A" then
121             if bat_now.status ~= "Full" and sum_rate_power == 0 and bat_now.ac_status == 1 then
122                 bat_now.perc  = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
123                 bat_now.time  = "00:00"
124                 bat_now.watt  = 0
125
126             -- update {perc,time,watt} iff battery not full and rate > 0
127             elseif bat_now.status ~= "Full" then
128                 local rate_time = 0
129                 -- Calculate time and watt if rates are greater then 0
130                 if (sum_rate_power > 0 or sum_rate_current > 0) then
131                     local div = (sum_rate_power > 0 and sum_rate_power) or sum_rate_current
132
133                     if bat_now.status == "Charging" then
134                         rate_time = (sum_energy_full - sum_energy_now) / div
135                     else -- Discharging
136                         rate_time = sum_energy_now / div
137                     end
138
139                     if 0 < rate_time and rate_time < 0.01 then -- check for magnitude discrepancies (#199)
140                         rate_time_magnitude = math.abs(math.floor(math.log10(rate_time)))
141                         rate_time = rate_time * 10^(rate_time_magnitude - 2)
142                     end
143                  end
144
145                 local hours   = math.floor(rate_time)
146                 local minutes = math.floor((rate_time - hours) * 60)
147                 bat_now.perc  = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
148                 bat_now.time  = string.format("%02d:%02d", hours, minutes)
149                 bat_now.watt  = tonumber(string.format("%.2f", sum_rate_energy / 1e6))
150             elseif bat_now.status == "Full" then
151                 bat_now.perc  = 100
152                 bat_now.time  = "00:00"
153                 bat_now.watt  = 0
154             end
155         end
156
157         widget = bat.widget
158         settings()
159
160         -- notifications for critical and low levels
161         if notify == "on" and bat_now.status == "Discharging" then
162             if tonumber(bat_now.perc) <= n_perc[1] then
163                 bat.id = naughty.notify({
164                     preset = bat_notification_critical_preset,
165                     replaces_id = bat.id
166                 }).id
167             elseif tonumber(bat_now.perc) <= n_perc[2] then
168                 bat.id = naughty.notify({
169                     preset = bat_notification_low_preset,
170                     replaces_id = bat.id
171                 }).id
172             end
173         end
174     end
175
176     newtimer("batteries", timeout, bat.update)
177
178     return bat
179 end
180
181 return factory