improved support for the Nano receiver

This commit is contained in:
Daniel Pavel 2013-05-22 07:31:16 +03:00
parent cd33314d0b
commit 790fc7c04b
4 changed files with 68 additions and 49 deletions

View File

@ -57,9 +57,9 @@ def exit():
return True return True
def _match(action, device, vendor_id=None, product_id=None, interface_number=None, driver=None): def _match(action, device, vendor_id=None, product_id=None, interface_number=None, hid_driver=None):
usb_device = device.find_parent('usb', 'usb_device') usb_device = device.find_parent('usb', 'usb_device')
# print (action, device, "usb:", usb_device) # print ("* parent", action, device, "usb:", usb_device)
if not usb_device: if not usb_device:
return return
@ -71,15 +71,15 @@ def _match(action, device, vendor_id=None, product_id=None, interface_number=Non
if action == 'add': if action == 'add':
hid_device = device.find_parent('hid') hid_device = device.find_parent('hid')
# print (action, device, "hid:", usb_device) # print ("** found hid", action, device, "hid:", hid_device, hid_device['DRIVER'])
if not hid_device: if not hid_device:
return return
hid_driver_name = hid_device['DRIVER'] hid_driver_name = hid_device['DRIVER']
if driver is not None and driver != hid_driver_name: if hid_driver is not None and hid_driver != hid_driver_name:
return return
intf_device = device.find_parent('usb', 'usb_interface') intf_device = device.find_parent('usb', 'usb_interface')
# print (action, device, "usb_interface:", usb_device) # print ("*** usb interface", action, device, "usb_interface:", intf_device)
if interface_number is None: if interface_number is None:
usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber') usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber')
else: else:
@ -163,7 +163,7 @@ def monitor(callback, *device_filters):
raise raise
def enumerate(vendor_id=None, product_id=None, interface_number=None, driver=None): def enumerate(vendor_id=None, product_id=None, interface_number=None, hid_driver=None):
"""Enumerate the HID Devices. """Enumerate the HID Devices.
List all the HID devices attached to the system, optionally filtering by List all the HID devices attached to the system, optionally filtering by
@ -172,7 +172,7 @@ def enumerate(vendor_id=None, product_id=None, interface_number=None, driver=Non
:returns: a list of matching ``DeviceInfo`` tuples. :returns: a list of matching ``DeviceInfo`` tuples.
""" """
for dev in _Context().list_devices(subsystem='hidraw'): for dev in _Context().list_devices(subsystem='hidraw'):
dev_info = _match('add', dev, vendor_id, product_id, interface_number, driver) dev_info = _match('add', dev, vendor_id, product_id, interface_number, hid_driver)
if dev_info: if dev_info:
yield dev_info yield dev_info

View File

@ -66,10 +66,10 @@ class DeviceUnreachable(_KwException):
# #
# #
# vendor_id, product_id, interface number, 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, 'generic-usb') DEVICE_NANO_RECEIVER = (0x046d, 0xc526, 1, 'hid-generic')
def receivers(): def receivers():

View File

@ -33,14 +33,14 @@ class PairedDevice(object):
self.number = number self.number = number
self._unifying = receiver.max_devices > 1 self._unifying = receiver.max_devices > 1
self._protocol = None self._protocol = None if self._unifying else 1.0
self._wpid = None self._wpid = None
self._power_switch = None self._power_switch = None
self._polling_rate = 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._kind = None
self._serial = None self._serial = None if self._unifying else receiver.serial
self._firmware = None self._firmware = None
self._keys = None self._keys = None
@ -67,42 +67,49 @@ class PairedDevice(object):
self._kind = _hidpp10.DEVICE_KIND[kind] self._kind = _hidpp10.DEVICE_KIND[kind]
if self._polling_rate is None: if self._polling_rate is None:
self._polling_rate = ord(pair_info[2:3]) self._polling_rate = ord(pair_info[2:3])
# else: else:
# device_info = self.receiver.request(0x83B5, 0x04) # guesswork...
# self.wpid = _strhex(device_info[3:5]) device_info = self.receiver.request(0x83B5, 0x04)
self.wpid = _strhex(device_info[3:5])
return self._wpid return self._wpid
@property @property
def polling_rate(self): def polling_rate(self):
if self._polling_rate is None and self._unifying: if self._polling_rate is None:
self.wpid, 0 if self._unifying:
self.wpid, 0
else:
self._polling_rate = 0
return self._polling_rate return self._polling_rate
@property @property
def power_switch_location(self): def power_switch_location(self):
if self._power_switch is None and self._unifying: if self._power_switch is None:
ps = self.receiver.request(0x83B5, 0x30 + self.number - 1) if self._unifying:
if ps: ps = self.receiver.request(0x83B5, 0x30 + self.number - 1)
ps = ord(ps[9:10]) & 0x0F if ps:
self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps] ps = ord(ps[9:10]) & 0x0F
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 and self._unifying: if self._codename is None:
codename = self.receiver.request(0x83B5, 0x40 + self.number - 1) if self._unifying:
if codename: codename = self.receiver.request(0x83B5, 0x40 + self.number - 1)
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8') if codename:
# _log.debug("device %d codename %s", self.number, self._codename) self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
# _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 and self._unifying: if self._name is None:
if self.codename in _descriptors.DEVICES: if self._unifying:
self._name, self._kind = _descriptors.DEVICES[self._codename][:2] if self.codename in _descriptors.DEVICES:
elif self.protocol >= 2.0: self._name, self._kind = _descriptors.DEVICES[self._codename][:2]
self._name = _hidpp20.get_name(self) elif self.protocol >= 2.0:
self._name = _hidpp20.get_name(self)
return self._name or self.codename or '?' return self._name or self.codename or '?'
@property @property
@ -132,14 +139,15 @@ class PairedDevice(object):
@property @property
def serial(self): def serial(self):
if self._serial is None and self._unifying: if self._serial is None:
self._serial = _hidpp10.get_serial(self) self._serial = _hidpp10.get_serial(self)
return self._serial or '?' return self._serial or '?'
@property @property
def keys(self): def keys(self):
if self._keys is None and self._unifying: if self._keys is None:
self._keys = _hidpp20.get_keys(self) or () if self._unifying:
self._keys = _hidpp20.get_keys(self) or ()
return self._keys return self._keys
@property @property
@ -277,7 +285,8 @@ class Receiver(object):
if not self.handle: if not self.handle:
return False return False
flag_bits = _hidpp10.NOTIFICATION_FLAG.all_bits() if enable else 0 # flag_bits = _hidpp10.NOTIFICATION_FLAG.all_bits() if enable else 0
flag_bits = 0xFFFFFF if enable else 0
ok = _hidpp10.set_notification_flags(self, flag_bits) ok = _hidpp10.set_notification_flags(self, flag_bits)
flag_bits = _hidpp10.get_notification_flags(self) flag_bits = _hidpp10.get_notification_flags(self)
@ -303,12 +312,12 @@ class Receiver(object):
# create a device object, but only use it if the receiver knows about it # create a device object, but only use it if the receiver knows about it
# Nano receiver # Nano receiver
#if self.max_devices == 1 and number == 1: if self.max_devices == 1 and number == 1:
# # the Nano receiver does not provide the wpid # the Nano receiver does not provide the wpid
# _log.info("%s: found Nano device %d (%s)", self, number, dev.serial) _log.info("%s: found Nano device %d (%s)", self, number, dev.serial)
# # dev._wpid = self.serial + ':1' # dev._wpid = self.serial + ':1'
# self._devices[number] = dev self._devices[number] = dev
# return 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 Unifying device %d (%s)", self, number, dev.wpid)

