pairing fixes

This commit is contained in:
Daniel Pavel 2012-11-29 21:26:03 +02:00
parent 932a015e49
commit ab5e09db93
4 changed files with 138 additions and 114 deletions

View File

@ -28,7 +28,7 @@ DUMMY = _DUMMY_RECEIVER()
_DEVICE_TIMEOUT = 3 * 60 # seconds
_DEVICE_STATUS_POLL = 60 # seconds
# def fake_device(listener):
# def _fake_device(listener):
# dev = _lur.PairedDevice(listener.receiver, 6)
# dev._wpid = '1234'
# dev._kind = 'touchpad'
@ -64,7 +64,7 @@ class ReceiverListener(_lur.listener.EventsListener):
dev.codename, dev.kind, dev.name
# dev.status._changed(dev.protocol > 0)
# fake = fake_device(self)
# fake = _fake_device(self)
# self.receiver._devices[fake.number] = fake
# self._status_changed(fake, _lur.status.ALERT.LOW)
@ -81,7 +81,7 @@ class ReceiverListener(_lur.listener.EventsListener):
def tick(self, timestamp):
if _log.isEnabledFor(_DEBUG):
_log.debug("tick: polling status")
_log.debug("tick: polling status: %s %s", self.receiver, self.receiver._devices)
# read these in case they haven't been read already
self.receiver.serial, self.receiver.firmware
@ -130,8 +130,7 @@ class ReceiverListener(_lur.listener.EventsListener):
if dev.status is not None and dev.status.process_event(event):
if self.receiver.status.lock_open and not known_device:
assert event.sub_id == 0x41
self.receiver.pairing_result = dev
return
self.receiver.status.new_device = dev
else:
_log.warn("received event %s for invalid device %d", event, event.devnumber)

View File

@ -24,6 +24,7 @@ def _make_receiver_box(name):
icon_name = ui.get_icon(name, 'preferences-desktop-peripherals')
icon = Gtk.Image.new_from_icon_name(icon_name, _RECEIVER_ICON_SIZE)
icon.set_name('icon')
icon.set_padding(2, 2)
label = Gtk.Label('Scanning...')
@ -272,20 +273,21 @@ def _toggle_info_box(action, label_widget, box_widget, frame, update_function):
def _update_receiver_box(frame, receiver):
label, pairing_icon, toolbar, info_label = ui.find_children(frame, 'label', 'pairing-icon', 'toolbar', 'info-label')
icon, label, pairing_icon, toolbar, info_label = ui.find_children(frame, 'icon', 'label', 'pairing-icon', 'toolbar', 'info-label')
if receiver.status is None:
label.set_text(str(receiver.status))
if receiver:
frame._device = receiver
icon.set_sensitive(True)
pairing_icon.set_visible(receiver.status.lock_open)
toolbar.set_visible(True)
else:
frame._device = None
label.set_text('No receiver found.')
icon.set_sensitive(False)
pairing_icon.set_visible(False)
toolbar.set_visible(False)
toolbar.get_children()[0].set_active(False)
info_label.set_text('')
else:
frame._device = receiver
label.set_text(str(receiver.status))
pairing_icon.set_visible(receiver.status.lock_open)
toolbar.set_visible(True)
def _update_device_box(frame, dev):

View File

@ -6,9 +6,9 @@ import logging
from gi.repository import (Gtk, GObject)
import ui
from logitech.unifying_receiver import status as _status
_PAIRING_TIMEOUT = 30
_PAIRING_TIMEOUT = 15
def _create_page(assistant, kind, header=None, icon_name=None, text=None):
@ -40,29 +40,31 @@ def _create_page(assistant, kind, header=None, icon_name=None, text=None):
return p
def _fake_device(receiver):
from logitech.unifying_receiver import PairedDevice
dev = PairedDevice(receiver, 6)
dev._kind = 'touchpad'
dev._codename = 'T650'
dev._name = 'Wireless Rechargeable Touchpad T650'
dev._serial = '0123456789'
dev._protocol = 2.0
dev.status = {'encrypted': False}
return dev
# def _fake_device(receiver):
# from logitech.unifying_receiver import PairedDevice
# dev = PairedDevice(receiver, 6)
# dev._kind = 'touchpad'
# dev._codename = 'T650'
# dev._name = 'Wireless Rechargeable Touchpad T650'
# dev._serial = '0123456789'
# dev._protocol = 2.0
# dev.status = _status.DeviceStatus(dev, lambda *foo: None)
# return dev
def _check_lock_state(assistant, receiver):
if not assistant.is_drawable():
return False
if receiver.pairing_result:
receiver.pairing_result = _fake_device(receiver)
if type(receiver.pairing_result) == str:
_pairing_failed(assistant, receiver, receiver.pairing_result)
else:
assert hasattr(receiver.pairing_result, 'number')
_pairing_succeeded(assistant, receiver, receiver.pairing_result)
if receiver.status.get(_status.ERROR):
# fake = _fake_device(receiver)
# receiver._devices[fake.number] = fake
# receiver.status.new_device = fake
# fake.status._changed()
_pairing_failed(assistant, receiver, receiver.status.pop(_status.ERROR))
return False
if receiver.status.new_device:
_pairing_succeeded(assistant, receiver)
return False
return receiver.status.lock_open
@ -73,11 +75,12 @@ def _prepare(assistant, page, receiver):
# logging.debug("prepare %s %d %s", assistant, index, page)
if index == 0:
receiver.pairing_result = None
if receiver.set_lock(False, timeout=_PAIRING_TIMEOUT):
assert receiver.status.new_device is None
assert receiver.status.get(_status.ERROR) is None
spinner = page.get_children()[-1]
spinner.start()
GObject.timeout_add(200, _check_lock_state, assistant, receiver)
GObject.timeout_add(300, _check_lock_state, assistant, receiver)
assistant.set_page_complete(page, True)
else:
GObject.idle_add(_pairing_failed, assistant, receiver, 'the pairing lock did not open')
@ -88,6 +91,7 @@ def _prepare(assistant, page, receiver):
def _finish(assistant, receiver):
logging.debug("finish %s", assistant)
assistant.destroy()
receiver.status.new_device = None
if receiver.status.lock_open:
receiver.set_lock()
@ -95,9 +99,8 @@ def _finish(assistant, receiver):
def _cancel(assistant, receiver):
logging.debug("cancel %s", assistant)
assistant.destroy()
device, receiver.pairing_result = receiver.pairing_result, None
device, receiver.status.new_device = receiver.status.new_device, None
if device:
assert type(device) != str
try:
del receiver[device.number]
except:
@ -107,11 +110,10 @@ def _cancel(assistant, receiver):
def _pairing_failed(assistant, receiver, error):
receiver.pairing_result = None
assistant.commit()
header = 'Pairing failed: %s.' % error
if 'timeout' in error:
if 'timeout' in str(error):
text = 'Make sure your device is within range,\nand it has a decent battery charge.'
else:
text = None
@ -121,7 +123,9 @@ def _pairing_failed(assistant, receiver, error):
assistant.commit()
def _pairing_succeeded(assistant, receiver, device):
def _pairing_succeeded(assistant, receiver):
device = receiver.status.new_device
assert device
page = _create_page(assistant, Gtk.AssistantPageType.CONFIRM)
device_icon = Gtk.Image()

View File

@ -41,6 +41,7 @@ class ReceiverStatus(dict):
# self.updated = 0
self.lock_open = False
self.new_device = None
self[ERROR] = None
def __str__(self):
@ -58,13 +59,20 @@ class ReceiverStatus(dict):
self.lock_open = bool(event.address & 0x01)
reason = 'pairing lock is ' + ('open' if self.lock_open else 'closed')
_log.info("%s: %s", self._receiver, reason)
if self.lock_open:
self[ERROR] = None
self.new_device = None
pair_error = ord(event.data[:1])
if pair_error:
self[ERROR] = _hidpp10.PAIRING_ERRORS[pair_error]
self.new_device = None
_log.warn("pairing error %d: %s", pair_error, self[ERROR])
else:
self[ERROR] = None
self._changed(reason=reason)
return True
#
#
@ -128,8 +136,9 @@ class DeviceStatus(dict):
self._device = None
else:
_log.warn("device %d disconnection notification %s with unknown type %02X", self._device.number, event, event.address)
return True
elif event.sub_id == 0x41:
if event.sub_id == 0x41:
if event.address == 0x04: # unifying protocol
# wpid = _strhex(event.data[4:5] + event.data[3:4])
# assert wpid == device.wpid
@ -151,81 +160,91 @@ class DeviceStatus(dict):
else:
_log.warn("device %d connection notification %s with unknown protocol %02X", self._device.number, event, event.address)
elif event.sub_id < 0x40:
# a feature event, assuming no device has more than 0x40 features
if event.sub_id >= len(self._device.features):
_log.warn("device %d got event from unknown feature index %02X", self._device.number, event.sub_id)
return None
return True
feature = self._device.features[event.sub_id]
if event.sub_id >= 0x40:
# this can't possibly be an event, can it?
if _log.isEnabledFor(_DEBUG):
_log.debug("ignoring non-event %s", event)
return False
if feature == _hidpp20.FEATURE.BATTERY:
if event.address == 0x00:
discharge = ord(event.data[:1])
battery_status = ord(event.data[1:2])
self[BATTERY_LEVEL] = discharge
self[BATTERY_STATUS] = BATTERY_STATUS[battery_status]
if _hidpp20.BATTERY_OK(battery_status):
alert = ALERT.NONE
reason = self[ERROR] = None
else:
alert = ALERT.MED
reason = self[ERROR] = self[BATTERY_STATUS]
self._changed(alert=alert, reason=reason)
# this must be a feature event, assuming no device has more than 0x40 features
if event.sub_id >= len(self._device.features):
_log.warn("device %d got event from unknown feature index %02X", self._device.number, event.sub_id)
return False
feature = self._device.features[event.sub_id]
if feature == _hidpp20.FEATURE.BATTERY:
if event.address == 0x00:
discharge = ord(event.data[:1])
battery_status = ord(event.data[1:2])
self[BATTERY_LEVEL] = discharge
self[BATTERY_STATUS] = BATTERY_STATUS[battery_status]
if _hidpp20.BATTERY_OK(battery_status):
alert = ALERT.NONE
reason = self[ERROR] = None
else:
_log.warn("don't know how to handle BATTERY event %s", event)
elif feature == _hidpp20.FEATURE.REPROGRAMMABLE_KEYS:
if event.address == 0x00:
_log.debug('reprogrammable key: %s', event)
else:
_log.warn("don't know how to handle REPROGRAMMABLE KEYS event %s", event)
elif feature == _hidpp20.FEATURE.WIRELESS:
if event.address == 0x00:
_log.debug("wireless status: %s", event)
if event.data[0:3] == b'\x01\x01\x01':
self._changed(alert=ALERT.LOW, reason='powered on')
else:
_log.warn("don't know how to handle WIRELESS event %s", event)
elif feature == _hidpp20.FEATURE.SOLAR_CHARGE:
if event.data[5:9] == b'GOOD':
charge, lux, adc = _unpack('!BHH', event.data[:5])
self[BATTERY_LEVEL] = charge
# guesstimate the battery voltage, emphasis on 'guess'
self[BATTERY_STATUS] = '%1.2fV' % (adc * 2.67793237653 / 0x0672)
if event.address == 0x00:
self[LIGHT_LEVEL] = None
self._changed()
elif event.address == 0x10:
self[LIGHT_LEVEL] = lux
if lux > 200: # guesstimate
self[BATTERY_STATUS] += ', charging'
self._changed()
elif event.address == 0x20:
_log.debug("Solar key pressed")
# first cancel any reporting
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE)
reports_count = 10
reports_period = 3 # seconds
self._changed(alert=ALERT.MED)
# trigger a new report chain
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE, 0x00, reports_count, reports_period)
else:
self._changed()
else:
_log.warn("SOLAR_CHARGE event not GOOD? %s", event)
elif feature == _hidpp20.FEATURE.TOUCH_MOUSE:
if event.address == 0x00:
_log.debug("TOUCH MOUSE points event: %s", event)
elif event.address == 0x10:
touch = ord(event.data[:1])
button_down = bool(touch & 0x02)
mouse_lifted = bool(touch & 0x01)
_log.debug("TOUCH MOUSE status: button_down=%s mouse_lifted=%s", button_down, mouse_lifted)
alert = ALERT.MED
reason = self[ERROR] = self[BATTERY_STATUS]
self._changed(alert=alert, reason=reason)
else:
_log.warn("don't know how to handle event %s", event)
_log.warn("don't know how to handle BATTERY event %s", event)
return True
if feature == _hidpp20.FEATURE.REPROGRAMMABLE_KEYS:
if event.address == 0x00:
_log.debug('reprogrammable key: %s', event)
else:
_log.warn("don't know how to handle REPROGRAMMABLE KEYS event %s", event)
return True
if feature == _hidpp20.FEATURE.WIRELESS:
if event.address == 0x00:
_log.debug("wireless status: %s", event)
if event.data[0:3] == b'\x01\x01\x01':
self._changed(alert=ALERT.LOW, reason='powered on')
else:
_log.warn("don't know how to handle WIRELESS event %s", event)
return True
if feature == _hidpp20.FEATURE.SOLAR_CHARGE:
if event.data[5:9] == b'GOOD':
charge, lux, adc = _unpack('!BHH', event.data[:5])
self[BATTERY_LEVEL] = charge
# guesstimate the battery voltage, emphasis on 'guess'
self[BATTERY_STATUS] = '%1.2fV' % (adc * 2.67793237653 / 0x0672)
if event.address == 0x00:
self[LIGHT_LEVEL] = None
self._changed()
elif event.address == 0x10:
self[LIGHT_LEVEL] = lux
if lux > 200: # guesstimate
self[BATTERY_STATUS] += ', charging'
self._changed()
elif event.address == 0x20:
_log.debug("Solar key pressed")
# first cancel any reporting
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE)
reports_count = 10
reports_period = 3 # seconds
self._changed(alert=ALERT.MED)
# trigger a new report chain
self._device.feature_request(_hidpp20.FEATURE.SOLAR_CHARGE, 0x00, reports_count, reports_period)
else:
self._changed()
else:
_log.warn("SOLAR_CHARGE event not GOOD? %s", event)
return True
if feature == _hidpp20.FEATURE.TOUCH_MOUSE:
if event.address == 0x00:
_log.debug("TOUCH MOUSE points event: %s", event)
elif event.address == 0x10:
touch = ord(event.data[:1])
button_down = bool(touch & 0x02)
mouse_lifted = bool(touch & 0x01)
_log.debug("TOUCH MOUSE status: button_down=%s mouse_lifted=%s", button_down, mouse_lifted)
return True
_log.warn("don't know how to handle event %s", event)