clean-up and simpler monitoring of receiver state

This commit is contained in:
Daniel Pavel 2012-11-01 13:47:11 +02:00
parent a8a72f7ae5
commit 1d8ac27614
12 changed files with 74 additions and 66 deletions

View File

@ -10,7 +10,7 @@ from logitech.unifying_receiver import base as _base
state = None
class State(object):
TICK = 300
TICK = 400
PAIR_TIMEOUT = 60 * 1000 / TICK
def __init__(self, listener):

View File

@ -258,7 +258,7 @@ class ReceiverListener(_EventsListener):
self.events_filter = None
self.events_handler = None
self.status_changed_callback = status_changed_callback or (lambda reason=None, urgent=False: None)
self.status_changed_callback = status_changed_callback or (lambda reason, urgent=False: None)
receiver.kind = receiver.name
receiver.devices = {}
@ -354,7 +354,7 @@ class ReceiverListener(_EventsListener):
return True
def __str__(self):
return '<ReceiverListener(%s,%d)>' % (self.path, self.receiver.status)
return '<ReceiverListener(%s,%d)>' % (self.receiver.path, self.receiver.status)
@classmethod
def open(self, status_changed_callback=None):

View File

@ -1,16 +1,14 @@
#!/usr/bin/env python
APPNAME = 'Solaar'
__author__ = "Daniel Pavel <daniel.pavel@gmail.com>"
__version__ = '0.5'
__version__ = '0.6'
__license__ = "GPL"
#
#
#
APPNAME = 'Solaar'
def _parse_arguments():
import argparse
arg_parser = argparse.ArgumentParser(prog=APPNAME.lower())
@ -31,7 +29,7 @@ def _parse_arguments():
args = arg_parser.parse_args()
import logging
log_level = logging.ERROR - 10 * args.verbose
log_level = logging.WARNING - 10 * args.verbose
log_format='%(asctime)s %(levelname)8s [%(threadName)s] %(name)s: %(message)s'
logging.basicConfig(level=max(log_level, logging.DEBUG), format=log_format)
@ -66,6 +64,7 @@ if __name__ == '__main__':
window.present()
import pairing
from gi.repository import Gtk, GObject
listener = None
notify_missing = True
@ -74,8 +73,11 @@ if __name__ == '__main__':
global listener
receiver = DUMMY if listener is None else listener.receiver
ui.update(receiver, icon, window, reason)
if ui.notify.available and reason and urgent:
ui.notify.show(reason or receiver)
if ui.notify.available and urgent:
ui.notify.show(reason)
if not listener:
listener = None
GObject.timeout_add(3000, check_for_listener)
def check_for_listener():
global listener, notify_missing
@ -85,18 +87,14 @@ if __name__ == '__main__':
pairing.state = None
if notify_missing:
status_changed(DUMMY, True)
ui.notify.show(DUMMY)
notify_missing = False
else:
return True
# print ("opened receiver", listener, listener.receiver)
pairing.state = pairing.State(listener)
notify_missing = True
status_changed(listener.receiver, True)
return True
from gi.repository import Gtk, GObject
GObject.timeout_add(5000, check_for_listener)
check_for_listener()
Gtk.main()

View File

@ -27,6 +27,8 @@ def icon_file(name):
def find_children(container, *child_names):
assert container is not None
def _iterate_children(widget, names, result, count):
wname = widget.get_name()
if wname in names:

View File

@ -1,3 +1,6 @@
#
#
#
from gi.repository import Gtk

View File

@ -183,7 +183,7 @@ def create(title, name, max_devices, systray=False):
geometry = Gdk.Geometry()
geometry.min_width = 320
geometry.min_height = 20
geometry.min_height = 32
window.set_geometry_hints(vbox, geometry, Gdk.WindowHints.MIN_SIZE)
window.set_resizable(False)
@ -206,10 +206,10 @@ def _update_receiver_box(frame, receiver):
label.set_text(receiver.status_text or '')
if receiver.status < STATUS.CONNECTED:
frame._device = None
toolbar.set_sensitive(False)
toolbar.get_children()[0].set_active(False)
info_label.set_text('')
frame._device = None
else:
toolbar.set_sensitive(True)
frame._device = receiver
@ -274,20 +274,25 @@ def _update_device_box(frame, dev):
frame.set_visible(True)
def update(window, receiver, reason):
print ("update", receiver, receiver.status, reason)
def update(window, receiver, device):
print ("update", receiver, receiver.status, device)
window.set_icon_name(ui.appicon(receiver.status))
vbox = window.get_child()
controls = list(vbox.get_children())
frames = list(vbox.get_children())
if reason == receiver:
_update_receiver_box(controls[0], receiver)
else:
frame = controls[reason.number]
if reason.status == STATUS.UNPAIRED:
if id(device) == id(receiver):
_update_receiver_box(frames[0], receiver)
if receiver.status < STATUS.CONNECTED:
for frame in frames[1:]:
frame.set_visible(False)
frame.set_name(_PLACEHOLDER)
frame._device = None
else:
_update_device_box(frame, reason)
frame = frames[device.number]
if device.status == STATUS.UNPAIRED:
frame.set_visible(False)
frame.set_name(_PLACEHOLDER)
frame._device = None
else:
_update_device_box(frame, device)

View File

@ -68,7 +68,7 @@ try:
logging.exception("showing %s", n)
except ImportError:
logging.warn("Notify not found in gi.repository, desktop notifications are disabled")
logging.warn("desktop notifications disabled")
available = False
init = lambda app_title: False
uninit = lambda: None

View File

@ -2,7 +2,7 @@
#
#
import logging
# import logging
from gi.repository import (Gtk, GObject)
import ui
@ -31,17 +31,17 @@ def _device_confirmed(entry, _2, trigger, assistant, page):
def _finish(assistant):
logging.debug("finish %s", assistant)
# logging.debug("finish %s", assistant)
assistant.destroy()
def _cancel(assistant, state):
logging.debug("cancel %s", assistant)
# logging.debug("cancel %s", assistant)
state.stop_scan()
_finish(assistant)
def _prepare(assistant, page, state):
index = assistant.get_current_page()
logging.debug("prepare %s %d %s", assistant, index, page)
# logging.debug("prepare %s %d %s", assistant, index, page)
if index == 0:
state.reset()

View File

@ -231,7 +231,7 @@ def get_device(handle, devnumber, features=None):
"""
if ping(handle, devnumber):
devinfo = PairedDevice(handle, devnumber)
# _log.debug("(%d) found device %s", devnumber, devinfo)
# _log.debug("found device %s", devinfo)
return devinfo
@ -240,7 +240,7 @@ def get_feature_index(handle, devnumber, feature):
:returns: An int, or ``None`` if the feature is not available.
"""
# _log.debug("(%d) get feature index <%s:%s>", devnumber, _hex(feature), FEATURE_NAME[feature])
# _log.debug("device %d get feature index <%s:%s>", devnumber, _hex(feature), FEATURE_NAME[feature])
if len(feature) != 2:
raise ValueError("invalid feature <%s>: it must be a two-byte string" % feature)
@ -251,11 +251,11 @@ def get_feature_index(handle, devnumber, feature):
if feature_index:
# feature_flags = ord(reply[1:2]) & 0xE0
# if feature_flags:
# _log.debug("(%d) feature <%s:%s> has index %d: %s",
# _log.debug("device %d feature <%s:%s> has index %d: %s",
# devnumber, _hex(feature), FEATURE_NAME[feature], feature_index,
# ','.join([FEATURE_FLAGS[k] for k in FEATURE_FLAGS if feature_flags & k]))
# else:
# _log.debug("(%d) feature <%s:%s> has index %d", devnumber, _hex(feature), FEATURE_NAME[feature], feature_index)
# _log.debug("device %d feature <%s:%s> has index %d", devnumber, _hex(feature), FEATURE_NAME[feature], feature_index)
# only consider active and supported features?
# if feature_flags:
@ -263,7 +263,7 @@ def get_feature_index(handle, devnumber, feature):
return feature_index
_log.warn("(%d) feature <%s:%s> not supported by the device", devnumber, _hex(feature), FEATURE_NAME[feature])
_log.warn("device %d feature <%s:%s> not supported by the device", devnumber, _hex(feature), FEATURE_NAME[feature])
raise _FeatureNotSupported(devnumber, feature)
@ -288,13 +288,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.
"""
# _log.debug("(%d) get device features", devnumber)
# _log.debug("device %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, FEATURE.ROOT, FEATURE.FEATURE_SET)
if fs_index is None:
# _l.warn("(%d) FEATURE_SET not available", device)
_log.warn("device %d FEATURE_SET not available", devnumber)
return None
fs_index = fs_index[:1]
@ -306,11 +306,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)
_log.debug("(%d) no features available?!", devnumber)
_log.debug("device %d no features available?!", devnumber)
return None
features_count = ord(features_count[:1])
# _log.debug("(%d) found %d features", devnumber, features_count)
# _log.debug("device %d found %d features", devnumber, features_count)
features = [None] * 0x20
for index in range(1, 1 + features_count):
@ -322,11 +322,11 @@ def get_device_features(handle, devnumber):
features[index] = feature
# if feature_flags:
# _log.debug("(%d) feature <%s:%s> at index %d: %s",
# _log.debug("device %d feature <%s:%s> at index %d: %s",
# devnumber, _hex(feature), FEATURE_NAME[feature], index,
# ','.join([FEATURE_FLAGS[k] for k in FEATURE_FLAGS if feature_flags & k]))
# else:
# _log.debug("(%d) feature <%s:%s> at index %d", devnumber, _hex(feature), FEATURE_NAME[feature], index)
# _log.debug("device %d feature <%s:%s> at index %d", devnumber, _hex(feature), FEATURE_NAME[feature], index)
features[0] = FEATURE.ROOT
while features[-1] is None:
@ -369,7 +369,7 @@ def get_device_firmware(handle, devnumber, features=None):
fw_info = _FirmwareInfo(level, FIRMWARE_KIND[-1], '', '', None)
fw.append(fw_info)
# _log.debug("(%d) firmware %s", devnumber, fw_info)
# _log.debug("device %d firmware %s", devnumber, fw_info)
return tuple(fw)
@ -387,7 +387,7 @@ def get_device_kind(handle, devnumber, features=None):
d_kind = _base.request(handle, devnumber, _pack('!BB', name_fi, 0x20))
if d_kind:
d_kind = ord(d_kind[:1])
# _log.debug("(%d) device type %d = %s", devnumber, d_kind, DEVICE_KIND[d_kind])
# _log.debug("device %d type %d = %s", devnumber, d_kind, DEVICE_KIND[d_kind])
return DEVICE_KIND[d_kind]
@ -415,7 +415,7 @@ def get_device_name(handle, devnumber, features=None):
break
d_name = d_name.decode('ascii')
# _log.debug("(%d) device name %s", devnumber, d_name)
# _log.debug("device %d name %s", devnumber, d_name)
return d_name
@ -429,7 +429,7 @@ def get_device_battery_level(handle, devnumber, features=None):
battery = _base.request(handle, devnumber, _pack('!BB', bat_fi, 0))
if battery:
discharge, dischargeNext, status = _unpack('!BBB', battery[:3])
_log.debug("(%d) battery %d%% charged, next level %d%% charge, status %d = %s",
_log.debug("device %d battery %d%% charged, next level %d%% charge, status %d = %s",
devnumber, discharge, dischargeNext, status, BATTERY_STATUS[status])
return (discharge, dischargeNext, BATTERY_STATUS[status])

View File

@ -40,7 +40,7 @@ _MAX_REPLY_SIZE = _MAX_CALL_SIZE
"""Default timeout on read (in ms)."""
DEFAULT_TIMEOUT = 1000
DEFAULT_TIMEOUT = 1500
#
#
@ -166,9 +166,9 @@ def write(handle, devnumber, data):
assert _MAX_CALL_SIZE == 20
# the data is padded to either 5 or 18 bytes
wdata = _pack('!BB18s' if len(data) > 5 else '!BB5s', 0x10, devnumber, data)
_log.debug("(%d) <= w[10 %02X %s %s]", devnumber, devnumber, _hex(wdata[2:4]), _hex(wdata[4:]))
_log.debug("<= w[10 %02X %s %s]", devnumber, _hex(wdata[2:4]), _hex(wdata[4:]))
if not _hid.write(handle, wdata):
_log.warn("(%d) write failed, assuming receiver %X no longer available", devnumber, handle)
_log.warn("write failed, assuming receiver %X no longer available", handle)
close(handle)
raise _NoReceiver
@ -191,19 +191,19 @@ def read(handle, timeout=DEFAULT_TIMEOUT):
"""
data = _hid.read(handle, _MAX_REPLY_SIZE, timeout)
if data is None:
_log.warn("(-) read failed, assuming receiver %X no longer available", handle)
_log.warn("read failed, assuming receiver %X no longer available", handle)
close(handle)
raise _NoReceiver
if data:
if len(data) < _MIN_REPLY_SIZE:
_log.warn("(%d) => r[%s] read packet too short: %d bytes", ord(data[1:2]), _hex(data), len(data))
_log.warn("=> r[%s] read packet too short: %d bytes", _hex(data), len(data))
data += b'\x00' * (_MIN_REPLY_SIZE - len(data))
if len(data) > _MAX_REPLY_SIZE:
_log.warn("(%d) => r[%s] read packet too long: %d bytes", ord(data[1:2]), _hex(data), len(data))
_log.warn("=> r[%s] read packet too long: %d bytes", _hex(data), len(data))
code = ord(data[:1])
devnumber = ord(data[1:2])
_log.debug("(%d) => r[%02X %02X %s %s]", devnumber, code, devnumber, _hex(data[2:4]), _hex(data[4:]))
_log.debug("=> r[%02X %02X %s %s]", code, devnumber, _hex(data[2:4]), _hex(data[4:]))
return code, devnumber, data[2:]
# _l.log(_LOG_LEVEL, "(-) => r[]")
@ -236,7 +236,7 @@ def request(handle, devnumber, feature_index_function, params=b'', features=None
if type(params) == int:
params = _pack('!B', params)
# _log.debug("(%d) request {%s} params [%s]", devnumber, _hex(feature_index_function), _hex(params))
# _log.debug("device %d request {%s} params [%s]", devnumber, _hex(feature_index_function), _hex(params))
if len(feature_index_function) != 2:
raise ValueError('invalid feature_index_function {%s}: it must be a two-byte string' % _hex(feature_index_function))
@ -263,7 +263,7 @@ def request(handle, devnumber, feature_index_function, params=b'', features=None
if reply_devnumber != devnumber:
# this message not for the device we're interested in
# _l.log(_LOG_LEVEL, "(%d) request got reply for unexpected device %d: [%s]", devnumber, reply_devnumber, _hex(reply_data))
# _l.log(_LOG_LEVEL, "device %d request got reply for unexpected device %d: [%s]", devnumber, reply_devnumber, _hex(reply_data))
# worst case scenario, this is a reply for a concurrent request
# on this receiver
if _unhandled:
@ -272,18 +272,18 @@ 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:
# device not present
_log.debug("(%d) request ping failed on {%s} call: [%s]", devnumber, _hex(feature_index_function), _hex(reply_data))
_log.debug("device %d request ping failed on {%s} call: [%s]", devnumber, _hex(feature_index_function), _hex(reply_data))
return None
if reply_code == 0x10 and reply_data[:1] == b'\x8F':
# device not present
_log.debug("(%d) request ping failed: [%s]", devnumber, _hex(reply_data))
_log.debug("request ping failed: [%s]", devnumber, _hex(reply_data))
return None
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
error_code = ord(reply_data[3])
_log.warn("(%d) request feature call error %d = %s: %s", devnumber, error_code, ERROR_NAME[error_code], _hex(reply_data))
_log.warn("device %d request feature call error %d = %s: %s", devnumber, error_code, ERROR_NAME[error_code], _hex(reply_data))
feature_index = ord(feature_index_function[:1])
feature_function = feature_index_function[1:2]
feature = None if features is None else features[feature_index] if feature_index < len(features) else None
@ -291,14 +291,14 @@ def request(handle, devnumber, feature_index_function, params=b'', features=None
if reply_code == 0x11 and reply_data[:2] == feature_index_function:
# a matching reply
# _log.debug("(%d) matched reply with feature-index-function [%s]", devnumber, _hex(reply_data[2:]))
# _log.debug("device %d matched reply with feature-index-function [%s]", devnumber, _hex(reply_data[2:]))
return reply_data[2:]
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
# _log.debug("(%d) matched reply with feature-index-function [%s]", devnumber, _hex(reply_data[2:]))
# _log.debug("device %d matched reply with feature-index-function [%s]", devnumber, _hex(reply_data[2:]))
return reply_data[2:]
# _log.debug("(%d) unmatched reply {%s} (expected {%s})", devnumber, _hex(reply_data[:2]), _hex(feature_index_function))
# _log.debug("device %d unmatched reply {%s} (expected {%s})", devnumber, _hex(reply_data[:2]), _hex(feature_index_function))
if _unhandled:
_unhandled(reply_code, reply_devnumber, reply_data)

View File

@ -21,7 +21,7 @@ _log = getLogger('LUR').getChild('listener')
del getLogger
_READ_EVENT_TIMEOUT = int(_base.DEFAULT_TIMEOUT) # ms
_READ_EVENT_TIMEOUT = int(_base.DEFAULT_TIMEOUT / 2) # ms
def _event_dispatch(listener, callback):
while listener._active: # or not listener._events.empty():