ui and pairing updates
This commit is contained in:
parent
7354429316
commit
60c13270f9
|
@ -4,9 +4,6 @@
|
||||||
|
|
||||||
from logging import getLogger as _Logger
|
from logging import getLogger as _Logger
|
||||||
|
|
||||||
from receiver import DeviceInfo as _DeviceInfo
|
|
||||||
from logitech.devices.constants import (STATUS, NAMES)
|
|
||||||
|
|
||||||
_l = _Logger('pairing')
|
_l = _Logger('pairing')
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,31 +67,8 @@ class State(object):
|
||||||
|
|
||||||
_l.debug("event for new device? %s", event)
|
_l.debug("event for new device? %s", event)
|
||||||
if event.code == 0x10 and event.data[0:2] == b'\x41\x04':
|
if event.code == 0x10 and event.data[0:2] == b'\x41\x04':
|
||||||
state_code = ord(event.data[2:3]) & 0xF0
|
self.detected_device = self._watcher.receiver.make_device(event)
|
||||||
state = STATUS.UNAVAILABLE if state_code == 0x60 else \
|
return True
|
||||||
STATUS.CONNECTED if state_code == 0xA0 else \
|
|
||||||
STATUS.CONNECTED if state_code == 0x20 else \
|
|
||||||
None
|
|
||||||
if state is None:
|
|
||||||
_l.warn("don't know how to handle status 0x%02x: %s", state_code, event)
|
|
||||||
elif event.devnumber < 1 or event.devnumber > self.max_devices:
|
|
||||||
_l.warn("got event for invalid device number %d: %s", event.devnumber, event)
|
|
||||||
else:
|
|
||||||
dev = _DeviceInfo(self._watcher.receiver, event.devnumber, state)
|
|
||||||
if state == STATUS.CONNECTED:
|
|
||||||
n, k = dev.name, dev.kind
|
|
||||||
_l.debug("detected active device %s", dev)
|
|
||||||
else:
|
|
||||||
# we can query the receiver for the device short name
|
|
||||||
dev_id = self.request(0xFF, b'\x83\xB5', event.data[4:5])
|
|
||||||
if dev_id:
|
|
||||||
shortname = str(dev_id[2:].rstrip(b'\x00'))
|
|
||||||
if shortname in NAMES:
|
|
||||||
dev._name, dev._kind = NAMES[shortname]
|
|
||||||
_l.debug("detected new device %s", dev)
|
|
||||||
else:
|
|
||||||
_l.warn("could not properly detect inactive device %d: %s", event.devnumber, shortname)
|
|
||||||
self.detected_device = dev
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from logging import getLogger as _Logger
|
||||||
_LOG_LEVEL = 6
|
_LOG_LEVEL = 6
|
||||||
|
|
||||||
from threading import Event as _Event
|
from threading import Event as _Event
|
||||||
|
from binascii import hexlify as _hexlify
|
||||||
|
|
||||||
from logitech.unifying_receiver import base as _base
|
from logitech.unifying_receiver import base as _base
|
||||||
from logitech.unifying_receiver import api as _api
|
from logitech.unifying_receiver import api as _api
|
||||||
|
@ -26,6 +27,7 @@ class DeviceInfo(object):
|
||||||
self.number = number
|
self.number = number
|
||||||
self._name = None
|
self._name = None
|
||||||
self._kind = None
|
self._kind = None
|
||||||
|
self._serial = None
|
||||||
self._firmware = None
|
self._firmware = None
|
||||||
self._features = None
|
self._features = None
|
||||||
|
|
||||||
|
@ -80,6 +82,13 @@ class DeviceInfo(object):
|
||||||
self._kind = self.receiver.call_api(_api.get_device_kind, self.number, self.features)
|
self._kind = self.receiver.call_api(_api.get_device_kind, self.number, self.features)
|
||||||
return self._kind or '?'
|
return self._kind or '?'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serial(self):
|
||||||
|
if self._serial is None:
|
||||||
|
if self._status >= STATUS.CONNECTED:
|
||||||
|
pass
|
||||||
|
return self._serial or '?'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def firmware(self):
|
def firmware(self):
|
||||||
if self._firmware is None:
|
if self._firmware is None:
|
||||||
|
@ -219,13 +228,14 @@ class Receiver(_listener.EventsListener):
|
||||||
def serial(self):
|
def serial(self):
|
||||||
if self._serial is None:
|
if self._serial is None:
|
||||||
if self:
|
if self:
|
||||||
self._serial, firmware, bootloader = self.call_api(_api.get_receiver_info)
|
self._serial, self._firmware = self.call_api(_api.get_receiver_info)
|
||||||
self._firmware = (firmware, bootloader)
|
|
||||||
return self._serial or '?'
|
return self._serial or '?'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def firmware(self):
|
def firmware(self):
|
||||||
s = self.serial
|
if self._firmware is None:
|
||||||
|
if self:
|
||||||
|
self._serial, self._firmware = self.call_api(_api.get_receiver_info)
|
||||||
return self._firmware or ('?', '?')
|
return self._firmware or ('?', '?')
|
||||||
|
|
||||||
|
|
||||||
|
@ -239,38 +249,25 @@ class Receiver(_listener.EventsListener):
|
||||||
return
|
return
|
||||||
|
|
||||||
if event.code == 0x10 and event.data[0:2] == b'\x41\x04':
|
if event.code == 0x10 and event.data[0:2] == b'\x41\x04':
|
||||||
state_code = ord(event.data[2:3]) & 0xF0
|
|
||||||
state = STATUS.UNAVAILABLE if state_code == 0x60 else \
|
|
||||||
STATUS.CONNECTED if state_code == 0xA0 else \
|
|
||||||
STATUS.CONNECTED if state_code == 0x20 else \
|
|
||||||
None
|
|
||||||
if state is None:
|
|
||||||
self.LOG.warn("don't know how to handle status 0x%02x: %s", state_code, event)
|
|
||||||
return
|
|
||||||
|
|
||||||
if event.devnumber in self.devices:
|
if event.devnumber in self.devices:
|
||||||
self.devices[event.devnumber].status = state
|
state_code = ord(event.data[2:3]) & 0xF0
|
||||||
|
state = STATUS.UNAVAILABLE if state_code == 0x60 else \
|
||||||
|
STATUS.CONNECTED if state_code == 0xA0 else \
|
||||||
|
STATUS.CONNECTED if state_code == 0x20 else \
|
||||||
|
None
|
||||||
|
if state is None:
|
||||||
|
self.LOG.warn("don't know how to handle status 0x%02x: %s", state_code, event)
|
||||||
|
else:
|
||||||
|
self.devices[event.devnumber].status = state
|
||||||
return
|
return
|
||||||
|
|
||||||
if event.devnumber < 1 or event.devnumber > self.max_devices:
|
dev = self.make_device(event)
|
||||||
self.LOG.warn("got event for invalid device number %d: %s", event.devnumber, event)
|
if dev is None:
|
||||||
return
|
self.LOG.warn("failed to make new device from %s", event)
|
||||||
|
|
||||||
dev = DeviceInfo(self, event.devnumber, state)
|
|
||||||
if state == STATUS.CONNECTED:
|
|
||||||
n, k = dev.name, dev.kind
|
|
||||||
else:
|
else:
|
||||||
# we can query the receiver for the device short name
|
self.devices[event.devnumber] = dev
|
||||||
dev_id = self.request(0xFF, b'\x83\xB5', event.data[4:5])
|
self.LOG.info("new device ready %s", dev)
|
||||||
if dev_id:
|
self.status = STATUS.CONNECTED + len(self.devices)
|
||||||
shortname = str(dev_id[2:].rstrip(b'\x00'))
|
|
||||||
if shortname in NAMES:
|
|
||||||
dev._name, dev._kind = NAMES[shortname]
|
|
||||||
else:
|
|
||||||
self.LOG.warn("could not properly detect inactive device %d: %s", event.devnumber, shortname)
|
|
||||||
self.devices[event.devnumber] = dev
|
|
||||||
self.LOG.info("new device ready %s", dev)
|
|
||||||
self.status = STATUS.CONNECTED + len(self.devices)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if event.devnumber == 0xFF:
|
if event.devnumber == 0xFF:
|
||||||
|
@ -290,6 +287,40 @@ class Receiver(_listener.EventsListener):
|
||||||
|
|
||||||
self.LOG.warn("don't know how to handle event %s", event)
|
self.LOG.warn("don't know how to handle event %s", event)
|
||||||
|
|
||||||
|
def make_device(self, event):
|
||||||
|
if event.devnumber < 1 or event.devnumber > self.max_devices:
|
||||||
|
self.LOG.warn("got event for invalid device number %d: %s", event.devnumber, event)
|
||||||
|
return None
|
||||||
|
|
||||||
|
state_code = ord(event.data[2:3]) & 0xF0
|
||||||
|
state = STATUS.UNAVAILABLE if state_code == 0x60 else \
|
||||||
|
STATUS.CONNECTED if state_code == 0xA0 else \
|
||||||
|
STATUS.CONNECTED if state_code == 0x20 else \
|
||||||
|
None
|
||||||
|
if state is None:
|
||||||
|
self.LOG.warn("don't know how to handle device status 0x%02x: %s", state_code, event)
|
||||||
|
return None
|
||||||
|
|
||||||
|
dev = DeviceInfo(self, event.devnumber, state)
|
||||||
|
if state == STATUS.CONNECTED:
|
||||||
|
n, k = dev.name, dev.kind
|
||||||
|
else:
|
||||||
|
# we can query the receiver for the device short name
|
||||||
|
dev_id = self.request(0xFF, b'\x83\xB5', event.data[4:5])
|
||||||
|
if dev_id:
|
||||||
|
shortname = dev_id[2:].rstrip(b'\x00').decode('ascii')
|
||||||
|
if shortname in NAMES:
|
||||||
|
dev._name, dev._kind = NAMES[shortname]
|
||||||
|
else:
|
||||||
|
self.LOG.warn("could not identify inactive device %d: %s", event.devnumber, shortname)
|
||||||
|
|
||||||
|
b = bytearray(event.data[4:5])
|
||||||
|
b[0] -= 0x10
|
||||||
|
serial = self.request(0xFF, b'\x83\xB5', bytes(b))
|
||||||
|
if serial:
|
||||||
|
dev._serial = _hexlify(serial[1:5]).decode('ascii').upper()
|
||||||
|
return dev
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Receiver(%s,%x,%d:%d)' % (self.path, self._handle, self._active, self._status)
|
return 'Receiver(%s,%x,%d:%d)' % (self.path, self._handle, self._active, self._status)
|
||||||
|
|
||||||
|
|
|
@ -56,3 +56,4 @@ def _pair_device(action, window, state):
|
||||||
# pair_dialog.set_modal(True)
|
# pair_dialog.set_modal(True)
|
||||||
pair_dialog.present()
|
pair_dialog.present()
|
||||||
pair = _action('add', 'Pair new device', None)
|
pair = _action('add', 'Pair new device', None)
|
||||||
|
pair.set_sensitive(False)
|
||||||
|
|
|
@ -10,24 +10,198 @@ from logitech.devices.constants import (STATUS, PROPS)
|
||||||
|
|
||||||
_SMALL_DEVICE_ICON_SIZE = Gtk.IconSize.BUTTON
|
_SMALL_DEVICE_ICON_SIZE = Gtk.IconSize.BUTTON
|
||||||
_DEVICE_ICON_SIZE = Gtk.IconSize.DIALOG
|
_DEVICE_ICON_SIZE = Gtk.IconSize.DIALOG
|
||||||
_STATUS_ICON_SIZE = Gtk.IconSize.DND
|
_STATUS_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
||||||
_PLACEHOLDER = '~'
|
_PLACEHOLDER = '~'
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
def _show_info(action, widget):
|
||||||
|
widget.set_visible(action.get_active())
|
||||||
|
|
||||||
|
def _receiver_box(name):
|
||||||
|
icon = Gtk.Image.new_from_icon_name(name, _SMALL_DEVICE_ICON_SIZE)
|
||||||
|
|
||||||
|
label = Gtk.Label('Initializing...')
|
||||||
|
label.set_name('status-label')
|
||||||
|
label.set_alignment(0, 0.5)
|
||||||
|
|
||||||
|
toolbar = Gtk.Toolbar()
|
||||||
|
toolbar.set_name('buttons')
|
||||||
|
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
||||||
|
toolbar.set_icon_size(Gtk.IconSize.MENU)
|
||||||
|
toolbar.set_show_arrow(False)
|
||||||
|
|
||||||
|
hbox = Gtk.HBox(homogeneous=False, spacing=8)
|
||||||
|
hbox.pack_start(icon, False, False, 0)
|
||||||
|
hbox.pack_start(label, True, True, 0)
|
||||||
|
hbox.pack_end(toolbar, False, False, 0)
|
||||||
|
|
||||||
|
info_label = Gtk.Label()
|
||||||
|
info_label.set_name('info-label')
|
||||||
|
info_label.set_alignment(0, 0.5)
|
||||||
|
info_label.set_padding(32, 2)
|
||||||
|
info_label.set_selectable(True)
|
||||||
|
|
||||||
|
info_action = ui.action._toggle_action('info', 'Receiver info', _show_info, info_label)
|
||||||
|
toolbar.insert(info_action.create_tool_item(), 0)
|
||||||
|
toolbar.insert(ui.action.pair.create_tool_item(), -1)
|
||||||
|
|
||||||
|
vbox = Gtk.VBox(homogeneous=False, spacing=4)
|
||||||
|
vbox.set_border_width(4)
|
||||||
|
vbox.pack_start(hbox, True, True, 0)
|
||||||
|
vbox.pack_start(info_label, True, True, 0)
|
||||||
|
|
||||||
|
frame = Gtk.Frame()
|
||||||
|
frame.add(vbox)
|
||||||
|
frame.show_all()
|
||||||
|
info_label.set_visible(False)
|
||||||
|
return frame
|
||||||
|
|
||||||
|
|
||||||
|
def _device_box():
|
||||||
|
icon = Gtk.Image.new_from_icon_name('image-missing', _DEVICE_ICON_SIZE)
|
||||||
|
icon.set_name('icon')
|
||||||
|
icon.set_alignment(0.5, 0)
|
||||||
|
|
||||||
|
label = Gtk.Label('Initializing...')
|
||||||
|
label.set_name('label')
|
||||||
|
label.set_alignment(0, 0.5)
|
||||||
|
label.set_padding(0, 2)
|
||||||
|
|
||||||
|
battery_icon = Gtk.Image.new_from_icon_name('battery_unknown', _STATUS_ICON_SIZE)
|
||||||
|
|
||||||
|
battery_label = Gtk.Label()
|
||||||
|
battery_label.set_width_chars(6)
|
||||||
|
battery_label.set_alignment(0, 0.5)
|
||||||
|
|
||||||
|
light_icon = Gtk.Image.new_from_icon_name('light_unknown', _STATUS_ICON_SIZE)
|
||||||
|
|
||||||
|
light_label = Gtk.Label()
|
||||||
|
light_label.set_alignment(0, 0.5)
|
||||||
|
light_label.set_width_chars(8)
|
||||||
|
|
||||||
|
toolbar = Gtk.Toolbar()
|
||||||
|
toolbar.set_name('buttons')
|
||||||
|
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
||||||
|
toolbar.set_icon_size(Gtk.IconSize.MENU)
|
||||||
|
toolbar.set_show_arrow(False)
|
||||||
|
|
||||||
|
status_box = Gtk.HBox(homogeneous=False, spacing=0)
|
||||||
|
status_box.set_name('status')
|
||||||
|
status_box.pack_start(battery_icon, False, True, 0)
|
||||||
|
status_box.pack_start(battery_label, False, True, 0)
|
||||||
|
status_box.pack_start(light_icon, False, True, 0)
|
||||||
|
status_box.pack_start(light_label, False, True, 0)
|
||||||
|
status_box.pack_end(toolbar, False, False, 0)
|
||||||
|
|
||||||
|
info_label = Gtk.Label()
|
||||||
|
info_label.set_name('info-label')
|
||||||
|
info_label.set_alignment(0, 0.5)
|
||||||
|
info_label.set_padding(6, 2)
|
||||||
|
info_label.set_selectable(True)
|
||||||
|
|
||||||
|
info_action = ui.action._toggle_action('info', 'Device info', _show_info, info_label)
|
||||||
|
toolbar.insert(info_action.create_tool_item(), 0)
|
||||||
|
|
||||||
|
unpair_action = ui.action._action('remove', 'Unpair', None)
|
||||||
|
toolbar.insert(unpair_action.create_tool_item(), -1)
|
||||||
|
|
||||||
|
vbox = Gtk.VBox(homogeneous=False, spacing=8)
|
||||||
|
vbox.pack_start(label, True, True, 0)
|
||||||
|
vbox.pack_start(status_box, True, True, 0)
|
||||||
|
vbox.pack_start(info_label, True, True, 0)
|
||||||
|
|
||||||
|
box = Gtk.HBox(homogeneous=False, spacing=10)
|
||||||
|
box.set_border_width(4)
|
||||||
|
box.pack_start(icon, False, False, 0)
|
||||||
|
box.pack_start(vbox, True, True, 0)
|
||||||
|
box.show_all()
|
||||||
|
|
||||||
|
frame = Gtk.Frame()
|
||||||
|
frame.add(box)
|
||||||
|
info_label.set_visible(False)
|
||||||
|
return frame
|
||||||
|
|
||||||
|
|
||||||
|
def toggle(window, trigger):
|
||||||
|
# print 'window toggle', window, trigger
|
||||||
|
if window.get_visible():
|
||||||
|
position = window.get_position()
|
||||||
|
window.hide()
|
||||||
|
window.move(*position)
|
||||||
|
else:
|
||||||
|
if trigger and type(trigger) == Gtk.StatusIcon:
|
||||||
|
x, y = window.get_position()
|
||||||
|
if x == 0 and y == 0:
|
||||||
|
x, y, _ = Gtk.StatusIcon.position_menu(Gtk.Menu(), trigger)
|
||||||
|
window.move(x, y)
|
||||||
|
window.present()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def create(title, name, max_devices, systray=False):
|
||||||
|
window = Gtk.Window()
|
||||||
|
window.set_title(title)
|
||||||
|
window.set_icon_name(ui.appicon(0))
|
||||||
|
window.set_role('status-window')
|
||||||
|
|
||||||
|
vbox = Gtk.VBox(homogeneous=False, spacing=4)
|
||||||
|
vbox.set_border_width(4)
|
||||||
|
|
||||||
|
rbox = _receiver_box(name)
|
||||||
|
vbox.add(rbox)
|
||||||
|
for i in range(1, 1 + max_devices):
|
||||||
|
dbox = _device_box()
|
||||||
|
vbox.add(dbox)
|
||||||
|
vbox.set_visible(True)
|
||||||
|
|
||||||
|
window.add(vbox)
|
||||||
|
|
||||||
|
geometry = Gdk.Geometry()
|
||||||
|
geometry.min_width = 360
|
||||||
|
geometry.min_height = 20
|
||||||
|
window.set_geometry_hints(vbox, geometry, Gdk.WindowHints.MIN_SIZE)
|
||||||
|
window.set_resizable(False)
|
||||||
|
|
||||||
|
window.toggle_visible = lambda i: toggle(window, i)
|
||||||
|
|
||||||
|
if systray:
|
||||||
|
window.set_keep_above(True)
|
||||||
|
window.connect('delete-event', toggle)
|
||||||
|
else:
|
||||||
|
window.connect('delete-event', Gtk.main_quit)
|
||||||
|
|
||||||
|
return window
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
def _info_text(dev):
|
||||||
|
fw_text = '\n'.join(['%-12s\t<tt>%s%s%s</tt>' %
|
||||||
|
(f.kind, f.name, ' ' if f.name else '', f.version) for f in dev.firmware])
|
||||||
|
return ('<small>'
|
||||||
|
'Serial \t\t<tt>%s</tt>\n'
|
||||||
|
'%s'
|
||||||
|
'</small>' % (dev.serial, fw_text))
|
||||||
|
|
||||||
|
|
||||||
|
def _update_receiver_box(frame, receiver):
|
||||||
|
label, toolbar, info_label = ui.find_children(frame, 'status-label', 'buttons', 'info-label')
|
||||||
|
|
||||||
def _update_receiver_box(box, receiver):
|
|
||||||
button, label, frame, info = ui.find_children(box,
|
|
||||||
'info-button', 'status-label', 'info-frame', 'info-label')
|
|
||||||
label.set_text(receiver.status_text or '')
|
label.set_text(receiver.status_text or '')
|
||||||
if receiver.status < STATUS.CONNECTED:
|
if receiver.status < STATUS.CONNECTED:
|
||||||
button.set_sensitive(False)
|
toolbar.set_sensitive(False)
|
||||||
button.set_active(False)
|
info_label.set_visible(False)
|
||||||
frame.set_visible(False)
|
info_label.set_text('')
|
||||||
info.set_text('')
|
|
||||||
else:
|
else:
|
||||||
button.set_sensitive(True)
|
toolbar.set_sensitive(True)
|
||||||
if not info.get_text():
|
if not info_label.get_text():
|
||||||
info.set_text('Serial:\t\t%s\nFirmware: \t%s\nBootloader: \t%s\nMax devices:\t%s' %
|
info_label.set_markup(_info_text(receiver))
|
||||||
(receiver.serial, receiver.firmware[0], receiver.firmware[1], receiver.max_devices))
|
info_label.set_visible(toolbar.get_children()[0].get_active())
|
||||||
|
|
||||||
|
|
||||||
def _update_device_box(frame, dev):
|
def _update_device_box(frame, dev):
|
||||||
if dev is None:
|
if dev is None:
|
||||||
|
@ -35,27 +209,29 @@ def _update_device_box(frame, dev):
|
||||||
frame.set_name(_PLACEHOLDER)
|
frame.set_name(_PLACEHOLDER)
|
||||||
return
|
return
|
||||||
|
|
||||||
icon, label = ui.find_children(frame, 'icon', 'label')
|
icon, label, toolbar, info_label = ui.find_children(frame, 'icon', 'label', 'buttons', 'info-label')
|
||||||
|
|
||||||
frame.set_visible(True)
|
frame.set_visible(True)
|
||||||
if frame.get_name() != dev.name:
|
if frame.get_name() != dev.name:
|
||||||
frame.set_name(dev.name)
|
frame.set_name(dev.name)
|
||||||
|
icon.set_tooltip_text('')
|
||||||
icon.set_from_icon_name(ui.get_icon(dev.name, dev.kind), _DEVICE_ICON_SIZE)
|
icon.set_from_icon_name(ui.get_icon(dev.name, dev.kind), _DEVICE_ICON_SIZE)
|
||||||
icon.set_tooltip_text(dev.name)
|
|
||||||
label.set_markup('<b>' + dev.name + '</b>')
|
label.set_markup('<b>' + dev.name + '</b>')
|
||||||
|
|
||||||
status = ui.find_children(frame, 'status')
|
status = ui.find_children(frame, 'status')
|
||||||
if dev.status < STATUS.CONNECTED:
|
if dev.status < STATUS.CONNECTED:
|
||||||
icon.set_sensitive(False)
|
|
||||||
icon.set_tooltip_text(dev.status_text)
|
|
||||||
label.set_sensitive(False)
|
label.set_sensitive(False)
|
||||||
status.set_visible(False)
|
status.set_sensitive(False)
|
||||||
|
info_label.set_visible(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
icon.set_sensitive(True)
|
|
||||||
icon.set_tooltip_text('')
|
|
||||||
label.set_sensitive(True)
|
label.set_sensitive(True)
|
||||||
status.set_visible(True)
|
status.set_sensitive(True)
|
||||||
|
info_label.set_visible(toolbar.get_children()[0].get_active())
|
||||||
|
|
||||||
|
if not info_label.get_text():
|
||||||
|
info_label.set_markup(_info_text(dev))
|
||||||
|
|
||||||
status_icons = status.get_children()
|
status_icons = status.get_children()
|
||||||
|
|
||||||
battery_icon, battery_label = status_icons[0:2]
|
battery_icon, battery_label = status_icons[0:2]
|
||||||
|
@ -103,158 +279,3 @@ def update(window, receiver):
|
||||||
for index in range(1, len(controls)):
|
for index in range(1, len(controls)):
|
||||||
dev = receiver.devices[index] if index in receiver.devices else None
|
dev = receiver.devices[index] if index in receiver.devices else None
|
||||||
_update_device_box(controls[index], dev)
|
_update_device_box(controls[index], dev)
|
||||||
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
def _receiver_box(name):
|
|
||||||
info_button = Gtk.ToggleButton()
|
|
||||||
info_button.set_name('info-button')
|
|
||||||
info_button.set_alignment(0.5, 0)
|
|
||||||
info_button.set_image(Gtk.Image.new_from_icon_name(name, _SMALL_DEVICE_ICON_SIZE))
|
|
||||||
info_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
||||||
info_button.set_tooltip_text(name)
|
|
||||||
info_button.set_sensitive(False)
|
|
||||||
|
|
||||||
label = Gtk.Label('Initializing...')
|
|
||||||
label.set_name('status-label')
|
|
||||||
label.set_alignment(0, 0.5)
|
|
||||||
|
|
||||||
toolbar = Gtk.Toolbar()
|
|
||||||
toolbar.set_name('buttons')
|
|
||||||
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
|
||||||
toolbar.set_icon_size(Gtk.IconSize.MENU)
|
|
||||||
toolbar.set_show_arrow(False)
|
|
||||||
toolbar.insert(ui.action.pair.create_tool_item(), 0)
|
|
||||||
|
|
||||||
info_label = Gtk.Label('')
|
|
||||||
info_label.set_name('info-label')
|
|
||||||
info_label.set_alignment(0, 0.5)
|
|
||||||
info_label.set_padding(24, 4)
|
|
||||||
info_label.set_selectable(True)
|
|
||||||
|
|
||||||
info_frame = Gtk.Frame()
|
|
||||||
info_frame.set_name('info-frame')
|
|
||||||
info_frame.set_label(name)
|
|
||||||
info_frame.add(info_label)
|
|
||||||
|
|
||||||
info_button.connect('toggled', lambda b: info_frame.set_visible(b.get_active()))
|
|
||||||
|
|
||||||
hbox = Gtk.HBox(homogeneous=False, spacing=8)
|
|
||||||
hbox.pack_start(info_button, False, False, 0)
|
|
||||||
hbox.pack_start(label, True, True, 0)
|
|
||||||
hbox.pack_end(toolbar, False, False, 0)
|
|
||||||
|
|
||||||
vbox = Gtk.VBox(homogeneous=False, spacing=4)
|
|
||||||
vbox.set_border_width(4)
|
|
||||||
vbox.pack_start(hbox, True, True, 0)
|
|
||||||
vbox.pack_start(info_frame, True, True, 0)
|
|
||||||
vbox.show_all()
|
|
||||||
|
|
||||||
info_frame.set_visible(False)
|
|
||||||
return vbox
|
|
||||||
|
|
||||||
|
|
||||||
def _device_box(has_status_icons=True, has_frame=True):
|
|
||||||
box = Gtk.HBox(homogeneous=False, spacing=10)
|
|
||||||
box.set_border_width(4)
|
|
||||||
|
|
||||||
icon = Gtk.Image()
|
|
||||||
icon.set_name('icon')
|
|
||||||
icon.set_from_icon_name('image-missing', _DEVICE_ICON_SIZE)
|
|
||||||
icon.set_alignment(0.5, 0)
|
|
||||||
box.pack_start(icon, False, False, 0)
|
|
||||||
|
|
||||||
vbox = Gtk.VBox(homogeneous=False, spacing=8)
|
|
||||||
box.pack_start(vbox, True, True, 0)
|
|
||||||
|
|
||||||
label = Gtk.Label('Initializing...')
|
|
||||||
label.set_name('label')
|
|
||||||
label.set_alignment(0, 0.5)
|
|
||||||
|
|
||||||
status_box = Gtk.HBox(homogeneous=False, spacing=0)
|
|
||||||
status_box.set_name('status')
|
|
||||||
|
|
||||||
if has_status_icons:
|
|
||||||
vbox.pack_start(label, True, True, 0)
|
|
||||||
|
|
||||||
battery_icon = Gtk.Image.new_from_icon_name('battery_unknown', _STATUS_ICON_SIZE)
|
|
||||||
status_box.pack_start(battery_icon, False, True, 0)
|
|
||||||
|
|
||||||
battery_label = Gtk.Label()
|
|
||||||
battery_label.set_width_chars(6)
|
|
||||||
battery_label.set_alignment(0, 0.5)
|
|
||||||
status_box.pack_start(battery_label, False, True, 0)
|
|
||||||
|
|
||||||
light_icon = Gtk.Image.new_from_icon_name('light_unknown', _STATUS_ICON_SIZE)
|
|
||||||
status_box.pack_start(light_icon, False, True, 0)
|
|
||||||
|
|
||||||
light_label = Gtk.Label()
|
|
||||||
light_label.set_alignment(0, 0.5)
|
|
||||||
light_label.set_width_chars(8)
|
|
||||||
status_box.pack_start(light_label, False, True, 0)
|
|
||||||
else:
|
|
||||||
status_box.pack_start(label, True, True, 0)
|
|
||||||
|
|
||||||
vbox.pack_start(status_box, True, True, 0)
|
|
||||||
|
|
||||||
box.show_all()
|
|
||||||
|
|
||||||
if has_frame:
|
|
||||||
frame = Gtk.Frame()
|
|
||||||
frame.add(box)
|
|
||||||
return frame
|
|
||||||
else:
|
|
||||||
return box
|
|
||||||
|
|
||||||
|
|
||||||
def toggle(window, trigger):
|
|
||||||
# print 'window toggle', window, trigger
|
|
||||||
if window.get_visible():
|
|
||||||
position = window.get_position()
|
|
||||||
window.hide()
|
|
||||||
window.move(*position)
|
|
||||||
else:
|
|
||||||
if trigger and type(trigger) == Gtk.StatusIcon:
|
|
||||||
x, y = window.get_position()
|
|
||||||
if x == 0 and y == 0:
|
|
||||||
x, y, _ = Gtk.StatusIcon.position_menu(Gtk.Menu(), trigger)
|
|
||||||
window.move(x, y)
|
|
||||||
window.present()
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def create(title, name, max_devices, systray=False):
|
|
||||||
window = Gtk.Window()
|
|
||||||
window.set_title(title)
|
|
||||||
window.set_icon_name(ui.appicon(0))
|
|
||||||
window.set_role('status-window')
|
|
||||||
|
|
||||||
vbox = Gtk.VBox(homogeneous=False, spacing=4)
|
|
||||||
vbox.set_border_width(4)
|
|
||||||
|
|
||||||
rbox = _receiver_box(name)
|
|
||||||
vbox.add(rbox)
|
|
||||||
for i in range(1, 1 + max_devices):
|
|
||||||
dbox = _device_box()
|
|
||||||
vbox.add(dbox)
|
|
||||||
vbox.set_visible(True)
|
|
||||||
|
|
||||||
window.add(vbox)
|
|
||||||
|
|
||||||
geometry = Gdk.Geometry()
|
|
||||||
geometry.min_width = 300
|
|
||||||
geometry.min_height = 40
|
|
||||||
window.set_geometry_hints(vbox, geometry, Gdk.WindowHints.MIN_SIZE)
|
|
||||||
window.set_resizable(False)
|
|
||||||
|
|
||||||
window.toggle_visible = lambda i: toggle(window, i)
|
|
||||||
|
|
||||||
if systray:
|
|
||||||
window.set_keep_above(True)
|
|
||||||
window.connect('delete-event', toggle)
|
|
||||||
else:
|
|
||||||
window.connect('delete-event', Gtk.main_quit)
|
|
||||||
|
|
||||||
return window
|
|
||||||
|
|
|
@ -5,16 +5,21 @@
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import time
|
import time
|
||||||
from logging import getLogger as _Logger
|
from logging import getLogger as _Logger
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from logitech.devices.constants import STATUS
|
from logitech.devices.constants import STATUS
|
||||||
from receiver import Receiver
|
from receiver import Receiver
|
||||||
|
|
||||||
|
|
||||||
_DUMMY_RECEIVER = namedtuple('_DUMMY_RECEIVER', ['NAME', 'kind', 'status', 'status_text', 'max_devices', 'devices'])
|
class _DUMMY_RECEIVER:
|
||||||
_DUMMY_RECEIVER.__nonzero__ = lambda _: False
|
NAME = Receiver.NAME
|
||||||
_DUMMY_RECEIVER.device_name = Receiver.NAME
|
device_name = NAME
|
||||||
DUMMY = _DUMMY_RECEIVER(Receiver.NAME, Receiver.NAME, STATUS.UNAVAILABLE, 'Receiver not found.', Receiver.max_devices, {})
|
kind = Receiver.NAME
|
||||||
|
status = STATUS.UNAVAILABLE
|
||||||
|
status_text = 'Receiver not found.'
|
||||||
|
max_devices = Receiver.max_devices
|
||||||
|
devices = {}
|
||||||
|
def __nonzero__(self): return False
|
||||||
|
DUMMY = _DUMMY_RECEIVER()
|
||||||
|
|
||||||
_l = _Logger('watcher')
|
_l = _Logger('watcher')
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,11 @@ NAMES = {
|
||||||
'M325': ('Wireless Mouse M325', 'mouse'),
|
'M325': ('Wireless Mouse M325', 'mouse'),
|
||||||
'M510': ('Wireless Mouse M510', 'mouse'),
|
'M510': ('Wireless Mouse M510', 'mouse'),
|
||||||
'M515': ('Couch Mouse M515', 'mouse'),
|
'M515': ('Couch Mouse M515', 'mouse'),
|
||||||
|
'M525': ('Wireless Mouse M525', 'mouse'),
|
||||||
'M570': ('Wireless Trackball M570', 'trackball'),
|
'M570': ('Wireless Trackball M570', 'trackball'),
|
||||||
'K270': ('Wireless Keyboard K270', 'keyboard'),
|
'K270': ('Wireless Keyboard K270', 'keyboard'),
|
||||||
'K350': ('Wireless Keyboard K350', 'keyboard'),
|
'K350': ('Wireless Keyboard K350', 'keyboard'),
|
||||||
'K750': ('Wireless Solar Keyboard K750', 'keyboard'),
|
'K750': ('Wireless Solar Keyboard K750', 'keyboard'),
|
||||||
'K800': ('Wireless Illuminated Keyboard K800', 'keyboard'),
|
'K800': ('Wireless Illuminated Keyboard K800', 'keyboard'),
|
||||||
|
'T650': ('Wireless Rechargeable Touchpad T650', 'touchpad'),
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,23 +42,21 @@ def get_receiver_info(handle):
|
||||||
if reply and reply[0:1] == b'\x03':
|
if reply and reply[0:1] == b'\x03':
|
||||||
serial = _hexlify(reply[1:5]).decode('ascii').upper()
|
serial = _hexlify(reply[1:5]).decode('ascii').upper()
|
||||||
|
|
||||||
firmware = '??.??'
|
firmware = []
|
||||||
reply = _base.request(handle, 0xFF, b'\x81\xF1', b'\x01')
|
|
||||||
if reply and reply[0:1] == b'\x01':
|
|
||||||
fw_version = _hexlify(reply[1:3]).decode('ascii')
|
|
||||||
firmware = '%s.%s' % (fw_version[0:2], fw_version[2:4])
|
|
||||||
|
|
||||||
reply = _base.request(handle, 0xFF, b'\x81\xF1', b'\x02')
|
reply = _base.request(handle, 0xFF, b'\x83\xB5', b'\x02')
|
||||||
if reply and reply[0:1] == b'\x02':
|
if reply and reply[0:1] == b'\x02':
|
||||||
firmware += '.' + _hexlify(reply[1:3]).decode('ascii')
|
fw_version = _hexlify(reply[1:5]).decode('ascii')
|
||||||
|
fw_version = '%s.%s.%s' % (fw_version[0:2], fw_version[2:4], fw_version[4:8])
|
||||||
|
firmware.append(_FirmwareInfo(0, FIRMWARE_KIND[0], '', fw_version, None))
|
||||||
|
|
||||||
bootloader = None
|
|
||||||
reply = _base.request(handle, 0xFF, b'\x81\xF1', b'\x04')
|
reply = _base.request(handle, 0xFF, b'\x81\xF1', b'\x04')
|
||||||
if reply and reply[0:1] == b'\x04':
|
if reply and reply[0:1] == b'\x04':
|
||||||
bl_version = _hexlify(reply[1:3]).decode('ascii')
|
bl_version = _hexlify(reply[1:3]).decode('ascii')
|
||||||
bootloader = '%s.%s' % (bl_version[0:2], bl_version[2:4])
|
bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4])
|
||||||
|
firmware.append(_FirmwareInfo(1, FIRMWARE_KIND[1], '', bl_version, None))
|
||||||
|
|
||||||
return (serial, firmware, bootloader)
|
return (serial, tuple(firmware))
|
||||||
|
|
||||||
|
|
||||||
def count_devices(handle):
|
def count_devices(handle):
|
||||||
|
@ -270,8 +268,8 @@ def get_device_firmware(handle, devnumber, features=None):
|
||||||
|
|
||||||
:returns: a list of FirmwareInfo tuples, ordered by firmware layer.
|
:returns: a list of FirmwareInfo tuples, ordered by firmware layer.
|
||||||
"""
|
"""
|
||||||
def _makeFirmwareInfo(level, kind, name=None, version=None, build=None, extras=None):
|
def _makeFirmwareInfo(level, kind, name='', version='', extras=None):
|
||||||
return _FirmwareInfo(level, kind, name, version, build, extras)
|
return _FirmwareInfo(level, kind, name, version, extras)
|
||||||
|
|
||||||
fw_count = request(handle, devnumber, FEATURE.FIRMWARE, features=features)
|
fw_count = request(handle, devnumber, FEATURE.FIRMWARE, features=features)
|
||||||
if fw_count:
|
if fw_count:
|
||||||
|
@ -286,14 +284,16 @@ def get_device_firmware(handle, devnumber, features=None):
|
||||||
kind = FIRMWARE_KIND[level]
|
kind = FIRMWARE_KIND[level]
|
||||||
name, = _unpack('!3s', fw_info[1:4])
|
name, = _unpack('!3s', fw_info[1:4])
|
||||||
name = name.decode('ascii')
|
name = name.decode('ascii')
|
||||||
version = _hexlify(fw_info[4:6])
|
version = _hexlify(fw_info[4:6]).decode('ascii')
|
||||||
version = '%s.%s' % (version[0:2], version[2:4])
|
version = '%s.%s' % (version[0:2], version[2:4])
|
||||||
build, = _unpack('!H', fw_info[6:8])
|
build, = _unpack('!H', fw_info[6:8])
|
||||||
|
if build:
|
||||||
|
version += ' b%d' % build
|
||||||
extras = fw_info[9:].rstrip(b'\x00')
|
extras = fw_info[9:].rstrip(b'\x00')
|
||||||
if extras:
|
if extras:
|
||||||
fw_info = _makeFirmwareInfo(level, kind, name, version, build, extras)
|
fw_info = _makeFirmwareInfo(level, kind, name, version, extras)
|
||||||
else:
|
else:
|
||||||
fw_info = _makeFirmwareInfo(level, kind, name, version, build)
|
fw_info = _makeFirmwareInfo(level, kind, name, version)
|
||||||
elif level == 2:
|
elif level == 2:
|
||||||
fw_info = _makeFirmwareInfo(2, FIRMWARE_KIND[2], version=ord(fw_info[1:2]))
|
fw_info = _makeFirmwareInfo(2, FIRMWARE_KIND[2], version=ord(fw_info[1:2]))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -105,7 +105,7 @@ def try_open(path):
|
||||||
# otherwise, the read should produce nothing
|
# otherwise, the read should produce nothing
|
||||||
reply = _hid.read(receiver_handle, _MAX_REPLY_SIZE, DEFAULT_TIMEOUT)
|
reply = _hid.read(receiver_handle, _MAX_REPLY_SIZE, DEFAULT_TIMEOUT)
|
||||||
if reply:
|
if reply:
|
||||||
if reply[:4] == b'\x10\x00\x8F\x00':
|
if reply[:5] == b'\x10\x00\x8F\x00\x10':
|
||||||
# 'device 0 unreachable' is the expected reply from a valid receiver handle
|
# 'device 0 unreachable' is the expected reply from a valid receiver handle
|
||||||
_l.log(_LOG_LEVEL, "[%s] success: handle %x", path, receiver_handle)
|
_l.log(_LOG_LEVEL, "[%s] success: handle %x", path, receiver_handle)
|
||||||
return receiver_handle
|
return receiver_handle
|
||||||
|
@ -114,9 +114,9 @@ def try_open(path):
|
||||||
if _l.isEnabledFor(_LOG_LEVEL):
|
if _l.isEnabledFor(_LOG_LEVEL):
|
||||||
if reply == b'\x01\x00\x00\x00\x00\x00\x00\x00':
|
if reply == b'\x01\x00\x00\x00\x00\x00\x00\x00':
|
||||||
# no idea what this is, but it comes up occasionally
|
# no idea what this is, but it comes up occasionally
|
||||||
_l.log(_LOG_LEVEL, "[%s] %x mistery reply [%s]", path, receiver_handle, _hexlify(reply))
|
_l.log(_LOG_LEVEL, "[%s] %x mistery reply [%s]", path, receiver_handle, _hexlify(reply).decode('ascii'))
|
||||||
else:
|
else:
|
||||||
_l.log(_LOG_LEVEL, "[%s] %x unknown reply [%s]", path, receiver_handle, _hexlify(reply))
|
_l.log(_LOG_LEVEL, "[%s] %x unknown reply [%s]", path, receiver_handle, _hexlify(reply).decode('ascii'))
|
||||||
else:
|
else:
|
||||||
_l.log(_LOG_LEVEL, "[%s] %x no reply", path, receiver_handle)
|
_l.log(_LOG_LEVEL, "[%s] %x no reply", path, receiver_handle)
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ def write(handle, devnumber, data):
|
||||||
wdata = _pack('!BB18s' if len(data) > 5 else '!BB5s', 0x10, devnumber, data)
|
wdata = _pack('!BB18s' if len(data) > 5 else '!BB5s', 0x10, devnumber, data)
|
||||||
|
|
||||||
if _l.isEnabledFor(_LOG_LEVEL):
|
if _l.isEnabledFor(_LOG_LEVEL):
|
||||||
hexs = _hexlify(wdata)
|
hexs = _hexlify(wdata).decode('ascii')
|
||||||
_l.log(_LOG_LEVEL, "(%d) <= w[%s %s %s %s]", devnumber, hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:])
|
_l.log(_LOG_LEVEL, "(%d) <= w[%s %s %s %s]", devnumber, hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:])
|
||||||
|
|
||||||
if not _hid.write(handle, wdata):
|
if not _hid.write(handle, wdata):
|
||||||
|
@ -208,7 +208,7 @@ def read(handle, timeout=DEFAULT_TIMEOUT):
|
||||||
if len(data) > _MAX_REPLY_SIZE:
|
if len(data) > _MAX_REPLY_SIZE:
|
||||||
_l.warn("(%d) => r[%s] read packet too long: %d bytes", ord(data[1:2]), _hexlify(data), len(data))
|
_l.warn("(%d) => r[%s] read packet too long: %d bytes", ord(data[1:2]), _hexlify(data), len(data))
|
||||||
if _l.isEnabledFor(_LOG_LEVEL):
|
if _l.isEnabledFor(_LOG_LEVEL):
|
||||||
hexs = _hexlify(data)
|
hexs = _hexlify(data).decode('ascii')
|
||||||
_l.log(_LOG_LEVEL, "(%d) => r[%s %s %s %s]", ord(data[1:2]), hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:])
|
_l.log(_LOG_LEVEL, "(%d) => r[%s %s %s %s]", ord(data[1:2]), hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:])
|
||||||
code = ord(data[:1])
|
code = ord(data[:1])
|
||||||
devnumber = ord(data[1:2])
|
devnumber = ord(data[1:2])
|
||||||
|
@ -235,7 +235,9 @@ def request(handle, devnumber, feature_index_function, params=b'', features=None
|
||||||
:raisees FeatureCallError: if the feature call replied with an error.
|
:raisees FeatureCallError: if the feature call replied with an error.
|
||||||
"""
|
"""
|
||||||
if _l.isEnabledFor(_LOG_LEVEL):
|
if _l.isEnabledFor(_LOG_LEVEL):
|
||||||
_l.log(_LOG_LEVEL, "(%d) request {%s} params [%s]", devnumber, _hexlify(feature_index_function), _hexlify(params))
|
_l.log(_LOG_LEVEL, "(%d) request {%s} params [%s]", devnumber,
|
||||||
|
_hexlify(feature_index_function).decode('ascii'),
|
||||||
|
_hexlify(params).decode('ascii'))
|
||||||
if len(feature_index_function) != 2:
|
if len(feature_index_function) != 2:
|
||||||
raise ValueError('invalid feature_index_function {%s}: it must be a two-byte string' % _hexlify(feature_index_function))
|
raise ValueError('invalid feature_index_function {%s}: it must be a two-byte string' % _hexlify(feature_index_function))
|
||||||
|
|
||||||
|
@ -264,18 +266,20 @@ def request(handle, devnumber, feature_index_function, params=b'', features=None
|
||||||
|
|
||||||
if reply_code == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == feature_index_function:
|
if reply_code == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == feature_index_function:
|
||||||
# device not present
|
# device not present
|
||||||
_l.log(_LOG_LEVEL, "(%d) request ping failed on {%s} call: [%s]", devnumber, _hexlify(feature_index_function), _hexlify(reply_data))
|
_l.log(_LOG_LEVEL, "(%d) request ping failed on {%s} call: [%s]", devnumber,
|
||||||
|
_hexlify(feature_index_function).decode('ascii'),
|
||||||
|
_hexlify(reply_data).decode('ascii'))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if reply_code == 0x10 and reply_data[:1] == b'\x8F':
|
if reply_code == 0x10 and reply_data[:1] == b'\x8F':
|
||||||
# device not present
|
# device not present
|
||||||
_l.log(_LOG_LEVEL, "(%d) request ping failed: [%s]", devnumber, _hexlify(reply_data))
|
_l.log(_LOG_LEVEL, "(%d) request ping failed: [%s]", devnumber, _hexlify(reply_data).decode('ascii'))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if reply_code == 0x11 and reply_data[0] == b'\xFF' and reply_data[1:3] == feature_index_function:
|
if reply_code == 0x11 and reply_data[0] == b'\xFF' and reply_data[1:3] == feature_index_function:
|
||||||
# the feature call returned with an error
|
# the feature call returned with an error
|
||||||
error_code = ord(reply_data[3])
|
error_code = ord(reply_data[3])
|
||||||
_l.warn("(%d) request feature call error %d = %s: %s", devnumber, error_code, ERROR_NAME[error_code], _hexlify(reply_data))
|
_l.warn("(%d) request feature call error %d = %s: %s", devnumber, error_code, ERROR_NAME[error_code], _hexlify(reply_data).decode('ascii'))
|
||||||
feature_index = ord(feature_index_function[:1])
|
feature_index = ord(feature_index_function[:1])
|
||||||
feature_function = feature_index_function[1:2]
|
feature_function = feature_index_function[1:2]
|
||||||
feature = None if features is None else features[feature_index] if feature_index < len(features) else None
|
feature = None if features is None else features[feature_index] if feature_index < len(features) else None
|
||||||
|
@ -283,14 +287,16 @@ def request(handle, devnumber, feature_index_function, params=b'', features=None
|
||||||
|
|
||||||
if reply_code == 0x11 and reply_data[:2] == feature_index_function:
|
if reply_code == 0x11 and reply_data[:2] == feature_index_function:
|
||||||
# a matching reply
|
# a matching reply
|
||||||
# _l.log(_LOG_LEVEL, "(%d) matched reply with feature-index-function [%s]", devnumber, _hexlify(reply_data[2:]))
|
# _l.log(_LOG_LEVEL, "(%d) matched reply with feature-index-function [%s]", devnumber, _hexlify(reply_data[2:]).decode('ascii'))
|
||||||
return reply_data[2:]
|
return reply_data[2:]
|
||||||
|
|
||||||
if reply_code == 0x10 and devnumber == 0xFF and reply_data[:2] == feature_index_function:
|
if reply_code == 0x10 and devnumber == 0xFF and reply_data[:2] == feature_index_function:
|
||||||
# direct calls to the receiver (device 0xFF) may also return successfully with reply code 0x10
|
# direct calls to the receiver (device 0xFF) may also return successfully with reply code 0x10
|
||||||
# _l.log(_LOG_LEVEL, "(%d) matched reply with feature-index-function [%s]", devnumber, _hexlify(reply_data[2:]))
|
# _l.log(_LOG_LEVEL, "(%d) matched reply with feature-index-function [%s]", devnumber, _hexlify(reply_data[2:]).decode('ascii'))
|
||||||
return reply_data[2:]
|
return reply_data[2:]
|
||||||
|
|
||||||
# _l.log(_LOG_LEVEL, "(%d) unmatched reply {%s} (expected {%s})", devnumber, _hexlify(reply_data[:2]), _hexlify(feature_index_function))
|
# _l.log(_LOG_LEVEL, "(%d) unmatched reply {%s} (expected {%s})", devnumber,
|
||||||
|
# _hexlify(reply_data[:2]).decode('ascii'),
|
||||||
|
# _hexlify(feature_index_function).decode('ascii'))
|
||||||
if unhandled_hook:
|
if unhandled_hook:
|
||||||
unhandled_hook(reply_code, reply_devnumber, reply_data)
|
unhandled_hook(reply_code, reply_devnumber, reply_data)
|
||||||
|
|
|
@ -36,7 +36,6 @@ FirmwareInfo = namedtuple('FirmwareInfo', [
|
||||||
'kind',
|
'kind',
|
||||||
'name',
|
'name',
|
||||||
'version',
|
'version',
|
||||||
'build',
|
|
||||||
'extras'])
|
'extras'])
|
||||||
|
|
||||||
"""Reprogrammable keys informations."""
|
"""Reprogrammable keys informations."""
|
||||||
|
|
|
@ -55,7 +55,7 @@ _DEVICE_KINDS = ('keyboard', 'remote control', 'numpad', 'mouse',
|
||||||
DEVICE_KIND = FallbackDict(lambda x: 'unknown', list2dict(_DEVICE_KINDS))
|
DEVICE_KIND = FallbackDict(lambda x: 'unknown', list2dict(_DEVICE_KINDS))
|
||||||
|
|
||||||
|
|
||||||
_FIRMWARE_KINDS = ('Main (HID)', 'Bootloader', 'Hardware', 'Other')
|
_FIRMWARE_KINDS = ('Firmware', 'Bootloader', 'Hardware', 'Other')
|
||||||
|
|
||||||
"""Names of different firmware levels possible, indexed by level."""
|
"""Names of different firmware levels possible, indexed by level."""
|
||||||
FIRMWARE_KIND = FallbackDict(lambda x: 'Unknown', list2dict(_FIRMWARE_KINDS))
|
FIRMWARE_KIND = FallbackDict(lambda x: 'Unknown', list2dict(_FIRMWARE_KINDS))
|
||||||
|
|
|
@ -13,11 +13,11 @@ from .unifying_receiver.constants import *
|
||||||
def print_receiver(receiver):
|
def print_receiver(receiver):
|
||||||
print ("Unifying Receiver")
|
print ("Unifying Receiver")
|
||||||
|
|
||||||
serial, firmware, bootloader = api.get_receiver_info(receiver)
|
serial, firmware = api.get_receiver_info(receiver)
|
||||||
|
|
||||||
print (" Serial: %s" % serial)
|
print (" Serial : %s" % serial)
|
||||||
print (" Firmware version: %s" % firmware)
|
for f in firmware:
|
||||||
print (" Bootloader: %s" % bootloader)
|
print (" %-10s: %s" % (f.kind, f.version))
|
||||||
|
|
||||||
print ("--------")
|
print ("--------")
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ def scan_devices(receiver):
|
||||||
|
|
||||||
firmware = api.get_device_firmware(receiver, devinfo.number, features=devinfo.features)
|
firmware = api.get_device_firmware(receiver, devinfo.number, features=devinfo.features)
|
||||||
for fw in firmware:
|
for fw in firmware:
|
||||||
print (" %s firmware: %s version %s build %d" % (fw.kind, fw.name, fw.version, fw.build))
|
print (" %-10s: %s %s" % (fw.kind, fw.name, fw.version))
|
||||||
|
|
||||||
for index in range(0, len(devinfo.features)):
|
for index in range(0, len(devinfo.features)):
|
||||||
feature = devinfo.features[index]
|
feature = devinfo.features[index]
|
||||||
|
@ -62,7 +62,7 @@ if __name__ == '__main__':
|
||||||
import argparse
|
import argparse
|
||||||
arg_parser = argparse.ArgumentParser()
|
arg_parser = argparse.ArgumentParser()
|
||||||
arg_parser.add_argument('-v', '--verbose', action='count', default=0,
|
arg_parser.add_argument('-v', '--verbose', action='count', default=0,
|
||||||
help='increase the logger verbosity')
|
help='log the HID data traffic with the receiver')
|
||||||
args = arg_parser.parse_args()
|
args = arg_parser.parse_args()
|
||||||
|
|
||||||
log_level = logging.root.level - 10 * args.verbose
|
log_level = logging.root.level - 10 * args.verbose
|
||||||
|
|
Loading…
Reference in New Issue