reworked the event listener
This commit is contained in:
parent
c61297a871
commit
e34ad5104f
|
@ -2,27 +2,28 @@
|
|||
#
|
||||
#
|
||||
|
||||
import threading
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
|
||||
from .watcher import WatcherThread
|
||||
from . import ui
|
||||
from logitech.devices import constants as C
|
||||
|
||||
|
||||
APP_TITLE = 'Solaar'
|
||||
|
||||
|
||||
def _status_updated(watcher, icon, window):
|
||||
while True:
|
||||
watcher.status_changed.wait()
|
||||
text = watcher.status_text
|
||||
watcher.status_changed.clear()
|
||||
|
||||
icon_name = APP_TITLE + '-fail' if watcher.rstatus.code < C.STATUS.CONNECTED else APP_TITLE
|
||||
|
||||
if icon:
|
||||
GObject.idle_add(ui.icon.update, icon, watcher.rstatus, text)
|
||||
GObject.idle_add(ui.icon.update, icon, watcher.rstatus, text, icon_name)
|
||||
|
||||
if window:
|
||||
GObject.idle_add(ui.window.update, window, watcher.rstatus, dict(watcher.devices))
|
||||
GObject.idle_add(ui.window.update, window, watcher.rstatus, dict(watcher.devices), icon_name)
|
||||
|
||||
|
||||
def run(config):
|
||||
|
@ -30,16 +31,22 @@ def run(config):
|
|||
|
||||
ui.notify.init(APP_TITLE, config.notifications)
|
||||
|
||||
from .watcher import WatcherThread
|
||||
watcher = WatcherThread(ui.notify.show)
|
||||
watcher.start()
|
||||
|
||||
window = ui.window.create(APP_TITLE, watcher.rstatus, not config.start_hidden, config.close_to_tray)
|
||||
tray_icon = ui.icon.create(APP_TITLE, (ui.window.toggle, window))
|
||||
window.set_icon_name(APP_TITLE + '-fail')
|
||||
|
||||
tray_icon = ui.icon.create(APP_TITLE, (ui.window.toggle, window))
|
||||
tray_icon.set_from_icon_name(APP_TITLE + '-fail')
|
||||
|
||||
import threading
|
||||
ui_update_thread = threading.Thread(target=_status_updated, name='ui_update', args=(watcher, tray_icon, window))
|
||||
ui_update_thread.daemon = True
|
||||
ui_update_thread.start()
|
||||
|
||||
from gi.repository import Gtk
|
||||
Gtk.main()
|
||||
|
||||
watcher.stop()
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from logitech.unifying_receiver import api
|
||||
from logitech import devices
|
||||
|
||||
from . import ui
|
||||
import ui.pair
|
||||
|
||||
|
||||
def full_scan(button, watcher):
|
||||
if watcher.active and watcher.listener:
|
||||
updated = False
|
||||
|
||||
for devnumber in range(1, 1 + api.C.MAX_ATTACHED_DEVICES):
|
||||
devstatus = watcher.devices.get(devnumber)
|
||||
if devstatus:
|
||||
status = devices.request_status(devstatus, watcher.listener)
|
||||
updated |= watcher._device_status_changed(devstatus, status)
|
||||
else:
|
||||
devstatus = watcher._new_device(devnumber)
|
||||
updated |= devstatus is not None
|
||||
|
||||
if updated:
|
||||
watcher._update_status_text()
|
||||
|
||||
return updated
|
||||
|
||||
|
||||
def pair(button, watcher):
|
||||
if watcher.active and watcher.listener:
|
||||
logging.debug("pair")
|
||||
|
||||
parent = button.get_toplevel()
|
||||
title = parent.get_title() + ': ' + button.get_tooltip_text()
|
||||
w = ui.pair.create(parent, title)
|
||||
w.run()
|
||||
w.destroy()
|
|
@ -4,14 +4,9 @@
|
|||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from logitech.devices import constants as C
|
||||
|
||||
_ICON_OK = 'Solaar'
|
||||
_ICON_FAIL = _ICON_OK + '-fail'
|
||||
|
||||
|
||||
def create(title, click_action=None):
|
||||
icon = Gtk.StatusIcon.new_from_icon_name(_ICON_OK)
|
||||
icon = Gtk.StatusIcon()
|
||||
icon.set_title(title)
|
||||
icon.set_name(title)
|
||||
|
||||
|
@ -37,9 +32,8 @@ def create(title, click_action=None):
|
|||
return icon
|
||||
|
||||
|
||||
def update(icon, receiver, tooltip):
|
||||
icon.set_tooltip_markup(tooltip)
|
||||
if receiver.code < C.STATUS.CONNECTED:
|
||||
icon.set_from_icon_name(_ICON_FAIL)
|
||||
else:
|
||||
icon.set_from_icon_name(_ICON_OK)
|
||||
def update(icon, receiver, tooltip=None, icon_name=None):
|
||||
if tooltip is not None:
|
||||
icon.set_tooltip_markup(tooltip)
|
||||
if icon_name is not None:
|
||||
icon.set_from_icon_name(icon_name)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
def create(parent_window, title):
|
||||
window = Gtk.Dialog(title, parent_window, Gtk.DialogFlags.MODAL, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))
|
||||
|
||||
Gtk.Window.set_default_icon_name('add')
|
||||
window.set_resizable(False)
|
||||
|
||||
# window.set_wmclass(title, 'status-window')
|
||||
# window.set_role('pair')
|
||||
|
||||
return window
|
|
@ -77,8 +77,11 @@ def _update_device_box(frame, devstatus):
|
|||
expander.set_label(_PLACEHOLDER)
|
||||
|
||||
|
||||
def update(window, receiver, devices):
|
||||
def update(window, receiver, devices, icon_name=None):
|
||||
if window and window.get_child():
|
||||
if icon_name is not None:
|
||||
window.set_icon_name(icon_name)
|
||||
|
||||
controls = list(window.get_child().get_children())
|
||||
_update_receiver_box(controls[0], receiver)
|
||||
for index in range(1, 1 + _MAX_DEVICES):
|
||||
|
@ -107,9 +110,9 @@ def _receiver_box(rstatus):
|
|||
buttons_box.set_layout(Gtk.ButtonBoxStyle.START)
|
||||
vbox.pack_start(buttons_box, True, True, 0)
|
||||
|
||||
def _action(button, action):
|
||||
def _action(button, function, params):
|
||||
button.set_sensitive(False)
|
||||
action()
|
||||
function(button, *params)
|
||||
button.set_sensitive(True)
|
||||
|
||||
def _add_button(name, icon, action):
|
||||
|
@ -119,7 +122,9 @@ def _receiver_box(rstatus):
|
|||
button.set_tooltip_text(name)
|
||||
button.set_focus_on_click(False)
|
||||
if action:
|
||||
button.connect('clicked', _action, action)
|
||||
function = action[0]
|
||||
params = action[1:]
|
||||
button.connect('clicked', _action, function, params)
|
||||
else:
|
||||
button.set_sensitive(False)
|
||||
buttons_box.pack_start(button, False, False, 0)
|
||||
|
@ -167,14 +172,12 @@ def _device_box():
|
|||
def create(title, rstatus, show=True, close_to_tray=False):
|
||||
window = Gtk.Window()
|
||||
|
||||
Gtk.Window.set_default_icon_name('mouse')
|
||||
window.set_icon_name(title)
|
||||
|
||||
window.set_title(title)
|
||||
|
||||
window.set_keep_above(True)
|
||||
window.set_deletable(False)
|
||||
window.set_resizable(False)
|
||||
window.set_size_request(200, 50)
|
||||
# window.set_size_request(200, 50)
|
||||
window.set_default_size(200, 50)
|
||||
|
||||
window.set_position(Gtk.WindowPosition.MOUSE)
|
||||
|
|
|
@ -5,13 +5,17 @@
|
|||
import logging
|
||||
import threading
|
||||
import time
|
||||
from binascii import hexlify as _hexlify
|
||||
|
||||
from logitech.unifying_receiver import api
|
||||
from logitech.unifying_receiver.listener import EventsListener
|
||||
from logitech import devices
|
||||
from logitech.devices import constants as C
|
||||
|
||||
from . import actions
|
||||
|
||||
|
||||
_l = logging.getLogger('watcher')
|
||||
|
||||
|
||||
_STATUS_TIMEOUT = 31 # seconds
|
||||
_THREAD_SLEEP = 2 # seconds
|
||||
|
@ -30,11 +34,14 @@ class _DevStatus(api.AttachedDeviceInfo):
|
|||
text = _INITIALIZING
|
||||
refresh = None
|
||||
|
||||
def __str__(self):
|
||||
return 'DevStatus(%d,%s,%d)' % (self.number, self.name, self.code)
|
||||
|
||||
|
||||
class WatcherThread(threading.Thread):
|
||||
"""Keeps a map of all attached devices and their statuses."""
|
||||
def __init__(self, notify_callback=None):
|
||||
super(WatcherThread, self).__init__(name='WatcherThread')
|
||||
super(WatcherThread, self).__init__(group='Solaar', name='Watcher')
|
||||
self.daemon = True
|
||||
self.active = False
|
||||
|
||||
|
@ -44,9 +51,9 @@ class WatcherThread(threading.Thread):
|
|||
|
||||
self.listener = None
|
||||
|
||||
self.rstatus = _DevStatus(0, _UNIFYING_RECEIVER, _UNIFYING_RECEIVER, None, None)
|
||||
self.rstatus.refresh = self.full_scan
|
||||
self.rstatus.pair = None
|
||||
self.rstatus = _DevStatus(0, 0xFF, None, _UNIFYING_RECEIVER, None, None)
|
||||
self.rstatus.refresh = (actions.full_scan, self)
|
||||
self.rstatus.pair = None # (actions.pair, self)
|
||||
|
||||
self.devices = {}
|
||||
|
||||
|
@ -55,7 +62,7 @@ class WatcherThread(threading.Thread):
|
|||
|
||||
while self.active:
|
||||
if self.listener is None:
|
||||
self._device_status_changed(self.rstatus, (C.STATUS.UNKNOWN, _INITIALIZING))
|
||||
self._device_status_changed(self.rstatus, (C.STATUS.UNKNOWN, _SCANNING))
|
||||
self._update_status_text()
|
||||
|
||||
receiver = api.open()
|
||||
|
@ -65,57 +72,38 @@ class WatcherThread(threading.Thread):
|
|||
|
||||
for devinfo in api.list_devices(receiver):
|
||||
self._new_device(devinfo)
|
||||
logging.debug("initial scan finished: %s", self.devices)
|
||||
if self.devices:
|
||||
self._device_status_changed(self.rstatus, (C.STATUS.CONNECTED, _OKAY))
|
||||
else:
|
||||
self._device_status_changed(self.rstatus, (C.STATUS.CONNECTED, _NO_DEVICES))
|
||||
self._update_status_text()
|
||||
self._update_status_text()
|
||||
|
||||
self.listener = EventsListener(receiver, self._events_callback)
|
||||
self.listener.start()
|
||||
else:
|
||||
self._device_status_changed(self.rstatus, (C.STATUS.UNAVAILABLE, _NO_RECEIVER))
|
||||
elif not self.listener.active:
|
||||
elif not self.listener:
|
||||
self.listener = None
|
||||
self._device_status_changed(self.rstatus, (C.STATUS.UNAVAILABLE, _NO_RECEIVER))
|
||||
self.devices.clear()
|
||||
|
||||
if self.active:
|
||||
update_icon = True
|
||||
if self.listener and self.devices:
|
||||
update_icon &= self._check_old_statuses()
|
||||
if self.listener:
|
||||
if self.devices:
|
||||
update_icon = self._check_old_statuses()
|
||||
else:
|
||||
update_icon = self._device_status_changed(self.rstatus, (C.STATUS.CONNECTED, _NO_DEVICES))
|
||||
else:
|
||||
update_icon = self._device_status_changed(self.rstatus, (C.STATUS.UNAVAILABLE, _NO_RECEIVER))
|
||||
|
||||
if update_icon:
|
||||
self._update_status_text()
|
||||
|
||||
if self.active:
|
||||
if update_icon:
|
||||
self._update_status_text()
|
||||
time.sleep(_THREAD_SLEEP)
|
||||
|
||||
self.listener.stop()
|
||||
if self.listener:
|
||||
self.listener.stop()
|
||||
api.close(self.listener.receiver)
|
||||
self.listener = None
|
||||
self.listener = None
|
||||
|
||||
def stop(self):
|
||||
self.active = False
|
||||
self.join()
|
||||
|
||||
def full_scan(self, *args):
|
||||
if self.active and self.listener:
|
||||
updated = False
|
||||
|
||||
for devnumber in range(1, 1 + api.C.MAX_ATTACHED_DEVICES):
|
||||
devstatus = self.devices.get(devnumber)
|
||||
if devstatus:
|
||||
status = devices.request_status(devstatus, self.listener)
|
||||
updated |= self._device_status_changed(devstatus, status)
|
||||
else:
|
||||
devstatus = self._new_device(devnumber)
|
||||
updated |= devstatus is not None
|
||||
|
||||
if updated:
|
||||
self._update_status_text()
|
||||
|
||||
def _request_status(self, devstatus):
|
||||
if self.listener and devstatus:
|
||||
status = devices.request_status(devstatus, self.listener)
|
||||
|
@ -124,7 +112,7 @@ class WatcherThread(threading.Thread):
|
|||
def _check_old_statuses(self):
|
||||
updated = False
|
||||
|
||||
for devstatus in list(self.devices.values()):
|
||||
for devstatus in self.devices.values():
|
||||
if devstatus != self.rstatus:
|
||||
if time.time() - devstatus.timestamp > _STATUS_TIMEOUT:
|
||||
status = devices.ping(devstatus, self.listener)
|
||||
|
@ -137,17 +125,20 @@ class WatcherThread(threading.Thread):
|
|||
return None
|
||||
|
||||
if type(dev) == int:
|
||||
# assert self.listener
|
||||
dev = self.listener.request(api.get_device_info, dev)
|
||||
|
||||
if dev:
|
||||
devstatus = _DevStatus(*dev)
|
||||
devstatus.refresh = self._request_status
|
||||
self.devices[dev.number] = devstatus
|
||||
_l.debug("new devstatus %s", devstatus)
|
||||
self._device_status_changed(devstatus, C.STATUS.CONNECTED)
|
||||
logging.debug("new devstatus %s", devstatus)
|
||||
self._device_status_changed(self.rstatus, (C.STATUS.CONNECTED, _OKAY))
|
||||
return devstatus
|
||||
|
||||
def _events_callback(self, code, devnumber, data):
|
||||
logging.debug("%s: event (%02x %02x [%s])", time.asctime(), code, devnumber, _hexlify(data))
|
||||
# _l.debug("event %s", (code, devnumber, data))
|
||||
|
||||
updated = False
|
||||
|
||||
|
@ -160,12 +151,12 @@ class WatcherThread(threading.Thread):
|
|||
status = devices.process_event(devstatus, self.listener, data)
|
||||
updated |= self._device_status_changed(devstatus, status)
|
||||
else:
|
||||
logging.warn("unknown event code %02x", code)
|
||||
_l.warn("unknown event code %02x", code)
|
||||
elif devnumber:
|
||||
self._new_device(devnumber)
|
||||
updated = True
|
||||
else:
|
||||
logging.warn("don't know how to handle event (%02x, %02x, [%s])", code, devnumber, _hexlify(data))
|
||||
_l.warn("don't know how to handle event %s", (code, devnumber, data))
|
||||
|
||||
if updated:
|
||||
self._update_status_text()
|
||||
|
@ -196,21 +187,21 @@ class WatcherThread(threading.Thread):
|
|||
status_code = C.STATUS.UNKNOWN
|
||||
status_text = ''
|
||||
|
||||
if not (status_code == C.STATUS.CONNECTED and old_status_code > C.STATUS.CONNECTED):
|
||||
# if this is not just a ping for a device with an already known status
|
||||
devstatus.code = status_code
|
||||
devstatus.text = status_text
|
||||
logging.debug("%s: device '%s' status update %s => %s: %s", time.asctime(), devstatus.name, old_status_code, status_code, status_text)
|
||||
if ((status_code == old_status_code and status_text == devstatus.text) or
|
||||
(status_code == C.STATUS.CONNECTED and old_status_code > C.STATUS.CONNECTED)):
|
||||
# this is just successful ping for a device with an already known status
|
||||
return False
|
||||
|
||||
devstatus.code = status_code
|
||||
devstatus.text = status_text
|
||||
_l.debug("%s status update %s => %s: %s", devstatus, old_status_code, status_code, status_text)
|
||||
|
||||
if self.notify:
|
||||
if status_code < C.STATUS.CONNECTED or old_status_code < C.STATUS.CONNECTED or status_code < old_status_code:
|
||||
self._notify(devstatus.code, devstatus.name, devstatus.text)
|
||||
self.notify(devstatus.code, devstatus.name, devstatus.text)
|
||||
|
||||
return True
|
||||
|
||||
def _notify(self, *args):
|
||||
if self.notify:
|
||||
self.notify(*args)
|
||||
|
||||
def _update_status_text(self):
|
||||
last_status_text = self.status_text
|
||||
|
||||
|
|
|
@ -44,10 +44,10 @@ def _continuous_read(handle, timeout=1000):
|
|||
if __name__ == '__main__':
|
||||
import argparse
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('device', default=None,
|
||||
help='linux device to connect to')
|
||||
arg_parser.add_argument('--history', default='.hidconsole-history',
|
||||
help='history file')
|
||||
arg_parser.add_argument('device', default=None,
|
||||
help='linux device to connect to')
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
import hidapi
|
||||
|
|
|
@ -18,8 +18,8 @@ NAME = 'Wireless Solar Keyboard K750'
|
|||
#
|
||||
#
|
||||
|
||||
def _trigger_solar_charge_events(receiver, devinfo):
|
||||
return _api.request(receiver, devinfo.number,
|
||||
def _trigger_solar_charge_events(handle, devinfo):
|
||||
return _api.request(handle, devinfo.number,
|
||||
feature=_api.C.FEATURE.SOLAR_CHARGE, function=b'\x03', params=b'\x78\x01',
|
||||
features_array=devinfo.features)
|
||||
|
||||
|
|
|
@ -23,3 +23,9 @@ http://6xq.net/git/lars/lshidpp.git/plain/doc/
|
|||
from .constants import *
|
||||
from .exceptions import *
|
||||
from .api import *
|
||||
|
||||
|
||||
import logging
|
||||
logging.addLevelName(4, 'UR_TRACE')
|
||||
logging.addLevelName(5, 'UR_DEBUG')
|
||||
logging.addLevelName(6, 'UR_INFO')
|
||||
|
|
|
@ -58,13 +58,13 @@ def request(handle, devnumber, feature, function=b'\x00', params=b'', features_a
|
|||
if features_array is None:
|
||||
features_array = get_device_features(handle, devnumber)
|
||||
if features_array is None:
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) no features array available", handle, devnumber)
|
||||
_l.log(_LOG_LEVEL, "(%d) no features array available", devnumber)
|
||||
return None
|
||||
if feature in features_array:
|
||||
feature_index = _pack('!B', features_array.index(feature))
|
||||
|
||||
if feature_index is None:
|
||||
_l.warn("(%d,%d) feature <%s:%s> not supported", handle, devnumber, _hexlify(feature), C.FEATURE_NAME[feature])
|
||||
_l.warn("(%d) feature <%s:%s> not supported", devnumber, _hexlify(feature), C.FEATURE_NAME[feature])
|
||||
raise E.FeatureNotSupported(devnumber, feature)
|
||||
|
||||
return _base.request(handle, devnumber, feature_index + function, params)
|
||||
|
@ -91,7 +91,7 @@ def find_device_by_name(handle, device_name):
|
|||
|
||||
:returns: an AttachedDeviceInfo tuple, or ``None``.
|
||||
"""
|
||||
_l.log(_LOG_LEVEL, "(%d,) searching for device '%s'", handle, device_name)
|
||||
_l.log(_LOG_LEVEL, "searching for device '%s'", device_name)
|
||||
|
||||
for devnumber in range(1, 1 + C.MAX_ATTACHED_DEVICES):
|
||||
features_array = get_device_features(handle, devnumber)
|
||||
|
@ -106,7 +106,7 @@ def list_devices(handle):
|
|||
|
||||
:returns: a list of AttachedDeviceInfo tuples.
|
||||
"""
|
||||
_l.log(_LOG_LEVEL, "(%d,) listing all devices", handle)
|
||||
_l.log(_LOG_LEVEL, "listing all devices")
|
||||
|
||||
devices = []
|
||||
|
||||
|
@ -131,8 +131,8 @@ def get_device_info(handle, devnumber, device_name=None, features_array=None):
|
|||
d_type = get_device_type(handle, devnumber, features_array)
|
||||
d_name = get_device_name(handle, devnumber, features_array) if device_name is None else device_name
|
||||
d_firmware = get_device_firmware(handle, devnumber, features_array)
|
||||
devinfo = AttachedDeviceInfo(devnumber, d_type, d_name, d_firmware, features_array)
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) found device %s", handle, devnumber, devinfo)
|
||||
devinfo = AttachedDeviceInfo(handle, devnumber, d_type, d_name, d_firmware, features_array)
|
||||
_l.log(_LOG_LEVEL, "(%d) found device %s", devnumber, devinfo)
|
||||
return devinfo
|
||||
|
||||
|
||||
|
@ -141,7 +141,7 @@ def get_feature_index(handle, devnumber, feature):
|
|||
|
||||
:returns: An int, or ``None`` if the feature is not available.
|
||||
"""
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) get feature index <%s:%s>", handle, devnumber, _hexlify(feature), C.FEATURE_NAME[feature])
|
||||
_l.log(_LOG_LEVEL, "(%d) get feature index <%s:%s>", devnumber, _hexlify(feature), C.FEATURE_NAME[feature])
|
||||
if len(feature) != 2:
|
||||
raise ValueError("invalid feature <%s>: it must be a two-byte string" % feature)
|
||||
|
||||
|
@ -154,18 +154,18 @@ def get_feature_index(handle, devnumber, feature):
|
|||
feature_flags = ord(reply[1:2]) & 0xE0
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
if feature_flags:
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) feature <%s:%s> has index %d: %s",
|
||||
handle, devnumber, _hexlify(feature), C.FEATURE_NAME[feature], feature_index,
|
||||
_l.log(_LOG_LEVEL, "(%d) feature <%s:%s> has index %d: %s",
|
||||
devnumber, _hexlify(feature), C.FEATURE_NAME[feature], feature_index,
|
||||
','.join([C.FEATURE_FLAGS[k] for k in C.FEATURE_FLAGS if feature_flags & k]))
|
||||
else:
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) feature <%s:%s> has index %d", handle, devnumber, _hexlify(feature), C.FEATURE_NAME[feature], feature_index)
|
||||
_l.log(_LOG_LEVEL, "(%d) feature <%s:%s> has index %d", devnumber, _hexlify(feature), C.FEATURE_NAME[feature], feature_index)
|
||||
|
||||
# if feature_flags:
|
||||
# raise E.FeatureNotSupported(devnumber, feature)
|
||||
|
||||
return feature_index
|
||||
|
||||
_l.warn("(%d,%d) feature <%s:%s> not supported by the device", handle, devnumber, _hexlify(feature), C.FEATURE_NAME[feature])
|
||||
_l.warn("(%d) feature <%s:%s> not supported by the device", devnumber, _hexlify(feature), C.FEATURE_NAME[feature])
|
||||
raise E.FeatureNotSupported(devnumber, feature)
|
||||
|
||||
|
||||
|
@ -175,13 +175,13 @@ def get_device_features(handle, devnumber):
|
|||
Their position in the array is the index to be used when requesting that
|
||||
feature on the device.
|
||||
"""
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) get device features", handle, devnumber)
|
||||
_l.log(_LOG_LEVEL, "(%d) get device features", devnumber)
|
||||
|
||||
# get the index of the FEATURE_SET
|
||||
# FEATURE.ROOT should always be available for all devices
|
||||
fs_index = _base.request(handle, devnumber, C.FEATURE.ROOT, C.FEATURE.FEATURE_SET)
|
||||
if fs_index is None:
|
||||
# _l.warn("(%d,%d) FEATURE_SET not available", handle, device)
|
||||
# _l.warn("(%d) FEATURE_SET not available", device)
|
||||
return None
|
||||
fs_index = fs_index[:1]
|
||||
|
||||
|
@ -193,11 +193,11 @@ def get_device_features(handle, devnumber):
|
|||
if not features_count:
|
||||
# this can happen if the device disappeard since the fs_index request
|
||||
# otherwise we should get at least a count of 1 (the FEATURE_SET we've just used above)
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) no features available?!", handle, devnumber)
|
||||
_l.log(_LOG_LEVEL, "(%d) no features available?!", devnumber)
|
||||
return None
|
||||
|
||||
features_count = ord(features_count[:1])
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) found %d features", handle, devnumber, features_count)
|
||||
_l.log(_LOG_LEVEL, "(%d) found %d features", devnumber, features_count)
|
||||
|
||||
features = [None] * 0x20
|
||||
for index in range(1, 1 + features_count):
|
||||
|
@ -210,11 +210,11 @@ def get_device_features(handle, devnumber):
|
|||
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
if feature_flags:
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) feature <%s:%s> at index %d: %s",
|
||||
handle, devnumber, _hexlify(feature), C.FEATURE_NAME[feature], index,
|
||||
_l.log(_LOG_LEVEL, "(%d) feature <%s:%s> at index %d: %s",
|
||||
devnumber, _hexlify(feature), C.FEATURE_NAME[feature], index,
|
||||
','.join([C.FEATURE_FLAGS[k] for k in C.FEATURE_FLAGS if feature_flags & k]))
|
||||
else:
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) feature <%s:%s> at index %d", handle, devnumber, _hexlify(feature), C.FEATURE_NAME[feature], index)
|
||||
_l.log(_LOG_LEVEL, "(%d) feature <%s:%s> at index %d", devnumber, _hexlify(feature), C.FEATURE_NAME[feature], index)
|
||||
|
||||
features[0] = C.FEATURE.ROOT
|
||||
while features[-1] is None:
|
||||
|
@ -258,7 +258,7 @@ def get_device_firmware(handle, devnumber, features_array=None):
|
|||
fw_info = _makeFirmwareInfo(level=fw_level, type=C.FIRMWARE_TYPE[-1])
|
||||
|
||||
fw.append(fw_info)
|
||||
_l.log(_LOG_LEVEL, "(%d:%d) firmware %s", handle, devnumber, fw_info)
|
||||
_l.log(_LOG_LEVEL, "(%d) firmware %s", devnumber, fw_info)
|
||||
return fw
|
||||
|
||||
|
||||
|
@ -272,7 +272,7 @@ def get_device_type(handle, devnumber, features_array=None):
|
|||
d_type = request(handle, devnumber, C.FEATURE.NAME, function=b'\x20', features_array=features_array)
|
||||
if d_type:
|
||||
d_type = ord(d_type[:1])
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) device type %d = %s", handle, devnumber, d_type, C.DEVICE_TYPE[d_type])
|
||||
_l.log(_LOG_LEVEL, "(%d) device type %d = %s", devnumber, d_type, C.DEVICE_TYPE[d_type])
|
||||
return C.DEVICE_TYPE[d_type]
|
||||
|
||||
|
||||
|
@ -297,7 +297,7 @@ def get_device_name(handle, devnumber, features_array=None):
|
|||
break
|
||||
|
||||
d_name = d_name.decode('ascii')
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) device name %s", handle, devnumber, d_name)
|
||||
_l.log(_LOG_LEVEL, "(%d) device name %s", devnumber, d_name)
|
||||
return d_name
|
||||
|
||||
|
||||
|
@ -309,7 +309,8 @@ def get_device_battery_level(handle, devnumber, features_array=None):
|
|||
battery = request(handle, devnumber, C.FEATURE.BATTERY, features_array=features_array)
|
||||
if battery:
|
||||
discharge, dischargeNext, status = _unpack('!BBB', battery[:3])
|
||||
_l.log(_LOG_LEVEL, "(%d:%d) battery %d%% charged, next level %d%% charge, status %d = %s", discharge, dischargeNext, status, C.BATTERY_STATUSE[status])
|
||||
_l.log(_LOG_LEVEL, "(%d) battery %d%% charged, next level %d%% charge, status %d = %s",
|
||||
devnumber, discharge, dischargeNext, status, C.BATTERY_STATUSE[status])
|
||||
return (discharge, dischargeNext, C.BATTERY_STATUS[status])
|
||||
|
||||
|
||||
|
|
|
@ -10,11 +10,10 @@ from binascii import hexlify as _hexlify
|
|||
from . import constants as C
|
||||
from . import exceptions as E
|
||||
|
||||
from . import unhandled as _unhandled
|
||||
import hidapi as _hid
|
||||
|
||||
|
||||
_LOG_LEVEL = 5
|
||||
_LOG_LEVEL = 4
|
||||
_l = logging.getLogger('lur.base')
|
||||
|
||||
#
|
||||
|
@ -45,6 +44,31 @@ DEFAULT_TIMEOUT = 1000
|
|||
#
|
||||
#
|
||||
|
||||
def _logdebug_hook(reply_code, devnumber, data):
|
||||
"""Default unhandled hook, logs the reply as DEBUG."""
|
||||
_l.debug("UNHANDLED %s", (reply_code, devnumber, reply_code, data))
|
||||
|
||||
|
||||
"""The function that will be called on unhandled incoming events.
|
||||
|
||||
The hook must be a function with the signature: ``_(int, int, str)``, where
|
||||
the parameters are: (reply_code, devnumber, data).
|
||||
|
||||
This hook will only be called by the request() function, when it receives
|
||||
replies that do not match the requested feature call. As such, it is not
|
||||
suitable for intercepting broadcast events from the device (e.g. special
|
||||
keys being pressed, battery charge events, etc), at least not in a timely
|
||||
manner. However, these events *may* be delivered here if they happen while
|
||||
doing a feature call to the device.
|
||||
|
||||
The default implementation logs the unhandled reply as DEBUG.
|
||||
"""
|
||||
unhandled_hook = _logdebug_hook
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def list_receiver_devices():
|
||||
"""List all the Linux devices exposed by the UR attached to the machine."""
|
||||
# (Vendor ID, Product ID) = ('Logitech', 'Unifying Receiver')
|
||||
|
@ -72,7 +96,7 @@ def try_open(path):
|
|||
_l.log(_LOG_LEVEL, "[%s] open failed", path)
|
||||
return None
|
||||
|
||||
_l.log(_LOG_LEVEL, "[%s] receiver handle (%d,)", path, receiver_handle)
|
||||
_l.log(_LOG_LEVEL, "[%s] receiver handle 0x%x", path, receiver_handle)
|
||||
# ping on device id 0 (always an error)
|
||||
_hid.write(receiver_handle, b'\x10\x00\x00\x10\x00\x00\xAA')
|
||||
|
||||
|
@ -82,18 +106,18 @@ def try_open(path):
|
|||
if reply:
|
||||
if reply[:4] == b'\x10\x00\x8F\x00':
|
||||
# 'device 0 unreachable' is the expected reply from a valid receiver handle
|
||||
_l.log(_LOG_LEVEL, "[%s] success: handle (%d,)", path, receiver_handle)
|
||||
_l.log(_LOG_LEVEL, "[%s] success: handle %x", path, receiver_handle)
|
||||
return receiver_handle
|
||||
|
||||
# any other replies are ignored, and will assume this is the wrong Linux device
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
if reply == b'\x01\x00\x00\x00\x00\x00\x00\x00':
|
||||
# no idea what this is, but it comes up occasionally
|
||||
_l.log(_LOG_LEVEL, "[%s] (%d,) mistery reply [%s]", path, receiver_handle, _hexlify(reply))
|
||||
_l.log(_LOG_LEVEL, "[%s] %x mistery reply [%s]", path, receiver_handle, _hexlify(reply))
|
||||
else:
|
||||
_l.log(_LOG_LEVEL, "[%s] (%d,) unknown reply [%s]", path, receiver_handle, _hexlify(reply))
|
||||
_l.log(_LOG_LEVEL, "[%s] %x unknown reply [%s]", path, receiver_handle, _hexlify(reply))
|
||||
else:
|
||||
_l.log(_LOG_LEVEL, "[%s] (%d,) no reply", path, receiver_handle)
|
||||
_l.log(_LOG_LEVEL, "[%s] %x no reply", path, receiver_handle)
|
||||
|
||||
close(receiver_handle)
|
||||
|
||||
|
@ -118,10 +142,10 @@ def close(handle):
|
|||
if handle:
|
||||
try:
|
||||
_hid.close(handle)
|
||||
_l.log(_LOG_LEVEL, "(%d,) closed", handle)
|
||||
_l.log(_LOG_LEVEL, "%x closed", handle)
|
||||
return True
|
||||
except:
|
||||
_l.exception("(%d,) closing", handle)
|
||||
_l.exception("%x closing", handle)
|
||||
|
||||
return False
|
||||
|
||||
|
@ -147,10 +171,10 @@ def write(handle, devnumber, data):
|
|||
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
hexs = _hexlify(wdata)
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) <= w[%s %s %s %s]", handle, 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):
|
||||
_l.warn("(%d,%d) write failed, assuming receiver no longer available", handle, devnumber)
|
||||
_l.warn("(%d) write failed, assuming receiver %x no longer available", devnumber, handle)
|
||||
close(handle)
|
||||
raise E.NoReceiver
|
||||
|
||||
|
@ -173,23 +197,23 @@ def read(handle, timeout=DEFAULT_TIMEOUT):
|
|||
"""
|
||||
data = _hid.read(handle, _MAX_REPLY_SIZE * 2, timeout)
|
||||
if data is None:
|
||||
_l.warn("(%d,*) read failed, assuming receiver no longer available", handle)
|
||||
_l.warn("(-) read failed, assuming receiver %x no longer available", handle)
|
||||
close(handle)
|
||||
raise E.NoReceiver
|
||||
|
||||
if data:
|
||||
if len(data) < _MIN_REPLY_SIZE:
|
||||
_l.warn("(%d,*) => r[%s] read packet too short: %d bytes", handle, _hexlify(data), len(data))
|
||||
_l.warn("(%d) => r[%s] read packet too short: %d bytes", ord(data[1:2]), _hexlify(data), len(data))
|
||||
if len(data) > _MAX_REPLY_SIZE:
|
||||
_l.warn("(%d,*) => r[%s] read packet too long: %d bytes", handle, _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):
|
||||
hexs = _hexlify(data)
|
||||
_l.log(_LOG_LEVEL, "(%d,*) => r[%s %s %s %s]", handle, 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])
|
||||
devnumber = ord(data[1:2])
|
||||
return code, devnumber, data[2:]
|
||||
|
||||
# _l.log(_LOG_LEVEL, "(%d,*) => r[]", handle)
|
||||
# _l.log(_LOG_LEVEL, "(-) => r[]", handle)
|
||||
|
||||
|
||||
def request(handle, devnumber, feature_index_function, params=b'', features_array=None):
|
||||
|
@ -210,7 +234,7 @@ def request(handle, devnumber, feature_index_function, params=b'', features_arra
|
|||
:raisees FeatureCallError: if the feature call replied with an error.
|
||||
"""
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) request {%s} params [%s]", handle, devnumber, _hexlify(feature_index_function), _hexlify(params))
|
||||
_l.log(_LOG_LEVEL, "(%d) request {%s} params [%s]", devnumber, _hexlify(feature_index_function), _hexlify(params))
|
||||
if len(feature_index_function) != 2:
|
||||
raise ValueError('invalid feature_index_function {%s}: it must be a two-byte string' % _hexlify(feature_index_function))
|
||||
|
||||
|
@ -230,26 +254,27 @@ def request(handle, devnumber, feature_index_function, params=b'', features_arra
|
|||
|
||||
if reply_devnumber != devnumber:
|
||||
# this message not for the device we're interested in
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) request got reply for unexpected device %d: [%s]", handle, devnumber, reply_devnumber, _hexlify(reply_data))
|
||||
_l.log(_LOG_LEVEL, "(%d) request got reply for unexpected device %d: [%s]", devnumber, reply_devnumber, _hexlify(reply_data))
|
||||
# worst case scenario, this is a reply for a concurrent request
|
||||
# on this receiver
|
||||
_unhandled._publish(reply_code, reply_devnumber, reply_data)
|
||||
if unhandled_hook:
|
||||
unhandled_hook(reply_code, reply_devnumber, reply_data)
|
||||
continue
|
||||
|
||||
if reply_code == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == feature_index_function:
|
||||
# device not present
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) request ping failed on {%s} call: [%s]", handle, 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), _hexlify(reply_data))
|
||||
return None
|
||||
|
||||
if reply_code == 0x10 and reply_data[:1] == b'\x8F':
|
||||
# device not present
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) request ping failed: [%s]", handle, devnumber, _hexlify(reply_data))
|
||||
_l.log(_LOG_LEVEL, "(%d) request ping failed: [%s]", devnumber, _hexlify(reply_data))
|
||||
return None
|
||||
|
||||
if reply_code == 0x11 and reply_data[0] == b'\xFF' and reply_data[1:3] == feature_index_function:
|
||||
# an error returned from the device
|
||||
error_code = ord(reply_data[3])
|
||||
_l.warn("(%d,%d) request feature call error %d = %s: %s", handle, devnumber, error_code, C.ERROR_NAME[error_code], _hexlify(reply_data))
|
||||
_l.warn("(%d) request feature call error %d = %s: %s", devnumber, error_code, C.ERROR_NAME[error_code], _hexlify(reply_data))
|
||||
feature_index = ord(feature_index_function[:1])
|
||||
feature_function = feature_index_function[1:2]
|
||||
feature = None if features_array is None else features_array[feature_index]
|
||||
|
@ -257,8 +282,9 @@ def request(handle, devnumber, feature_index_function, params=b'', features_arra
|
|||
|
||||
if reply_code == 0x11 and reply_data[:2] == feature_index_function:
|
||||
# a matching reply
|
||||
# _l.log(_LOG_LEVEL, "(%d,%d) matched reply with feature-index-function [%s]", handle, devnumber, _hexlify(reply_data[2:]))
|
||||
# _l.log(_LOG_LEVEL, "(%d) matched reply with feature-index-function [%s]", devnumber, _hexlify(reply_data[2:]))
|
||||
return reply_data[2:]
|
||||
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) unmatched reply {%s} (expected {%s})", handle, devnumber, _hexlify(reply_data[:2]), _hexlify(feature_index_function))
|
||||
_unhandled._publish(reply_code, reply_devnumber, reply_data)
|
||||
_l.log(_LOG_LEVEL, "(%d) unmatched reply {%s} (expected {%s})", devnumber, _hexlify(reply_data[:2]), _hexlify(feature_index_function))
|
||||
if unhandled_hook:
|
||||
unhandled_hook(reply_code, reply_devnumber, reply_data)
|
||||
|
|
|
@ -22,6 +22,7 @@ from collections import namedtuple
|
|||
|
||||
"""Tuple returned by list_devices and find_device_by_name."""
|
||||
AttachedDeviceInfo = namedtuple('AttachedDeviceInfo', [
|
||||
'handle',
|
||||
'number',
|
||||
'type',
|
||||
'name',
|
||||
|
|
|
@ -5,40 +5,52 @@
|
|||
import logging
|
||||
import threading
|
||||
from time import sleep as _sleep
|
||||
from binascii import hexlify as _hexlify
|
||||
|
||||
from . import base as _base
|
||||
from . import exceptions as E
|
||||
# from . import unhandled as _unhandled
|
||||
|
||||
# for both Python 2 and 3
|
||||
try:
|
||||
import Queue as queue
|
||||
except ImportError:
|
||||
import queue
|
||||
|
||||
|
||||
_LOG_LEVEL = 5
|
||||
_l = logging.getLogger('lur.listener')
|
||||
|
||||
|
||||
_READ_EVENT_TIMEOUT = int(_base.DEFAULT_TIMEOUT / 5) # ms
|
||||
_IDLE_SLEEP = _base.DEFAULT_TIMEOUT / 2 # ms
|
||||
_READ_EVENT_TIMEOUT = int(_base.DEFAULT_TIMEOUT / 4) # ms
|
||||
_IDLE_SLEEP = _base.DEFAULT_TIMEOUT / 4 # ms
|
||||
|
||||
|
||||
def _callback_caller(listener, callback):
|
||||
# _l.log(_LOG_LEVEL, "%s starting callback caller", listener)
|
||||
while listener._active:
|
||||
event = listener.events.get()
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
_l.log(_LOG_LEVEL, "%s delivering event %s", listener, event)
|
||||
callback.__call__(*event)
|
||||
# _l.log(_LOG_LEVEL, "%s stopped callback caller", listener)
|
||||
|
||||
|
||||
class EventsListener(threading.Thread):
|
||||
"""Listener thread for events from the Unifying Receiver.
|
||||
|
||||
Incoming events (reply_code, devnumber, data) will be passed to the callback
|
||||
function. The callback is called in the listener thread, so for best results
|
||||
it should return as fast as possible.
|
||||
function. The callback is called in a separate thread.
|
||||
|
||||
While this listener is running, you should use the request() method to make
|
||||
regular UR API calls, otherwise the replies may be captured by the listener
|
||||
and delivered as events to the callback. As an exception, you can make API
|
||||
calls in the events callback.
|
||||
regular UR API calls, otherwise the replies are very likely to be captured
|
||||
by the listener and delivered as events to the callback. As an exception,
|
||||
you can make API calls in the events callback.
|
||||
"""
|
||||
def __init__(self, receiver, events_callback):
|
||||
super(EventsListener, self).__init__(name='Unifying_Receiver_Listener_' + hex(receiver))
|
||||
super(EventsListener, self).__init__(group='Unifying Receiver', name='Events-%x' % receiver)
|
||||
self.daemon = True
|
||||
self.active = False
|
||||
self._active = False
|
||||
|
||||
self.receiver = receiver
|
||||
self.callback = events_callback
|
||||
|
||||
self.task = None
|
||||
self.task_processing = threading.Lock()
|
||||
|
@ -46,40 +58,50 @@ class EventsListener(threading.Thread):
|
|||
self.task_reply = None
|
||||
self.task_done = threading.Event()
|
||||
|
||||
self.events = queue.Queue(32)
|
||||
|
||||
self.event_caller = threading.Thread(group='Unifying Receiver', name='Callback-%x' % receiver, target=_callback_caller, args=(self, events_callback))
|
||||
self.event_caller.daemon = True
|
||||
|
||||
self.__str_cached = 'Events(%x)' % self.receiver
|
||||
|
||||
def run(self):
|
||||
self.active = True
|
||||
_l.log(_LOG_LEVEL, "(%d) starting", self.receiver)
|
||||
self._active = True
|
||||
_l.log(_LOG_LEVEL, "%s started", self)
|
||||
|
||||
# last_hook = _unhandled.hook
|
||||
# _unhandled.hook = self.callback
|
||||
self.__str_cached = 'Events(%x:active)' % self.receiver
|
||||
self.event_caller.start()
|
||||
|
||||
while self.active:
|
||||
last_hook = _base.unhandled_hook
|
||||
_base.unhandled_hook = self._unhandled
|
||||
|
||||
while self._active:
|
||||
try:
|
||||
# _l.log(_LOG_LEVEL, "(%d) reading next event", self.receiver)
|
||||
event = _base.read(self.receiver, _READ_EVENT_TIMEOUT)
|
||||
except E.NoReceiver:
|
||||
_l.warn("(%d) receiver disconnected", self.receiver)
|
||||
self.active = False
|
||||
break
|
||||
_l.warn("%s receiver disconnected", self)
|
||||
self._active = False
|
||||
|
||||
if self.active:
|
||||
if self._active:
|
||||
if event:
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
_l.log(_LOG_LEVEL, "(%d) got event (%02x %02x [%s])", self.receiver, event[0], event[1], _hexlify(event[2]))
|
||||
self.callback.__call__(*event)
|
||||
elif self.task is None:
|
||||
# _l.log(_LOG_LEVEL, "(%d) idle sleep", self.receiver)
|
||||
# _l.log(_LOG_LEVEL, "%s queueing event %s", self, event)
|
||||
self.events.put(event)
|
||||
|
||||
if self.task is None:
|
||||
# _l.log(_LOG_LEVEL, "%s idle sleep", self)
|
||||
_sleep(_IDLE_SLEEP / 1000.0)
|
||||
else:
|
||||
self.task_reply = self._make_request(*self.task)
|
||||
self.task_done.set()
|
||||
|
||||
# _unhandled.hook = last_hook
|
||||
self.__str_cached = 'Events(%x)' % self.receiver
|
||||
|
||||
_base.unhandled_hook = last_hook
|
||||
|
||||
def stop(self):
|
||||
"""Tells the listener to stop as soon as possible."""
|
||||
_l.log(_LOG_LEVEL, "(%d) stopping", self.receiver)
|
||||
self.active = False
|
||||
_l.log(_LOG_LEVEL, "%s stopping", self)
|
||||
self._active = False
|
||||
|
||||
def request(self, api_function, *args, **kwargs):
|
||||
"""Make an UR API request.
|
||||
|
@ -88,7 +110,8 @@ class EventsListener(threading.Thread):
|
|||
other args and kwargs will follow.
|
||||
"""
|
||||
# if _l.isEnabledFor(_LOG_LEVEL):
|
||||
# _l.log(_LOG_LEVEL, "(%d) request '%s.%s' with %s, %s", self.receiver, api_function.__module__, api_function.__name__, args, kwargs)
|
||||
# _l.log(_LOG_LEVEL, "%s request '%s.%s' with %s, %s", self, api_function.__module__, api_function.__name__, args, kwargs)
|
||||
|
||||
self.task_processing.acquire()
|
||||
self.task_done.clear()
|
||||
self.task = (api_function, args, kwargs)
|
||||
|
@ -99,18 +122,29 @@ class EventsListener(threading.Thread):
|
|||
self.task_processing.release()
|
||||
|
||||
# if _l.isEnabledFor(_LOG_LEVEL):
|
||||
# _l.log(_LOG_LEVEL, "(%d) request '%s.%s' => [%s]", self.receiver, api_function.__module__, api_function.__name__, _hexlify(reply))
|
||||
# _l.log(_LOG_LEVEL, "%s request '%s.%s' => %s", self, api_function.__module__, api_function.__name__, repr(reply))
|
||||
if isinstance(reply, Exception):
|
||||
raise reply
|
||||
return reply
|
||||
|
||||
def _make_request(self, api_function, args, kwargs):
|
||||
if _l.isEnabledFor(_LOG_LEVEL):
|
||||
_l.log(_LOG_LEVEL, "(%d) calling '%s.%s' with %s, %s", self.receiver, api_function.__module__, api_function.__name__, args, kwargs)
|
||||
_l.log(_LOG_LEVEL, "%s calling '%s.%s' with %s, %s", self, api_function.__module__, api_function.__name__, args, kwargs)
|
||||
try:
|
||||
return api_function.__call__(self.receiver, *args, **kwargs)
|
||||
except E.NoReceiver as nr:
|
||||
self.task_reply = nr
|
||||
self.active = False
|
||||
self._active = False
|
||||
except Exception as e:
|
||||
self.task_reply = e
|
||||
|
||||
def _unhandled(self, reply_code, devnumber, data):
|
||||
event = (reply_code, devnumber, data)
|
||||
_l.log(_LOG_LEVEL, "%s queueing unhandled event %s", self, event)
|
||||
self.events.put(event)
|
||||
|
||||
def __str__(self):
|
||||
return self.__str_cached
|
||||
|
||||
def __nonzero__(self):
|
||||
return self._active
|
||||
|
|
|
@ -8,7 +8,6 @@ from binascii import hexlify
|
|||
from .. import base
|
||||
from ..exceptions import *
|
||||
from ..constants import *
|
||||
from .. import unhandled
|
||||
|
||||
|
||||
class Test_UR_Base(unittest.TestCase):
|
||||
|
@ -157,7 +156,7 @@ class Test_UR_Base(unittest.TestCase):
|
|||
global received_unhandled
|
||||
received_unhandled = (code, device, data)
|
||||
|
||||
unhandled.hook = _unhandled
|
||||
base.unhandled_hook = _unhandled
|
||||
base.write(self.handle, self.device, FEATURE.ROOT + FEATURE.FEATURE_SET)
|
||||
reply = base.request(self.handle, self.device, fs_index + b'\x00')
|
||||
self.assertIsNotNone(reply, "request returned None reply")
|
||||
|
@ -165,7 +164,7 @@ class Test_UR_Base(unittest.TestCase):
|
|||
self.assertIsNotNone(received_unhandled, "extra message not received by unhandled hook")
|
||||
|
||||
received_unhandled = None
|
||||
unhandled.hook = None
|
||||
base.unhandled_hook = None
|
||||
base.write(self.handle, self.device, FEATURE.ROOT + FEATURE.FEATURE_SET)
|
||||
reply = base.request(self.handle, self.device, fs_index + b'\x00')
|
||||
self.assertIsNotNone(reply, "request returned None reply")
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
#
|
||||
# Optional hook for unhandled data packets received while talking to the UR.
|
||||
# These are usually broadcast events received from the attached devices.
|
||||
#
|
||||
|
||||
import logging
|
||||
from binascii import hexlify as _hexlify
|
||||
|
||||
|
||||
def _logdebug_hook(reply_code, devnumber, data):
|
||||
"""Default unhandled hook, logs the reply as DEBUG."""
|
||||
_l = logging.getLogger('lur.unhandled')
|
||||
_l.debug("UNHANDLED (,%d) code 0x%02x data [%s]", devnumber, reply_code, _hexlify(data))
|
||||
|
||||
|
||||
"""The function that will be called on unhandled incoming events.
|
||||
|
||||
The hook must be a function with the signature: ``_(int, int, str)``, where
|
||||
the parameters are: (reply_code, devnumber, data).
|
||||
|
||||
This hook will only be called by the request() function, when it receives
|
||||
replies that do not match the requested feature call. As such, it is not
|
||||
suitable for intercepting broadcast events from the device (e.g. special
|
||||
keys being pressed, battery charge events, etc), at least not in a timely
|
||||
manner. However, these events *may* be delivered here if they happen while
|
||||
doing a feature call to the device.
|
||||
|
||||
The default implementation logs the unhandled reply as DEBUG.
|
||||
"""
|
||||
hook = _logdebug_hook
|
||||
|
||||
|
||||
def _publish(reply_code, devnumber, data):
|
||||
"""Delivers a reply to the unhandled hook, if any."""
|
||||
if hook is not None:
|
||||
hook.__call__(reply_code, devnumber, data)
|
|
@ -1,6 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd `dirname "$0"`
|
||||
export LD_LIBRARY_PATH=$PWD
|
||||
cd -P `dirname "$0"`
|
||||
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/native/`uname -m`
|
||||
|
||||
exec python -m unittest discover -v "$@"
|
||||
|
|
6
solaar
6
solaar
|
@ -1,10 +1,12 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd `dirname "$0"`
|
||||
cd -P `dirname "$0"`
|
||||
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib/native/`uname -m`
|
||||
export PYTHONPATH=$PWD:$PWD/lib
|
||||
export XDG_DATA_DIRS=$PWD/resources:$XDG_DATA_DIRS
|
||||
|
||||
cd -
|
||||
|
||||
exec python -OO solaar.py "$@"
|
||||
# exec python -OO -m profile -o $TMPDIR/profile.log solaar.py "$@"
|
||||
|
|
|
@ -22,7 +22,8 @@ if __name__ == '__main__':
|
|||
|
||||
import logging
|
||||
log_level = logging.root.level - 10 * args.verbose
|
||||
logging.basicConfig(level=log_level if log_level > 0 else 1)
|
||||
log_format='%(asctime)s %(levelname)8s [%(threadName)s] %(name)s: %(message)s'
|
||||
logging.basicConfig(level=log_level if log_level > 0 else 1, format=log_format)
|
||||
|
||||
import app
|
||||
app.run(args)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd `dirname "$0"`/..
|
||||
cd -P `dirname "$0"`/..
|
||||
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib/native/`uname -m`
|
||||
export PYTHONPATH=$PWD/lib
|
||||
|
||||
cd -
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd `dirname "$0"`/../lib
|
||||
cd -P `dirname "$0"`/..
|
||||
|
||||
export LD_LIBRARY_PATH=$PWD
|
||||
export PYTHONPATH=$PWD
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib/native/`uname -m`
|
||||
export PYTHONPATH=$PWD/lib
|
||||
|
||||
cd -
|
||||
|
||||
exec python -OO -m cli.ur_scanner "$@"
|
||||
|
|
Loading…
Reference in New Issue