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 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 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 _POLL_TICK = 60 # seconds
@ -46,6 +45,11 @@ class ReceiverListener(_listener.EventsListener):
self._last_tick = 0 self._last_tick = 0
self.status_changed_callback = status_changed_callback 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) receiver.status = _status.ReceiverStatus(receiver, self._status_changed)
def has_started(self): def has_started(self):
@ -100,9 +104,12 @@ class ReceiverListener(_listener.EventsListener):
if device.status is None: if device.status is None:
# device was unpaired, and since the object is weakref'ed # device was unpaired, and since the object is weakref'ed
# it won't be valid for much longer # 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) self.status_changed_callback(r, device, alert, reason)
if device.status is None: if device.status is None:
# the receiver changed status as well
self.status_changed_callback(r) self.status_changed_callback(r)
def _notifications_handler(self, n): def _notifications_handler(self, n):
@ -117,21 +124,25 @@ class ReceiverListener(_listener.EventsListener):
already_known = n.devnumber in self.receiver already_known = n.devnumber in self.receiver
dev = self.receiver[n.devnumber] 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 # read these as soon as possible, they will be used everywhere
dev.protocol, dev.codename dev.protocol, dev.codename
dev.status = _status.DeviceStatus(dev, self._status_changed) dev.status = _status.DeviceStatus(dev, self._status_changed)
# the receiver changed status as well
self._status_changed(self.receiver) 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) dev.status.process_notification(n)
if self.receiver.status.lock_open and not already_known: if self.receiver.status.lock_open and not already_known:
# this should be the first notification after a device was paired # this should be the first notification after a device was paired
assert n.sub_id == 0x41 and n.address == 0x04 assert n.sub_id == 0x41 and n.address == 0x04
_log.info("pairing detected new device") _log.info("pairing detected new device")
self.receiver.status.new_device = dev self.receiver.status.new_device = dev
else:
_log.warn("received notification %s for invalid device %d: %s", n, n.devnumber, dev)
def __str__(self): def __str__(self):
return '<ReceiverListener(%s,%s)>' % (self.receiver.path, self.receiver.handle) 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): def open(self, status_changed_callback=None):
receiver = Receiver.open() receiver = Receiver.open()
if receiver: if receiver:
receiver.handle = _listener.ThreadedHandle(receiver.handle, receiver.path)
receiver.kind = None
rl = ReceiverListener(receiver, status_changed_callback) rl = ReceiverListener(receiver, status_changed_callback)
rl.start() rl.start()
return rl return rl

View File

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

View File

@ -424,10 +424,10 @@ def update(window, receiver, device=None):
frames = list(vbox.get_children()) frames = list(vbox.get_children())
assert len(frames) == 1 + receiver.max_devices, frames assert len(frames) == 1 + receiver.max_devices, frames
if device: if device is None:
_update_device_box(frames[device.number], None if device.status is None else device)
else:
_update_receiver_box(frames[0], receiver) _update_receiver_box(frames[0], receiver)
if not receiver: if not receiver:
for frame in frames[1:]: for frame in frames[1:]:
_update_device_box(frame, None) _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): def update(icon, receiver, device=None):
# print ("icon update", receiver, receiver.status, len(receiver), device) # 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 icon._devices[device.number] = None if device.status is None else device
if not receiver: if not receiver:
icon._devices[:] = _NO_DEVICES icon._devices[:] = _NO_DEVICES

View File

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

View File

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

View File

@ -186,11 +186,7 @@ class DeviceStatus(dict):
if n.sub_id >= 0x40: if n.sub_id >= 0x40:
return self._process_hidpp10_notification(n) return self._process_hidpp10_notification(n)
# if n.sub_id >= len(self._device.features): # assuming 0x00 to 0x3F are feature (HID++ 2.0) notifications
# _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
try: try:
feature = self._device.features[n.sub_id] feature = self._device.features[n.sub_id]
except IndexError: except IndexError: