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
|
local GFile = require("lgi").Gio.File
local gears = require("gears")
local spawn = require("awful.spawn")
local wibox = require("wibox")
local utils = require('utils')
local BatteryWidget = require('battery_wgt')
local Battery = require('battery')
local BatteryManager = {}
BatteryManager.__index = BatteryManager
setmetatable(BatteryManager, {
__call = function (cls, ...) return cls.new(...) end,
})
function BatteryManager:_log(msg, ...)
utils.log('BatteryManager', msg, ...)
end
function BatteryManager:_warn(msg, ...)
utils.warn('BatteryManager', msg, ...)
end
function BatteryManager:_battery_add(name)
if self._batteries[name] ~= nil then
-- only warn for usable batteries that are added twice for some reason
if self._batteries[name] ~= false then
self:_warn('Battery %s already exists', name)
end
return
end
self:_log('Trying power supply: ' .. name)
local bat = Battery(name)
if not bat then
self:_log('Skipping ' .. name .. ': could not be opened or is not a battery')
-- remember device as unusable
self._batteries[name] = false
return
end
self:_log('Opened battery %s: %s', name, bat.desc)
local wgt = BatteryWidget(bat)
self._layout:add(wgt)
self._batteries[name] = { widget = wgt, battery = bat }
end
function BatteryManager:_battery_remove(name)
self:_log('Removing battery ' .. name)
local tbl = self._batteries[name]
if tbl then
self._layout:remove_widgets(tbl.widget)
end
self._batteries[name] = nil
end
function BatteryManager:_refresh()
for name, item in pairs(self._batteries) do
if item and not item.battery:update() then
self:_log('Could not update battery %s, removing', name)
self:_battery_remove(name)
end
end
end
function BatteryManager.new()
local self = setmetatable({}, BatteryManager)
-- the layout containing the individual battery widgets
self._layout = wibox.layout.flex.horizontal()
-- entries are either
-- - tables { battery, widget } for existing batteries
-- - false for devices that we've seen, but are not valid batteries
self._batteries = {}
-- try adding all existing batteries
local ps = GFile.new_for_path('/sys/class/power_supply')
local ret = ps:enumerate_children('*', 0)
while true do
local file_info = ret:next_file()
if not file_info then
break
end
self:_battery_add(file_info:get_name())
end
-- monitor udev events
local function monitor_stdout(line)
local action, device = string.match(line, '^KERNEL%[%d+.%d*%]%s+(%a+)%s+([^%s]+)%s+%(power_supply%)$')
if action then
local name = string.match(device, '([^/]+)$')
if name == nil then
self:_warn('Could not extract device name from path: %s', device)
return
end
if action == 'add' or action == 'change' then
-- battery device not seen before, try adding
if self._batteries[name] == nil then
self:_battery_add(name)
end
-- device is a valid battery, refresh state
if self._batteries[name] then
self._batteries[name].battery:update()
end
elseif action == 'remove' then
self:_battery_remove(name)
end
end
end
local function monitor_exit(reason, code)
self:_warn('udevadm monitor exited: %s/%d', reason, code)
end
spawn.with_line_callback('udevadm monitor --kernel --subsystem-match=power_supply',
{ stdout = monitor_stdout, exit = monitor_exit, })
self:_refresh()
gears.timer.start_new(5, function ()
self:_refresh()
return true
end)
return self._layout
end
return BatteryManager
|