From 79947dca6c8bd63ca4824d93a63d447bf7f38bbb Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sun, 17 May 2020 19:22:40 -0400 Subject: [PATCH] receiver: gather and show battery next level where available --- lib/logitech_receiver/hidpp10.py | 3 ++- lib/logitech_receiver/hidpp20.py | 2 +- lib/logitech_receiver/notifications.py | 8 ++++---- lib/logitech_receiver/status.py | 8 +++++--- lib/solaar/cli/show.py | 22 ++++++++++++---------- lib/solaar/ui/window.py | 7 +++++++ 6 files changed, 31 insertions(+), 19 deletions(-) diff --git a/lib/logitech_receiver/hidpp10.py b/lib/logitech_receiver/hidpp10.py index b883d715..8582ff88 100644 --- a/lib/logitech_receiver/hidpp10.py +++ b/lib/logitech_receiver/hidpp10.py @@ -215,7 +215,8 @@ def parse_battery_status(register, reply): # some 'charging' notifications may come with no battery level information charge = None - return charge, status_text + # Return None for next charge level as this is not in HID++ 1.0 spec + return charge, status_text, None def get_firmware(device): diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index bd92ba46..13d2c817 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.py @@ -524,7 +524,7 @@ def get_battery(device): if _log.isEnabledFor(_DEBUG): _log.debug("device %d battery %d%% charged, next level %d%% charge, status %d = %s", device.number, discharge, dischargeNext, status, BATTERY_STATUS[status]) - return discharge, BATTERY_STATUS[status] + return discharge, BATTERY_STATUS[status], dischargeNext def get_voltage(device): diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index 0439c46f..abb31aa4 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -152,7 +152,7 @@ def _process_hidpp10_custom_notification(device, status, n): assert n.data[-1:] == b'\x00' data = chr(n.address).encode() + n.data charge, status_text = _hidpp10.parse_battery_status(n.sub_id, data) - status.set_battery_info(charge, status_text) + status.set_battery_info(charge, status_text, None) return True if n.sub_id == _R.keyboard_illumination: @@ -242,7 +242,7 @@ def _process_feature_notification(device, status, n, feature): discharge_level = None if discharge_level == 0 else discharge_level discharge_next_level = ord(n.data[1:2]) battery_status = ord(n.data[2:3]) - status.set_battery_info(discharge_level, _hidpp20.BATTERY_STATUS[battery_status]) + status.set_battery_info(discharge_level, _hidpp20.BATTERY_STATUS[battery_status], discharge_next_level) else: _log.warn("%s: unknown BATTERY %s", device, n) return True @@ -277,12 +277,12 @@ def _process_feature_notification(device, status, n, feature): if n.address == 0x00: status[_K.LIGHT_LEVEL] = None - status.set_battery_info(charge, status_text) + status.set_battery_info(charge, status_text, None) elif n.address == 0x10: status[_K.LIGHT_LEVEL] = lux if lux > 200: status_text = _hidpp20.BATTERY_STATUS.recharging - status.set_battery_info(charge, status_text) + status.set_battery_info(charge, status_text, None) elif n.address == 0x20: if _log.isEnabledFor(_DEBUG): _log.debug("%s: Light Check button pressed", device) diff --git a/lib/logitech_receiver/status.py b/lib/logitech_receiver/status.py index 06e85ad3..821dd002 100644 --- a/lib/logitech_receiver/status.py +++ b/lib/logitech_receiver/status.py @@ -47,6 +47,7 @@ KEYS = _NamedInts( LINK_ENCRYPTED=5, NOTIFICATION_FLAGS=6, ERROR=7, + BATTERY_NEXT_LEVEL=8, ) # If the battery charge is under this percentage, trigger an attention event @@ -170,7 +171,7 @@ class DeviceStatus(dict): return bool(self._active) __nonzero__ = __bool__ - def set_battery_info(self, level, status, timestamp=None): + def set_battery_info(self, level, status, nextLevel=None, timestamp=None): if _log.isEnabledFor(_DEBUG): _log.debug("%s: battery %s, %s", self._device, level, status) @@ -192,6 +193,7 @@ class DeviceStatus(dict): # TODO: this is also executed when pressing Fn+F7 on K800. old_level, self[KEYS.BATTERY_LEVEL] = self.get(KEYS.BATTERY_LEVEL), level old_status, self[KEYS.BATTERY_STATUS] = self.get(KEYS.BATTERY_STATUS), status + self[KEYS.BATTERY_NEXT_LEVEL] = nextLevel charging = status in (_hidpp20.BATTERY_STATUS.recharging, _hidpp20.BATTERY_STATUS.almost_full, _hidpp20.BATTERY_STATUS.full, _hidpp20.BATTERY_STATUS.slow_recharge) @@ -237,8 +239,8 @@ class DeviceStatus(dict): return if battery is not None: - level, status = battery - self.set_battery_info(level, status) + level, status, nextLevel = battery + self.set_battery_info(level, status, nextLevel) elif KEYS.BATTERY_STATUS in self: self[KEYS.BATTERY_STATUS] = None self[KEYS.BATTERY_CHARGING] = None diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py index 423247e3..f5ebd276 100644 --- a/lib/solaar/cli/show.py +++ b/lib/solaar/cli/show.py @@ -25,6 +25,7 @@ from logitech_receiver import ( hidpp20 as _hidpp20, special_keys as _special_keys, ) +from logitech_receiver.common import NamedInt as _NamedInt def _print_receiver(receiver): @@ -56,6 +57,13 @@ def _print_receiver(receiver): activity_text = ', '.join(('%d=%d' % (d, a)) for d, a in activity if a > 0) print (' Device activity counters:', activity_text or '(empty)') +def _battery_text(level) : + if level is None: + return 'N/A' + elif isinstance(level, _NamedInt): + return str(level) + else: + return '%d%%' % level def _print_device(dev): assert dev @@ -173,16 +181,10 @@ def _print_device(dev): if battery is None: battery = _hidpp10.get_battery(dev) if battery is not None: - from logitech_receiver.common import NamedInt as _NamedInt - level, status = battery - if level is not None: - if isinstance(level, _NamedInt): - text = str(level) - else: - text = '%d%%' % level - else: - text = 'N/A' - print (' Battery: %s, %s.' % (text, status)) + level, status, nextLevel = battery + text = _battery_text(level) + nextText = '' if nextLevel is None else ', next level ' +_battery_text(nextLevel) + print (' Battery: %s, %s%s.' % (text, status, nextText)) else: battery_voltage = _hidpp20.get_voltage(dev) if battery_voltage : diff --git a/lib/solaar/ui/window.py b/lib/solaar/ui/window.py index 0f39004b..fe5d6822 100644 --- a/lib/solaar/ui/window.py +++ b/lib/solaar/ui/window.py @@ -618,6 +618,8 @@ def _update_device_panel(device, panel, buttons, full=False): panel.set_sensitive(is_online) battery_level = device.status.get(_K.BATTERY_LEVEL) + battery_next_level = device.status.get(_K.BATTERY_NEXT_LEVEL) + if battery_level is None: icon_name = _icons.battery() panel._battery._icon.set_sensitive(False) @@ -634,6 +636,11 @@ def _update_device_panel(device, panel, buttons, full=False): text = _(str(battery_level)) else: text = _("%(battery_percent)d%%") % { 'battery_percent': battery_level } + if battery_next_level is not None: + if isinstance(battery_next_level, _NamedInt): + text += " (" +_("next ") + _(str(battery_next_level)) + ")" + else: + text += " (" + _("next ") + ( "%d%%" % battery_next_level ) + ")" if is_online: if charging: text += ' (%s)' % _("charging")