From c2b0c8e7545cfc1d460ed5a74146390e99743e4d Mon Sep 17 00:00:00 2001
From: Tobias Witek <tobi@tobi-wan-kenobi.at>
Date: Fri, 6 Mar 2020 20:34:30 +0100
Subject: [PATCH] [modules/core] Add battery module

---
 modules/core/battery.py | 140 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 modules/core/battery.py

diff --git a/modules/core/battery.py b/modules/core/battery.py
new file mode 100644
index 0000000..09c1a30
--- /dev/null
+++ b/modules/core/battery.py
@@ -0,0 +1,140 @@
+# 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)
+'''
+
+import os
+import glob
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+import bumblebee.util
+
+try:
+    import power
+except ImportError:
+    pass
+
+class Module(bumblebee.engine.Module):
+    def __init__(self, engine, config):
+        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)
+        engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+            cmd='gnome-power-statistics')
+
+    def update(self, widgets):
+        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:
+            if bumblebee.util.asbool(self.parameter('decorate', True)) == False:
+                widget.set('theme.exclude', 'suffix')
+            widgets.append(widget)
+        self._widgets = widgets
+
+    def remaining(self):
+        estimate = 0.0
+        try:
+            estimate = power.PowerManagement().get_time_remaining_estimate()
+            # do not show remaining if on AC
+            if estimate == power.common.TIME_REMAINING_UNLIMITED:
+                return None
+            if estimate == power.common.TIME_REMAINING_UNKNOWN:
+                return ''
+        except Exception:
+            return ''
+        return bumblebee.util.durationfmt(estimate*60, shorten=True, suffix=True) # estimate is in minutes
+
+    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('{}/capacity'.format(widget.name)) as f:
+                capacity = int(f.read())
+        except IOError:
+            return 'n/a'
+
+        capacity = capacity if capacity < 100 else 100
+        widget.set('capacity', capacity)
+
+        # Read power conumption
+        if bumblebee.util.asbool(self.parameter('showpowerconsumption', False)):
+            r=open(widget.name + '/power_now', 'r')
+            output =  '{}% ({})'.format(capacity,str(int(r.read())/1000000) + 'W')
+        else:
+             output =  '{}%'.format(capacity)
+
+        widget.set('theme.minwidth', '100%')
+        if bumblebee.util.asbool(self.parameter('showremaining', True))\
+                and self.getCharge(widget) == 'Discharging':
+            output = '{} {}'.format(output, self.remaining())
+
+        if bumblebee.util.asbool(self.parameter('showdevice', False)):
+            output = '{} ({})'.format(output, os.path.basename(widget.name))
+
+        return output
+       
+    def state(self, widget):
+        state = []
+        capacity = widget.get('capacity')
+
+        if capacity < 0:
+            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:
+            charge = self.getCharge(widget)
+            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
+
+    def getCharge(self, widget):
+        charge = ''
+        try:
+            with open('{}/status'.format(widget.name)) as f:
+                charge = f.read().strip()
+        except IOError:
+                pass
+        return charge
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4