improved support for some Nano receivers
This commit is contained in:
parent
790fc7c04b
commit
c3b73964d5
|
@ -67,9 +67,10 @@ class DeviceUnreachable(_KwException):
|
||||||
#
|
#
|
||||||
|
|
||||||
# vendor_id, product_id, usb interface number, hid driver
|
# vendor_id, product_id, usb interface number, hid driver
|
||||||
DEVICE_UNIFYING_RECEIVER = (0x046d, 0xc52b, 2, 'logitech-djreceiver')
|
DEVICE_UNIFYING_RECEIVER = (0x046d, 0xc52b, 2, 'logitech-djreceiver')
|
||||||
DEVICE_UNIFYING_RECEIVER_2 = (0x046d, 0xc532, 2, 'logitech-djreceiver')
|
DEVICE_UNIFYING_RECEIVER_2 = (0x046d, 0xc532, 2, 'logitech-djreceiver')
|
||||||
DEVICE_NANO_RECEIVER = (0x046d, 0xc526, 1, 'hid-generic')
|
DEVICE_NANO_RECEIVER = (0x046d, 0xc52f, 1, 'hid-generic')
|
||||||
|
DEVICE_VXNANO_RECEIVER = (0x046d, 0xc526, 1, 'hid-generic')
|
||||||
|
|
||||||
|
|
||||||
def receivers():
|
def receivers():
|
||||||
|
@ -78,8 +79,10 @@ def receivers():
|
||||||
yield d
|
yield d
|
||||||
for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER_2):
|
for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER_2):
|
||||||
yield d
|
yield d
|
||||||
#for d in _hid.enumerate(*DEVICE_NANO_RECEIVER):
|
for d in _hid.enumerate(*DEVICE_NANO_RECEIVER):
|
||||||
# yield d
|
yield d
|
||||||
|
for d in _hid.enumerate(*DEVICE_VXNANO_RECEIVER):
|
||||||
|
yield d
|
||||||
|
|
||||||
|
|
||||||
def notify_on_receivers(callback):
|
def notify_on_receivers(callback):
|
||||||
|
@ -87,7 +90,8 @@ def notify_on_receivers(callback):
|
||||||
_hid.monitor_async(callback,
|
_hid.monitor_async(callback,
|
||||||
DEVICE_UNIFYING_RECEIVER,
|
DEVICE_UNIFYING_RECEIVER,
|
||||||
DEVICE_UNIFYING_RECEIVER_2,
|
DEVICE_UNIFYING_RECEIVER_2,
|
||||||
# DEVICE_NANO_RECEIVER,
|
DEVICE_NANO_RECEIVER,
|
||||||
|
DEVICE_VXNANO_RECEIVER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -51,11 +51,11 @@ def check_features(device, already_known):
|
||||||
#
|
#
|
||||||
|
|
||||||
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
|
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
|
||||||
['name', 'kind', 'codename', 'registers', 'settings'])
|
['name', 'kind', 'product_id', 'codename', 'registers', 'settings'])
|
||||||
|
|
||||||
DEVICES = {}
|
DEVICES = {}
|
||||||
|
|
||||||
def _D(name, codename=None, kind=None, registers=None, settings=None):
|
def _D(name, codename=None, kind=None, product_id=None, registers=None, settings=None):
|
||||||
if kind is None:
|
if kind is None:
|
||||||
kind = (_hidpp10.DEVICE_KIND.mouse if 'Mouse' in name
|
kind = (_hidpp10.DEVICE_KIND.mouse if 'Mouse' in name
|
||||||
else _hidpp10.DEVICE_KIND.keyboard if 'Keyboard' in name
|
else _hidpp10.DEVICE_KIND.keyboard if 'Keyboard' in name
|
||||||
|
@ -68,7 +68,9 @@ def _D(name, codename=None, kind=None, registers=None, settings=None):
|
||||||
codename = name.split(' ')[-1]
|
codename = name.split(' ')[-1]
|
||||||
assert codename is not None
|
assert codename is not None
|
||||||
|
|
||||||
DEVICES[codename] = _DeviceDescriptor(name, kind, codename, registers, settings)
|
DEVICES[codename] = _DeviceDescriptor(name, kind, product_id, codename, registers, settings)
|
||||||
|
if product_id:
|
||||||
|
DEVICES[product_id] = DEVICES[codename]
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
@ -133,6 +135,13 @@ _D('Wireless Illuminated Keyboard K800',
|
||||||
|
|
||||||
# Mice
|
# Mice
|
||||||
|
|
||||||
|
# _D('VX Nano Cordless Laser Mouse', product_id='c526', codename='VXNano',
|
||||||
|
# registers={'battery_charge': 0x0D},
|
||||||
|
# settings=[
|
||||||
|
# _register_smooth_scroll(0x01, true_value=0x40, mask=0x40),
|
||||||
|
# ],
|
||||||
|
# )
|
||||||
|
|
||||||
_D('Wireless Mouse M315')
|
_D('Wireless Mouse M315')
|
||||||
_D('Wireless Mouse M325')
|
_D('Wireless Mouse M325')
|
||||||
_D('Wireless Mouse M505')
|
_D('Wireless Mouse M505')
|
||||||
|
@ -154,7 +163,7 @@ _D('Marathon Mouse M705',
|
||||||
)
|
)
|
||||||
_D('Zone Touch Mouse T400')
|
_D('Zone Touch Mouse T400')
|
||||||
_D('Touch Mouse T620')
|
_D('Touch Mouse T620')
|
||||||
_D('Logitech Cube', kind='mouse')
|
_D('Logitech Cube', kind=_hidpp10.DEVICE_KIND.mouse)
|
||||||
_D('Anywhere Mouse MX', codename='Anywhere MX',
|
_D('Anywhere Mouse MX', codename='Anywhere MX',
|
||||||
# registers={'battery_charge': 0x0D},
|
# registers={'battery_charge': 0x0D},
|
||||||
# settings=[
|
# settings=[
|
||||||
|
|
|
@ -153,7 +153,7 @@ def get_serial(device):
|
||||||
|
|
||||||
|
|
||||||
def get_firmware(device):
|
def get_firmware(device):
|
||||||
firmware = []
|
firmware = [None, None]
|
||||||
|
|
||||||
reply = device.request(0x81F1, 0x01)
|
reply = device.request(0x81F1, 0x01)
|
||||||
if reply:
|
if reply:
|
||||||
|
@ -163,23 +163,17 @@ def get_firmware(device):
|
||||||
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.append(fw)
|
firmware[0] = fw
|
||||||
|
|
||||||
if device.kind is None and device.max_devices == 1:
|
|
||||||
# Nano receiver
|
|
||||||
return firmware
|
|
||||||
if device.kind is not None and not device._unifying:
|
|
||||||
# Nano device
|
|
||||||
return firmware
|
|
||||||
|
|
||||||
reply = device.request(0x81F1, 0x04)
|
reply = device.request(0x81F1, 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.append(bl)
|
firmware[1] = bl
|
||||||
|
|
||||||
return tuple(firmware)
|
if any(firmware):
|
||||||
|
return tuple(f for f in firmware if f)
|
||||||
|
|
||||||
|
|
||||||
def get_notification_flags(device):
|
def get_notification_flags(device):
|
||||||
|
|
|
@ -32,19 +32,40 @@ class PairedDevice(object):
|
||||||
assert number > 0 and number <= receiver.max_devices
|
assert number > 0 and number <= receiver.max_devices
|
||||||
self.number = number
|
self.number = number
|
||||||
|
|
||||||
self._unifying = receiver.max_devices > 1
|
self.wpid = None
|
||||||
self._protocol = None if self._unifying else 1.0
|
self.polling_rate = 0
|
||||||
self._wpid = None
|
|
||||||
self._power_switch = None
|
self._kind = None
|
||||||
self._polling_rate = None if self._unifying else 0
|
|
||||||
self._codename = None
|
self._codename = None
|
||||||
self._name = None
|
self._name = None
|
||||||
self._kind = None
|
|
||||||
self._serial = None if self._unifying else receiver.serial
|
self._protocol = None if self.receiver.unifying_supported else 1.0
|
||||||
|
self._power_switch = None if self.receiver.unifying_supported else '(unknown)'
|
||||||
|
self._serial = None if self.receiver.unifying_supported else self.receiver.serial
|
||||||
|
|
||||||
|
if self.receiver.unifying_supported:
|
||||||
|
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
||||||
|
if pair_info:
|
||||||
|
self.wpid = _strhex(pair_info[3:5])
|
||||||
|
kind = ord(pair_info[7:8]) & 0x0F
|
||||||
|
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||||
|
self.polling_rate = ord(pair_info[2:3])
|
||||||
|
# else:
|
||||||
|
# # guesswork...
|
||||||
|
# descriptor = _descriptors.DEVICES.get(self.receiver.product_id)
|
||||||
|
# if descriptor:
|
||||||
|
# self._kind = descriptor.kind
|
||||||
|
# self._codename = descriptor.codename
|
||||||
|
# self._name = descriptor.name
|
||||||
|
|
||||||
|
# device_info = self.receiver.request(0x83B5, 0x04)
|
||||||
|
# if device_info:
|
||||||
|
# self.wpid = _strhex(device_info[3:5])
|
||||||
|
|
||||||
self._firmware = None
|
self._firmware = None
|
||||||
self._keys = None
|
self._keys = None
|
||||||
|
|
||||||
self.features = _hidpp20.FeaturesArray(self) if self._unifying else None
|
self.features = _hidpp20.FeaturesArray(self) if self.receiver.unifying_supported else None
|
||||||
self._registers = None
|
self._registers = None
|
||||||
self._settings = None
|
self._settings = None
|
||||||
|
|
||||||
|
@ -55,66 +76,36 @@ class PairedDevice(object):
|
||||||
# _log.debug("device %d protocol %s", self.number, self._protocol)
|
# _log.debug("device %d protocol %s", self.number, self._protocol)
|
||||||
return self._protocol or 0
|
return self._protocol or 0
|
||||||
|
|
||||||
@property
|
|
||||||
def wpid(self):
|
|
||||||
if self._wpid is None:
|
|
||||||
if self._unifying:
|
|
||||||
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
|
||||||
if pair_info:
|
|
||||||
self._wpid = _strhex(pair_info[3:5])
|
|
||||||
if self._kind is None:
|
|
||||||
kind = ord(pair_info[7:8]) & 0x0F
|
|
||||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
|
||||||
if self._polling_rate is None:
|
|
||||||
self._polling_rate = ord(pair_info[2:3])
|
|
||||||
else:
|
|
||||||
# guesswork...
|
|
||||||
device_info = self.receiver.request(0x83B5, 0x04)
|
|
||||||
self.wpid = _strhex(device_info[3:5])
|
|
||||||
return self._wpid
|
|
||||||
|
|
||||||
@property
|
|
||||||
def polling_rate(self):
|
|
||||||
if self._polling_rate is None:
|
|
||||||
if self._unifying:
|
|
||||||
self.wpid, 0
|
|
||||||
else:
|
|
||||||
self._polling_rate = 0
|
|
||||||
return self._polling_rate
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def power_switch_location(self):
|
def power_switch_location(self):
|
||||||
if self._power_switch is None:
|
if self._power_switch is None:
|
||||||
if self._unifying:
|
ps = self.receiver.request(0x83B5, 0x30 + self.number - 1)
|
||||||
ps = self.receiver.request(0x83B5, 0x30 + self.number - 1)
|
if ps:
|
||||||
if ps:
|
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]
|
|
||||||
return self._power_switch
|
return self._power_switch
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def codename(self):
|
def codename(self):
|
||||||
if self._codename is None:
|
if self._codename is None:
|
||||||
if self._unifying:
|
codename = self.receiver.request(0x83B5, 0x40 + self.number - 1)
|
||||||
codename = self.receiver.request(0x83B5, 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)
|
|
||||||
return self._codename
|
return self._codename
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name is None:
|
if self._name is None:
|
||||||
if self._unifying:
|
if self.codename in _descriptors.DEVICES:
|
||||||
if self.codename in _descriptors.DEVICES:
|
self._name, self._kind = _descriptors.DEVICES[self._codename][:2]
|
||||||
self._name, self._kind = _descriptors.DEVICES[self._codename][:2]
|
elif self.protocol >= 2.0:
|
||||||
elif self.protocol >= 2.0:
|
self._name = _hidpp20.get_name(self)
|
||||||
self._name = _hidpp20.get_name(self)
|
|
||||||
return self._name or self.codename or '?'
|
return self._name or self.codename or '?'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kind(self):
|
def kind(self):
|
||||||
if self._kind is None and self._unifying:
|
if self._kind is None and self.receiver.unifying_supported:
|
||||||
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
||||||
if pair_info:
|
if pair_info:
|
||||||
kind = ord(pair_info[7:8]) & 0x0F
|
kind = ord(pair_info[7:8]) & 0x0F
|
||||||
|
@ -146,7 +137,7 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def keys(self):
|
def keys(self):
|
||||||
if self._keys is None:
|
if self._keys is None:
|
||||||
if self._unifying:
|
if self.protocol >= 2.0:
|
||||||
self._keys = _hidpp20.get_keys(self) or ()
|
self._keys = _hidpp20.get_keys(self) or ()
|
||||||
return self._keys
|
return self._keys
|
||||||
|
|
||||||
|
@ -201,7 +192,7 @@ class PairedDevice(object):
|
||||||
return _base.request(self.receiver.handle, self.number, request_id, *params)
|
return _base.request(self.receiver.handle, self.number, request_id, *params)
|
||||||
|
|
||||||
def feature_request(self, feature, function=0x00, *params):
|
def feature_request(self, feature, function=0x00, *params):
|
||||||
if self._unifying:
|
if self.protocol >= 2.0:
|
||||||
return _hidpp20.feature_request(self, feature, function, *params)
|
return _hidpp20.feature_request(self, feature, function, *params)
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
|
@ -236,15 +227,18 @@ class Receiver(object):
|
||||||
number = 0xFF
|
number = 0xFF
|
||||||
kind = None
|
kind = None
|
||||||
|
|
||||||
def __init__(self, handle, path=None):
|
def __init__(self, handle, device_info):
|
||||||
assert handle
|
assert handle
|
||||||
self.handle = handle
|
self.handle = handle
|
||||||
assert path
|
assert device_info
|
||||||
self.path = path
|
self.path = device_info.path
|
||||||
|
self.product_id = device_info.product_id
|
||||||
|
|
||||||
|
# read the serial immediately, so we can find out max_devices
|
||||||
|
# this will tell us if it's a Unifying or Nano receiver
|
||||||
serial_reply = self.request(0x83B5, 0x03)
|
serial_reply = self.request(0x83B5, 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])
|
||||||
|
|
||||||
if self.max_devices == 1:
|
if self.max_devices == 1:
|
||||||
|
@ -255,6 +249,9 @@ class Receiver(object):
|
||||||
raise Exception("unknown receiver type")
|
raise Exception("unknown receiver type")
|
||||||
self._str = '<%s(%s,%s%s)>' % (self.name.replace(' ', ''), self.path, '' if type(self.handle) == int else 'T', self.handle)
|
self._str = '<%s(%s,%s%s)>' % (self.name.replace(' ', ''), self.path, '' if type(self.handle) == int else 'T', self.handle)
|
||||||
|
|
||||||
|
old_equad_reply = self.request(0x83B5, 0x04)
|
||||||
|
self.unifying_supported = old_equad_reply is None
|
||||||
|
|
||||||
self._firmware = None
|
self._firmware = None
|
||||||
self._devices = {}
|
self._devices = {}
|
||||||
|
|
||||||
|
@ -266,13 +263,6 @@ class Receiver(object):
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
@property
|
|
||||||
def serial(self):
|
|
||||||
assert self._serial
|
|
||||||
# if self._serial is None and self.handle:
|
|
||||||
# self._serial = _hidpp10.get_serial(self)
|
|
||||||
return self._serial
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def firmware(self):
|
def firmware(self):
|
||||||
if self._firmware is None and self.handle:
|
if self._firmware is None and self.handle:
|
||||||
|
@ -308,21 +298,13 @@ class Receiver(object):
|
||||||
def register_new_device(self, number):
|
def register_new_device(self, number):
|
||||||
if self._devices.get(number) is not None:
|
if self._devices.get(number) is not None:
|
||||||
raise IndexError("%s: device number %d already registered" % (self, number))
|
raise IndexError("%s: device number %d already registered" % (self, number))
|
||||||
|
|
||||||
dev = PairedDevice(self, number)
|
dev = PairedDevice(self, number)
|
||||||
# create a device object, but only use it if the receiver knows about it
|
|
||||||
|
|
||||||
# Nano receiver
|
|
||||||
if self.max_devices == 1 and number == 1:
|
|
||||||
# the Nano receiver does not provide the wpid
|
|
||||||
_log.info("%s: found Nano device %d (%s)", self, number, dev.serial)
|
|
||||||
# dev._wpid = self.serial + ':1'
|
|
||||||
self._devices[number] = dev
|
|
||||||
return dev
|
|
||||||
|
|
||||||
if dev.wpid:
|
if dev.wpid:
|
||||||
_log.info("%s: found Unifying device %d (%s)", self, number, dev.wpid)
|
_log.info("%s: found device %d (%s)", self, number, dev.wpid)
|
||||||
self._devices[number] = dev
|
self._devices[number] = dev
|
||||||
return dev
|
return dev
|
||||||
|
|
||||||
self._devices[number] = None
|
self._devices[number] = None
|
||||||
|
|
||||||
def set_lock(self, lock_closed=True, device=0, timeout=0):
|
def set_lock(self, lock_closed=True, device=0, timeout=0):
|
||||||
|
@ -403,18 +385,18 @@ class Receiver(object):
|
||||||
__bool__ = __nonzero__ = lambda self: self.handle is not None
|
__bool__ = __nonzero__ = lambda self: self.handle is not None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def open(self, path):
|
def open(self, device_info):
|
||||||
"""Opens a Logitech Receiver found attached to the machine, by Linux device path.
|
"""Opens a Logitech Receiver found attached to the machine, by Linux device path.
|
||||||
|
|
||||||
:returns: An open file handle for the found receiver, or ``None``.
|
:returns: An open file handle for the found receiver, or ``None``.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
handle = _base.open_path(path)
|
handle = _base.open_path(device_info.path)
|
||||||
if handle:
|
if handle:
|
||||||
return Receiver(handle, path)
|
return Receiver(handle, device_info)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
_log.exception("open %s", path)
|
_log.exception("open %s", device_info)
|
||||||
if e.errno == _errno.EACCES:
|
if e.errno == _errno.EACCES:
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
_log.exception("open %s", path)
|
_log.exception("open %s", device_info)
|
||||||
|
|
|
@ -273,6 +273,7 @@ class DeviceStatus(dict):
|
||||||
_log.warn("%s: unrecognized %s", self._device, n)
|
_log.warn("%s: unrecognized %s", self._device, n)
|
||||||
|
|
||||||
def _process_hidpp10_notification(self, n):
|
def _process_hidpp10_notification(self, n):
|
||||||
|
# unpair notification
|
||||||
if n.sub_id == 0x40:
|
if n.sub_id == 0x40:
|
||||||
if n.address == 0x02:
|
if n.address == 0x02:
|
||||||
# device un-paired
|
# device un-paired
|
||||||
|
@ -283,44 +284,33 @@ class DeviceStatus(dict):
|
||||||
_log.warn("%s: disconnection with unknown type %02X: %s", self._device, n.address, n)
|
_log.warn("%s: disconnection with unknown type %02X: %s", self._device, n.address, n)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# wireless link notification
|
||||||
if n.sub_id == 0x41:
|
if n.sub_id == 0x41:
|
||||||
if n.address == 0x04: # unifying protocol
|
protocol_name = ('unifying (eQuad DJ)' if n.address == 0x04
|
||||||
# wpid = _strhex(n.data[4:5] + n.data[3:4])
|
else 'eQuad' if n.address == 0x03
|
||||||
# assert wpid == device.wpid
|
else None)
|
||||||
|
if protocol_name:
|
||||||
flags = ord(n.data[:1]) & 0xF0
|
flags = ord(n.data[:1]) & 0xF0
|
||||||
link_encrypyed = bool(flags & 0x20)
|
link_encrypyed = bool(flags & 0x20)
|
||||||
link_established = not (flags & 0x40)
|
link_established = not (flags & 0x40)
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
sw_present = bool(flags & 0x10)
|
sw_present = bool(flags & 0x10)
|
||||||
has_payload = bool(flags & 0x80)
|
has_payload = bool(flags & 0x80)
|
||||||
_log.debug("%s: unifying connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
|
_log.debug("%s: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
|
||||||
self._device, sw_present, link_encrypyed, link_established, has_payload)
|
self._device, protocol_name, sw_present, link_encrypyed, link_established, has_payload)
|
||||||
self[ENCRYPTED] = link_encrypyed
|
|
||||||
self._changed(link_established)
|
|
||||||
|
|
||||||
elif n.address == 0x03: # eQuad protocol
|
|
||||||
# Nano devices might not have been initialized fully
|
|
||||||
if self._device._kind is None:
|
|
||||||
kind = ord(n.data[:1]) & 0x0F
|
|
||||||
self._device._kind = _hidpp10.DEVICE_KIND[kind]
|
|
||||||
if self._device._wpid is None:
|
|
||||||
self._device._wpid = _strhex(n.data[2:3] + n.data[1:2])
|
|
||||||
|
|
||||||
flags = ord(n.data[:1]) & 0xF0
|
|
||||||
link_encrypyed = bool(flags & 0x20)
|
|
||||||
link_established = not (flags & 0x40)
|
|
||||||
if _log.isEnabledFor(_DEBUG):
|
|
||||||
sw_present = bool(flags & 0x10)
|
|
||||||
has_payload = bool(flags & 0x80)
|
|
||||||
_log.debug("%s: eQuad connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
|
|
||||||
self._device, sw_present, link_encrypyed, link_established, has_payload)
|
|
||||||
self[ENCRYPTED] = link_encrypyed
|
self[ENCRYPTED] = link_encrypyed
|
||||||
self._changed(link_established)
|
self._changed(link_established)
|
||||||
|
|
||||||
|
if protocol_name == 'eQuad':
|
||||||
|
# some Nano devices might not have been initialized fully
|
||||||
|
if self._device._kind is None:
|
||||||
|
kind = ord(n.data[:1]) & 0x0F
|
||||||
|
self._device._kind = _hidpp10.DEVICE_KIND[kind]
|
||||||
|
assert self._device.wpid == _strhex(n.data[2:3] + n.data[1:2])
|
||||||
else:
|
else:
|
||||||
_log.warn("%s: connection notification with unknown protocol %02X: %s", self._device.number, n.address, n)
|
_log.warn("%s: connection notification with unknown protocol %02X: %s", self._device.number, n.address, n)
|
||||||
|
|
||||||
|
# if the device just came online, read the battery charge
|
||||||
if self._active: # and BATTERY_LEVEL not in self:
|
if self._active: # and BATTERY_LEVEL not in self:
|
||||||
self.read_battery()
|
self.read_battery()
|
||||||
|
|
||||||
|
@ -332,6 +322,7 @@ class DeviceStatus(dict):
|
||||||
# if n.address == 0x03, it's an actual input event
|
# if n.address == 0x03, it's an actual input event
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# power notification
|
||||||
if n.sub_id == 0x4B:
|
if n.sub_id == 0x4B:
|
||||||
if n.address == 0x01:
|
if n.address == 0x01:
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
|
|
@ -36,7 +36,7 @@ def _receiver():
|
||||||
from logitech.unifying_receiver.base import receivers
|
from logitech.unifying_receiver.base import receivers
|
||||||
for dev_info in receivers():
|
for dev_info in receivers():
|
||||||
try:
|
try:
|
||||||
r = Receiver.open(dev_info.path)
|
r = Receiver.open(dev_info)
|
||||||
if r:
|
if r:
|
||||||
return r
|
return r
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -99,7 +99,7 @@ def _print_receiver(receiver, verbose=False):
|
||||||
else:
|
else:
|
||||||
print (" All notifications disabled")
|
print (" All notifications disabled")
|
||||||
|
|
||||||
if paired_count > 0:
|
if receiver.unifying_supported:
|
||||||
activity = receiver.request(0x83B3)
|
activity = receiver.request(0x83B3)
|
||||||
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)]
|
||||||
|
|
|
@ -50,12 +50,12 @@ def _run(args):
|
||||||
listeners = {}
|
listeners = {}
|
||||||
from solaar.listener import ReceiverListener
|
from solaar.listener import ReceiverListener
|
||||||
|
|
||||||
def handle_receivers_events(action, device):
|
def handle_receivers_events(action, device_info):
|
||||||
assert action is not None
|
assert action is not None
|
||||||
assert device is not None
|
assert device_info is not None
|
||||||
|
|
||||||
# whatever the action, stop any previous receivers at this path
|
# whatever the action, stop any previous receivers at this path
|
||||||
l = listeners.pop(device.path, None)
|
l = listeners.pop(device_info.path, None)
|
||||||
if l is not None:
|
if l is not None:
|
||||||
assert isinstance(l, ReceiverListener)
|
assert isinstance(l, ReceiverListener)
|
||||||
l.stop()
|
l.stop()
|
||||||
|
@ -63,18 +63,17 @@ def _run(args):
|
||||||
if action == 'add':
|
if action == 'add':
|
||||||
# a new receiver device was detected
|
# a new receiver device was detected
|
||||||
try:
|
try:
|
||||||
l = ReceiverListener.open(device.path, status_changed)
|
l = ReceiverListener.open(device_info, status_changed)
|
||||||
if l is not None:
|
if l is not None:
|
||||||
listeners[device.path] = l
|
listeners[device_info.path] = l
|
||||||
except OSError:
|
except OSError:
|
||||||
# permission error, blacklist this path for now
|
# permission error, blacklist this path for now
|
||||||
listeners.pop(device.path, None)
|
listeners.pop(device_info.path, None)
|
||||||
GLib.idle_add(ui.error_dialog, 'Permissions error',
|
GLib.idle_add(ui.error_dialog, 'Permissions error',
|
||||||
'Found a Logitech Unifying Receiver device,\n'
|
'Found a Logitech Receiver, but did not have permission to open it.\n'
|
||||||
'but did not have permission to open it.\n'
|
|
||||||
'\n'
|
'\n'
|
||||||
'If you\'ve just installed Solaar, try removing\n'
|
'If you\'ve just installed Solaar, try removing the receiver\n'
|
||||||
'the receiver and plugging it back in.')
|
'and plugging it back in.')
|
||||||
|
|
||||||
# elif action == 'remove':
|
# elif action == 'remove':
|
||||||
# # we'll be receiving remove events for any hidraw devices,
|
# # we'll be receiving remove events for any hidraw devices,
|
||||||
|
|
|
@ -161,12 +161,12 @@ class ReceiverListener(_listener.EventsListener):
|
||||||
__unicode__ = __str__
|
__unicode__ = __str__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def open(self, path, status_changed_callback):
|
def open(self, device_info, status_changed_callback):
|
||||||
assert status_changed_callback
|
assert status_changed_callback
|
||||||
receiver = Receiver.open(path)
|
receiver = Receiver.open(device_info)
|
||||||
if receiver:
|
if receiver:
|
||||||
rl = ReceiverListener(receiver, status_changed_callback)
|
rl = ReceiverListener(receiver, status_changed_callback)
|
||||||
rl.start()
|
rl.start()
|
||||||
return rl
|
return rl
|
||||||
else:
|
else:
|
||||||
_log.warn("failed to open %s", path)
|
_log.warn("failed to open %s", device_info)
|
||||||
|
|
|
@ -70,16 +70,17 @@ def _make_receiver_box(receiver):
|
||||||
device = f._device
|
device = f._device
|
||||||
if True: # f._info_label.get_visible() and '\n' not in f._info_label.get_text():
|
if True: # f._info_label.get_visible() and '\n' not in f._info_label.get_text():
|
||||||
items = [('Path', device.path), ('Serial', device.serial)] + \
|
items = [('Path', device.path), ('Serial', device.serial)] + \
|
||||||
[(fw.kind, fw.version) for fw in device.firmware]
|
list((fw.kind, fw.version) for fw in device.firmware) + \
|
||||||
|
[None]
|
||||||
|
|
||||||
notification_flags = _hidpp10.get_notification_flags(device)
|
notification_flags = _hidpp10.get_notification_flags(device)
|
||||||
if notification_flags:
|
if notification_flags:
|
||||||
notification_flags = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags)
|
notification_flags = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags)
|
||||||
else:
|
else:
|
||||||
notification_flags = ('(none)',)
|
notification_flags = ('(none)',)
|
||||||
items.append(('Notifications', ('\n%16s' % ' ').join(notification_flags)))
|
items[-1] = ('Notifications', ('\n%16s' % ' ').join(notification_flags))
|
||||||
|
|
||||||
f._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % item for item in items))
|
f._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % i for i in items if i))
|
||||||
f._info_label.set_sensitive(True)
|
f._info_label.set_sensitive(True)
|
||||||
|
|
||||||
def _toggle_info_label(action, f):
|
def _toggle_info_label(action, f):
|
||||||
|
@ -94,7 +95,7 @@ def _make_receiver_box(receiver):
|
||||||
toggle_info_action = _action.make_toggle('dialog-information', 'Details', _toggle_info_label, frame)
|
toggle_info_action = _action.make_toggle('dialog-information', 'Details', _toggle_info_label, frame)
|
||||||
toolbar.insert(toggle_info_action.create_tool_item(), 0)
|
toolbar.insert(toggle_info_action.create_tool_item(), 0)
|
||||||
pair_action = _action.pair(frame)
|
pair_action = _action.pair(frame)
|
||||||
if receiver.max_devices == 1:
|
if not receiver.unifying_supported:
|
||||||
pair_action.set_sensitive(False)
|
pair_action.set_sensitive(False)
|
||||||
pair_action.set_tooltip('Pairing not supported by this receiver')
|
pair_action.set_tooltip('Pairing not supported by this receiver')
|
||||||
toolbar.insert(pair_action.create_tool_item(), -1)
|
toolbar.insert(pair_action.create_tool_item(), -1)
|
||||||
|
@ -186,16 +187,14 @@ def _make_device_box(index):
|
||||||
device = f._device
|
device = f._device
|
||||||
assert device
|
assert device
|
||||||
|
|
||||||
items = [None] * 8
|
|
||||||
hid = device.protocol
|
hid = device.protocol
|
||||||
items[0] = ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown')
|
items = [
|
||||||
items[1] = ('Polling rate', '%d ms' % device.polling_rate) if device.polling_rate else None
|
('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown'),
|
||||||
items[2] = ('Wireless PID', device.wpid)
|
('Polling rate', '%d ms' % device.polling_rate) if device.polling_rate else None,
|
||||||
items[3] = ('Serial', device.serial)
|
('Wireless PID', device.wpid),
|
||||||
firmware = device.firmware
|
('Serial', device.serial) ] + \
|
||||||
if firmware:
|
list((fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in device.firmware) + \
|
||||||
firmware = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware]
|
[None]
|
||||||
items[4:4+len(firmware)] = firmware
|
|
||||||
|
|
||||||
if device.status:
|
if device.status:
|
||||||
notification_flags = _hidpp10.get_notification_flags(device)
|
notification_flags = _hidpp10.get_notification_flags(device)
|
||||||
|
@ -204,8 +203,6 @@ def _make_device_box(index):
|
||||||
else:
|
else:
|
||||||
notification_flags = ('(none)',)
|
notification_flags = ('(none)',)
|
||||||
items[-1] = ('Notifications', ('\n%16s' % ' ').join(notification_flags))
|
items[-1] = ('Notifications', ('\n%16s' % ' ').join(notification_flags))
|
||||||
else:
|
|
||||||
items[-1] = None
|
|
||||||
|
|
||||||
frame._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % i for i in items if i))
|
frame._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % i for i in items if i))
|
||||||
frame._info_label.set_sensitive(True)
|
frame._info_label.set_sensitive(True)
|
||||||
|
@ -422,7 +419,7 @@ def _update_device_box(frame, dev):
|
||||||
for i in frame._toolbar.get_children():
|
for i in frame._toolbar.get_children():
|
||||||
i.set_active(False)
|
i.set_active(False)
|
||||||
|
|
||||||
if dev.receiver.max_devices == 1:
|
if not dev.receiver.unifying_supported:
|
||||||
unpair_button = frame.get_child().get_children()[-1]
|
unpair_button = frame.get_child().get_children()[-1]
|
||||||
unpair_button.set_sensitive(False)
|
unpair_button.set_sensitive(False)
|
||||||
unpair_button.set_tooltip_text('Unpairing not supported by this device')
|
unpair_button.set_tooltip_text('Unpairing not supported by this device')
|
||||||
|
|
|
@ -8,8 +8,13 @@
|
||||||
|
|
||||||
# HIDAPI/hidraw for Logitech Unifying Receiver
|
# HIDAPI/hidraw for Logitech Unifying Receiver
|
||||||
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", GROUP="plugdev", MODE="0660"
|
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", GROUP="plugdev", MODE="0660"
|
||||||
|
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c532", GROUP="plugdev", MODE="0660"
|
||||||
|
|
||||||
# HIDAPI/hidraw for Logitech Nano Receiver
|
# HIDAPI/hidraw for Logitech Nano Receiver, "Unifying ready"
|
||||||
|
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52f", GROUP="plugdev", MODE="0660"
|
||||||
|
|
||||||
|
# HIDAPI/hidraw for VX Nano receiver
|
||||||
|
#ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c51a", GROUP="plugdev", MODE="0660"
|
||||||
#ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c526", GROUP="plugdev", MODE="0660"
|
#ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c526", GROUP="plugdev", MODE="0660"
|
||||||
|
|
||||||
# vim: ft=udevrules
|
# vim: ft=udevrules
|
||||||
|
|
Loading…
Reference in New Issue