added registers and settings to device descriptors
This commit is contained in:
parent
6c3fa224e0
commit
3569489ce7
|
@ -0,0 +1,63 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from .common import NamedInts as _NamedInts
|
||||
from . import hidpp10
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
|
||||
['name', 'kind', 'codename', 'registers', 'settings'])
|
||||
|
||||
DEVICES = {}
|
||||
|
||||
def _D(name, codename=None, kind=None, registers=None, settings=None):
|
||||
if kind is None:
|
||||
kind = (hidpp10.DEVICE_KIND.mouse if 'Mouse' in name
|
||||
else hidpp10.DEVICE_KIND.keyboard if 'Keyboard' in name
|
||||
else hidpp10.DEVICE_KIND.touchpad if 'Touchpad' in name
|
||||
else hidpp10.DEVICE_KIND.trackball if 'Trackball' in name
|
||||
else None)
|
||||
assert kind is not None
|
||||
|
||||
if codename is None:
|
||||
codename = name.split(' ')[-1]
|
||||
assert codename is not None
|
||||
|
||||
DEVICES[codename] = _DeviceDescriptor(name, kind, codename, registers, settings)
|
||||
|
||||
|
||||
_D('Wireless Mouse M315')
|
||||
_D('Wireless Mouse M325')
|
||||
_D('Wireless Mouse M505')
|
||||
_D('Wireless Mouse M510')
|
||||
_D('Couch Mouse M515')
|
||||
_D('Wireless Mouse M525')
|
||||
_D('Wireless Trackball M570')
|
||||
_D('Touch Mouse M600')
|
||||
_D('Marathon Mouse M705',
|
||||
registers=_NamedInts(battery=0x0D),
|
||||
settings=[hidpp10.SmoothScroll_Setting(0x01)]
|
||||
)
|
||||
_D('Wireless Keyboard K270')
|
||||
_D('Wireless Keyboard K350')
|
||||
_D('Wireless Keyboard K360')
|
||||
_D('Wireless Touch Keyboard K400')
|
||||
_D('Wireless Solar Keyboard K750')
|
||||
_D('Wireless Illuminated Keyboard K800')
|
||||
_D('Zone Touch Mouse T400')
|
||||
_D('Wireless Rechargeable Touchpad T650')
|
||||
_D('Logitech Cube', kind='mouse')
|
||||
_D('Anywhere Mouse MX', codename='Anywhere MX')
|
||||
_D('Performance Mouse MX', codename='Performance MX',
|
||||
settings=[
|
||||
hidpp10.MouseDPI_Setting(0x63, _NamedInts(**dict((str(x * 100), 0x80 + x) for x in range(1, 16)))),
|
||||
]
|
||||
)
|
||||
|
||||
del namedtuple
|
|
@ -1,66 +0,0 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
|
||||
['name', 'kind', 'codename', 'settings'])
|
||||
|
||||
DEVICES = {}
|
||||
|
||||
def _D(name, codename=None, kind=None):
|
||||
if kind is None:
|
||||
kind = ('mouse' if 'Mouse' in name
|
||||
else 'keyboard' if 'Keyboard' in name
|
||||
else 'touchpad' if 'Touchpad' in name
|
||||
else 'trackball' if 'Trackball' in name
|
||||
else None)
|
||||
assert kind is not None
|
||||
|
||||
if codename is None:
|
||||
codename = name.split(' ')[-1]
|
||||
assert codename is not None
|
||||
|
||||
DEVICES[codename] = _DeviceDescriptor(name, kind, codename, None)
|
||||
|
||||
|
||||
_D('Wireless Mouse M315')
|
||||
_D('Wireless Mouse M325')
|
||||
_D('Wireless Mouse M505')
|
||||
_D('Wireless Mouse M510')
|
||||
_D('Couch Mouse M515')
|
||||
_D('Wireless Mouse M525')
|
||||
_D('Wireless Trackball M570')
|
||||
_D('Touch Mouse M600')
|
||||
_D('Marathon Mouse M705')
|
||||
_D('Wireless Keyboard K270')
|
||||
_D('Wireless Keyboard K350')
|
||||
_D('Wireless Keyboard K360')
|
||||
_D('Wireless Touch Keyboard K400')
|
||||
_D('Wireless Solar Keyboard K750')
|
||||
_D('Wireless Illuminated Keyboard K800')
|
||||
_D('Zone Touch Mouse T400')
|
||||
_D('Wireless Rechargeable Touchpad T650')
|
||||
_D('Logitech Cube', kind='mouse')
|
||||
_D('Anywhere Mouse MX', codename='Anywhere MX')
|
||||
_D('Performance Mouse MX', codename='Performance MX')
|
||||
# DPI=(0x64, {0x80: 100,
|
||||
# 0x81: 200,
|
||||
# 0x82: 300,
|
||||
# 0x83: 400,
|
||||
# 0x84: 500,
|
||||
# 0x85: 600,
|
||||
# 0x86: 800,
|
||||
# 0x87: 900,
|
||||
# 0x88: 1000,
|
||||
# 0x89: 1100,
|
||||
# 0x8A: 1200,
|
||||
# 0x8B: 1300,
|
||||
# 0x8C: 1400,
|
||||
# 0x8D: 1500}),
|
||||
# Leds=(0x51, {}),
|
||||
|
||||
del _D
|
||||
del _DeviceDescriptor
|
||||
del namedtuple
|
|
@ -5,6 +5,7 @@
|
|||
from .common import (strhex as _strhex,
|
||||
NamedInts as _NamedInts,
|
||||
FirmwareInfo as _FirmwareInfo)
|
||||
from . import settings as _settings
|
||||
from .hidpp20 import FIRMWARE_KIND
|
||||
|
||||
#
|
||||
|
@ -57,6 +58,60 @@ PAIRING_ERRORS = _NamedInts(
|
|||
too_many_devices=0x03,
|
||||
sequence_timeout=0x06)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
class SmoothScroll_Setting(_settings.Setting):
|
||||
def __init__(self, register):
|
||||
super(SmoothScroll_Setting, self).__init__('smooth-scroll', _settings.KIND.toggle,
|
||||
'Smooth Scrolling', 'High-sensitivity mode for vertical scroll with the wheel.')
|
||||
assert register is not None
|
||||
self.register = register
|
||||
|
||||
def read(self):
|
||||
if self._value is None and self._device:
|
||||
ss = self.read_register()
|
||||
if ss:
|
||||
self._value = (ss[:1] == b'\x40')
|
||||
return self._value
|
||||
|
||||
def write(self, value):
|
||||
if self._device:
|
||||
reply = self.write_register(0x40 if bool(value) else 0x00)
|
||||
self._value = None
|
||||
if reply:
|
||||
return self.read()
|
||||
|
||||
|
||||
class MouseDPI_Setting(_settings.Setting):
|
||||
def __init__(self, register, choices):
|
||||
super(MouseDPI_Setting, self).__init__('dpi', _settings.KIND.choice,
|
||||
'Sensitivity (DPI)', choices=choices)
|
||||
assert choices
|
||||
assert isinstance(choices, _NamedInts)
|
||||
assert register is not None
|
||||
self.register = register
|
||||
|
||||
def read(self):
|
||||
if self._value is None and self._device:
|
||||
dpi = self.read_register()
|
||||
if dpi:
|
||||
value = ord(dpi[:1])
|
||||
self._value = self.choices[value]
|
||||
assert self._value is not None
|
||||
return self._value
|
||||
|
||||
def write(self, value):
|
||||
if self._device:
|
||||
choice = self.choices[value]
|
||||
if choice is None:
|
||||
raise ValueError(repr(value))
|
||||
reply = self.write_register(value)
|
||||
self._value = None
|
||||
if reply:
|
||||
return self.read()
|
||||
|
||||
#
|
||||
# functions
|
||||
|
@ -64,15 +119,18 @@ PAIRING_ERRORS = _NamedInts(
|
|||
|
||||
def get_battery(device):
|
||||
"""Reads a device's battery level, if provided by the HID++ 1.0 protocol."""
|
||||
reply = device.request(0x810D)
|
||||
if reply:
|
||||
charge = ord(reply[:1])
|
||||
status = ord(reply[2:3]) & 0xF0
|
||||
status = ('discharging' if status == 0x30
|
||||
else 'charging' if status == 0x50
|
||||
else 'fully charged' if status == 0x90
|
||||
else None)
|
||||
return charge, status
|
||||
if 'battery' in device.registers:
|
||||
register = device.registers['battery']
|
||||
|
||||
reply = device.request(0x8100 + (register & 0xFF))
|
||||
if reply:
|
||||
charge = ord(reply[:1])
|
||||
status = ord(reply[2:3]) & 0xF0
|
||||
status = ('discharging' if status == 0x30
|
||||
else 'charging' if status == 0x50
|
||||
else 'fully charged' if status == 0x90
|
||||
else None)
|
||||
return charge, status
|
||||
|
||||
|
||||
def get_serial(device):
|
||||
|
|
|
@ -9,6 +9,7 @@ from logging import getLogger, DEBUG as _DEBUG
|
|||
_log = getLogger('LUR').getChild('hidpp20')
|
||||
del getLogger
|
||||
|
||||
from . import settings as _settings
|
||||
from .common import (FirmwareInfo as _FirmwareInfo,
|
||||
ReprogrammableKeyInfo as _ReprogrammableKeyInfo,
|
||||
KwException as _KwException,
|
||||
|
@ -290,6 +291,32 @@ class KeysArray(object):
|
|||
#
|
||||
#
|
||||
|
||||
class ToggleFN_Setting(_settings.Setting):
|
||||
def __init__(self):
|
||||
super(ToggleFN_Setting, self).__init__('fn-toggle', _settings.KIND.toggle, 'Swap Fx function',
|
||||
'When set, the F1..F12 keys will activate their special function,\n'
|
||||
'and you must hold the FN key to activate their standard function.\n'
|
||||
'\n'
|
||||
'When unset, the F1..F12 keys will activate their standard function,\n'
|
||||
'and you must hold the FN key to activate their special function.')
|
||||
|
||||
def read(self):
|
||||
if self._value is None and self._device:
|
||||
fn = self._device.feature_request(FEATURE.FN_STATUS)
|
||||
if fn:
|
||||
self._value = (fn[:1] == b'\x01')
|
||||
return self._value
|
||||
|
||||
def write(self, value):
|
||||
if self._device:
|
||||
reply = self._device.feature_request(FEATURE.FN_STATUS, 0x10, 0x01 if value else 0x00)
|
||||
self._value = (reply[:1] == b'\x01') if reply else None
|
||||
return self._value
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def feature_request(device, feature, function=0x00, *params):
|
||||
if device.features:
|
||||
if feature in device.features:
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import errno as _errno
|
||||
from weakref import proxy as _proxy
|
||||
from collections import defaultdict as _defaultdict
|
||||
|
||||
from logging import getLogger
|
||||
_log = getLogger('LUR').getChild('receiver')
|
||||
|
@ -13,7 +14,7 @@ from . import base as _base
|
|||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
from .common import strhex as _strhex
|
||||
from .devices import DEVICES as _DEVICES
|
||||
from .descriptors import DEVICES as _DEVICES
|
||||
|
||||
#
|
||||
#
|
||||
|
@ -42,6 +43,8 @@ class PairedDevice(object):
|
|||
self._keys = None
|
||||
|
||||
self.features = _hidpp20.FeaturesArray(self)
|
||||
self._registers = None
|
||||
self._settings = None
|
||||
|
||||
@property
|
||||
def protocol(self):
|
||||
|
@ -134,6 +137,31 @@ class PairedDevice(object):
|
|||
self._keys = _hidpp20.get_keys(self) or ()
|
||||
return self._keys
|
||||
|
||||
@property
|
||||
def registers(self):
|
||||
if self._registers is None:
|
||||
descriptor = _DEVICES.get(self.codename)
|
||||
if descriptor is None or descriptor.registers is None:
|
||||
self._registers = _defaultdict(lambda: None)
|
||||
else:
|
||||
self._registers = descriptor.registers
|
||||
return self._registers
|
||||
|
||||
@property
|
||||
def settings(self):
|
||||
if self._settings is None:
|
||||
descriptor = _DEVICES.get(self.codename)
|
||||
if descriptor is None or descriptor.settings is None:
|
||||
self._settings = []
|
||||
else:
|
||||
self._settings = [s(self) for s in descriptor.settings]
|
||||
|
||||
if _hidpp20.FEATURE.FN_STATUS in self.features:
|
||||
tfn = _hidpp20.ToggleFN_Setting()
|
||||
self._settings.insert(0, tfn(self))
|
||||
|
||||
return self._settings
|
||||
|
||||
def request(self, request_id, *params):
|
||||
return _base.request(self.receiver.handle, self.number, request_id, *params)
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
|
||||
from weakref import proxy as _proxy
|
||||
from copy import copy as _copy
|
||||
|
||||
from .common import NamedInts as _NamedInts
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
KIND = _NamedInts(toggle=0x1, choice=0x02, range=0x03)
|
||||
|
||||
class Setting(object):
|
||||
__slots__ = ['name', 'kind', 'label', 'description', 'choices', '_device', '_value', 'register']
|
||||
|
||||
def __init__(self, name, kind, label, description=None, choices=None):
|
||||
self.name = name
|
||||
self.kind = kind
|
||||
self.label = label
|
||||
self.description = description
|
||||
self.choices = choices
|
||||
self.register = None
|
||||
|
||||
def __call__(self, device):
|
||||
o = _copy(self)
|
||||
o._value = None
|
||||
o._device = _proxy(device)
|
||||
return o
|
||||
|
||||
def read_register(self):
|
||||
return self._device.request(0x8100 | (self.register & 0x2FF))
|
||||
|
||||
def write_register(self, value, value2=0):
|
||||
return self._device.request(0x8000 | (self.register & 0x2FF), int(value) & 0xFF, int(value2) & 0xFF)
|
||||
|
||||
def read(self):
|
||||
raise NotImplemented
|
||||
|
||||
def write(self, value):
|
||||
raise NotImplemented
|
||||
|
||||
def __str__(self):
|
||||
return '<%s(%s=%s)>' % (self.__class__.__name__, self.name, self._value)
|
Loading…
Reference in New Issue