[modules/battery] Autodetect battery devices
The module now creates a widget for each battery device it detects and shows the status for each of them (I don't know of anyone with more than a single battery, but if I'm overhauling the module anyhow, might as well do it comprehensively). fixes #117
This commit is contained in:
parent
24a7c5c926
commit
d69f13f0b4
2 changed files with 70 additions and 51 deletions
|
@ -3,12 +3,14 @@
|
|||
"""Displays battery status, remaining percentage and charging information.
|
||||
|
||||
Parameters:
|
||||
* battery.device : The device to read information from (defaults to BAT0)
|
||||
* 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
|
||||
"""
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
import bumblebee.input
|
||||
import bumblebee.output
|
||||
|
@ -16,56 +18,73 @@ import bumblebee.engine
|
|||
|
||||
class Module(bumblebee.engine.Module):
|
||||
def __init__(self, engine, config):
|
||||
widget = bumblebee.output.Widget(full_text=self.capacity)
|
||||
widget.set("theme.minwidth", "100%")
|
||||
super(Module, self).__init__(engine, config, widget)
|
||||
battery = self.parameter("device", "BAT0")
|
||||
self._path = "/sys/class/power_supply/{}".format(battery)
|
||||
self._capacity = 100
|
||||
self._ac = False
|
||||
|
||||
def capacity(self, widget):
|
||||
if self._ac:
|
||||
return "ac"
|
||||
if self._capacity == -1:
|
||||
return "n/a"
|
||||
return "{}%".format(self._capacity)
|
||||
widgets = []
|
||||
super(Module, self).__init__(engine, config, widgets)
|
||||
self._batteries = self.parameter("device", "auto").split(",")
|
||||
if self._batteries[0] == "auto":
|
||||
self._batteries = glob.glob("/sys/class/power_supply/BAT*")
|
||||
else:
|
||||
self._batteries = [ "/sys/class/power_supply/{}".format(b) for b in self._batteries ]
|
||||
if len(self._batteries) == 0:
|
||||
self._batteries = [ "/sys/class/power_supply/BAT0" ]
|
||||
self.update(widgets)
|
||||
|
||||
def update(self, widgets):
|
||||
self._ac = False
|
||||
if not os.path.exists(self._path):
|
||||
self._ac = True
|
||||
self._capacity = 100
|
||||
return
|
||||
new_widgets = []
|
||||
for path in self._batteries:
|
||||
widget = self.widget(path)
|
||||
if not widget:
|
||||
widget = bumblebee.output.Widget(full_text=self.capacity, name=path)
|
||||
new_widgets.append(widget)
|
||||
self.capacity(widget)
|
||||
while len(widgets) > 0: del widgets[0]
|
||||
for widget in new_widgets:
|
||||
widgets.append(widget)
|
||||
self._widgets = widgets
|
||||
|
||||
def capacity(self, widget):
|
||||
widget.set("capacity", -1)
|
||||
widget.set("ac", False)
|
||||
if not os.path.exists(widget.name):
|
||||
widget.set("capacity", 100)
|
||||
widget.set("ac", True)
|
||||
return "ac"
|
||||
capacity = 100
|
||||
try:
|
||||
with open(self._path + "/capacity") as f:
|
||||
self._capacity = int(f.read())
|
||||
with open("{}/capacity".format(widget.name)) as f:
|
||||
capacity = int(f.read())
|
||||
except IOError:
|
||||
self._capacity = -1
|
||||
self._capacity = self._capacity if self._capacity < 100 else 100
|
||||
return "n/a"
|
||||
capacity = capacity if capacity < 100 else 100
|
||||
widget.set("capacity", capacity)
|
||||
if self.parameter("showdevice") == "true":
|
||||
widget.set("theme.minwidth", "100% ({})".format(os.path.basename(widget.name)))
|
||||
return "{}% ({})".format(capacity, os.path.basename(widget.name))
|
||||
widget.set("theme.minwidth", "100%")
|
||||
return "{}%".format(capacity)
|
||||
|
||||
def state(self, widget):
|
||||
state = []
|
||||
capacity = widget.get("capacity")
|
||||
|
||||
if self._capacity < 0:
|
||||
if capacity < 0:
|
||||
return ["critical", "unknown"]
|
||||
|
||||
if self._capacity < int(self.parameter("critical", 10)):
|
||||
if capacity < int(self.parameter("critical", 10)):
|
||||
state.append("critical")
|
||||
elif self._capacity < int(self.parameter("warning", 20)):
|
||||
elif capacity < int(self.parameter("warning", 20)):
|
||||
state.append("warning")
|
||||
|
||||
if self._ac:
|
||||
if widget.get("ac"):
|
||||
state.append("AC")
|
||||
else:
|
||||
charge = ""
|
||||
with open(self._path + "/status") as f:
|
||||
with open("{}/status".format(widget.name)) as f:
|
||||
charge = f.read().strip()
|
||||
if charge == "Discharging":
|
||||
state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity))))
|
||||
state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-capacity))))
|
||||
else:
|
||||
if self._capacity > 95:
|
||||
if capacity > 95:
|
||||
state.append("charged")
|
||||
else:
|
||||
state.append("charging")
|
||||
|
|
|
@ -26,6 +26,7 @@ class TestBatteryModule(unittest.TestCase):
|
|||
self.file = mock.Mock()
|
||||
self.file.__enter__ = lambda x: self.file
|
||||
self.file.__exit__ = lambda x, a, b, c: ""
|
||||
self.file.read.return_value = "120"
|
||||
self.open.return_value = self.file
|
||||
|
||||
self.exists.return_value = True
|
||||
|
@ -40,9 +41,7 @@ class TestBatteryModule(unittest.TestCase):
|
|||
self.normalValue = "26"
|
||||
self.chargedValue = "96"
|
||||
|
||||
for widget in self.module.widgets():
|
||||
widget.link_module(self.module)
|
||||
self.anyWidget = widget
|
||||
self.module.widgets()[0]
|
||||
|
||||
def tearDown(self):
|
||||
self._stdout.stop()
|
||||
|
@ -56,46 +55,47 @@ class TestBatteryModule(unittest.TestCase):
|
|||
def test_critical(self):
|
||||
self.file.read.return_value = self.criticalValue
|
||||
self.module.update_all()
|
||||
self.assertTrue("critical" in self.module.state(self.anyWidget))
|
||||
self.assertTrue("critical" in self.module.state(self.module.widgets()[0]))
|
||||
|
||||
def test_warning(self):
|
||||
self.file.read.return_value = self.warningValue
|
||||
self.module.update_all()
|
||||
self.assertTrue("warning" in self.module.state(self.anyWidget))
|
||||
self.assertTrue("warning" in self.module.state(self.module.widgets()[0]))
|
||||
|
||||
def test_normal(self):
|
||||
self.file.read.return_value = self.normalValue
|
||||
self.module.update_all()
|
||||
self.assertTrue(not "warning" in self.module.state(self.anyWidget))
|
||||
self.assertTrue(not "critical" in self.module.state(self.anyWidget))
|
||||
self.assertTrue(not "warning" in self.module.state(self.module.widgets()[0]))
|
||||
self.assertTrue(not "critical" in self.module.state(self.module.widgets()[0]))
|
||||
|
||||
def test_overload(self):
|
||||
self.file.read.return_value = "120"
|
||||
self.module.update_all()
|
||||
self.assertTrue(not "warning" in self.module.state(self.anyWidget))
|
||||
self.assertTrue(not "critical" in self.module.state(self.anyWidget))
|
||||
self.assertEquals(self.module.capacity(self.anyWidget), "100%")
|
||||
self.assertTrue(not "warning" in self.module.state(self.module.widgets()[0]))
|
||||
self.assertTrue(not "critical" in self.module.state(self.module.widgets()[0]))
|
||||
self.assertEquals(self.module.capacity(self.module.widgets()[0]), "100%")
|
||||
|
||||
def test_ac(self):
|
||||
self.exists.return_value = False
|
||||
self.file.read.return_value = "120"
|
||||
self.module.update_all()
|
||||
self.assertEquals(self.module.capacity(self.anyWidget), "ac")
|
||||
self.assertTrue("AC" in self.module.state(self.anyWidget))
|
||||
self.assertEquals(self.module.capacity(self.module.widgets()[0]), "ac")
|
||||
self.assertTrue("AC" in self.module.state(self.module.widgets()[0]))
|
||||
|
||||
def test_error(self):
|
||||
self.file.read.side_effect = IOError("failed to read")
|
||||
self.module.update_all()
|
||||
self.assertEquals(self.module.capacity(self.anyWidget), "n/a")
|
||||
self.assertTrue("critical" in self.module.state(self.anyWidget))
|
||||
self.assertTrue("unknown" in self.module.state(self.anyWidget))
|
||||
self.assertEquals(self.module.capacity(self.module.widgets()[0]), "n/a")
|
||||
self.assertTrue("critical" in self.module.state(self.module.widgets()[0]))
|
||||
self.assertTrue("unknown" in self.module.state(self.module.widgets()[0]))
|
||||
|
||||
def test_charging(self):
|
||||
self.file.read.return_value = self.chargedValue
|
||||
self.module.update_all()
|
||||
self.assertTrue("charged" in self.module.state(self.anyWidget))
|
||||
self.assertTrue("charged" in self.module.state(self.module.widgets()[0]))
|
||||
self.file.read.return_value = self.normalValue
|
||||
self.module.update_all()
|
||||
self.assertTrue("charging" in self.module.state(self.anyWidget))
|
||||
self.assertTrue("charging" in self.module.state(self.module.widgets()[0]))
|
||||
|
||||
def test_discharging(self):
|
||||
for limit in [ 10, 25, 50, 80, 100 ]:
|
||||
|
@ -103,6 +103,6 @@ class TestBatteryModule(unittest.TestCase):
|
|||
self.file.read.return_value = str(value)
|
||||
self.module.update_all()
|
||||
self.file.read.return_value = "Discharging"
|
||||
self.assertTrue("discharging-{}".format(limit) in self.module.state(self.anyWidget))
|
||||
self.assertTrue("discharging-{}".format(limit) in self.module.state(self.module.widgets()[0]))
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
Loading…
Reference in a new issue