From d8e469a33ab59423f4a7f6507e570505e2defafa Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 30 Apr 2013 19:53:15 +0200 Subject: [PATCH 1/4] hidpp10: split reg07 (battery) parsing so it can be reused --- lib/logitech/unifying_receiver/hidpp10.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/logitech/unifying_receiver/hidpp10.py b/lib/logitech/unifying_receiver/hidpp10.py index 95da0f37..4c0666a8 100644 --- a/lib/logitech/unifying_receiver/hidpp10.py +++ b/lib/logitech/unifying_receiver/hidpp10.py @@ -96,15 +96,18 @@ def get_battery(device): if reply: level = ord(reply[:1]) battery_status = ord(reply[1:2]) - charge = (90 if level == 7 # full - else 50 if level == 5 # good - else 20 if level == 3 # low - else 5 if level == 1 # critical - else 0 ) # wtf? - status = ('charging' if battery_status == 0x25 - else 'fully charged' if battery_status == 0x22 - else 'discharging') - return charge, status + return parse_battery_reply(level, battery_status) + +def parse_battery_reply(level, battery_status): + charge = (90 if level == 7 # full + else 50 if level == 5 # good + else 20 if level == 3 # low + else 5 if level == 1 # critical + else 0 ) # wtf? + status = ('charging' if battery_status == 0x25 + else 'fully charged' if battery_status == 0x22 + else 'discharging') + return charge, status def get_serial(device): From fbdd923d432308e0badc8226cd6d86cfa8c5091a Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 2 May 2013 23:51:25 +0200 Subject: [PATCH 2/4] Refactor battery info update - `self[BATTERY_STATUS] = BATTERY_STATUS[battery_status]` should be: `self[BATTERY_STATUS] = _hidpp20.BATTERY_STATUS[battery_status]`, otherwise the battery status would be a single char from the string `battery-status`. - Make `_hidpp20.BATTERY_OK` check against strings instead of a number. - Move setting battery information to a separate function, `set_battery_info`. This prepares for notifications when a battery error/warning occurs. --- lib/logitech/unifying_receiver/hidpp20.py | 2 +- lib/logitech/unifying_receiver/status.py | 31 ++++++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/logitech/unifying_receiver/hidpp20.py b/lib/logitech/unifying_receiver/hidpp20.py index d59ebdc7..42da666b 100644 --- a/lib/logitech/unifying_receiver/hidpp20.py +++ b/lib/logitech/unifying_receiver/hidpp20.py @@ -59,7 +59,7 @@ FIRMWARE_KIND = _NamedInts( Hardware=0x02, Other=0x03) -BATTERY_OK = lambda status: status < 5 +BATTERY_OK = lambda status: status not in ("invalid_battery", "thermal_error") BATTERY_STATUS = _NamedInts( discharging=0x00, diff --git a/lib/logitech/unifying_receiver/status.py b/lib/logitech/unifying_receiver/status.py index 0d4cfda8..df5c5c64 100644 --- a/lib/logitech/unifying_receiver/status.py +++ b/lib/logitech/unifying_receiver/status.py @@ -125,6 +125,24 @@ class DeviceStatus(dict): return bool(self._active) __nonzero__ = __bool__ + def set_battery_info(self, level, status): + self[BATTERY_LEVEL] = level + self[BATTERY_STATUS] = status + error = None + if _hidpp20.BATTERY_OK(status): + alert = ALERT.NONE + reason = self[ERROR] = None + if _log.isEnabledFor(_DEBUG): + _log.debug("%s: battery %d%% charged, %s", self._device, level, status) + else: + alert = ALERT.ALL + error = status + _log.warn("%s: battery %d%% charged, ALERT %s", self._device, level, error) + if error is not None: + # TODO: show visual warning/notif to user + self[ERROR] = error + self._changed(alert=alert, reason=error) + def read_battery(self, timestamp=None): d = self._device if d and self._active: @@ -294,18 +312,7 @@ class DeviceStatus(dict): if n.address == 0x00: discharge = ord(n.data[:1]) battery_status = ord(n.data[1:2]) - self[BATTERY_LEVEL] = discharge - self[BATTERY_STATUS] = BATTERY_STATUS[battery_status] - if _hidpp20.BATTERY_OK(battery_status): - alert = ALERT.NONE - reason = self[ERROR] = None - if _log.isEnabledFor(_DEBUG): - _log.debug("%s: battery %d%% charged, %s", self._device, discharge, self[BATTERY_STATUS]) - else: - alert = ALERT.ALL - reason = self[ERROR] = self[BATTERY_STATUS] - _log.warn("%s: battery %d%% charged, ALERT %s", self._device, discharge, reason) - self._changed(alert=alert, reason=reason) + self.set_battery_info(discharge, _hidpp20.BATTERY_STATUS[battery_status]) else: _log.info("%s: unknown BATTERY %s", self._device, n) return True From d1b1be32caf69cd87e64d8bd3aded45502fdb3f8 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 2 May 2013 23:56:03 +0200 Subject: [PATCH 3/4] Make read_battery use set_battery_info This allows battery readouts to generate warnings and debug logs for a given status and level. --- lib/logitech/unifying_receiver/status.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/logitech/unifying_receiver/status.py b/lib/logitech/unifying_receiver/status.py index df5c5c64..35176c31 100644 --- a/lib/logitech/unifying_receiver/status.py +++ b/lib/logitech/unifying_receiver/status.py @@ -125,7 +125,7 @@ class DeviceStatus(dict): return bool(self._active) __nonzero__ = __bool__ - def set_battery_info(self, level, status): + def set_battery_info(self, level, status, timestamp=None): self[BATTERY_LEVEL] = level self[BATTERY_STATUS] = status error = None @@ -141,7 +141,7 @@ class DeviceStatus(dict): if error is not None: # TODO: show visual warning/notif to user self[ERROR] = error - self._changed(alert=alert, reason=error) + self._changed(alert=alert, reason=error, timestamp=timestamp) def read_battery(self, timestamp=None): d = self._device @@ -157,8 +157,8 @@ class DeviceStatus(dict): # return if battery: - self[BATTERY_LEVEL], self[BATTERY_STATUS] = battery - self._changed(timestamp=timestamp) + level, status = battery + self.set_battery_info(level, status, timestamp=timestamp) elif BATTERY_STATUS in self: self[BATTERY_STATUS] = None self._changed(timestamp=timestamp) From 266edd80f3404fe427924ca63f19911cfb67ddc1 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 3 May 2013 00:10:58 +0200 Subject: [PATCH 4/4] Process reg07 battery notification --- lib/logitech/unifying_receiver/status.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/logitech/unifying_receiver/status.py b/lib/logitech/unifying_receiver/status.py index 35176c31..fe73d105 100644 --- a/lib/logitech/unifying_receiver/status.py +++ b/lib/logitech/unifying_receiver/status.py @@ -126,6 +126,9 @@ class DeviceStatus(dict): __nonzero__ = __bool__ def set_battery_info(self, level, status, timestamp=None): + # TODO: this is also executed when pressing Fn+F7 on K800. + # Modify this such that alerts/writes are only done when the + # level/status actually changes. self[BATTERY_LEVEL] = level self[BATTERY_STATUS] = status error = None @@ -227,7 +230,9 @@ class DeviceStatus(dict): def _process_hidpp10_custom_notification(self, n): if n.sub_id == 0x07: - # TODO + # message layout: 10 ix 07("address") 00 00 + level, status = _hidpp10.parse_battery_reply(n.address, ord(n.data[:1])) + self.set_battery_info(level, status) return True if n.sub_id == 0x0D: