# pylint: disable=C0111,R0903 """Displays battery status, remaining percentage and charging information. Parameters: * battery.device : Comma-separated list of battery devices to read information from (defaults to auto for auto-detection) * battery.warning : Warning threshold in % of remaining charge (defaults to 20) * battery.critical : Critical threshold in % of remaining charge (defaults to 10) * battery.showdevice : If set to 'true', add the device name to the widget (defaults to False) * battery.decorate : If set to 'false', hides additional icons (charging, etc.) (defaults to True) * battery.showpowerconsumption: If set to 'true', show current power consumption (defaults to False) * battery.compact-devices : If set to 'true', compacts multiple batteries into a single entry (default to False) """ import os import glob import logging log = logging.getLogger(__name__) try: import power except ImportError: log.warning('unable to import module "power": Time estimates will not be available') import core.module import core.widget import core.input import util.format class BatteryManager(object): def remaining(self): try: estimate = power.PowerManagement().get_time_remaining_estimate() # do not show remaining if on AC if estimate == power.common.TIME_REMAINING_UNLIMITED: return None return estimate*60 # return value in seconds except Exception as e: return -1 return -1 def read(self, battery, component, default=None): path = '/sys/class/power_supply/{}'.format(battery) if not os.path.exists(path): return default try: with open('{}/{}'.format(path, component)) as f: return f.read().strip() except IOError: return 'n/a' return default def capacity(self, battery): capacity = self.read(battery, 'capacity', 100) if capacity != 'n/a': capacity = int(capacity) return capacity if capacity < 100 else 100 def capacity_all(self, batteries): now = 0 full = 0 for battery in batteries: try: with open('/sys/class/power_supply/{}/energy_full'.format(battery)) as f: full += int(f.read()) with open('/sys/class/power_supply/{}/energy_now'.format(battery)) as f: now += int(f.read()) except IOError: return 'n/a' return int(float(now)/float(full)*100.0) def isac(self, battery): path = '/sys/class/power_supply/{}'.format(battery) return not os.path.exists(path) def isac_any(self, batteries): for battery in batteries: if self.isac(battery): return True return False def consumption(self, battery): consumption = self.read(battery, 'power_now', 'n/a') if consumption == 'n/a': return 'n/a' return '{}W'.format(int(consumption)/1000000) def charge(self, battery): return self.read(battery, 'status', 'n/a') def charge_any(self, batteries): for battery in batteries: if self.charge(battery) == 'Discharging': return 'Discharging' return 'Charged' class Module(core.module.Module): def __init__(self, config, theme): widgets = [] super().__init__(config, theme, widgets) self.__manager = BatteryManager() self._batteries = util.format.aslist(self.parameter('device', 'auto')) if self._batteries[0] == 'auto': self._batteries = [ os.path.basename(battery) for battery in glob.glob('/sys/class/power_supply/BAT*') ] if len(self._batteries) == 0: raise Exceptions('no batteries configured/found') core.input.register(self, button=core.input.LEFT_MOUSE, cmd='gnome-power-statistics') if util.format.asbool(self.parameter('compact-devices', False)): widget = core.widget.Widget(full_text=self.capacity, name='all-batteries', module=self) widgets.append(widget) else: for battery in self._batteries: log.debug('adding new widget for {}'.format(battery)) widget = core.widget.Widget(full_text=self.capacity, name=battery, module=self) widgets.append(widget) for w in self.widgets(): if util.format.asbool(self.parameter('decorate', True)) == False: widget.set('theme.exclude', 'suffix') def capacity(self, widget): if widget.name() == 'all-batteries': capacity = self.__manager.capacity_all(self._batteries) else: capacity = self.__manager.capacity(widget.name()) widget.set('capacity', capacity) widget.set('ac', self.__manager.isac_any(self._batteries)) widget.set('theme.minwidth', '100%') # Read power conumption if util.format.asbool(self.parameter('showpowerconsumption', False)): output = '{}% ({})'.format(capacity, self.__manager.consumption(widget.name())) else: output = '{}%'.format(capacity) if util.format.asbool(self.parameter('showremaining', True))\ and self.__manager.charge(widget.name()) == 'Discharging': remaining = self.__manager.remaining() if remaining >= 0: output = '{} {}'.format(output, util.format.duration(remaining, compact=True, unit=True)) if util.format.asbool(self.parameter('showdevice', False)): output = '{} ({})'.format(output, widget.name()) return output def state(self, widget): state = [] capacity = widget.get('capacity') if capacity < 0: log.debug('battery state: {}'.format(state)) return ['critical', 'unknown'] if capacity < int(self.parameter('critical', 10)): state.append('critical') elif capacity < int(self.parameter('warning', 20)): state.append('warning') if widget.get('ac'): state.append('AC') else: if widget.name() == 'all-batteries': charge = self.__manager.charge_any(self._batteries) else: charge = self.__manager.charge(widget.name()) if charge == 'Discharging': state.append('discharging-{}'.format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity)))) elif charge == 'Unknown': state.append('unknown-{}'.format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity)))) else: if capacity > 95: state.append('charged') else: state.append('charging') return state # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4