View File

@ -29,6 +29,7 @@ _MAX_DEVICES = 7
def _make_receiver_box(receiver): def _make_receiver_box(receiver):
frame = Gtk.Frame() frame = Gtk.Frame()
# frame.set_shadow_type(Gtk.ShadowType.NONE)
frame._device = receiver frame._device = receiver
icon_set = _icons.device_icon_set(receiver.name) icon_set = _icons.device_icon_set(receiver.name)
@ -92,8 +93,11 @@ 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)
toolbar.insert(_action.pair(frame).create_tool_item(), -1) pair_action = _action.pair(frame)
# toolbar.insert(ui.action.about.create_tool_item(), -1) if receiver.max_devices == 1:
pair_action.set_sensitive(False)
pair_action.set_tooltip('Pairing not supported by this receiver')
toolbar.insert(pair_action.create_tool_item(), -1)
vbox = Gtk.VBox(homogeneous=False, spacing=2) vbox = Gtk.VBox(homogeneous=False, spacing=2)
vbox.set_border_width(2) vbox.set_border_width(2)
@ -137,7 +141,7 @@ def _make_device_box(index):
not_encrypted_icon = Gtk.Image.new_from_icon_name('security-low', _STATUS_ICON_SIZE) not_encrypted_icon = Gtk.Image.new_from_icon_name('security-low', _STATUS_ICON_SIZE)
not_encrypted_icon.set_name('not-encrypted') not_encrypted_icon.set_name('not-encrypted')
not_encrypted_icon.set_tooltip_text('The wireless link between this device and the Unifying Receiver is not encrypted.\n' not_encrypted_icon.set_tooltip_text('The wireless link between this device and its receiver is not encrypted.\n'
'\n' '\n'
'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n' 'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n'
'\n' '\n'
@ -182,15 +186,16 @@ def _make_device_box(index):
device = f._device device = f._device
assert device assert device
items = [None, None, None, None, None, None, None, None, None] items = [None] * 8
hid = device.protocol hid = device.protocol
items[0] = ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown') items[0] = ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown')
items[1] = ('Polling rate', '%d ms' % device.polling_rate) items[1] = ('Polling rate', '%d ms' % device.polling_rate) if device.polling_rate else None
items[2] = ('Wireless PID', device.wpid) items[2] = ('Wireless PID', device.wpid)
items[3] = ('Serial', device.serial) items[3] = ('Serial', device.serial)
firmware = device.firmware firmware = device.firmware
if firmware: if firmware:
items[4:] = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware] firmware = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware]
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)
@ -417,6 +422,11 @@ 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:
unpair_button = frame.get_child().get_children()[-1]
unpair_button.set_sensitive(False)
unpair_button.set_tooltip_text('Unpairing not supported by this device')
battery_icon, battery_label, light_icon, light_label, not_encrypted_icon, _ = frame._status_icons battery_icon, battery_label, light_icon, light_label, not_encrypted_icon, _ = frame._status_icons
battery_level = dev.status.get(_status.BATTERY_LEVEL) battery_level = dev.status.get(_status.BATTERY_LEVEL)
@ -425,7 +435,7 @@ def _update_device_box(frame, dev):
if battery_level is None: if battery_level is None:
battery_icon.set_sensitive(False) battery_icon.set_sensitive(False)
battery_icon.set_from_icon_name(_icons.battery(-1), _STATUS_ICON_SIZE) battery_icon.set_from_icon_name(_icons.battery(None), _STATUS_ICON_SIZE)
battery_label.set_markup('<small>no status</small>') battery_label.set_markup('<small>no status</small>')
battery_label.set_sensitive(True) battery_label.set_sensitive(True)
else: else: