From a59ad221a13d8229134774c77a72d8360c1e0e50 Mon Sep 17 00:00:00 2001 From: Daniel Pavel Date: Thu, 8 Nov 2012 11:41:09 +0200 Subject: [PATCH] improved support for HID 1.0 devices --- app/receiver.py | 56 +---------------- app/solaar.py | 2 +- app/ui/main_window.py | 3 +- lib/logitech/scanner.py | 18 ++++-- lib/logitech/unifying_receiver/api.py | 71 ++++++++++++++++------ lib/logitech/unifying_receiver/listener.py | 2 +- 6 files changed, 73 insertions(+), 79 deletions(-) diff --git a/app/receiver.py b/app/receiver.py index 8b72f95c..c5f8594b 100644 --- a/app/receiver.py +++ b/app/receiver.py @@ -11,7 +11,7 @@ from logitech.unifying_receiver import api as _api from logitech.unifying_receiver.listener import EventsListener as _EventsListener from logitech.unifying_receiver.common import FallbackDict as _FallbackDict from logitech import devices as _devices -from logitech.devices.constants import (STATUS, STATUS_NAME, PROPS, NAMES) +from logitech.devices.constants import (STATUS, STATUS_NAME, PROPS) # # @@ -107,17 +107,14 @@ class DeviceInfo(_api.PairedDevice): """ def __init__(self, listener, number, status=STATUS.UNKNOWN): super(DeviceInfo, self).__init__(listener.handle, number) + self._features = _FeaturesArray(self) self.LOG = _Logger("Device[%d]" % number) self._listener = listener - self._serial = None - self._codename = None self._status = status self.props = {} - self.features = _FeaturesArray(self) - # read them now, otherwise it it temporarily hang the UI # if status >= STATUS.CONNECTED: # n, k, s, f = self.name, self.kind, self.serial, self.firmware @@ -155,53 +152,6 @@ class DeviceInfo(_api.PairedDevice): t.append('Light: %d lux' % self.props[PROPS.LIGHT_LEVEL]) return ', '.join(t) if t else STATUS_NAME[STATUS.CONNECTED] - @property - def name(self): - if self._name is None: - if self._status < STATUS.CONNECTED: - codename = self.codename - if codename in NAMES: - self._name, self._kind = NAMES[codename] - elif self.features: - self._name = _api.get_device_name(self.handle, self.number, self.features) - return self._name or self.codename - - @property - def kind(self): - if self._kind is None: - if self._status < STATUS.CONNECTED: - codename = self.codename - if codename in NAMES: - self._name, self._kind = NAMES[codename] - elif self.features: - self._kind = _api.get_device_kind(self.handle, self.number, self.features) - return self._kind or '?' - - @property - def serial(self): - if self._serial is None: - prefix = _base.request(self.handle, 0xFF, b'\x83\xB5', 0x20 + self.number - 1) - prefix = (_base._hex(prefix[3:5]) + '-') if prefix else '' - serial = _base.request(self.handle, 0xFF, b'\x83\xB5', 0x30 + self.number - 1) - serial = _base._hex(serial[1:5]) if serial else '?' - self._serial = prefix + serial - return self._serial or '?' - - @property - def codename(self): - if self._codename is None: - codename = _base.request(self.handle, 0xFF, b'\x83\xB5', 0x40 + self.number - 1) - if codename: - self._codename = codename[2:].rstrip(b'\x00').decode('ascii') - return self._codename or '?' - - @property - def firmware(self): - if self._firmware is None: - if self._status >= STATUS.CONNECTED and self.features: - self._firmware = _api.get_device_firmware(self.handle, self.number, self.features) - return self._firmware or () - def process_event(self, code, data): if code == 0x10 and data[:1] == b'\x8F': self.status = STATUS.UNAVAILABLE @@ -229,7 +179,7 @@ class DeviceInfo(_api.PairedDevice): return False def __str__(self): - return 'DeviceInfo(%d,%s,%d)' % (self.number, self._name or '?', self._status) + return '' % (self.number, self._name or '?', self._status) # # diff --git a/app/solaar.py b/app/solaar.py index 001b6e32..8adb8af9 100644 --- a/app/solaar.py +++ b/app/solaar.py @@ -89,7 +89,7 @@ if __name__ == '__main__': def status_changed(receiver, device=None, urgent=False): ui.update(receiver, icon, window, device) if ui.notify.available and urgent: - ui.notify.show(device or receiver) + GObject.idle_add(ui.notify.show, device or receiver) global listener if not listener: diff --git a/app/ui/main_window.py b/app/ui/main_window.py index b241180a..146abcae 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -22,8 +22,9 @@ def _info_text(dev): (f.kind, f.name, ' ' if f.name else '', f.version) for f in dev.firmware]) return ('' 'Serial \t\t%s\n' + 'HID protocol\t%1.1f\n' '%s' - '' % (dev.serial, fw_text)) + '' % (dev.serial, dev.protocol, fw_text)) def _toggle_info(action, label_widget, box_widget, frame): if action.get_active(): diff --git a/lib/logitech/scanner.py b/lib/logitech/scanner.py index 25104c43..071bff5e 100644 --- a/lib/logitech/scanner.py +++ b/lib/logitech/scanner.py @@ -8,18 +8,26 @@ def print_receiver(receiver): for f in receiver.firmware: print (" %-10s: %s" % (f.kind, f.version)) - print ("--------") - def scan_devices(receiver): for dev in receiver: + print ("--------") print (str(dev)) - print ("Name: %s" % dev.name) - print ("Kind: %s" % dev.kind) + print ("Name : %s" % dev.name) + print ("Kind : %s" % dev.kind) + print ("Serial number: %s" % dev.serial) + if not dev.protocol: + print ("HID protocol : UNKNOWN") + continue + + print ("HID protocol : HID %01.1f" % dev.protocol) + if dev.protocol < 2.0: + print ("Features query not supported by this device") + continue firmware = dev.firmware for fw in firmware: - print (" %-10s: %s %s" % (fw.kind, fw.name, fw.version)) + print (" %-10s: %s %s" % (fw.kind, fw.name, fw.version)) all_features = api.get_device_features(dev.handle, dev.number) for index in range(0, len(all_features)): diff --git a/lib/logitech/unifying_receiver/api.py b/lib/logitech/unifying_receiver/api.py index 25a377f0..ff7b288b 100644 --- a/lib/logitech/unifying_receiver/api.py +++ b/lib/logitech/unifying_receiver/api.py @@ -32,32 +32,74 @@ class PairedDevice(object): self.handle = handle self.number = number + self._protocol = None + self._features = None + self._codename = None self._name = None self._kind = None + self._serial = None self._firmware = None - self.features = [FEATURE.ROOT] + + @property + def protocol(self): + if self._protocol is None: + self._protocol = _base.ping(self.handle, self.number) + return 0 if self._protocol is None else self._protocol + + @property + def features(self): + if self._features is None: + if self.protocol >= 2.0: + self._features = [FEATURE.ROOT] + return self._features + + @property + def codename(self): + if self._codename is None: + codename = _base.request(self.handle, 0xFF, b'\x83\xB5', 0x40 + self.number - 1) + if codename: + self._codename = codename[2:].rstrip(b'\x00').decode('ascii') + return self._codename or '?' @property def name(self): if self._name is None: - self._name = get_device_name(self.handle, self.number, self.features) - return self._name or '?' + if self.protocol < 2.0: + from ..devices.constants import NAMES as _DEVICE_NAMES + if self.codename in _DEVICE_NAMES: + self._name, self._kind = _DEVICE_NAMES[self._codename] + else: + self._name = get_device_name(self.handle, self.number, self.features) + return self._name or self.codename @property def kind(self): if self._kind is None: - self._kind = get_device_kind(self.handle, self.number, self.features) + if self.protocol < 2.0: + from ..devices.constants import NAMES as _DEVICE_NAMES + if self.codename in _DEVICE_NAMES: + self._name, self._kind = _DEVICE_NAMES[self._codename] + else: + self._kind = get_device_kind(self.handle, self.number, self.features) return self._kind or '?' @property def firmware(self): - if self._firmware is None: + if self._firmware is None and self.protocol >= 2.0: self._firmware = get_device_firmware(self.handle, self.number, self.features) return self._firmware or () + @property + def serial(self): + if self._serial is None: + prefix = _base.request(self.handle, 0xFF, b'\x83\xB5', 0x20 + self.number - 1) + serial = _base.request(self.handle, 0xFF, b'\x83\xB5', 0x30 + self.number - 1) + if prefix and serial: + self._serial = _base._hex(prefix[3:5]) + '-' + _base._hex(serial[1:5]) + return self._serial or '?' + def ping(self): - reply = _base.request(self.handle, self.number, b'\x00\x10', b'\x00\x00\xAA') - return reply is not None and reply[2:3] == b'\xAA' + return _base.ping(self.handle, self.number) is not None def __str__(self): return '' % (self.handle, self.number, self._name or '?') @@ -148,8 +190,8 @@ class Receiver(object): if self.handle == 0: return False if type(dev) == int: - return (dev < 1 or dev > MAX_ATTACHED_DEVICES) and ping(self.handle, dev) - return ping(self.handle, dev.number) + return dev > 0 and dev <= MAX_ATTACHED_DEVICES and _base.ping(self.handle, dev) is not None + return dev.ping() def __str__(self): return '' % (self.handle, self.path) @@ -228,20 +270,12 @@ def request(handle, devnumber, feature, function=b'\x00', params=b'', features=N return _base.request(handle, devnumber, feature_index + function, params) -def ping(handle, devnumber): - """ - :returns: True if the device is connected to the UR. - """ - reply = _base.request(handle, devnumber, b'\x00\x10', b'\x00\x00\xAA') - return reply is not None and reply[2:3] == b'\xAA' - - def get_device(handle, devnumber, features=None): """Gets the complete info for a device (type, features). :returns: a PairedDevice or ``None``. """ - if ping(handle, devnumber): + if _base.ping(handle, devnumber): devinfo = PairedDevice(handle, devnumber) # _log.debug("found device %s", devinfo) return devinfo @@ -291,6 +325,7 @@ def _get_feature_index(handle, devnumber, feature, features=None): if len(features) <= index: features += [None] * (index + 1 - len(features)) features[index] = feature + # _log.debug("%s: found feature %s at %d", features, _base._hex(feature), index) return index diff --git a/lib/logitech/unifying_receiver/listener.py b/lib/logitech/unifying_receiver/listener.py index 9bbf8870..db32ba44 100644 --- a/lib/logitech/unifying_receiver/listener.py +++ b/lib/logitech/unifying_receiver/listener.py @@ -85,7 +85,7 @@ class EventsListener(_Thread): if task and task[-1] is None: task_dev, task_data = task[:2] if event[1] == task_dev: - _log.debug("matching %s to (%d, %s)", event, task_dev, repr(task_data)) + # _log.debug("matching %s to (%d, %s)", event, task_dev, repr(task_data)) matched = event[2][:2] == task_data[:2] or (event[2][:1] in b'\x8F\xFF' and event[2][1:3] == task_data[:2]) if matched: