From f098ed2fe0a444f5ba790d9a2cc8332c01203517 Mon Sep 17 00:00:00 2001 From: Fumihiro Xue Date: Wed, 1 Jul 2015 19:59:52 +0800 Subject: [PATCH] Add ac-charging indication for battery segment --- powerline/config_files/themes/ascii.json | 4 +- powerline/config_files/themes/powerline.json | 4 +- .../themes/powerline_unicode7.json | 4 +- powerline/config_files/themes/unicode.json | 4 +- .../config_files/themes/unicode_terminus.json | 4 +- .../themes/unicode_terminus_condensed.json | 4 +- powerline/segments/common/bat.py | 81 ++++++++++++------- tests/test_segments.py | 50 ++++++++++-- 8 files changed, 113 insertions(+), 42 deletions(-) diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json index 9ba09c31..9e876734 100644 --- a/powerline/config_files/themes/ascii.json +++ b/powerline/config_files/themes/ascii.json @@ -51,7 +51,9 @@ "powerline.segments.common.bat.battery": { "args": { "full_heart": "O", - "empty_heart": "O" + "empty_heart": "O", + "online": "C", + "offline": " " } }, "powerline.segments.common.sys.uptime": { diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index 2bbe7bfd..c33b5c1f 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -50,7 +50,9 @@ "powerline.segments.common.bat.battery": { "args": { "full_heart": "♥", - "empty_heart": "♥" + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " } }, "powerline.segments.common.sys.uptime": { diff --git a/powerline/config_files/themes/powerline_unicode7.json b/powerline/config_files/themes/powerline_unicode7.json index 2b0b0184..bfa86fe0 100644 --- a/powerline/config_files/themes/powerline_unicode7.json +++ b/powerline/config_files/themes/powerline_unicode7.json @@ -50,7 +50,9 @@ "powerline.segments.common.bat.battery": { "args": { "full_heart": "💙", - "empty_heart": "💛" + "empty_heart": "💛", + "online": "⚡️", + "offline": " " } }, "powerline.segments.common.sys.uptime": { diff --git a/powerline/config_files/themes/unicode.json b/powerline/config_files/themes/unicode.json index f6d6f5e5..eadfc870 100644 --- a/powerline/config_files/themes/unicode.json +++ b/powerline/config_files/themes/unicode.json @@ -50,7 +50,9 @@ "powerline.segments.common.bat.battery": { "args": { "full_heart": "♥", - "empty_heart": "♥" + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " } }, "powerline.segments.common.sys.uptime": { diff --git a/powerline/config_files/themes/unicode_terminus.json b/powerline/config_files/themes/unicode_terminus.json index 5b0a165b..8c1e045a 100644 --- a/powerline/config_files/themes/unicode_terminus.json +++ b/powerline/config_files/themes/unicode_terminus.json @@ -50,7 +50,9 @@ "powerline.segments.common.bat.battery": { "args": { "full_heart": "♥", - "empty_heart": "♥" + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " } }, "powerline.segments.common.sys.uptime": { diff --git a/powerline/config_files/themes/unicode_terminus_condensed.json b/powerline/config_files/themes/unicode_terminus_condensed.json index a725a470..1c567dc7 100644 --- a/powerline/config_files/themes/unicode_terminus_condensed.json +++ b/powerline/config_files/themes/unicode_terminus_condensed.json @@ -51,7 +51,9 @@ "powerline.segments.common.bat.battery": { "args": { "full_heart": "♥", - "empty_heart": "♥" + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " } }, "powerline.segments.common.sys.uptime": { diff --git a/powerline/segments/common/bat.py b/powerline/segments/common/bat.py index 732d8e11..332829f9 100644 --- a/powerline/segments/common/bat.py +++ b/powerline/segments/common/bat.py @@ -12,7 +12,7 @@ from powerline.lib.shell import run_cmd # segment is imported into powerline.segments.common module. -def _get_battery(pl): +def _fetch_battery_info(pl): try: import dbus except ImportError: @@ -55,23 +55,31 @@ def _get_battery(pl): dbus.Interface(dev, dbus_interface=devinterface).Get( devtype_name, 'Percentage' - ) + ), + dbus.Interface(dev, dbus_interface=devinterface).Get( + devtype_name, + 'State' + ) == 1 ) pl.debug('Not using DBUS+UPower as no batteries were found') if os.path.isdir('/sys/class/power_supply'): linux_bat_fmt = '/sys/class/power_supply/{0}/capacity' + linux_ac_fmt = '/sys/class/power_supply/{0}/online' for linux_bat in os.listdir('/sys/class/power_supply'): cap_path = linux_bat_fmt.format(linux_bat) + online_path = linux_ac_fmt.format(linux_bat) if linux_bat.startswith('BAT') and os.path.exists(cap_path): pl.debug('Using /sys/class/power_supply with battery {0}', linux_bat) - def _get_capacity(pl): + def _get_battery_status(pl): with open(cap_path, 'r') as f: - return int(float(f.readline().split()[0])) - - return _get_capacity - pl.debug('Not using /sys/class/power_supply as no batteries were found') + _capacity = int(float(f.readline().split()[0])) + with open(online_path, 'r') as f: + _ac_powered = f.readline() == 1 + return _capacity, _ac_powered + return _get_battery_status + pl.debug('Not using /sys/class/power_supply as no batteries were found') else: pl.debug('Not using /sys/class/power_supply: no directory') @@ -86,12 +94,12 @@ def _get_battery(pl): BATTERY_PERCENT_RE = re.compile(r'(\d+)%') - def _get_capacity(pl): + def _get_battery_status(pl): battery_summary = run_cmd(pl, ['pmset', '-g', 'batt']) battery_percent = BATTERY_PERCENT_RE.search(battery_summary).group(1) - return int(battery_percent) - - return _get_capacity + ac_charging = 'AC' in battery_summary + return int(battery_percent), ac_charging + return _get_battery_status else: pl.debug('Not using pmset: executable not found') @@ -110,11 +118,11 @@ def _get_battery(pl): for battery in wmi.InstancesOf('Win32_Battery'): pl.debug('Using win32com.client with Win32_Battery') - def _get_capacity(pl): + def _get_battery_status(pl): # http://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx - return battery.EstimatedChargeRemaining + return battery.EstimatedChargeRemaining, battery.BatteryStatus == 6 - return _get_capacity + return _get_battery_status pl.debug('Not using win32com.client as no batteries were found') from ctypes import Structure, c_byte, c_ulong, byref if sys.platform == 'cygwin': @@ -136,41 +144,41 @@ def _get_battery(pl): ('BatteryFullLifeTime', c_ulong) ] - def _get_capacity(pl): + def _get_battery_status(pl): powerclass = PowerClass() result = library_loader.kernel32.GetSystemPowerStatus(byref(powerclass)) # http://msdn.microsoft.com/en-us/library/windows/desktop/aa372693(v=vs.85).aspx if result: return None - return powerclass.BatteryLifePercent + return powerclass.BatteryLifePercent, powerclass.ACLineStatus == 1 - if _get_capacity() is None: + if _get_battery_status() is None: pl.debug('Not using GetSystemPowerStatus because it failed') else: pl.debug('Using GetSystemPowerStatus') - return _get_capacity + return _get_battery_status raise NotImplementedError -def _get_capacity(pl): - global _get_capacity +def _get_battery_status(pl): + global _get_battery_status - def _failing_get_capacity(pl): + def _failing_get_status(pl): raise NotImplementedError try: - _get_capacity = _get_battery(pl) + _get_battery_status = _fetch_battery_info(pl) except NotImplementedError: - _get_capacity = _failing_get_capacity + _get_battery_status = _failing_get_status except Exception as e: - pl.exception('Exception while obtaining battery capacity getter: {0}', str(e)) - _get_capacity = _failing_get_capacity - return _get_capacity(pl) + pl.exception('Exception while obtaining battery status: {0}', str(e)) + _get_battery_status = _failing_get_status + return _get_battery_status(pl) -def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='O', empty_heart='O'): +def battery(pl, format='{ac_state} {capacity:3.0%}', steps=5, gamify=False, full_heart='O', empty_heart='O', online='C', offline=' '): '''Return battery charge status. :param str format: @@ -189,21 +197,32 @@ def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='O', another gradient level and highlighting group, so it is OK for it to be the same as full_heart as long as necessary highlighting groups are defined. + :param str online: + If computer is connected to a power supply this symbol is prepended to the segment. + :param str offline: + If computer is NOT connected to a power supply this symbol is prepended to the segment. ``battery_gradient`` and ``battery`` groups are used in any case, first is preferred. - Highlight groups used: ``battery_full`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_empty`` or ``battery_gradient`` (gradient) or ``battery``. + Highlight groups used: ``battery_full`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_empty`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_online`` or ``battery_ac_state`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_offline`` or ``battery_ac_state`` or ``battery_gradient`` (gradient) or ``battery``. ''' try: - capacity = _get_capacity(pl) + capacity, ac_powered = _get_battery_status(pl) except NotImplementedError: - pl.info('Unable to get battery capacity.') + pl.info('Unable to get battery status.') return None + ret = [] if gamify: denom = int(steps) numer = int(denom * capacity / 100) + ret.append({ + 'contents': online if ac_powered else offline, + 'draw_inner_divider': False, + 'highlight_groups': ['battery_online' if ac_powered else 'battery_offline', 'battery_ac_state', 'battery_gradient', 'battery'], + 'gradient_level': 0, + }) ret.append({ 'contents': full_heart * numer, 'draw_inner_divider': False, @@ -220,7 +239,7 @@ def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='O', }) else: ret.append({ - 'contents': format.format(capacity=(capacity / 100.0)), + 'contents': format.format(ac_state=(online if ac_powered else offline), capacity=(capacity / 100.0)), 'highlight_groups': ['battery_gradient', 'battery'], # Gradients are “least alert – most alert” by default, capacity has # the opposite semantics. diff --git a/tests/test_segments.py b/tests/test_segments.py index ab56810b..c01dd6d5 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -881,12 +881,12 @@ class TestBat(TestCommon): def test_battery(self): pl = Pl() - def _get_capacity(pl): - return 86 + def _get_battery_status(pl): + return 86, False - with replace_attr(self.module, '_get_capacity', _get_capacity): + with replace_attr(self.module, '_get_battery_status', _get_battery_status): self.assertEqual(self.module.battery(pl=pl), [{ - 'contents': '86%', + 'contents': ' 86%', 'highlight_groups': ['battery_gradient', 'battery'], 'gradient_level': 14, }]) @@ -896,11 +896,17 @@ class TestBat(TestCommon): 'gradient_level': 14, }]) self.assertEqual(self.module.battery(pl=pl, steps=7), [{ - 'contents': '86%', + 'contents': ' 86%', 'highlight_groups': ['battery_gradient', 'battery'], 'gradient_level': 14, }]) self.assertEqual(self.module.battery(pl=pl, gamify=True), [ + { + 'contents': ' ', + 'draw_inner_divider': False, + 'highlight_groups': ['battery_offline', 'battery_ac_state', 'battery_gradient', 'battery'], + 'gradient_level': 0 + }, { 'contents': 'OOOO', 'draw_inner_divider': False, @@ -915,6 +921,12 @@ class TestBat(TestCommon): } ]) self.assertEqual(self.module.battery(pl=pl, gamify=True, full_heart='+', empty_heart='-', steps='10'), [ + { + 'contents': ' ', + 'draw_inner_divider': False, + 'highlight_groups': ['battery_offline', 'battery_ac_state', 'battery_gradient', 'battery'], + 'gradient_level': 0 + }, { 'contents': '++++++++', 'draw_inner_divider': False, @@ -929,6 +941,34 @@ class TestBat(TestCommon): } ]) + def test_battery_with_ac_online(self): + pl = Pl() + + def _get_battery_status(pl): + return 86, True + + with replace_attr(self.module, '_get_battery_status', _get_battery_status): + self.assertEqual(self.module.battery(pl=pl, online='C', offline=' '), [ + { + 'contents': 'C 86%', + 'highlight_groups': ['battery_gradient', 'battery'], + 'gradient_level': 14, + }]) + + def test_battery_with_ac_offline(self): + pl = Pl() + + def _get_battery_status(pl): + return 86, False + + with replace_attr(self.module, '_get_battery_status', _get_battery_status): + self.assertEqual(self.module.battery(pl=pl, online='C', offline=' '), [ + { + 'contents': ' 86%', + 'highlight_groups': ['battery_gradient', 'battery'], + 'gradient_level': 14, + }]) + class TestVim(TestCase): def test_mode(self):