one a separate flag to check if the device is active
This commit is contained in:
parent
c04851f64e
commit
fd35f23af7
|
|
@ -105,8 +105,8 @@ def get_register(device, name, default_number=-1):
|
|||
if reply:
|
||||
return reply
|
||||
|
||||
if not known_register and device.ping():
|
||||
_log.warn("%s: failed to read '%s' from default register 0x%02X, blacklisting",
|
||||
if not known_register and device.kind is not None and device.online:
|
||||
_log.warn("%s: failed to read register '%s' (0x%02X), blacklisting",
|
||||
device, name, default_number)
|
||||
device.registers[name] = -default_number
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class FeaturesArray(object):
|
|||
if self.features is not None:
|
||||
return True
|
||||
|
||||
if hasattr(self.device, 'status') and not bool(self.device.status):
|
||||
if not self.device.online:
|
||||
# device is not connected right now, will have to try later
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,12 @@ MAX_PAIRED_DEVICES = 6
|
|||
|
||||
|
||||
class PairedDevice(object):
|
||||
def __init__(self, receiver, number):
|
||||
def __init__(self, receiver, number, link_notification=None):
|
||||
assert receiver
|
||||
self.receiver = _proxy(receiver)
|
||||
assert number > 0 and number <= receiver.max_devices
|
||||
self.number = number
|
||||
self.online = None
|
||||
|
||||
self.wpid = None
|
||||
self.polling_rate = 0
|
||||
|
|
@ -38,56 +39,61 @@ class PairedDevice(object):
|
|||
self._kind = None
|
||||
self._codename = None
|
||||
self._name = None
|
||||
self._protocol = None
|
||||
self._serial = None
|
||||
|
||||
unifying = self.receiver.unifying_supported
|
||||
if unifying:
|
||||
# force a reading of the codename
|
||||
if self.codename is None:
|
||||
raise _base.NoSuchDevice(nuber=number, receiver=receiver, error="read codename")
|
||||
if link_notification is None:
|
||||
if unifying:
|
||||
# force a reading of the codename
|
||||
pair_info = receiver.read_register(0x2B5, 0x20 + number - 1)
|
||||
if pair_info is None:
|
||||
raise _base.NoSuchDevice(nuber=number, receiver=receiver, error="read pair info")
|
||||
|
||||
pair_info = receiver.request(0x83B5, 0x20 + number - 1)
|
||||
if pair_info is None:
|
||||
raise _base.NoSuchDevice(nuber=number, receiver=receiver, error="read pair info")
|
||||
|
||||
self.wpid = _strhex(pair_info[3:5])
|
||||
kind = ord(pair_info[7:8]) & 0x0F
|
||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
self.polling_rate = ord(pair_info[2:3])
|
||||
else:
|
||||
# guesswork...
|
||||
descriptor = _descriptors.DEVICES.get(self.receiver.product_id)
|
||||
if descriptor is None:
|
||||
self._codename = self.receiver.product_id
|
||||
# actually there IS a device, just that we can't identify it
|
||||
# raise _base.NoSuchDevice(nuber=number, receiver=receiver, product_id=receiver.product_id, failed="no descriptor")
|
||||
self._name = 'Unknown device ' + self._codename
|
||||
self.wpid = _strhex(pair_info[3:5])
|
||||
kind = ord(pair_info[7:8]) & 0x0F
|
||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
self.polling_rate = ord(pair_info[2:3])
|
||||
else:
|
||||
self._codename = descriptor.codename
|
||||
self._name = descriptor.name
|
||||
# guesswork... look for the product id in the descriptors
|
||||
descriptor = _descriptors.DEVICES.get(self.receiver.product_id)
|
||||
if descriptor is None:
|
||||
self._codename = self.receiver.product_id
|
||||
# actually there IS a device, just that we can't identify it
|
||||
# raise _base.NoSuchDevice(nuber=number, receiver=receiver, product_id=receiver.product_id, failed="no descriptor")
|
||||
self._name = 'Unknown device ' + self._codename
|
||||
else:
|
||||
self._codename = descriptor.codename
|
||||
self._name = descriptor.name
|
||||
|
||||
device_info = self.receiver.request(0x83B5, 0x04)
|
||||
assert device_info, "failed to read Nano device info"
|
||||
self.wpid = _strhex(device_info[3:5])
|
||||
# self._kind = descriptor.kind
|
||||
self.polling_rate = 0
|
||||
device_info = self.receiver.read_register(0x2B5, 0x04)
|
||||
if device_info is None:
|
||||
raise _base.NoSuchDevice(nuber=number, receiver=receiver, error="read Nano wpid")
|
||||
self.wpid = _strhex(device_info[3:5])
|
||||
# self._kind = descriptor.kind
|
||||
self.serial = self.receiver.serial
|
||||
else:
|
||||
self.wpid = _strhex(link_notification.data[2:3] + link_notification.data[1:2])
|
||||
assert link_notification.address == (0x04 if unifying else 0x03)
|
||||
kind = ord(link_notification.data[1:2]) & 0x0F
|
||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
self.online = bool(ord(link_notification.data[0:1]) & 0x40)
|
||||
|
||||
# the wpid is necessary to properly identify wireless link on/off notifications
|
||||
# also it gets set to None when the device is unpaired
|
||||
assert self.wpid is not None, "failed to read wpid: device %d of %s" % (number, receiver)
|
||||
# the codename is necessary to guess all other info
|
||||
assert self._codename is not None, "failed to read codename: device %d of %s" % (number, receiver)
|
||||
|
||||
# knowing the protocol as soon as possible helps reading all other info
|
||||
# and avoids an unecessary ping
|
||||
descriptor = _descriptors.DEVICES.get(self.codename)
|
||||
if descriptor is None:
|
||||
_log.warn("device without descriptor found: %s (%d of %s)", self.codename, number, receiver)
|
||||
self._protocol = None if unifying else 1.0
|
||||
else:
|
||||
self._protocol = descriptor.protocol if unifying else 1.0 # may be None
|
||||
if self._codename is not None:
|
||||
descriptor = _descriptors.DEVICES.get(self._codename)
|
||||
if descriptor is None:
|
||||
_log.warn("device without descriptor found: %s (%d of %s)", self._codename, number, receiver)
|
||||
self._protocol = None if unifying else 1.0
|
||||
else:
|
||||
self._protocol = descriptor.protocol if unifying else 1.0 # may be None
|
||||
|
||||
self._power_switch = None if unifying else '(unknown)'
|
||||
self.serial = _hidpp10.get_serial(self) if unifying else self.receiver.serial
|
||||
|
||||
self._firmware = None
|
||||
self._keys = None
|
||||
|
||||
|
|
@ -106,10 +112,22 @@ class PairedDevice(object):
|
|||
def protocol(self):
|
||||
if self._protocol is None:
|
||||
self._protocol = _base.ping(self.receiver.handle, self.number)
|
||||
# if the ping failed, the peripheral is (almost) certainly offline
|
||||
self.online = self._protocol is not None
|
||||
|
||||
# use the descriptor only as a fallback, because it may not be 100% correct
|
||||
descriptor = _descriptors.DEVICES.get(self.codename)
|
||||
if self._protocol is None:
|
||||
descriptor = _descriptors.DEVICES.get(self.codename)
|
||||
if descriptor and descriptor.protocol is not None:
|
||||
self._protocol = descriptor.protocol
|
||||
else:
|
||||
if descriptor:
|
||||
if descriptor.protocol is None:
|
||||
_log.info("%s: descriptor has no protocol, should be %0.1f", self, self._protocol)
|
||||
elif descriptor.protocol != self._protocol:
|
||||
_log.error("%s: descriptor has wrong protocol %0.1f, should be %0.1f",
|
||||
self, descriptor.protocol, self._protocol)
|
||||
|
||||
# _log.debug("device %d protocol %s", self.number, self._protocol)
|
||||
return self._protocol or 0
|
||||
|
||||
|
|
@ -131,12 +149,12 @@ class PairedDevice(object):
|
|||
if codename:
|
||||
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
||||
# _log.debug("device %d codename %s", self.number, self._codename)
|
||||
return self._codename
|
||||
return self._codename or '?'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self._name is None:
|
||||
if self.protocol >= 2.0:
|
||||
if self.protocol >= 2.0 and self.online:
|
||||
self._name = _hidpp20.get_name(self)
|
||||
if self._name is None:
|
||||
descriptor = _descriptors.DEVICES.get(self.codename)
|
||||
|
|
@ -155,7 +173,7 @@ class PairedDevice(object):
|
|||
# self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
# if self.wpid is None:
|
||||
# self.wpid = _strhex(pair_info[3:5])
|
||||
if self.protocol >= 2.0:
|
||||
if self.protocol >= 2.0 and self.online:
|
||||
self._kind = _hidpp20.get_kind(self)
|
||||
if self._kind is None:
|
||||
descriptor = _descriptors.DEVICES.get(self.codename)
|
||||
|
|
@ -165,26 +183,25 @@ class PairedDevice(object):
|
|||
|
||||
@property
|
||||
def firmware(self):
|
||||
if self._firmware is None:
|
||||
if self._firmware is None and self.online:
|
||||
if self.protocol < 2.0:
|
||||
self._firmware = _hidpp10.get_firmware(self)
|
||||
else:
|
||||
self._firmware = _hidpp20.get_firmware(self)
|
||||
return self._firmware or ()
|
||||
|
||||
# @property
|
||||
# def serial(self):
|
||||
# if self._serial is None:
|
||||
# if self.receiver.unifying_supported:
|
||||
# self._serial = _hidpp10.get_serial(self)
|
||||
# else:
|
||||
# self._serial = self.receiver.serial
|
||||
# return self._serial or '?'
|
||||
@property
|
||||
def serial(self):
|
||||
if self._serial is None:
|
||||
assert self.receiver.unifying_supported
|
||||
# otherwise it should have been set in the constructor
|
||||
self._serial = _hidpp10.get_serial(self)
|
||||
return self._serial or '?'
|
||||
|
||||
@property
|
||||
def keys(self):
|
||||
if self._keys is None:
|
||||
if self.protocol >= 2.0:
|
||||
if self.protocol >= 2.0 and self.online:
|
||||
self._keys = _hidpp20.get_keys(self) or ()
|
||||
return self._keys
|
||||
|
||||
|
|
@ -207,14 +224,14 @@ class PairedDevice(object):
|
|||
else:
|
||||
self._settings = [s(self) for s in descriptor.settings]
|
||||
|
||||
if self.features:
|
||||
if self.online and self.features:
|
||||
_descriptors.check_features(self, self._settings)
|
||||
return self._settings
|
||||
|
||||
def enable_notifications(self, enable=True):
|
||||
"""Enable or disable device (dis)connection notifications on this
|
||||
receiver."""
|
||||
if not self.receiver or not self.receiver.handle or self.protocol >= 2.0:
|
||||
if not bool(self.receiver) or self.protocol >= 2.0:
|
||||
return False
|
||||
|
||||
if enable:
|
||||
|
|
@ -245,7 +262,10 @@ class PairedDevice(object):
|
|||
return _hidpp20.feature_request(self, feature, function, *params)
|
||||
|
||||
def ping(self):
|
||||
return _base.ping(self.receiver.handle, self.number) is not None
|
||||
"""Checks if the device is online, returns True of False"""
|
||||
protocol = _base.ping(self.receiver.handle, self.number)
|
||||
self.online = protocol is not None
|
||||
return self.online
|
||||
|
||||
def __index__(self):
|
||||
return self.number
|
||||
|
|
@ -260,6 +280,8 @@ class PairedDevice(object):
|
|||
def __hash__(self):
|
||||
return self.serial.__hash__()
|
||||
|
||||
__bool__ = __nonzero__ = lambda self: self.wpid is not None and self.number in self.receiver
|
||||
|
||||
def __str__(self):
|
||||
return '<PairedDevice(%d,%s,%s)>' % (self.number, self.wpid, self.codename or '?')
|
||||
__unicode__ = __repr__ = __str__
|
||||
|
|
@ -349,12 +371,15 @@ class Receiver(object):
|
|||
if not self.write_register(0x02, 0x02):
|
||||
_log.warn("%s: failed to trigger device link notifications", self)
|
||||
|
||||
def register_new_device(self, number):
|
||||
def register_new_device(self, number, notification=None):
|
||||
if self._devices.get(number) is not None:
|
||||
raise IndexError("%s: device number %d already registered" % (self, number))
|
||||
|
||||
assert notification is None or notification.devnumber == number
|
||||
assert notification is None or notification.sub_id == 0x41
|
||||
|
||||
try:
|
||||
dev = PairedDevice(self, number)
|
||||
dev = PairedDevice(self, number, notification)
|
||||
assert dev.wpid
|
||||
_log.info("%s: found new device %d (%s)", self, number, dev.wpid)
|
||||
self._devices[number] = dev
|
||||
|
|
@ -420,6 +445,7 @@ class Receiver(object):
|
|||
if reply:
|
||||
# invalidate the device
|
||||
dev.wpid = None
|
||||
dev.online = False
|
||||
del self._devices[key]
|
||||
_log.warn("%s unpaired device %s", self, dev)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -190,8 +190,10 @@ class DeviceStatus(dict):
|
|||
self._changed(alert=alert, reason=reason, timestamp=timestamp)
|
||||
|
||||
def read_battery(self, timestamp=None):
|
||||
d = self._device
|
||||
if d and self._active:
|
||||
if self._active:
|
||||
d = self._device
|
||||
assert d
|
||||
|
||||
if d.protocol < 2.0:
|
||||
battery = _hidpp10.get_battery(d)
|
||||
else:
|
||||
|
|
@ -213,29 +215,32 @@ class DeviceStatus(dict):
|
|||
self[KEYS.BATTERY_CHARGING] = None
|
||||
self._changed()
|
||||
|
||||
def _changed(self, active=True, alert=ALERT.NONE, reason=None, timestamp=None):
|
||||
def _changed(self, active=None, alert=ALERT.NONE, reason=None, timestamp=None):
|
||||
assert self._changed_callback
|
||||
assert self._device
|
||||
d = self._device
|
||||
was_active, self._active = self._active, active
|
||||
if active:
|
||||
if not was_active:
|
||||
# Make sure to set notification flags on the device, they
|
||||
# get cleared when the device is turned off (but not when the device
|
||||
# goes idle, and we can't tell the difference right now).
|
||||
self[KEYS.NOTIFICATION_FLAGS] = d.enable_notifications()
|
||||
if self.configuration:
|
||||
self.configuration.attach_to(d)
|
||||
else:
|
||||
if was_active:
|
||||
battery = self.get(KEYS.BATTERY_LEVEL)
|
||||
self.clear()
|
||||
# If we had a known battery level before, assume it's not going
|
||||
# to change much while the device is offline.
|
||||
if battery is not None:
|
||||
self[KEYS.BATTERY_LEVEL] = battery
|
||||
assert d
|
||||
|
||||
if self.updated == 0 and active:
|
||||
if active is not None:
|
||||
d.online = active
|
||||
was_active, self._active = self._active, active
|
||||
if active:
|
||||
if not was_active:
|
||||
# Make sure to set notification flags on the device, they
|
||||
# get cleared when the device is turned off (but not when the device
|
||||
# goes idle, and we can't tell the difference right now).
|
||||
self[KEYS.NOTIFICATION_FLAGS] = d.enable_notifications()
|
||||
if self.configuration:
|
||||
self.configuration.attach_to(d)
|
||||
else:
|
||||
if was_active:
|
||||
battery = self.get(KEYS.BATTERY_LEVEL)
|
||||
self.clear()
|
||||
# If we had a known battery level before, assume it's not going
|
||||
# to change much while the device is offline.
|
||||
if battery is not None:
|
||||
self[KEYS.BATTERY_LEVEL] = battery
|
||||
|
||||
if self.updated == 0 and active == True:
|
||||
# if the device is active on the very first status notification,
|
||||
# (meaning just when the program started or a new receiver was just
|
||||
# detected), pop-up a notification about it
|
||||
|
|
@ -330,8 +335,10 @@ class DeviceStatus(dict):
|
|||
if n.address == 0x02:
|
||||
# device un-paired
|
||||
self.clear()
|
||||
self._device.status = None
|
||||
self._changed(False, ALERT.ALL, 'unpaired')
|
||||
dev = self._device
|
||||
dev.wpid = None
|
||||
dev.status = None
|
||||
self._changed(active=False, alert=ALERT.ALL, reason='unpaired')
|
||||
else:
|
||||
_log.warn("%s: disconnection with unknown type %02X: %s", self._device, n.address, n)
|
||||
return True
|
||||
|
|
@ -342,6 +349,10 @@ class DeviceStatus(dict):
|
|||
else 'eQuad' if n.address == 0x03
|
||||
else None)
|
||||
if protocol_name:
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
wpid = _strhex(n.data[2:3] + n.data[1:2])
|
||||
assert wpid == self._device.wpid, "%s wpid mismatch, got %s" % (self._device, wpid)
|
||||
|
||||
flags = ord(n.data[:1]) & 0xF0
|
||||
link_encrypyed = bool(flags & 0x20)
|
||||
link_established = not (flags & 0x40)
|
||||
|
|
@ -351,21 +362,21 @@ class DeviceStatus(dict):
|
|||
_log.debug("%s: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
|
||||
self._device, protocol_name, sw_present, link_encrypyed, link_established, has_payload)
|
||||
self[KEYS.LINK_ENCRYPTED] = link_encrypyed
|
||||
self._changed(link_established)
|
||||
self._changed(active=link_established)
|
||||
|
||||
if protocol_name == 'eQuad':
|
||||
# some Nano devices might not have been initialized fully
|
||||
if self._device._kind is None:
|
||||
kind = ord(n.data[:1]) & 0x0F
|
||||
self._device._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
assert self._device.wpid == _strhex(n.data[2:3] + n.data[1:2])
|
||||
# if protocol_name == 'eQuad':
|
||||
# # some Nano devices might not have been initialized fully
|
||||
# if self._device._kind is None:
|
||||
# kind = ord(n.data[:1]) & 0x0F
|
||||
# self._device._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
# assert self._device.wpid == _strhex(n.data[2:3] + n.data[1:2])
|
||||
|
||||
# if the device just came online, read the battery charge
|
||||
if self._active and KEYS.BATTERY_LEVEL not in self:
|
||||
self.read_battery()
|
||||
else:
|
||||
_log.warn("%s: connection notification with unknown protocol %02X: %s", self._device.number, n.address, n)
|
||||
|
||||
# if the device just came online, read the battery charge
|
||||
if self._active and KEYS.BATTERY_LEVEL not in self:
|
||||
self.read_battery()
|
||||
|
||||
return True
|
||||
|
||||
if n.sub_id == 0x49:
|
||||
|
|
@ -380,7 +391,7 @@ class DeviceStatus(dict):
|
|||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("%s: device powered on", self._device)
|
||||
reason = str(self) or 'powered on'
|
||||
self._changed(alert=ALERT.NOTIFICATION, reason=reason)
|
||||
self._changed(active=True, alert=ALERT.NOTIFICATION, reason=reason)
|
||||
else:
|
||||
_log.info("%s: unknown %s", self._device, n)
|
||||
return True
|
||||
|
|
@ -410,7 +421,7 @@ class DeviceStatus(dict):
|
|||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("wireless status: %s", n)
|
||||
if n.data[0:3] == b'\x01\x01\x01':
|
||||
self._changed(alert=ALERT.NOTIFICATION, reason='powered on')
|
||||
self._changed(active=True, alert=ALERT.NOTIFICATION, reason='powered on')
|
||||
else:
|
||||
_log.info("%s: unknown WIRELESS %s", self._device, n)
|
||||
else:
|
||||
|
|
@ -426,11 +437,11 @@ class DeviceStatus(dict):
|
|||
if n.address == 0x00:
|
||||
self[KEYS.LIGHT_LEVEL] = None
|
||||
self[KEYS.BATTERY_CHARGING] = None
|
||||
self._changed()
|
||||
self._changed(active=True)
|
||||
elif n.address == 0x10:
|
||||
self[KEYS.LIGHT_LEVEL] = lux
|
||||
self[KEYS.BATTERY_CHARGING] = lux > 200
|
||||
self._changed()
|
||||
self._changed(active=True)
|
||||
elif n.address == 0x20:
|
||||
_log.debug("%s: Light Check button pressed", self._device)
|
||||
self._changed(alert=ALERT.SHOW_WINDOW)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ from logitech.unifying_receiver import (Receiver,
|
|||
#
|
||||
|
||||
from collections import namedtuple
|
||||
_GHOST_DEVICE = namedtuple('_GHOST_DEVICE', ['receiver', 'number', 'name', 'kind', 'serial', 'status'])
|
||||
_GHOST_DEVICE = namedtuple('_GHOST_DEVICE', ['receiver', 'number', 'name', 'kind', 'serial', 'status', 'online'])
|
||||
_GHOST_DEVICE.__bool__ = lambda self: False
|
||||
_GHOST_DEVICE.__nonzero__ = _GHOST_DEVICE.__bool__
|
||||
del namedtuple
|
||||
|
|
@ -30,7 +30,8 @@ def _ghost(device):
|
|||
name=device.name,
|
||||
kind=device.kind,
|
||||
serial=device.serial,
|
||||
status=None)
|
||||
status=None,
|
||||
online=False)
|
||||
|
||||
#
|
||||
#
|
||||
|
|
@ -109,8 +110,7 @@ class ReceiverListener(_listener.EventsListener):
|
|||
for number in range(1, 6):
|
||||
if number in self.receiver:
|
||||
dev = self.receiver[number]
|
||||
assert dev
|
||||
if dev.status is not None:
|
||||
if dev and dev.status is not None:
|
||||
dev.status.poll(timestamp)
|
||||
except Exception as e:
|
||||
_log.exception("polling", e)
|
||||
|
|
@ -118,9 +118,15 @@ class ReceiverListener(_listener.EventsListener):
|
|||
def _status_changed(self, device, alert=_status.ALERT.NONE, reason=None):
|
||||
assert device is not None
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("%s: status_changed %s: %s, %s (%X) %s", self.receiver, device,
|
||||
'active' if device.status else 'inactive',
|
||||
device.status, alert, reason or '')
|
||||
if device.kind is None:
|
||||
_log.debug("status_changed %s: %s, %s (%X) %s", device,
|
||||
'present' if bool(device) else 'removed',
|
||||
device.status, alert, reason or '')
|
||||
else:
|
||||
_log.debug("status_changed %s: %s %s, %s (%X) %s", device,
|
||||
'paired' if bool(device) else 'unpaired',
|
||||
'online' if device.online else 'offline',
|
||||
device.status, alert, reason or '')
|
||||
|
||||
if device.kind is None:
|
||||
assert device == self.receiver
|
||||
|
|
@ -129,16 +135,15 @@ class ReceiverListener(_listener.EventsListener):
|
|||
return
|
||||
|
||||
assert device.receiver == self.receiver
|
||||
|
||||
if device.status is None:
|
||||
if not device:
|
||||
# device was unpaired, and since the object is weakref'ed
|
||||
# it won't be valid for much longer
|
||||
_log.info("device %s was unpaired, ghosting", device)
|
||||
_log.warn("device %s was unpaired, ghosting", device)
|
||||
device = _ghost(device)
|
||||
|
||||
self.status_changed_callback(device, alert, reason)
|
||||
|
||||
if device.status is None:
|
||||
if not device:
|
||||
# the device was just unpaired, need to update the
|
||||
# status of the receiver as well
|
||||
self.status_changed_callback(self.receiver)
|
||||
|
|
@ -155,28 +160,33 @@ class ReceiverListener(_listener.EventsListener):
|
|||
# a device notification
|
||||
assert n.devnumber > 0 and n.devnumber <= self.receiver.max_devices
|
||||
already_known = n.devnumber in self.receiver
|
||||
dev = self.receiver[n.devnumber]
|
||||
if not already_known and n.sub_id == 0x41:
|
||||
dev = self.receiver.register_new_device(n.devnumber, n)
|
||||
else:
|
||||
dev = self.receiver[n.devnumber]
|
||||
|
||||
if not dev:
|
||||
_log.warn("%s: received %s for invalid device %d: %r", self.receiver, n, n.devnumber, dev)
|
||||
return
|
||||
|
||||
if not already_known:
|
||||
# read these as soon as possible, they will be used everywhere
|
||||
dev.protocol, dev.codename
|
||||
# _log.info("%s triggered new device %s", n, dev)
|
||||
dev.status = _status.DeviceStatus(dev, self._status_changed)
|
||||
dev.status.configuration = configuration
|
||||
# the receiver changed status as well
|
||||
self._status_changed(self.receiver)
|
||||
|
||||
# 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("%s: pairing detected new device", self.receiver)
|
||||
self.receiver.status.new_device = dev
|
||||
assert dev
|
||||
assert 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("%s: pairing detected new device", self.receiver)
|
||||
self.receiver.status.new_device = dev
|
||||
else:
|
||||
if dev.online is None:
|
||||
dev.ping()
|
||||
|
||||
def __str__(self):
|
||||
return '<ReceiverListener(%s,%s)>' % (self.receiver.path, self.receiver.handle)
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ def destroy():
|
|||
from logitech.unifying_receiver.status import ALERT
|
||||
def _status_changed(device, alert, reason):
|
||||
assert device is not None
|
||||
_log.info("status changed: %s, %s, %s", device, alert, reason)
|
||||
_log.info("status changed: %s (%s) %s", device, alert, reason)
|
||||
|
||||
tray.update(device)
|
||||
if alert & ALERT.ATTENTION:
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ def _create_sbox(s):
|
|||
return sbox
|
||||
|
||||
|
||||
def _update_setting_item(sbox, value, is_active=True):
|
||||
def _update_setting_item(sbox, value, is_online=True):
|
||||
_, failed, spinner, control = sbox.get_children()
|
||||
spinner.set_visible(False)
|
||||
spinner.stop()
|
||||
|
|
@ -135,7 +135,7 @@ def _update_setting_item(sbox, value, is_active=True):
|
|||
# print ("update", control, "with new value", value)
|
||||
if value is None:
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(is_active)
|
||||
failed.set_visible(is_online)
|
||||
return
|
||||
|
||||
failed.set_visible(False)
|
||||
|
|
|
|||
|
|
@ -391,16 +391,19 @@ def _remove_receiver(receiver):
|
|||
index += 1
|
||||
|
||||
|
||||
def _update_menu_item(index, device_status):
|
||||
def _update_menu_item(index, device):
|
||||
assert device
|
||||
assert device.status is not None
|
||||
|
||||
menu_items = _menu.get_children()
|
||||
menu_item = menu_items[index]
|
||||
|
||||
level = device_status.get(_K.BATTERY_LEVEL)
|
||||
charging = device_status.get(_K.BATTERY_CHARGING)
|
||||
level = device.status.get(_K.BATTERY_LEVEL)
|
||||
charging = device.status.get(_K.BATTERY_CHARGING)
|
||||
icon_name = _icons.battery(level, charging)
|
||||
|
||||
image_widget = menu_item.get_image()
|
||||
image_widget.set_sensitive(bool(device_status))
|
||||
image_widget.set_sensitive(bool(device.online))
|
||||
_update_menu_icon(image_widget, icon_name)
|
||||
|
||||
#
|
||||
|
|
@ -455,17 +458,17 @@ def update(device=None):
|
|||
|
||||
else:
|
||||
# peripheral
|
||||
is_alive = device.status is not None
|
||||
is_paired = bool(device)
|
||||
receiver_path = device.receiver.path
|
||||
index = None
|
||||
for idx, (path, serial, name, _, _) in enumerate(_devices_info):
|
||||
if path == receiver_path and serial == device.serial:
|
||||
index = idx
|
||||
|
||||
if is_alive:
|
||||
if is_paired:
|
||||
if index is None:
|
||||
index = _add_device(device)
|
||||
_update_menu_item(index, device.status)
|
||||
_update_menu_item(index, device)
|
||||
else:
|
||||
# was just unpaired
|
||||
if index:
|
||||
|
|
|
|||
|
|
@ -431,6 +431,7 @@ def _update_details(button):
|
|||
assert button
|
||||
visible = button.get_active()
|
||||
device = _find_selected_device()
|
||||
assert device
|
||||
|
||||
if visible:
|
||||
_details._text.set_markup('<small>reading...</small>')
|
||||
|
|
@ -465,6 +466,8 @@ def _update_details(button):
|
|||
|
||||
|
||||
def _update_receiver_panel(receiver, panel, buttons, full=False):
|
||||
assert receiver
|
||||
|
||||
devices_count = len(receiver)
|
||||
if receiver.max_devices > 1:
|
||||
if devices_count == 0:
|
||||
|
|
@ -498,8 +501,9 @@ def _update_receiver_panel(receiver, panel, buttons, full=False):
|
|||
|
||||
|
||||
def _update_device_panel(device, panel, buttons, full=False):
|
||||
is_active = bool(device.status)
|
||||
panel.set_sensitive(is_active)
|
||||
assert device
|
||||
is_online = bool(device.online)
|
||||
panel.set_sensitive(is_online)
|
||||
|
||||
battery_level = device.status.get(_K.BATTERY_LEVEL)
|
||||
if battery_level is None:
|
||||
|
|
@ -515,15 +519,15 @@ def _update_device_panel(device, panel, buttons, full=False):
|
|||
panel._battery._icon.set_sensitive(True)
|
||||
|
||||
text = '%d%%' % battery_level
|
||||
if is_active:
|
||||
if is_online:
|
||||
if charging:
|
||||
text += ' <small>(charging)</small>'
|
||||
else:
|
||||
text += ' <small>(last known)</small>'
|
||||
panel._battery._text.set_sensitive(is_active)
|
||||
panel._battery._text.set_sensitive(is_online)
|
||||
panel._battery._text.set_markup(text)
|
||||
|
||||
if is_active:
|
||||
if is_online:
|
||||
not_secure = device.status.get(_K.LINK_ENCRYPTED) == False
|
||||
if not_secure:
|
||||
panel._secure._text.set_text('not encrypted')
|
||||
|
|
@ -539,7 +543,7 @@ def _update_device_panel(device, panel, buttons, full=False):
|
|||
panel._secure._icon.set_visible(False)
|
||||
panel._secure.set_tooltip_text('')
|
||||
|
||||
if is_active:
|
||||
if is_online:
|
||||
light_level = device.status.get(_K.LIGHT_LEVEL)
|
||||
if light_level is None:
|
||||
panel._lux.set_visible(False)
|
||||
|
|
@ -557,7 +561,7 @@ def _update_device_panel(device, panel, buttons, full=False):
|
|||
panel.set_visible(True)
|
||||
|
||||
if full:
|
||||
_config_panel.update(panel._config, device, is_active)
|
||||
_config_panel.update(device, is_online)
|
||||
|
||||
|
||||
def _update_info_panel(device, full=False):
|
||||
|
|
@ -567,19 +571,24 @@ def _update_info_panel(device, full=False):
|
|||
_empty.set_visible(True)
|
||||
return
|
||||
|
||||
is_active = bool(device.status)
|
||||
# a receiver must be valid
|
||||
# a device must be paired
|
||||
assert device
|
||||
|
||||
_info._title.set_markup('<b>%s</b>' % device.name)
|
||||
_info._title.set_sensitive(is_active)
|
||||
icon_name = _icons.device_icon_name(device.name, device.kind)
|
||||
_info._icon.set_from_icon_name(icon_name, _DEVICE_ICON_SIZE)
|
||||
_info._icon.set_sensitive(is_active)
|
||||
|
||||
if device.kind is None:
|
||||
_info._device.set_visible(False)
|
||||
_info._icon.set_sensitive(True)
|
||||
_info._title.set_sensitive(True)
|
||||
_update_receiver_panel(device, _info._receiver, _info._buttons, full)
|
||||
else:
|
||||
_info._receiver.set_visible(False)
|
||||
is_online = bool(device.online)
|
||||
_info._icon.set_sensitive(is_online)
|
||||
_info._title.set_sensitive(is_online)
|
||||
_update_device_panel(device, _info._device, _info._buttons, full)
|
||||
|
||||
_empty.set_visible(False)
|
||||
|
|
@ -664,10 +673,16 @@ def update(device, need_popup=False):
|
|||
|
||||
else:
|
||||
# peripheral
|
||||
is_alive = device.status is not None
|
||||
item = _device_row(device.receiver.path, device.serial, device if is_alive else None)
|
||||
if is_alive and item:
|
||||
_model.set_value(item, _COLUMN.ACTIVE, bool(device.status))
|
||||
is_paired = bool(device)
|
||||
assert device.receiver
|
||||
assert device.serial
|
||||
item = _device_row(device.receiver.path, device.serial, device if is_paired else None)
|
||||
|
||||
if is_paired and item:
|
||||
was_online = _model.get_value(item, _COLUMN.ACTIVE)
|
||||
is_online = bool(device.online)
|
||||
_model.set_value(item, _COLUMN.ACTIVE, is_online)
|
||||
|
||||
battery_level = device.status.get(_K.BATTERY_LEVEL)
|
||||
if battery_level is None:
|
||||
_model.set_value(item, _COLUMN.STATUS_ICON, '')
|
||||
|
|
@ -679,7 +694,8 @@ def update(device, need_popup=False):
|
|||
if selected_device_id is None:
|
||||
select(device.receiver.path, device.serial)
|
||||
elif selected_device_id == device.serial:
|
||||
_update_info_panel(device, need_popup)
|
||||
full_update = need_popup or was_online != is_online
|
||||
_update_info_panel(device, full=full_update)
|
||||
|
||||
elif item:
|
||||
_model.remove(item)
|
||||
|
|
|
|||
Loading…
Reference in New Issue