device: implement UNIFIED_BATTERY feature
device: implement UNIFIED_BATTERY feature
This commit is contained in:
parent
1162ccb897
commit
733bf913e6
|
@ -270,4 +270,6 @@ class KwException(Exception):
|
|||
"""Firmware information."""
|
||||
FirmwareInfo = namedtuple('FirmwareInfo', ['kind', 'name', 'version', 'extras'])
|
||||
|
||||
BATTERY_APPROX = NamedInts(empty=0, critical=5, low=20, good=50, full=90)
|
||||
|
||||
del namedtuple
|
||||
|
|
|
@ -21,6 +21,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
|||
|
||||
from logging import getLogger # , DEBUG as _DEBUG
|
||||
|
||||
from .common import BATTERY_APPROX as _BATTERY_APPROX
|
||||
from .common import FirmwareInfo as _FirmwareInfo
|
||||
from .common import NamedInts as _NamedInts
|
||||
from .common import bytes2int as _bytes2int
|
||||
|
@ -102,8 +103,6 @@ ERROR = _NamedInts(
|
|||
)
|
||||
|
||||
PAIRING_ERRORS = _NamedInts(device_timeout=0x01, device_not_supported=0x02, too_many_devices=0x03, sequence_timeout=0x06)
|
||||
|
||||
BATTERY_APPOX = _NamedInts(empty=0, critical=5, low=20, good=50, full=90)
|
||||
"""Known registers.
|
||||
Devices usually have a (small) sub-set of these. Some registers are only
|
||||
applicable to certain device kinds (e.g. smooth_scroll only applies to mice."""
|
||||
|
@ -211,12 +210,12 @@ def parse_battery_status(register, reply):
|
|||
if register == REGISTERS.battery_status:
|
||||
status_byte = ord(reply[:1])
|
||||
charge = (
|
||||
BATTERY_APPOX.full if status_byte == 7 # full
|
||||
else BATTERY_APPOX.good if status_byte == 5 # good
|
||||
else BATTERY_APPOX.low if status_byte == 3 # low
|
||||
else BATTERY_APPOX.critical if status_byte == 1 # critical
|
||||
_BATTERY_APPROX.full if status_byte == 7 # full
|
||||
else _BATTERY_APPROX.good if status_byte == 5 # good
|
||||
else _BATTERY_APPROX.low if status_byte == 3 # low
|
||||
else _BATTERY_APPROX.critical if status_byte == 1 # critical
|
||||
# pure 'charging' notifications may come without a status
|
||||
else BATTERY_APPOX.empty
|
||||
else _BATTERY_APPROX.empty
|
||||
)
|
||||
|
||||
charging_byte = ord(reply[1:2])
|
||||
|
@ -284,17 +283,17 @@ def set_3leds(device, battery_level=None, charging=None, warning=None):
|
|||
return
|
||||
|
||||
if battery_level is not None:
|
||||
if battery_level < BATTERY_APPOX.critical:
|
||||
if battery_level < _BATTERY_APPROX.critical:
|
||||
# 1 orange, and force blink
|
||||
v1, v2 = 0x22, 0x00
|
||||
warning = True
|
||||
elif battery_level < BATTERY_APPOX.low:
|
||||
elif battery_level < _BATTERY_APPROX.low:
|
||||
# 1 orange
|
||||
v1, v2 = 0x22, 0x00
|
||||
elif battery_level < BATTERY_APPOX.good:
|
||||
elif battery_level < _BATTERY_APPROX.good:
|
||||
# 1 green
|
||||
v1, v2 = 0x20, 0x00
|
||||
elif battery_level < BATTERY_APPOX.full:
|
||||
elif battery_level < _BATTERY_APPROX.full:
|
||||
# 2 greens
|
||||
v1, v2 = 0x20, 0x02
|
||||
else:
|
||||
|
|
|
@ -28,6 +28,7 @@ from logging import getLogger
|
|||
from typing import List
|
||||
|
||||
from . import special_keys
|
||||
from .common import BATTERY_APPROX as _BATTERY_APPROX
|
||||
from .common import FirmwareInfo as _FirmwareInfo
|
||||
from .common import KwException as _KwException
|
||||
from .common import NamedInt as _NamedInt
|
||||
|
@ -72,6 +73,7 @@ FEATURE = _NamedInts(
|
|||
DFU=0x00D0,
|
||||
BATTERY_STATUS=0x1000,
|
||||
BATTERY_VOLTAGE=0x1001,
|
||||
UNIFIED_BATTERY=0x1004,
|
||||
CHARGING_CONTROL=0x1010,
|
||||
LED_CONTROL=0x1300,
|
||||
GENERIC_TEST=0x1800,
|
||||
|
@ -1131,14 +1133,31 @@ def get_battery(device):
|
|||
"""Reads a device's battery level."""
|
||||
battery = feature_request(device, FEATURE.BATTERY_STATUS)
|
||||
if battery:
|
||||
discharge, dischargeNext, status = _unpack('!BBB', battery[:3])
|
||||
discharge, next, status = _unpack('!BBB', battery[:3])
|
||||
discharge = None if discharge == 0 else discharge
|
||||
status = BATTERY_STATUS[status]
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug(
|
||||
'device %d battery %s%% charged, next level %s%% charge, status %s = %s', device.number, discharge,
|
||||
dischargeNext, status, BATTERY_STATUS[status]
|
||||
)
|
||||
return discharge, BATTERY_STATUS[status], dischargeNext
|
||||
_log.debug('device %d battery %s%% charged, next %s%%, status %s', device.number, discharge, next, status)
|
||||
return discharge, status, next
|
||||
else:
|
||||
battery = feature_request(device, FEATURE.UNIFIED_BATTERY, 0x10)
|
||||
if battery:
|
||||
return decipher_unified_battery(battery)
|
||||
|
||||
|
||||
def decipher_unified_battery(report):
|
||||
discharge, level, status, _ignore = _unpack('!BBBB', report[:4])
|
||||
status = BATTERY_STATUS[status]
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug('battery %s%% charged, level %s, charging %s', discharge, status)
|
||||
level = (
|
||||
_BATTERY_APPROX.full if level == 8 # full
|
||||
else _BATTERY_APPROX.good if level == 4 # good
|
||||
else _BATTERY_APPROX.low if level == 2 # low
|
||||
else _BATTERY_APPROX.critical if level == 1 # critical
|
||||
else _BATTERY_APPROX.empty
|
||||
)
|
||||
return discharge if discharge else level, status, None
|
||||
|
||||
|
||||
def get_voltage(device):
|
||||
|
|
|
@ -275,6 +275,14 @@ def _process_feature_notification(device, status, n, feature):
|
|||
_log.warn('%s: unknown VOLTAGE %s', device, n)
|
||||
return True
|
||||
|
||||
if feature == _F.UNIFIED_BATTERY:
|
||||
if n.address == 0x00:
|
||||
battery_level, battery_status, battery_voltage = _hidpp20.decipher_unified_battery(n.data)
|
||||
status.set_battery_info(battery_level, battery_status, None, battery_voltage)
|
||||
else:
|
||||
_log.warn('%s: unknown UNIFIED BATTERY %s', device, n)
|
||||
return True
|
||||
|
||||
# TODO: what are REPROG_CONTROLS_V{2,3}?
|
||||
if feature == _F.REPROG_CONTROLS:
|
||||
if n.address == 0x00:
|
||||
|
|
|
@ -25,6 +25,7 @@ from time import time as _timestamp
|
|||
|
||||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
from .common import BATTERY_APPROX as _BATTERY_APPROX
|
||||
from .common import NamedInt as _NamedInt
|
||||
from .common import NamedInts as _NamedInts
|
||||
from .i18n import _, ngettext
|
||||
|
@ -194,11 +195,11 @@ class DeviceStatus(dict):
|
|||
# charging state info, so do our best to infer a level (even if it is just the last level)
|
||||
# It is not always possible to do this well
|
||||
if status == _hidpp20.BATTERY_STATUS.full:
|
||||
level = _hidpp10.BATTERY_APPOX.full
|
||||
level = _BATTERY_APPROX.full
|
||||
elif status in (_hidpp20.BATTERY_STATUS.almost_full, _hidpp20.BATTERY_STATUS.recharging):
|
||||
level = _hidpp10.BATTERY_APPOX.good
|
||||
level = _BATTERY_APPROX.good
|
||||
elif status == _hidpp20.BATTERY_STATUS.slow_recharge:
|
||||
level = _hidpp10.BATTERY_APPOX.low
|
||||
level = _BATTERY_APPROX.low
|
||||
else:
|
||||
level = self.get(KEYS.BATTERY_LEVEL)
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue