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