small clean-ups in device status handling

This commit is contained in:
Daniel Pavel 2012-12-13 14:54:31 +02:00
parent c22fe6320d
commit b39016df7c
7 changed files with 40 additions and 46 deletions

View File

@ -16,24 +16,23 @@ from logitech.unifying_receiver import (Receiver,
#
#
class _DUMMY_RECEIVER(object):
__slots__ = []
name = Receiver.name
kind = None
max_devices = Receiver.max_devices
status = 'Receiver not found.'
__bool__ = __nonzero__ = lambda self: False
__unicode__ = __str__ = __repr__ = lambda self: 'DUMMY'
DUMMY = _DUMMY_RECEIVER()
from collections import namedtuple
_GHOST_DEVICE = namedtuple('_GHOST_DEVICE', ['number', 'name', 'kind', 'status'])
_GHOST_DEVICE = namedtuple('_GHOST_DEVICE', ['number', 'name', 'kind', 'status', 'max_devices'])
_GHOST_DEVICE.__bool__ = lambda self: False
_GHOST_DEVICE.__nonzero__ = _GHOST_DEVICE.__bool__
del namedtuple
def _ghost(device):
return _GHOST_DEVICE(number=device.number, name=device.name, kind=device.kind, status=None, max_devices=None)
DUMMY = _GHOST_DEVICE(Receiver.number, Receiver.name, None, 'Receiver not found.', Receiver.max_devices)
#
#
#
# how often to poll devices that haven't updated their statuses on their own
# (through notifications)
_POLL_TICK = 60 # seconds
@ -46,6 +45,11 @@ class ReceiverListener(_listener.EventsListener):
self._last_tick = 0
self.status_changed_callback = status_changed_callback
# make it a bit similar with the regular devices
receiver.kind = None
# replace the
receiver.handle = _listener.ThreadedHandle(receiver.handle, receiver.path)
receiver.status = _status.ReceiverStatus(receiver, self._status_changed)
def has_started(self):
@ -100,9 +104,12 @@ class ReceiverListener(_listener.EventsListener):
if device.status is None:
# device was unpaired, and since the object is weakref'ed
# it won't be valid for much longer
device = _GHOST_DEVICE(number=device.number, name=device.name, kind=device.kind, status=None)
device = _ghost(device)
self.status_changed_callback(r, device, alert, reason)
if device.status is None:
# the receiver changed status as well
self.status_changed_callback(r)
def _notifications_handler(self, n):
@ -117,21 +124,25 @@ class ReceiverListener(_listener.EventsListener):
already_known = n.devnumber in self.receiver
dev = self.receiver[n.devnumber]
if dev and not already_known:
if not dev:
_log.warn("received %s for invalid device %d: %s", n, n.devnumber, repr(dev))
return
if not already_known:
# read these as soon as possible, they will be used everywhere
dev.protocol, dev.codename
dev.status = _status.DeviceStatus(dev, self._status_changed)
# the receiver changed status as well
self._status_changed(self.receiver)
if dev and dev.status is not None:
# status may be None if the device has just been unpaired
if dev.status is not None:
dev.status.process_notification(n)
if self.receiver.status.lock_open and not already_known:
# this should be the first notification after a device was paired
assert n.sub_id == 0x41 and n.address == 0x04
_log.info("pairing detected new device")
self.receiver.status.new_device = dev
else:
_log.warn("received notification %s for invalid device %d: %s", n, n.devnumber, dev)
def __str__(self):
return '<ReceiverListener(%s,%s)>' % (self.receiver.path, self.receiver.handle)
@ -141,8 +152,6 @@ class ReceiverListener(_listener.EventsListener):
def open(self, status_changed_callback=None):
receiver = Receiver.open()
if receiver:
receiver.handle = _listener.ThreadedHandle(receiver.handle, receiver.path)
receiver.kind = None
rl = ReceiverListener(receiver, status_changed_callback)
rl.start()
return rl

View File

@ -183,6 +183,7 @@ def update(frame):
return
device_active = bool(device.status)
# if the device just became active, re-read the settings
force_read |= device_active and not box.get_sensitive()
box.set_sensitive(device_active)
if device_active:

View File

@ -424,10 +424,10 @@ def update(window, receiver, device=None):
frames = list(vbox.get_children())
assert len(frames) == 1 + receiver.max_devices, frames
if device:
_update_device_box(frames[device.number], None if device.status is None else device)
else:
if device is None:
_update_receiver_box(frames[0], receiver)
if not receiver:
for frame in frames[1:]:
_update_device_box(frame, None)
else:
_update_device_box(frames[device.number], None if device.status is None else device)

View File

@ -78,7 +78,7 @@ def _icon_with_battery(level, active):
def update(icon, receiver, device=None):
# print ("icon update", receiver, receiver.status, len(receiver), device)
if device:
if device is not None:
icon._devices[device.number] = None if device.status is None else device
if not receiver:
icon._devices[:] = _NO_DEVICES

View File

@ -148,11 +148,11 @@ class EventsListener(_threading.Thread):
if n:
# if _log.isEnabledFor(_DEBUG):
# _log.debug("processing notification %s", n)
# _log.debug("processing %s", n)
try:
self._notifications_callback(n)
except:
_log.exception("processing notification %s", n)
_log.exception("processing %s", n)
elif self.tick_period:
idle_reads += 1
if idle_reads % _IDLE_READS == 0:
@ -189,7 +189,7 @@ class EventsListener(_threading.Thread):
# i.e. triggered by a callback handling a previous notification.
if self._active and _threading.current_thread() == self:
if _log.isEnabledFor(_DEBUG):
_log.debug("queueing unhandled notification %s", n)
_log.debug("queueing unhandled %s", n)
self._queued_notifications.put(n)
def __bool__(self):

View File

@ -176,26 +176,14 @@ class PairedDevice(object):
return self.number
__int__ = __index__
def __lt__(self, other):
return self.number < other.number
def __le__(self, other):
return self.number <= other.number
def __gt__(self, other):
return self.number > other.number
def __ge__(self, other):
return self.number >= other.number
def __eq__(self, other):
return self.receiver == other.receiver and self.number == other.number
return self.serial == other.serial
def __ne__(self, other):
return self.receiver != other.receiver or self.number != other.number
return self.serial != other.serial
def __hash__(self):
return self.number
return self.serial.__hash__()
def __str__(self):
return '<PairedDevice(%d,%s)>' % (self.number, self.codename or '?')
@ -210,6 +198,7 @@ class Receiver(object):
The paired devices are available through the sequence interface.
"""
number = 0xFF
name = 'Unifying Receiver'
kind = None
max_devices = MAX_PAIRED_DEVICES
@ -220,7 +209,6 @@ class Receiver(object):
assert path
self.path = path
self.number = 0xFF
self._serial = None
self._firmware = None
self._devices = {}

View File

@ -186,11 +186,7 @@ class DeviceStatus(dict):
if n.sub_id >= 0x40:
return self._process_hidpp10_notification(n)
# if n.sub_id >= len(self._device.features):
# _log.warn("%s: notification from invalid feature index %02X", self._device, n.sub_id)
# return False
# assuming 0x00 to 0x3F are device feature (HID++ 2.0) notifications
# assuming 0x00 to 0x3F are feature (HID++ 2.0) notifications
try:
feature = self._device.features[n.sub_id]
except IndexError: