summaryrefslogtreecommitdiff
path: root/battery_mng.lua
blob: c04c52f8b9e0a2952a9c1e8dfb77d59ddb0f128a (plain)
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