]> 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:

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