improved support for hid++ 1.0 devices
This commit is contained in:
parent
0e551383ba
commit
6d70d2aada
|
|
@ -34,7 +34,7 @@ del namedtuple
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
_POLL_TICK = 30 # seconds
|
_POLL_TICK = 60 # seconds
|
||||||
|
|
||||||
|
|
||||||
class ReceiverListener(_listener.EventsListener):
|
class ReceiverListener(_listener.EventsListener):
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# from gi import pygtkcompat
|
|
||||||
# pygtkcompat.enable_gtk()
|
|
||||||
from gi.repository import GObject, Gtk
|
from gi.repository import GObject, Gtk
|
||||||
GObject.threads_init()
|
GObject.threads_init()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -219,22 +219,23 @@ def create(title, name, max_devices, systray=False):
|
||||||
#
|
#
|
||||||
|
|
||||||
def _update_device_info_label(label, dev):
|
def _update_device_info_label(label, dev):
|
||||||
items = [('Wireless PID', dev.wpid), ('Serial', dev.serial)]
|
items = []
|
||||||
hid = dev.protocol
|
hid = dev.protocol
|
||||||
if hid:
|
if hid:
|
||||||
items += [('Protocol', 'HID++ %1.1f' % dev.protocol)]
|
items += [('Protocol', 'HID++ %1.1f' % dev.protocol)]
|
||||||
|
items += [('Wireless PID', dev.wpid), ('Serial', dev.serial)]
|
||||||
firmware = dev.firmware
|
firmware = dev.firmware
|
||||||
if firmware:
|
if firmware:
|
||||||
items += [(f.kind, f.name + ' ' + f.version) for f in firmware]
|
items += [(f.kind, (f.name + ' ' + f.version).strip()) for f in firmware]
|
||||||
|
|
||||||
label.set_markup('<small><tt>' + '\n'.join('%-12s: %s' % item for item in items) + '</tt></small>')
|
label.set_markup('<small><tt>' + '\n'.join('%-13s: %s' % item for item in items) + '</tt></small>')
|
||||||
|
|
||||||
|
|
||||||
def _update_receiver_info_label(label, dev):
|
def _update_receiver_info_label(label, dev):
|
||||||
if label.get_visible() and '\n' not in label.get_text():
|
if label.get_visible() and '\n' not in label.get_text():
|
||||||
items = [('Serial', dev.serial)] + \
|
items = [('Path', dev.path), ('Serial', dev.serial)] + \
|
||||||
[(f.kind, f.version) for f in dev.firmware]
|
[(f.kind, f.version) for f in dev.firmware]
|
||||||
label.set_markup('<small><tt>' + '\n'.join('%-10s: %s' % item for item in items) + '</tt></small>')
|
label.set_markup('<small><tt>' + '\n'.join('%-13s: %s' % item for item in items) + '</tt></small>')
|
||||||
|
|
||||||
|
|
||||||
def _toggle_info_box(action, box, frame, update_function):
|
def _toggle_info_box(action, box, frame, update_function):
|
||||||
|
|
@ -268,18 +269,22 @@ def _update_receiver_box(frame, receiver):
|
||||||
pairing_icon.set_visible(False)
|
pairing_icon.set_visible(False)
|
||||||
pairing_icon.set_sensitive(True)
|
pairing_icon.set_sensitive(True)
|
||||||
pairing_icon._tick = 0
|
pairing_icon._tick = 0
|
||||||
toolbar.set_visible(True)
|
toolbar.set_sensitive(True)
|
||||||
else:
|
else:
|
||||||
frame._device = None
|
frame._device = None
|
||||||
icon.set_sensitive(False)
|
icon.set_sensitive(False)
|
||||||
pairing_icon.set_visible(False)
|
pairing_icon.set_visible(False)
|
||||||
toolbar.set_visible(False)
|
toolbar.set_sensitive(False)
|
||||||
toolbar.get_children()[0].set_active(False)
|
toolbar.get_children()[0].set_active(False)
|
||||||
info_label.set_text('')
|
info_label.set_text('')
|
||||||
|
|
||||||
|
|
||||||
def _update_device_box(frame, dev):
|
def _update_device_box(frame, dev):
|
||||||
# print (dev.name, dev.kind)
|
if dev is None:
|
||||||
|
frame.set_visible(False)
|
||||||
|
frame.set_name(_PLACEHOLDER)
|
||||||
|
frame._device = None
|
||||||
|
return
|
||||||
|
|
||||||
icon, label, toolbar, info_label = ui.find_children(frame, 'icon', 'label', 'toolbar', 'info-label')
|
icon, label, toolbar, info_label = ui.find_children(frame, 'icon', 'label', 'toolbar', 'info-label')
|
||||||
|
|
||||||
|
|
@ -356,17 +361,9 @@ def update(window, receiver, device=None):
|
||||||
assert len(frames) == 1 + receiver.max_devices, frames
|
assert len(frames) == 1 + receiver.max_devices, frames
|
||||||
|
|
||||||
if device:
|
if device:
|
||||||
frame = frames[device.number]
|
_update_device_box(frames[device.number], None if device.status is None else device)
|
||||||
if device.status is None:
|
|
||||||
frame.set_visible(False)
|
|
||||||
frame.set_name(_PLACEHOLDER)
|
|
||||||
frame._device = None
|
|
||||||
else:
|
|
||||||
_update_device_box(frame, device)
|
|
||||||
else:
|
else:
|
||||||
_update_receiver_box(frames[0], receiver)
|
_update_receiver_box(frames[0], receiver)
|
||||||
if not receiver:
|
if not receiver:
|
||||||
for frame in frames[1:]:
|
for frame in frames[1:]:
|
||||||
frame.set_visible(False)
|
_update_device_box(frame, None)
|
||||||
frame.set_name(_PLACEHOLDER)
|
|
||||||
frame._device = None
|
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ def make_event(devnumber, data):
|
||||||
return _Event(devnumber, sub_id, ord(data[1:2]), data[2:])
|
return _Event(devnumber, sub_id, ord(data[1:2]), data[2:])
|
||||||
else:
|
else:
|
||||||
address = ord(data[1:2])
|
address = ord(data[1:2])
|
||||||
if sub_id > 0x00 and sub_id < 0x80 and (address & 0x01) == 0:
|
if sub_id > 0x00 and (sub_id >= 0x40 or (address & 0x01 == 0)):
|
||||||
return _Event(devnumber, sub_id, address, data[2:])
|
return _Event(devnumber, sub_id, address, data[2:])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,64 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
_D = namedtuple('_DeviceDescriptor', ['codename', 'name', 'kind'])
|
|
||||||
del namedtuple
|
|
||||||
|
|
||||||
DEVICES = ( _D('M315', 'Wireless Mouse M315', 'mouse'),
|
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
|
||||||
_D('M325', 'Wireless Mouse M325', 'mouse'),
|
['name', 'kind', 'codename', 'settings'])
|
||||||
_D('M505', 'Wireless Mouse M505', 'mouse'),
|
|
||||||
_D('M510', 'Wireless Mouse M510', 'mouse'),
|
DEVICES = {}
|
||||||
_D('M515', 'Couch Mouse M515', 'mouse'),
|
|
||||||
_D('M525', 'Wireless Mouse M525', 'mouse'),
|
def _D(name, codename=None, kind=None):
|
||||||
_D('M570', 'Wireless Trackball M570', 'trackball'),
|
if kind is None:
|
||||||
_D('M600', 'Touch Mouse M600', 'mouse'),
|
kind = ('mouse' if 'Mouse' in name
|
||||||
_D('M705', 'Marathon Mouse M705', 'mouse'),
|
else 'keyboard' if 'Keyboard' in name
|
||||||
_D('K270', 'Wireless Keyboard K270', 'keyboard'),
|
else 'touchpad' if 'Touchpad' in name
|
||||||
_D('K350', 'Wireless Keyboard K350', 'keyboard'),
|
else 'trackball' if 'Trackball' in name
|
||||||
_D('K360', 'Wireless Keyboard K360', 'keyboard'),
|
else None)
|
||||||
_D('K400', 'Wireless Touch Keyboard K400', 'keyboard'),
|
assert kind is not None
|
||||||
_D('K750', 'Wireless Solar Keyboard K750', 'keyboard'),
|
|
||||||
_D('K800', 'Wireless Illuminated Keyboard K800', 'keyboard'),
|
if codename is None:
|
||||||
_D('T400', 'Zone Touch Mouse T400', 'mouse'),
|
codename = name.split(' ')[-1]
|
||||||
_D('T650', 'Wireless Rechargeable Touchpad T650', 'touchpad'),
|
assert codename is not None
|
||||||
_D('Cube', 'Logitech Cube', 'mouse'),
|
|
||||||
_D('Anywhere MX', 'Anywhere Mouse MX', 'mouse'),
|
DEVICES[codename] = _DeviceDescriptor(name, kind, codename, None)
|
||||||
_D('Performance MX', 'Performance Mouse MX', 'mouse'),
|
|
||||||
)
|
|
||||||
DEVICES = { d.codename: d for d in DEVICES }
|
_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
|
||||||
|
|
|
||||||
|
|
@ -74,23 +74,33 @@ def get_battery(device):
|
||||||
return charge, None
|
return charge, None
|
||||||
|
|
||||||
|
|
||||||
def get_receiver_serial(receiver):
|
def get_serial(device):
|
||||||
serial = receiver.request(0x83B5, 0x03)
|
if device.kind is None:
|
||||||
|
dev_id = 0x03
|
||||||
|
receiver = device
|
||||||
|
else:
|
||||||
|
dev_id = 0x30 + device.number - 1
|
||||||
|
receiver = device.receiver
|
||||||
|
|
||||||
|
serial = receiver.request(0x83B5, dev_id)
|
||||||
if serial:
|
if serial:
|
||||||
return _strhex(serial[1:5])
|
return _strhex(serial[1:5])
|
||||||
|
|
||||||
|
|
||||||
def get_receiver_firmware(receiver):
|
def get_firmware(device):
|
||||||
firmware = []
|
firmware = []
|
||||||
|
|
||||||
reply = receiver.request(0x83B5, 0x02)
|
reply = device.request(0x81F1, 0x01)
|
||||||
if reply:
|
if reply:
|
||||||
fw_version = _strhex(reply[1:5])
|
fw_version = _strhex(reply[1:3])
|
||||||
fw_version = '%s.%s.B%s' % (fw_version[0:2], fw_version[2:4], fw_version[4:8])
|
fw_version = '%s.%s' % (fw_version[0:2], fw_version[2:4])
|
||||||
|
reply = device.request(0x81F1, 0x02)
|
||||||
|
if reply:
|
||||||
|
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.append(fw)
|
||||||
|
|
||||||
reply = receiver.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])
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,10 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def power_switch_location(self):
|
def power_switch_location(self):
|
||||||
if self._power_switch is None:
|
if self._power_switch is None:
|
||||||
self.serial
|
ps = self.receiver.request(0x83B5, 0x30 + self.number - 1)
|
||||||
|
if 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
|
||||||
|
|
@ -79,7 +82,7 @@ class PairedDevice(object):
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name is None:
|
if self._name is None:
|
||||||
if self.codename in _DEVICES:
|
if self.codename in _DEVICES:
|
||||||
_, self._name, self._kind = _DEVICES[self._codename]
|
self._name, self._kind = _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 '?'
|
||||||
|
|
@ -95,27 +98,25 @@ class PairedDevice(object):
|
||||||
self._wpid = _strhex(pair_info[3:5])
|
self._wpid = _strhex(pair_info[3:5])
|
||||||
if self._kind is None:
|
if self._kind is None:
|
||||||
if self.codename in _DEVICES:
|
if self.codename in _DEVICES:
|
||||||
_, self._name, self._kind = _DEVICES[self._codename]
|
self._name, self._kind = _DEVICES[self._codename][:2]
|
||||||
elif self.protocol >= 2.0:
|
elif self.protocol >= 2.0:
|
||||||
self._kind = _hidpp20.get_kind(self)
|
self._kind = _hidpp20.get_kind(self)
|
||||||
return self._kind or '?'
|
return self._kind or '?'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def firmware(self):
|
def firmware(self):
|
||||||
if self._firmware is None and self.protocol >= 2.0:
|
if self._firmware is None:
|
||||||
self._firmware = _hidpp20.get_firmware(self)
|
p = self.protocol
|
||||||
# _log.debug("device %d firmware %s", self.number, self._firmware)
|
if p >= 2.0:
|
||||||
|
self._firmware = _hidpp20.get_firmware(self)
|
||||||
|
elif p >= 1.0:
|
||||||
|
self._firmware = _hidpp10.get_firmware(self)
|
||||||
return self._firmware or ()
|
return self._firmware or ()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serial(self):
|
def serial(self):
|
||||||
if self._serial is None:
|
if self._serial is None:
|
||||||
serial = self.receiver.request(0x83B5, 0x30 + self.number - 1)
|
self._serial = _hidpp10.get_serial(self)
|
||||||
if serial:
|
|
||||||
self._serial = _strhex(serial[1:5])
|
|
||||||
# _log.debug("device %d serial %s", self.number, self._serial)
|
|
||||||
ps_location = ord(serial[9:10]) & 0x0F
|
|
||||||
self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps_location]
|
|
||||||
return self._serial or '?'
|
return self._serial or '?'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -185,13 +186,13 @@ class Receiver(object):
|
||||||
@property
|
@property
|
||||||
def serial(self):
|
def serial(self):
|
||||||
if self._serial is None and self.handle:
|
if self._serial is None and self.handle:
|
||||||
self._serial = _hidpp10.get_receiver_serial(self)
|
self._serial = _hidpp10.get_serial(self)
|
||||||
return self._serial
|
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:
|
||||||
self._firmware = _hidpp10.get_receiver_firmware(self)
|
self._firmware = _hidpp10.get_firmware(self)
|
||||||
return self._firmware
|
return self._firmware
|
||||||
|
|
||||||
def enable_notifications(self, enable=True):
|
def enable_notifications(self, enable=True):
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,9 @@ BATTERY_STATUS='battery-status'
|
||||||
LIGHT_LEVEL='light-level'
|
LIGHT_LEVEL='light-level'
|
||||||
ERROR='error'
|
ERROR='error'
|
||||||
|
|
||||||
# make sure we try to update the device status at least once a minute
|
# if not updates have been receiver from the device for a while, assume
|
||||||
_STATUS_TIMEOUT = 60 # seconds
|
# it has gone offline and clear all its know properties.
|
||||||
|
_STATUS_TIMEOUT = 180 # seconds
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
@ -179,12 +180,15 @@ class DeviceStatus(dict):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if event.sub_id >= 0x40:
|
if event.sub_id >= 0x80:
|
||||||
# this can't possibly be an event, can it?
|
# this can't possibly be an event, can it?
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug("ignoring non-event %s", event)
|
_log.debug("ignoring non-event %s", event)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if event.sub_id >= 0x40:
|
||||||
|
_log.warn("don't know how to handle event %s", event)
|
||||||
|
|
||||||
# this must be a feature event, assuming no device has more than 0x40 features
|
# this must be a feature event, assuming no device has more than 0x40 features
|
||||||
if event.sub_id >= len(self._device.features):
|
if event.sub_id >= len(self._device.features):
|
||||||
_log.warn("device %d got event from unknown feature index %02X", self._device.number, event.sub_id)
|
_log.warn("device %d got event from unknown feature index %02X", self._device.number, event.sub_id)
|
||||||
|
|
@ -243,8 +247,8 @@ class DeviceStatus(dict):
|
||||||
_log.debug("Solar key pressed")
|
_log.debug("Solar key pressed")
|
||||||
# first cancel any reporting
|
# first cancel any reporting
|
||||||
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE)
|
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE)
|
||||||
reports_count = 10
|
reports_count = 15
|
||||||
reports_period = 3 # seconds
|
reports_period = 2 # seconds
|
||||||
self._changed(alert=ALERT.MED)
|
self._changed(alert=ALERT.MED)
|
||||||
# trigger a new report chain
|
# trigger a new report chain
|
||||||
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE, 0x00, reports_count, reports_period)
|
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE, 0x00, reports_count, reports_period)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue