added registers and settings to device descriptors

This commit is contained in:
Daniel Pavel 2012-12-07 13:54:03 +02:00
parent 6c3fa224e0
commit 3569489ce7
6 changed files with 232 additions and 76 deletions

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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:

View File

@ -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)

View File

@ -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)