bumblebee-status/modules/core/battery.py
tobi-wan-kenobi 9cd9ff626d [core] make widget name an attribute
first, this fixes #607

also, i think it slightly simplifies code to make "simple" stuff like
names, etc. attributes instead of methods all the time.

so, expect this to be extended to other components, as well.
2020-04-30 12:42:34 +02:00

183 lines
6.9 KiB
Python

# 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