replaced hard-coded register numbers with constants
also re-worked the battery reading code a bit
This commit is contained in:
parent
bf5fc42f07
commit
9a2a28e0aa
|
|
@ -98,7 +98,7 @@ class NamedInts(object):
|
||||||
self.__dict__ = values
|
self.__dict__ = values
|
||||||
self._values = sorted(list(values.values()))
|
self._values = sorted(list(values.values()))
|
||||||
self._indexed = {int(v): v for v in self._values}
|
self._indexed = {int(v): v for v in self._values}
|
||||||
assert len(values) == len(self._indexed), "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
|
# assert len(values) == len(self._indexed), "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
|
||||||
self._fallback = None
|
self._fallback = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||||
|
|
||||||
from . import hidpp10 as _hidpp10
|
from . import hidpp10 as _hidpp10
|
||||||
from .common import NamedInts as _NamedInts
|
from .common import NamedInts as _NamedInts
|
||||||
from .settings_templates import Register as _R, Feature as _F
|
from .settings_templates import RegisterSettings as _RS, FeatureSettings as _FS
|
||||||
|
|
||||||
|
_R = _hidpp10.REGISTERS
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
@ -118,29 +120,29 @@ _D('Wireless Keyboard K270')
|
||||||
_D('Wireless Keyboard K350')
|
_D('Wireless Keyboard K350')
|
||||||
_D('Wireless Keyboard K360', protocol=2.0, wpid='4004',
|
_D('Wireless Keyboard K360', protocol=2.0, wpid='4004',
|
||||||
settings=[
|
settings=[
|
||||||
_F.fn_swap()
|
_FS.fn_swap()
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_D('Wireless Touch Keyboard K400', protocol=2.0, wpid='4024',
|
_D('Wireless Touch Keyboard K400', protocol=2.0, wpid='4024',
|
||||||
settings=[
|
settings=[
|
||||||
_F.fn_swap()
|
_FS.fn_swap()
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_D('Wireless Keyboard MK700', protocol=1.0, wpid='2008',
|
_D('Wireless Keyboard MK700', protocol=1.0, wpid='2008',
|
||||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07},
|
registers=(_R.battery_status, ),
|
||||||
settings=[
|
settings=[
|
||||||
_R.fn_swap(),
|
_RS.fn_swap(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_D('Wireless Solar Keyboard K750', protocol=2.0, wpid='4002',
|
_D('Wireless Solar Keyboard K750', protocol=2.0, wpid='4002',
|
||||||
settings=[
|
settings=[
|
||||||
_F.fn_swap()
|
_FS.fn_swap()
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_D('Wireless Illuminated Keyboard K800', protocol=1.0, wpid='2010',
|
_D('Wireless Illuminated Keyboard K800', protocol=1.0, wpid='2010',
|
||||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07, '3leds': 0x51},
|
registers=(_R.battery_status, _R.three_leds, ),
|
||||||
settings=[
|
settings=[
|
||||||
_R.fn_swap(),
|
_RS.fn_swap(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -152,7 +154,7 @@ _D('Wireless Mouse M187', protocol=1.0)
|
||||||
_D('Wireless Mouse M215', protocol=1.0, wpid='1020')
|
_D('Wireless Mouse M215', protocol=1.0, wpid='1020')
|
||||||
_D('Wireless Mouse M235', protocol=1.0)
|
_D('Wireless Mouse M235', protocol=1.0)
|
||||||
_D('Wireless Mouse M305', protocol=1.0, wpid='101F',
|
_D('Wireless Mouse M305', protocol=1.0, wpid='101F',
|
||||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07},
|
registers=(_R.battery_status, ),
|
||||||
)
|
)
|
||||||
_D('Wireless Mouse M310', protocol=1.0)
|
_D('Wireless Mouse M310', protocol=1.0)
|
||||||
_D('Wireless Mouse M315', protocol=1.0)
|
_D('Wireless Mouse M315', protocol=1.0)
|
||||||
|
|
@ -161,18 +163,18 @@ _D('Wireless Mouse M325')
|
||||||
_D('Wireless Mouse M345')
|
_D('Wireless Mouse M345')
|
||||||
_D('Wireless Mouse M505')
|
_D('Wireless Mouse M505')
|
||||||
_D('Wireless Mouse M510', protocol=1.0, wpid='1025',
|
_D('Wireless Mouse M510', protocol=1.0, wpid='1025',
|
||||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07},
|
registers=(_R.battery_status, ),
|
||||||
settings=[
|
settings=[
|
||||||
_R.smooth_scroll(),
|
_RS.smooth_scroll(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_D('Couch Mouse M515', protocol=2.0)
|
_D('Couch Mouse M515', protocol=2.0)
|
||||||
_D('Wireless Mouse M525', protocol=2.0)
|
_D('Wireless Mouse M525', protocol=2.0)
|
||||||
_D('Touch Mouse M600', protocol=2.0, wpid='401A')
|
_D('Touch Mouse M600', protocol=2.0, wpid='401A')
|
||||||
_D('Marathon Mouse M705', protocol=1.0, wpid='101B',
|
_D('Marathon Mouse M705', protocol=1.0, wpid='101B',
|
||||||
registers={'battery_charge': 0x0D},
|
registers=(_R.battery_charge, ),
|
||||||
settings=[
|
settings=[
|
||||||
_R.smooth_scroll(),
|
_RS.smooth_scroll(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_D('Zone Touch Mouse T400')
|
_D('Zone Touch Mouse T400')
|
||||||
|
|
@ -180,9 +182,9 @@ _D('Touch Mouse T620')
|
||||||
_D('Logitech Cube', kind=_hidpp10.DEVICE_KIND.mouse, protocol=2.0)
|
_D('Logitech Cube', kind=_hidpp10.DEVICE_KIND.mouse, protocol=2.0)
|
||||||
_D('Anywhere Mouse MX', codename='Anywhere MX', protocol=1.0, wpid='1017')
|
_D('Anywhere Mouse MX', codename='Anywhere MX', protocol=1.0, wpid='1017')
|
||||||
_D('Performance Mouse MX', codename='Performance MX', protocol=1.0, wpid='101A',
|
_D('Performance Mouse MX', codename='Performance MX', protocol=1.0, wpid='101A',
|
||||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07, '3leds': 0x51},
|
registers=(_R.battery_status, _R.three_leds, ),
|
||||||
settings=[
|
settings=[
|
||||||
_R.dpi(choices=_PERFORMANCE_MX_DPIS),
|
_RS.dpi(choices=_PERFORMANCE_MX_DPIS),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -201,8 +203,8 @@ _D('Wireless Touchpad', codename='Wireless Touch', protocol=2.0, wpid='4011')
|
||||||
#
|
#
|
||||||
|
|
||||||
_D('VX Nano Cordless Laser Mouse', codename='VX Nano', protocol=1.0, wpid='100F',
|
_D('VX Nano Cordless Laser Mouse', codename='VX Nano', protocol=1.0, wpid='100F',
|
||||||
registers={'battery_charge': 0x0D, 'battery_status': -0x07},
|
registers=(_R.battery_charge, ),
|
||||||
settings=[
|
settings=[
|
||||||
_R.smooth_scroll(),
|
_RS.smooth_scroll(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ NOTIFICATION_FLAG = _NamedInts(
|
||||||
keyboard_multimedia_raw=0x010000, # consumer controls such as Mute and Calculator
|
keyboard_multimedia_raw=0x010000, # consumer controls such as Mute and Calculator
|
||||||
# reserved_r1b4= 0x001000, # unknown, seen on a unifying receiver
|
# reserved_r1b4= 0x001000, # unknown, seen on a unifying receiver
|
||||||
software_present= 0x000800, # .. no idea
|
software_present= 0x000800, # .. no idea
|
||||||
keyboard_backlight= 0x000200, # illumination brightness level changes (by pressing keys)
|
keyboard_illumination= 0x000200, # illumination brightness level changes (by pressing keys)
|
||||||
wireless= 0x000100, # notify when the device wireless goes on/off-line
|
wireless= 0x000100, # notify when the device wireless goes on/off-line
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -90,43 +90,49 @@ BATTERY_APPOX = _NamedInts(
|
||||||
good = 50,
|
good = 50,
|
||||||
full = 90)
|
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."""
|
||||||
|
REGISTERS = _NamedInts(
|
||||||
|
# only apply to receivers
|
||||||
|
receiver_connection=0x02,
|
||||||
|
receiver_pairing=0xB2,
|
||||||
|
devices_activity=0x2B3,
|
||||||
|
receiver_info=0x2B5,
|
||||||
|
|
||||||
|
# only apply to devices
|
||||||
|
mouse_smooth_scroll=0x01,
|
||||||
|
keyboard_hand_detection=0x01,
|
||||||
|
battery_status=0x07,
|
||||||
|
keyboard_fn_swap=0x09,
|
||||||
|
battery_charge=0x0D,
|
||||||
|
keyboard_illumination=0x17,
|
||||||
|
three_leds=0x51,
|
||||||
|
mouse_dpi=0x63,
|
||||||
|
|
||||||
|
# apply to both
|
||||||
|
notifications=0x00,
|
||||||
|
firmware=0xF1,
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# functions
|
# functions
|
||||||
#
|
#
|
||||||
|
|
||||||
def read_register(device, register_number, *params):
|
def read_register(device, register_number, *params):
|
||||||
assert device
|
assert device
|
||||||
# support long registers by adding a 2 in front of the number
|
# support long registers by adding a 2 in front of the register number
|
||||||
request_id = 0x8100 | (int(register_number) & 0x2FF)
|
request_id = 0x8100 | (int(register_number) & 0x2FF)
|
||||||
return device.request(request_id, *params)
|
return device.request(request_id, *params)
|
||||||
|
|
||||||
|
|
||||||
def write_register(device, register_number, *value):
|
def write_register(device, register_number, *value):
|
||||||
assert device
|
assert device
|
||||||
# support long registers by adding a 2 in front of the number
|
# support long registers by adding a 2 in front of the register number
|
||||||
request_id = 0x8000 | (int(register_number) & 0x2FF)
|
request_id = 0x8000 | (int(register_number) & 0x2FF)
|
||||||
return device.request(request_id, *value)
|
return device.request(request_id, *value)
|
||||||
|
|
||||||
|
|
||||||
def get_register(device, name, default_number=-1):
|
|
||||||
assert device
|
|
||||||
assert device.kind is not None
|
|
||||||
if not device.online:
|
|
||||||
return
|
|
||||||
|
|
||||||
known_register = device.registers.get(name)
|
|
||||||
register = known_register or default_number
|
|
||||||
if register > 0:
|
|
||||||
reply = read_register(device, register)
|
|
||||||
if reply:
|
|
||||||
return reply
|
|
||||||
|
|
||||||
if not known_register and device.kind is not None and device.online:
|
|
||||||
_log.warn("%s: failed to read register '%s' (0x%02X), blacklisting",
|
|
||||||
device, name, default_number)
|
|
||||||
device.registers[name] = -default_number
|
|
||||||
|
|
||||||
|
|
||||||
def get_battery(device):
|
def get_battery(device):
|
||||||
assert device
|
assert device
|
||||||
assert device.kind is not None
|
assert device.kind is not None
|
||||||
|
|
@ -138,76 +144,96 @@ def get_battery(device):
|
||||||
# let's just assume HID++ 2.0 devices do not provide the battery info in a register
|
# let's just assume HID++ 2.0 devices do not provide the battery info in a register
|
||||||
return
|
return
|
||||||
|
|
||||||
reply = get_register(device, 'battery_charge', 0x0D)
|
for r in (REGISTERS.battery_status, REGISTERS.battery_charge):
|
||||||
|
if r in device.registers:
|
||||||
|
reply = read_register(device, r)
|
||||||
|
if reply:
|
||||||
|
return parse_battery_status(r, reply)
|
||||||
|
return
|
||||||
|
|
||||||
|
# the descriptor does not tell us which register this device has, try them both
|
||||||
|
reply = read_register(device, REGISTERS.battery_charge)
|
||||||
if reply:
|
if reply:
|
||||||
level = ord(reply[:1])
|
# remember this for the next time
|
||||||
battery_status = ord(reply[2:3])
|
device.registers.append(REGISTERS.battery_charge)
|
||||||
return parse_battery_reply_0D(level, battery_status)
|
return parse_battery_status(REGISTERS.battery_charge, reply)
|
||||||
|
|
||||||
reply = get_register(device, 'battery_status', 0x07)
|
reply = read_register(device, REGISTERS.battery_status)
|
||||||
if reply:
|
if reply:
|
||||||
level = ord(reply[:1])
|
# remember this for the next time
|
||||||
battery_status = ord(reply[1:2])
|
device.registers.append(REGISTERS.battery_status)
|
||||||
return parse_battery_reply_07(level, battery_status)
|
return parse_battery_status(REGISTERS.battery_status, reply)
|
||||||
|
|
||||||
def parse_battery_reply_0D(level, battery_status):
|
|
||||||
charge = level
|
|
||||||
status = battery_status & 0xF0
|
|
||||||
status = ('discharging' if status == 0x30
|
|
||||||
else 'charging' if status == 0x50
|
|
||||||
else 'fully charged' if status == 0x90
|
|
||||||
else None)
|
|
||||||
return charge, status
|
|
||||||
|
|
||||||
def parse_battery_reply_07(level, battery_status):
|
def parse_battery_status(register, reply):
|
||||||
charge = (BATTERY_APPOX.full if level == 7 # full
|
if register == REGISTERS.battery_charge:
|
||||||
else BATTERY_APPOX.good if level == 5 # good
|
charge = ord(reply[:1])
|
||||||
else BATTERY_APPOX.low if level == 3 # low
|
status_byte = ord(reply[2:3]) & 0xF0
|
||||||
else BATTERY_APPOX.critical if level == 1 # critical
|
status_text = ('discharging' if status_byte == 0x30
|
||||||
else BATTERY_APPOX.empty ) # wtf?
|
else 'charging' if status_byte == 0x50
|
||||||
|
else 'fully charged' if status_byte == 0x90
|
||||||
|
else None)
|
||||||
|
return charge, status_text
|
||||||
|
|
||||||
if battery_status == 0x00:
|
if register == REGISTERS.battery_status:
|
||||||
status = 'discharging'
|
status_byte = ord(reply[:1])
|
||||||
elif battery_status & 0x21 == 0x21:
|
charge = (BATTERY_APPOX.full if status_byte == 7 # full
|
||||||
status = 'charging'
|
else BATTERY_APPOX.good if status_byte == 5 # good
|
||||||
elif battery_status & 0x22 == 0x22:
|
else BATTERY_APPOX.low if status_byte == 3 # low
|
||||||
status = 'fully charged'
|
else BATTERY_APPOX.critical if status_byte == 1 # critical
|
||||||
else:
|
# pure 'charging' notifications may come without a status
|
||||||
_log.warn("could not parse 0x07 battery status: %02X (level %02X)", battery_status, level)
|
else BATTERY_APPOX.empty)
|
||||||
status = None
|
|
||||||
|
|
||||||
if battery_status & 0x03 and level == 0:
|
charging_byte = ord(reply[1:2])
|
||||||
# some 'charging' notifications may come with no battery level information
|
if charging_byte == 0x00:
|
||||||
charge = None
|
status_text = 'discharging'
|
||||||
|
elif charging_byte & 0x21 == 0x21:
|
||||||
|
status_text = 'charging'
|
||||||
|
elif charging_byte & 0x22 == 0x22:
|
||||||
|
status_text = 'fully charged'
|
||||||
|
else:
|
||||||
|
_log.warn("could not parse 0x07 battery status: %02X (level %02X)", charging_byte, status_byte)
|
||||||
|
status_text = None
|
||||||
|
|
||||||
return charge, status
|
if charging_byte & 0x03 and status_byte == 0:
|
||||||
|
# some 'charging' notifications may come with no battery level information
|
||||||
|
charge = None
|
||||||
|
|
||||||
|
return charge, status_text
|
||||||
|
|
||||||
|
|
||||||
def get_firmware(device):
|
def get_firmware(device):
|
||||||
assert device
|
assert device
|
||||||
|
|
||||||
firmware = [None, None]
|
firmware = [None, None, None]
|
||||||
|
|
||||||
reply = read_register(device, 0xF1, 0x01)
|
reply = read_register(device, REGISTERS.firmware, 0x01)
|
||||||
if not reply:
|
if not reply:
|
||||||
# won't be able to read any of it now...
|
# won't be able to read any of it now...
|
||||||
return
|
return
|
||||||
|
|
||||||
fw_version = _strhex(reply[1:3])
|
fw_version = _strhex(reply[1:3])
|
||||||
fw_version = '%s.%s' % (fw_version[0:2], fw_version[2:4])
|
fw_version = '%s.%s' % (fw_version[0:2], fw_version[2:4])
|
||||||
reply = read_register(device, 0xF1, 0x02)
|
reply = read_register(device, REGISTERS.firmware, 0x02)
|
||||||
if reply:
|
if reply:
|
||||||
fw_version += '.B' + _strhex(reply[1:3])
|
fw_version += '.B' + _strhex(reply[1:3])
|
||||||
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
|
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
|
||||||
firmware[0] = fw
|
firmware[0] = fw
|
||||||
|
|
||||||
reply = read_register(device, 0xF1, 0x04)
|
reply = read_register(device, REGISTERS.firmware, 0x04)
|
||||||
if reply:
|
if reply:
|
||||||
bl_version = _strhex(reply[1:3])
|
bl_version = _strhex(reply[1:3])
|
||||||
bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4])
|
bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4])
|
||||||
bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, '', bl_version, None)
|
bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, '', bl_version, None)
|
||||||
firmware[1] = bl
|
firmware[1] = bl
|
||||||
|
|
||||||
|
reply = read_register(device, REGISTERS.firmware, 0x03)
|
||||||
|
if reply:
|
||||||
|
o_version = _strhex(reply[1:3])
|
||||||
|
o_version = '%s.%s' % (o_version[0:2], o_version[2:4])
|
||||||
|
o = _FirmwareInfo(FIRMWARE_KIND.Other, '', o_version, None)
|
||||||
|
firmware[2] = o
|
||||||
|
|
||||||
if any(firmware):
|
if any(firmware):
|
||||||
return tuple(f for f in firmware if f)
|
return tuple(f for f in firmware if f)
|
||||||
|
|
||||||
|
|
@ -218,8 +244,7 @@ def set_3leds(device, battery_level=None, charging=None, warning=None):
|
||||||
if not device.online:
|
if not device.online:
|
||||||
return
|
return
|
||||||
|
|
||||||
leds_register = device.registers.get('3leds')
|
if REGISTERS.three_leds not in device.registers:
|
||||||
if leds_register is None or leds_register < 0:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if battery_level is not None:
|
if battery_level is not None:
|
||||||
|
|
@ -243,6 +268,9 @@ def set_3leds(device, battery_level=None, charging=None, warning=None):
|
||||||
# set the blinking flag for the leds already set
|
# set the blinking flag for the leds already set
|
||||||
v1 |= (v1 >> 1)
|
v1 |= (v1 >> 1)
|
||||||
v2 |= (v2 >> 1)
|
v2 |= (v2 >> 1)
|
||||||
|
elif charging:
|
||||||
|
# blink all green
|
||||||
|
v1, v2 = 0x30,0x33
|
||||||
elif warning:
|
elif warning:
|
||||||
# 1 red
|
# 1 red
|
||||||
v1, v2 = 0x02, 0x00
|
v1, v2 = 0x02, 0x00
|
||||||
|
|
@ -250,19 +278,21 @@ def set_3leds(device, battery_level=None, charging=None, warning=None):
|
||||||
# turn off all leds
|
# turn off all leds
|
||||||
v1, v2 = 0x11, 0x11
|
v1, v2 = 0x11, 0x11
|
||||||
|
|
||||||
write_register(device, leds_register, v1, v2)
|
write_register(device, REGISTERS.three_leds, v1, v2)
|
||||||
|
|
||||||
|
|
||||||
def get_notification_flags(device):
|
def get_notification_flags(device):
|
||||||
assert device
|
assert device
|
||||||
|
|
||||||
|
# Avoid a call if the device is not online,
|
||||||
|
# or the device does not support registers.
|
||||||
if device.kind is not None:
|
if device.kind is not None:
|
||||||
# peripherals with protocol >= 2.0 don't support registers
|
# peripherals with protocol >= 2.0 don't support registers
|
||||||
p = device.protocol
|
p = device.protocol
|
||||||
if p is not None and p >= 2.0:
|
if p is not None and p >= 2.0:
|
||||||
return
|
return
|
||||||
|
|
||||||
flags = read_register(device, 0x00)
|
flags = read_register(device, REGISTERS.notifications)
|
||||||
if flags is not None:
|
if flags is not None:
|
||||||
assert len(flags) == 3
|
assert len(flags) == 3
|
||||||
return _bytes2int(flags)
|
return _bytes2int(flags)
|
||||||
|
|
@ -271,6 +301,8 @@ def get_notification_flags(device):
|
||||||
def set_notification_flags(device, *flag_bits):
|
def set_notification_flags(device, *flag_bits):
|
||||||
assert device
|
assert device
|
||||||
|
|
||||||
|
# Avoid a call if the device is not online,
|
||||||
|
# or the device does not support registers.
|
||||||
if device.kind is not None:
|
if device.kind is not None:
|
||||||
# peripherals with protocol >= 2.0 don't support registers
|
# peripherals with protocol >= 2.0 don't support registers
|
||||||
p = device.protocol
|
p = device.protocol
|
||||||
|
|
@ -279,5 +311,5 @@ def set_notification_flags(device, *flag_bits):
|
||||||
|
|
||||||
flag_bits = sum(int(b) for b in flag_bits)
|
flag_bits = sum(int(b) for b in flag_bits)
|
||||||
assert flag_bits & 0x00FFFFFF == flag_bits
|
assert flag_bits & 0x00FFFFFF == flag_bits
|
||||||
result = write_register(device, 0x00, _int2bytes(flag_bits, 3))
|
result = write_register(device, REGISTERS.notifications, _int2bytes(flag_bits, 3))
|
||||||
return result is not None
|
return result is not None
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ from .common import strhex as _strhex
|
||||||
from .descriptors import DEVICES as _DESCRIPTORS
|
from .descriptors import DEVICES as _DESCRIPTORS
|
||||||
from .settings_templates import check_feature_settings as _check_feature_settings
|
from .settings_templates import check_feature_settings as _check_feature_settings
|
||||||
|
|
||||||
|
_R = _hidpp10.REGISTERS
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
@ -66,7 +68,7 @@ class PairedDevice(object):
|
||||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||||
else:
|
else:
|
||||||
# force a reading of the wpid
|
# force a reading of the wpid
|
||||||
pair_info = receiver.read_register(0x2B5, 0x20 + number - 1)
|
pair_info = receiver.read_register(_R.receiver_info, 0x20 + number - 1)
|
||||||
if pair_info:
|
if pair_info:
|
||||||
# may be either a Unifying receiver, or an Unifying-ready receiver
|
# may be either a Unifying receiver, or an Unifying-ready receiver
|
||||||
self.wpid = _strhex(pair_info[3:5])
|
self.wpid = _strhex(pair_info[3:5])
|
||||||
|
|
@ -76,7 +78,7 @@ class PairedDevice(object):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# unifying protocol not supported, must be a Nano receiver
|
# unifying protocol not supported, must be a Nano receiver
|
||||||
device_info = self.receiver.read_register(0x2B5, 0x04)
|
device_info = self.receiver.read_register(_R.receiver_info, 0x04)
|
||||||
if device_info is None:
|
if device_info is None:
|
||||||
_log.error("failed to read Nano wpid for device %d of %s", number, receiver)
|
_log.error("failed to read Nano wpid for device %d of %s", number, receiver)
|
||||||
raise _base.NoSuchDevice(nuber=number, receiver=receiver, error="read Nano wpid")
|
raise _base.NoSuchDevice(nuber=number, receiver=receiver, error="read Nano wpid")
|
||||||
|
|
@ -93,7 +95,7 @@ class PairedDevice(object):
|
||||||
if self.descriptor is None:
|
if self.descriptor is None:
|
||||||
# Last chance to correctly identify the device; many Nano receivers
|
# Last chance to correctly identify the device; many Nano receivers
|
||||||
# do not support this call.
|
# do not support this call.
|
||||||
codename = self.receiver.read_register(0x2B5, 0x40 + self.number - 1)
|
codename = self.receiver.read_register(_R.receiver_info, 0x40 + self.number - 1)
|
||||||
if codename:
|
if codename:
|
||||||
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
||||||
self.descriptor = _DESCRIPTORS.get(self._codename)
|
self.descriptor = _DESCRIPTORS.get(self._codename)
|
||||||
|
|
@ -125,7 +127,7 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def codename(self):
|
def codename(self):
|
||||||
if self._codename is None:
|
if self._codename is None:
|
||||||
codename = self.receiver.read_register(0x2B5, 0x40 + self.number - 1)
|
codename = self.receiver.read_register(_R.receiver_info, 0x40 + self.number - 1)
|
||||||
if codename:
|
if codename:
|
||||||
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
||||||
# _log.debug("device %d codename %s", self.number, self._codename)
|
# _log.debug("device %d codename %s", self.number, self._codename)
|
||||||
|
|
@ -143,7 +145,7 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def kind(self):
|
def kind(self):
|
||||||
if self._kind is None:
|
if self._kind is None:
|
||||||
pair_info = self.receiver.read_register(0x2B5, 0x20 + self.number - 1)
|
pair_info = self.receiver.read_register(_R.receiver_info, 0x20 + self.number - 1)
|
||||||
if pair_info:
|
if pair_info:
|
||||||
kind = ord(pair_info[7:8]) & 0x0F
|
kind = ord(pair_info[7:8]) & 0x0F
|
||||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||||
|
|
@ -164,7 +166,7 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def serial(self):
|
def serial(self):
|
||||||
if self._serial is None:
|
if self._serial is None:
|
||||||
serial = self.receiver.read_register(0x2B5, 0x30 + self.number - 1)
|
serial = self.receiver.read_register(_R.receiver_info, 0x30 + self.number - 1)
|
||||||
if serial:
|
if serial:
|
||||||
ps = ord(serial[9:10]) & 0x0F
|
ps = ord(serial[9:10]) & 0x0F
|
||||||
self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps]
|
self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps]
|
||||||
|
|
@ -182,7 +184,7 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def power_switch_location(self):
|
def power_switch_location(self):
|
||||||
if self._power_switch is None:
|
if self._power_switch is None:
|
||||||
ps = self.receiver.read_register(0x2B5, 0x30 + self.number - 1)
|
ps = self.receiver.read_register(_R.receiver_info, 0x30 + self.number - 1)
|
||||||
if ps is not None:
|
if ps is not None:
|
||||||
ps = ord(ps[9:10]) & 0x0F
|
ps = ord(ps[9:10]) & 0x0F
|
||||||
self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps]
|
self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps]
|
||||||
|
|
@ -193,7 +195,7 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def polling_rate(self):
|
def polling_rate(self):
|
||||||
if self._polling_rate is None:
|
if self._polling_rate is None:
|
||||||
pair_info = self.receiver.read_register(0x2B5, 0x20 + self.number - 1)
|
pair_info = self.receiver.read_register(_R.receiver_info, 0x20 + self.number - 1)
|
||||||
if pair_info:
|
if pair_info:
|
||||||
self._polling_rate = ord(pair_info[2:3])
|
self._polling_rate = ord(pair_info[2:3])
|
||||||
else:
|
else:
|
||||||
|
|
@ -211,9 +213,9 @@ class PairedDevice(object):
|
||||||
def registers(self):
|
def registers(self):
|
||||||
if self._registers is None:
|
if self._registers is None:
|
||||||
if self.descriptor and self.descriptor.registers:
|
if self.descriptor and self.descriptor.registers:
|
||||||
self._registers = dict(self.descriptor.registers)
|
self._registers = list(self.descriptor.registers)
|
||||||
else:
|
else:
|
||||||
self._registers = {}
|
self._registers = []
|
||||||
return self._registers
|
return self._registers
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -235,7 +237,7 @@ class PairedDevice(object):
|
||||||
|
|
||||||
if enable:
|
if enable:
|
||||||
set_flag_bits = ( _hidpp10.NOTIFICATION_FLAG.battery_status
|
set_flag_bits = ( _hidpp10.NOTIFICATION_FLAG.battery_status
|
||||||
| _hidpp10.NOTIFICATION_FLAG.keyboard_backlight
|
| _hidpp10.NOTIFICATION_FLAG.keyboard_illumination
|
||||||
| _hidpp10.NOTIFICATION_FLAG.wireless
|
| _hidpp10.NOTIFICATION_FLAG.wireless
|
||||||
| _hidpp10.NOTIFICATION_FLAG.software_present )
|
| _hidpp10.NOTIFICATION_FLAG.software_present )
|
||||||
else:
|
else:
|
||||||
|
|
@ -308,7 +310,7 @@ class Receiver(object):
|
||||||
|
|
||||||
# read the serial immediately, so we can find out max_devices
|
# read the serial immediately, so we can find out max_devices
|
||||||
# this will tell us if it's a Unifying or Nano receiver
|
# this will tell us if it's a Unifying or Nano receiver
|
||||||
serial_reply = self.read_register(0x2B5, 0x03)
|
serial_reply = self.read_register(_R.receiver_info, 0x03)
|
||||||
assert serial_reply
|
assert serial_reply
|
||||||
self.serial = _strhex(serial_reply[1:5])
|
self.serial = _strhex(serial_reply[1:5])
|
||||||
self.max_devices = ord(serial_reply[6:7])
|
self.max_devices = ord(serial_reply[6:7])
|
||||||
|
|
@ -363,7 +365,7 @@ class Receiver(object):
|
||||||
def notify_devices(self):
|
def notify_devices(self):
|
||||||
"""Scan all devices."""
|
"""Scan all devices."""
|
||||||
if self.handle:
|
if self.handle:
|
||||||
if not self.write_register(0x02, 0x02):
|
if not self.write_register(_R.receiver_connection, 0x02):
|
||||||
_log.warn("%s: failed to trigger device link notifications", self)
|
_log.warn("%s: failed to trigger device link notifications", self)
|
||||||
|
|
||||||
def register_new_device(self, number, notification=None):
|
def register_new_device(self, number, notification=None):
|
||||||
|
|
@ -387,14 +389,14 @@ class Receiver(object):
|
||||||
|
|
||||||
def set_lock(self, lock_closed=True, device=0, timeout=0):
|
def set_lock(self, lock_closed=True, device=0, timeout=0):
|
||||||
if self.handle:
|
if self.handle:
|
||||||
lock = 0x02 if lock_closed else 0x01
|
action = 0x02 if lock_closed else 0x01
|
||||||
reply = self.write_register(0xB2, lock, device, timeout)
|
reply = self.write_register(_R.receiver_pairing, action, device, timeout)
|
||||||
if reply:
|
if reply:
|
||||||
return True
|
return True
|
||||||
_log.warn("%s: failed to %s the receiver lock", self, 'close' if lock_closed else 'open')
|
_log.warn("%s: failed to %s the receiver lock", self, 'close' if lock_closed else 'open')
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
count = self.read_register(0x02)
|
count = self.read_register(_R.receiver_connection)
|
||||||
return 0 if count is None else ord(count[1:2])
|
return 0 if count is None else ord(count[1:2])
|
||||||
|
|
||||||
# def has_devices(self):
|
# def has_devices(self):
|
||||||
|
|
@ -436,11 +438,12 @@ class Receiver(object):
|
||||||
raise IndexError(key)
|
raise IndexError(key)
|
||||||
|
|
||||||
dev = self._devices[key]
|
dev = self._devices[key]
|
||||||
reply = self.write_register(0xB2, 0x03, int(key))
|
action = 0x03
|
||||||
|
reply = self.write_register(_R.receiver_pairing, action, int(key))
|
||||||
if reply:
|
if reply:
|
||||||
# invalidate the device
|
# invalidate the device
|
||||||
dev.wpid = None
|
|
||||||
dev.online = False
|
dev.online = False
|
||||||
|
dev.wpid = None
|
||||||
del self._devices[key]
|
del self._devices[key]
|
||||||
_log.warn("%s unpaired device %s", self, dev)
|
_log.warn("%s unpaired device %s", self, dev)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ from .settings import (
|
||||||
ChoicesValidator as _ChoicesV,
|
ChoicesValidator as _ChoicesV,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_DK = _hidpp10.DEVICE_KIND
|
||||||
|
_R = _hidpp10.REGISTERS
|
||||||
|
_F = _hidpp20.FEATURE
|
||||||
|
|
||||||
#
|
#
|
||||||
# pre-defined basic setting descriptors
|
# pre-defined basic setting descriptors
|
||||||
#
|
#
|
||||||
|
|
@ -64,27 +68,27 @@ _FN_SWAP = ('fn-swap', 'Swap Fx function',
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
def _register_fn_swap(register=0x09, true_value=b'\x00\x01', mask=b'\x00\x01'):
|
def _register_fn_swap(register=_R.keyboard_fn_swap, true_value=b'\x00\x01', mask=b'\x00\x01'):
|
||||||
return register_toggle(_FN_SWAP[0], register, true_value=true_value, mask=mask,
|
return register_toggle(_FN_SWAP[0], register, true_value=true_value, mask=mask,
|
||||||
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
||||||
device_kind=_hidpp10.DEVICE_KIND.keyboard)
|
device_kind=_DK.keyboard)
|
||||||
|
|
||||||
def _register_smooth_scroll(register=0x01, true_value=0x40, mask=0x40):
|
def _register_smooth_scroll(register=_R.mouse_smooth_scroll, true_value=0x40, mask=0x40):
|
||||||
return register_toggle(_SMOOTH_SCROLL[0], register, true_value=true_value, mask=mask,
|
return register_toggle(_SMOOTH_SCROLL[0], register, true_value=true_value, mask=mask,
|
||||||
label=_SMOOTH_SCROLL[1], description=_SMOOTH_SCROLL[2],
|
label=_SMOOTH_SCROLL[1], description=_SMOOTH_SCROLL[2],
|
||||||
device_kind=_hidpp10.DEVICE_KIND.mouse)
|
device_kind=_DK.mouse)
|
||||||
|
|
||||||
def _register_dpi(register=0x63, choices=None):
|
def _register_dpi(register=_R.mouse_dpi, choices=None):
|
||||||
return register_choices(_DPI[0], register, choices,
|
return register_choices(_DPI[0], register, choices,
|
||||||
label=_DPI[1], description=_DPI[2],
|
label=_DPI[1], description=_DPI[2],
|
||||||
device_kind=_hidpp10.DEVICE_KIND.mouse)
|
device_kind=_DK.mouse)
|
||||||
|
|
||||||
|
|
||||||
def _feature_fn_swap():
|
def _feature_fn_swap():
|
||||||
return feature_toggle(_FN_SWAP[0], _hidpp20.FEATURE.FN_INVERSION,
|
return feature_toggle(_FN_SWAP[0], _F.FN_INVERSION,
|
||||||
write_returns_value=True,
|
write_returns_value=True,
|
||||||
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
||||||
device_kind=_hidpp10.DEVICE_KIND.keyboard)
|
device_kind=_DK.keyboard)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
@ -101,14 +105,14 @@ _SETTINGS_LIST = namedtuple('_SETTINGS_LIST', [
|
||||||
])
|
])
|
||||||
del namedtuple
|
del namedtuple
|
||||||
|
|
||||||
Register = _SETTINGS_LIST(
|
RegisterSettings = _SETTINGS_LIST(
|
||||||
fn_swap=_register_fn_swap,
|
fn_swap=_register_fn_swap,
|
||||||
smooth_scroll=_register_smooth_scroll,
|
smooth_scroll=_register_smooth_scroll,
|
||||||
dpi=_register_dpi,
|
dpi=_register_dpi,
|
||||||
hand_detection=None,
|
hand_detection=None,
|
||||||
typing_illumination=None,
|
typing_illumination=None,
|
||||||
)
|
)
|
||||||
Feature = _SETTINGS_LIST(
|
FeatureSettings = _SETTINGS_LIST(
|
||||||
fn_swap=_feature_fn_swap,
|
fn_swap=_feature_fn_swap,
|
||||||
smooth_scroll=None,
|
smooth_scroll=None,
|
||||||
dpi=None,
|
dpi=None,
|
||||||
|
|
@ -128,6 +132,6 @@ def check_feature_settings(device, already_known):
|
||||||
return
|
return
|
||||||
if device.protocol is not None and device.protocol < 2.0:
|
if device.protocol is not None and device.protocol < 2.0:
|
||||||
return
|
return
|
||||||
if not any(s.name == _FN_SWAP[0] for s in already_known) and _hidpp20.FEATURE.FN_INVERSION in device.features:
|
if not any(s.name == _FN_SWAP[0] for s in already_known) and _F.FN_INVERSION in device.features:
|
||||||
fn_swap = Feature.fn_swap()
|
fn_swap = FeatureSettings.fn_swap()
|
||||||
already_known.append(fn_swap(device))
|
already_known.append(fn_swap(device))
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ from .common import NamedInts as _NamedInts, NamedInt as _NamedInt, strhex as _s
|
||||||
from . import hidpp10 as _hidpp10
|
from . import hidpp10 as _hidpp10
|
||||||
from . import hidpp20 as _hidpp20
|
from . import hidpp20 as _hidpp20
|
||||||
|
|
||||||
|
_R = _hidpp10.REGISTERS
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
@ -82,18 +84,18 @@ class ReceiverStatus(dict):
|
||||||
# self.updated = _timestamp()
|
# self.updated = _timestamp()
|
||||||
self._changed_callback(self._receiver, alert=alert, reason=reason)
|
self._changed_callback(self._receiver, alert=alert, reason=reason)
|
||||||
|
|
||||||
def poll(self, timestamp):
|
# def poll(self, timestamp):
|
||||||
r = self._receiver
|
# r = self._receiver
|
||||||
assert r
|
# assert r
|
||||||
|
#
|
||||||
if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug("polling status of %s", r)
|
# _log.debug("polling status of %s", r)
|
||||||
|
#
|
||||||
# make sure to read some stuff that may be read later by the UI
|
# # make sure to read some stuff that may be read later by the UI
|
||||||
r.serial, r.firmware, None
|
# r.serial, r.firmware, None
|
||||||
|
#
|
||||||
# get an update of the notification flags
|
# # get an update of the notification flags
|
||||||
# self[KEYS.NOTIFICATION_FLAGS] = _hidpp10.get_notification_flags(r)
|
# # self[KEYS.NOTIFICATION_FLAGS] = _hidpp10.get_notification_flags(r)
|
||||||
|
|
||||||
def process_notification(self, n):
|
def process_notification(self, n):
|
||||||
if n.sub_id == 0x4A:
|
if n.sub_id == 0x4A:
|
||||||
|
|
@ -271,40 +273,40 @@ class DeviceStatus(dict):
|
||||||
# _log.debug("device %d changed: active=%s %s", self._device.number, self._active, dict(self))
|
# _log.debug("device %d changed: active=%s %s", self._device.number, self._active, dict(self))
|
||||||
self._changed_callback(d, alert, reason)
|
self._changed_callback(d, alert, reason)
|
||||||
|
|
||||||
def poll(self, timestamp):
|
# def poll(self, timestamp):
|
||||||
d = self._device
|
# d = self._device
|
||||||
if not d:
|
# if not d:
|
||||||
_log.error("polling status of invalid device")
|
# _log.error("polling status of invalid device")
|
||||||
return
|
# return
|
||||||
|
#
|
||||||
if self._active:
|
# if self._active:
|
||||||
if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug("polling status of %s", d)
|
# _log.debug("polling status of %s", d)
|
||||||
|
#
|
||||||
# read these from the device, the UI may need them later
|
# # read these from the device, the UI may need them later
|
||||||
d.protocol, d.serial, d.firmware, d.kind, d.name, d.settings, None
|
# d.protocol, d.serial, d.firmware, d.kind, d.name, d.settings, None
|
||||||
|
#
|
||||||
# make sure we know all the features of the device
|
# # make sure we know all the features of the device
|
||||||
# if d.features:
|
# # if d.features:
|
||||||
# d.features[:]
|
# # d.features[:]
|
||||||
|
#
|
||||||
# devices may go out-of-range while still active, or the computer
|
# # devices may go out-of-range while still active, or the computer
|
||||||
# may go to sleep and wake up without the devices available
|
# # may go to sleep and wake up without the devices available
|
||||||
if timestamp - self.updated > _STATUS_TIMEOUT:
|
# if timestamp - self.updated > _STATUS_TIMEOUT:
|
||||||
if d.ping():
|
# if d.ping():
|
||||||
timestamp = self.updated = _timestamp()
|
# timestamp = self.updated = _timestamp()
|
||||||
else:
|
# else:
|
||||||
self._changed(active=False, reason='out of range')
|
# self._changed(active=False, reason='out of range')
|
||||||
|
#
|
||||||
# if still active, make sure we know the battery level
|
# # if still active, make sure we know the battery level
|
||||||
if KEYS.BATTERY_LEVEL not in self:
|
# if KEYS.BATTERY_LEVEL not in self:
|
||||||
self.read_battery(timestamp)
|
# self.read_battery(timestamp)
|
||||||
|
#
|
||||||
elif timestamp - self.updated > _STATUS_TIMEOUT:
|
# elif timestamp - self.updated > _STATUS_TIMEOUT:
|
||||||
if d.ping():
|
# if d.ping():
|
||||||
self._changed(active=True)
|
# self._changed(active=True)
|
||||||
else:
|
# else:
|
||||||
self.updated = _timestamp()
|
# self.updated = _timestamp()
|
||||||
|
|
||||||
def process_notification(self, n):
|
def process_notification(self, n):
|
||||||
# incoming packets with SubId >= 0x80 are supposedly replies from
|
# incoming packets with SubId >= 0x80 are supposedly replies from
|
||||||
|
|
@ -319,9 +321,9 @@ class DeviceStatus(dict):
|
||||||
if self._device.protocol < 2.0:
|
if self._device.protocol < 2.0:
|
||||||
# README assuming HID++ 2.0 devices don't use the 0x07/0x0D registers
|
# README assuming HID++ 2.0 devices don't use the 0x07/0x0D registers
|
||||||
# however, this has not been fully verified yet
|
# however, this has not been fully verified yet
|
||||||
if n.sub_id in (0x07, 0x0D) and len(n.data) == 3 and n.data[2:3] == b'\x00':
|
if n.sub_id in (_R.battery_charge, _R.battery_status) and len(n.data) == 3 and n.data[2:3] == b'\x00':
|
||||||
return self._process_hidpp10_custom_notification(n)
|
return self._process_hidpp10_custom_notification(n)
|
||||||
if n.sub_id == 0x17 and len(n.data) == 3:
|
if n.sub_id == _R.illumination and len(n.data) == 3:
|
||||||
return self._process_hidpp10_custom_notification(n)
|
return self._process_hidpp10_custom_notification(n)
|
||||||
else:
|
else:
|
||||||
# assuming 0x00 to 0x3F are feature (HID++ 2.0) notifications
|
# assuming 0x00 to 0x3F are feature (HID++ 2.0) notifications
|
||||||
|
|
@ -337,22 +339,16 @@ class DeviceStatus(dict):
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug("%s (%s) custom battery notification %s", self._device, self._device.protocol, n)
|
_log.debug("%s (%s) custom battery notification %s", self._device, self._device.protocol, n)
|
||||||
|
|
||||||
if n.sub_id == 0x07:
|
if n.sub_id in (_R.battery_status, _R.battery_charge):
|
||||||
# message layout: 10 ix 07("address") <LEVEL> <STATUS> 00 00
|
data = '%c%s' % (n.address, n.data)
|
||||||
level, status = _hidpp10.parse_battery_reply_07(n.address, ord(n.data[:1]))
|
level, status = _hidpp10.parse_battery_status(n.sub_id, data)
|
||||||
self.set_battery_info(level, status)
|
self.set_battery_info(level, status)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if n.sub_id == 0x0D:
|
if n.sub_id == _R.illumination:
|
||||||
# message layout: 10 ix 0D("address") <CHARGE> <?> <STATUS> 00
|
|
||||||
level, status = _hidpp10.parse_battery_reply_0D(n.address, ord(n.data[1:2]))
|
|
||||||
self.set_battery_info(level, status)
|
|
||||||
return True
|
|
||||||
|
|
||||||
if n.sub_id == 0x17:
|
|
||||||
# message layout: 10 ix 17("address") <??> <?> <??> <light level 1=off..5=max>
|
# message layout: 10 ix 17("address") <??> <?> <??> <light level 1=off..5=max>
|
||||||
# TODO anything we can do with this?
|
# TODO anything we can do with this?
|
||||||
_log.info("backlight event: %s", n)
|
_log.info("illumination event: %s", n)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
_log.warn("%s: unrecognized %s", self._device, n)
|
_log.warn("%s: unrecognized %s", self._device, n)
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ def _print_receiver(receiver, verbose=False):
|
||||||
else:
|
else:
|
||||||
print (" Notifications: (none)")
|
print (" Notifications: (none)")
|
||||||
|
|
||||||
activity = receiver.read_register(0x2B3)
|
activity = receiver.read_register(hidpp10.REGISTERS.devices_activity)
|
||||||
if activity:
|
if activity:
|
||||||
activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)]
|
activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)]
|
||||||
activity_text = ', '.join(('%d=%d' % (d, a)) for d, a in activity if a > 0)
|
activity_text = ', '.join(('%d=%d' % (d, a)) for d, a in activity if a > 0)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue