Rename "pair" to "register"

"pair" is alrady taken by Bluetooth and since we have a bluetooth device here,
it can cause confusion. Use "register" instead, with an explanation in the
README for the more paranoid of us.

Fixes #67
This commit is contained in:
Peter Hutterer 2018-02-02 09:28:26 +10:00 committed by Benjamin Tissoires
parent fc219e4c57
commit 5e82d1b378
5 changed files with 167 additions and 155 deletions

View File

@ -16,21 +16,33 @@ Devices tested and known to be supported:
* Bamboo Spark * Bamboo Spark
* Bamboo Slate * Bamboo Slate
Warning Registering devices
------- -------------------
A device can only be paired with one application at a time. Thus, when a For a device to work with Tuhi, it must be registered first. This is
device is paired with Tuhi, other applications (e.g. Wacom Inkspace) achieved by holiding the device button for 6 or more seconds until the blue
cannot not connect to the device anymore. Likewise, when paired with another LED starts blinking. Only in that mode can Tuhi detect it during
application, Tuhi cannot connect. `Searching` and register it.
To make the tablet connect again, simply re-pair with the respective Registration sends a randomly generated UUID to the device. Subsequent
connections must use that UUID as identifier for the tablet device to
respond. Without knowing that UUID, other applications cannot connect.
A device can only be registered with one application at a time. Thus, when a
device is registered with Tuhi, other applications (e.g. Wacom Inkspace)
cannot not connect to the device anymore. Likewise, when registered with
another application, Tuhi cannot connect.
To make the tablet connect again, simply re-register with the respective
application or Tuhi, whichever desired. application or Tuhi, whichever desired.
The reason for this behavior is that pairing assigns a application-generated This is not registering the device with some cloud service, vendor, or
unique UUID to the device. Subsequent connections must use that UUID for the other networked service. It is a communication between Tuhi and the firmware
tablet device to respond. Without knowing that UUID, other applications on the device only. It is merely a process of "your ID is now $foo" followed
cannot connect. by "hi $foo, I want to connect".
The word "register" was chosen because "pairing" is already in use by
Bluetooth.
Installation Installation
------------ ------------
@ -64,36 +76,35 @@ The following interfaces are provided:
org.freedesktop.tuhi1.Manager org.freedesktop.tuhi1.Manager
Property: Devices (ao) Property: Devices (ao)
Array of object paths to known (previously paired, but not necessarily Array of object paths to known (previously registered, but not necessarily
connected) devices. Note that a "paired" device is one that has been connected) devices. Note that a "registered" device is one that has been
initialized via the Wacom SmartPad custom protocol. This initialized via the Wacom SmartPad custom protocol. A device does not
initialization is independent of the Bluetooth pairing process. A Tuhi need to be paired over Bluetooth to register.
paired device may or may not be paired over Bluetooth.
Property: Searching (b) Property: Searching (b)
Indicates whether the daemon is currently searching for pairable devices. Indicates whether the daemon is currently searching for devices.
This property is set to True when a StartSearching() request initiates This property is set to True when a StartSearching() request initiates
the search for device connections. When the StartSearching() request the search for device connections. When the StartSearching() request
completes upon timeout, or when StopSearching() is called, the property completes upon timeout, or when StopSearching() is called, the property
is set to False. is set to False.
When a pariable device is found, the PairableDevice signal is sent to When a pariable device is found, the UnregisteredDevice signal is sent to
the caller that initiated the search process. the caller that initiated the search process.
Read-only Read-only
Method: StartSearch() -> () Method: StartSearch() -> ()
Start searching for available devices in pairing mode for an Start searching for available devices ready for registering
unspecified timeout. When the timeout expires or an error occurs, a for an unspecified timeout. When the timeout expires or an error
SearchStopped signal is sent indicating success or error. occurs, a SearchStopped signal is sent indicating success or error.
If a client that successfully initated a listening process calls If a client that successfully initated a listening process calls
StartSearching() again, that call is ignored and no signal is StartSearching() again, that call is ignored and no signal is
generated for that call. generated for that call.
Method: StopSearch() -> () Method: StopSearch() -> ()
Stop listening to available devices in pairing mode. If called after Stop listening to available devices ready for registering. If called after
StartSearch() and before a SearchStopped signal has been received, StartSearch() and before a SearchStopped signal has been received,
this method triggers the SearchStopped signal. That signal indicates this method triggers the SearchStopped signal. That signal indicates
success or an error. success or an error.
@ -102,10 +113,10 @@ org.freedesktop.tuhi1.Manager
SearchStopped signal, it is ignored and no signal is generated. SearchStopped signal, it is ignored and no signal is generated.
Note that between calling StopSearch() and the SearchStopped signal Note that between calling StopSearch() and the SearchStopped signal
arriving, PairableDevice signals may still arrive. arriving, UnregisteredDevice signals may still arrive.
Signal: PairableDevice(o) Signal: UnregisteredDevice(o)
Indicates that a device is available for pairing. This signal may be Indicates that a device can be registered. This signal may be
sent after a StartSearch() call and before SearchStopped(). This sent after a StartSearch() call and before SearchStopped(). This
signal is sent once per available device and only to the client that signal is sent once per available device and only to the client that
initiated the search process with StartSearch. initiated the search process with StartSearch.
@ -113,18 +124,18 @@ org.freedesktop.tuhi1.Manager
When this signal is sent, a org.freedesktop.tuhi1.Device object was When this signal is sent, a org.freedesktop.tuhi1.Device object was
created, the object path is the argument to this signal. created, the object path is the argument to this signal.
A client must immediately call Pair() on that object if pairing with A client must immediately call Register() on that object if
that object is desired. See the documentation for that interface registering with that object is desired. See the documentation for
for details. that interface for details.
When the search timeout expires, the device may be removed by the When the search timeout expires, the device may be removed by the
daemon again. Note that until the device is paired, the device is not daemon again. Note that until the device is registered, the device is not
listed in the managers Devices property. listed in the managers Devices property.
Signal: SearchStopped(i) Signal: SearchStopped(i)
Sent when the search has stopped. An argument of 0 indicates a Sent when the search has stopped. An argument of 0 indicates a
successful termination of the search process, either when a device successful termination of the search process, either when a device
has been paired or the timeout expired. has been registered or the timeout expired.
If the errno is -EAGAIN, the daemon is already searching for devices If the errno is -EAGAIN, the daemon is already searching for devices
on behalf of another client. In this case, this client should wait for on behalf of another client. In this case, this client should wait for
@ -132,8 +143,8 @@ org.freedesktop.tuhi1.Manager
property is set to False. property is set to False.
Once this signal has been sent, all devices announced through Once this signal has been sent, all devices announced through
PairableDevice signals should be considered invalidated. Attempting to UnregisteredDevice signals should be considered invalidated. Attempting to
Pair() one of the devices after the SearchStopped() signal may result Register() one of the devices after the SearchStopped() signal may result
in an error. in an error.
In case of error, the argument is a negative errno. In case of error, the argument is a negative errno.
@ -208,11 +219,11 @@ org.freedesktop.tuhi1.Device
Read-only Read-only
Method: Pair() -> (i) Method: Register() -> (i)
Pair the device. If the device is already paired, calls to this method Register the device. If the device is already registered, calls to
immediately return success. this method immediately return success.
Otherwise, the device is paired and this function returns success (0) Otherwise, the device is registered and this function returns success (0)
or a negative errno on failure. or a negative errno on failure.
Method: StartListening() -> () Method: StartListening() -> ()
@ -270,14 +281,14 @@ org.freedesktop.tuhi1.Device
the Listening property to change and StartListening() once the the Listening property to change and StartListening() once the
property is set to False. property is set to False.
If the error is -EBADE, the device is not in pairing/listening mode If the error is -EBADE, the device is not ready for registering/in
and pairing/listening was requested. In this case, the client should listening mode and registration/listening was requested. In
indicate to the user that the device needs to be paired first or this case, the client should indicate to the user that the device
switched to listening mode. needs to be registered first or switched to listening mode.
If the error is -EACCES, the device is not paired with the daemon or If the error is -EACCES, the device is not registered with the daemon
incorrectly paired. This may happen when the device was paired with or incorrectly registered. This may happen when the device was
another host since the last connection. registered with another host since the last connection.
The following other errnos may be sent by the daemon: The following other errnos may be sent by the daemon:
-EPROTO: the daemon has encountered a protocol error with the device. -EPROTO: the daemon has encountered a protocol error with the device.

View File

@ -168,7 +168,7 @@ class TuhiKeteDevice(_DBusObject):
ORG_FREEDESKTOP_TUHI1_DEVICE, ORG_FREEDESKTOP_TUHI1_DEVICE,
objpath) objpath)
self.manager = manager self.manager = manager
self.is_pairing = False self.is_registering = False
self._bluez_device = BlueZDevice(self.property('BlueZDevice')) self._bluez_device = BlueZDevice(self.property('BlueZDevice'))
@classmethod @classmethod
@ -201,13 +201,13 @@ class TuhiKeteDevice(_DBusObject):
def battery_state(self): def battery_state(self):
return self.property('BatteryState') return self.property('BatteryState')
def pair(self): def register(self):
logger.debug(f'{self}: Pairing') logger.debug(f'{self}: Register')
# FIXME: Pair() doesn't return anything useful yet, so we wait until # FIXME: Register() doesn't return anything useful yet, so we wait until
# the device is in the Manager's Devices property # the device is in the Manager's Devices property
self.s1 = self.manager.connect('notify::devices', self._on_mgr_devices_updated) self.s1 = self.manager.connect('notify::devices', self._on_mgr_devices_updated)
self.is_pairing = True self.is_registering = True
self.proxy.Pair() self.proxy.Register()
def start_listening(self): def start_listening(self):
self.proxy.StartListening() self.proxy.StartListening()
@ -224,7 +224,7 @@ class TuhiKeteDevice(_DBusObject):
elif signal == 'ListeningStopped': elif signal == 'ListeningStopped':
err = parameters[0] err = parameters[0]
if err == -errno.EACCES: if err == -errno.EACCES:
logger.error(f'{self}: wrong device, please redo pairing.') logger.error(f'{self}: wrong device, please re-register.')
elif err < 0: elif err < 0:
logger.error(f'{self}: an error occured: {os.strerror(-err)}') logger.error(f'{self}: an error occured: {os.strerror(-err)}')
self.notify('listening') self.notify('listening')
@ -248,15 +248,15 @@ class TuhiKeteDevice(_DBusObject):
return f'{self.address} - {self.name}' return f'{self.address} - {self.name}'
def _on_mgr_devices_updated(self, manager, pspec): def _on_mgr_devices_updated(self, manager, pspec):
if not self.is_pairing: if not self.is_registering:
return return
for d in manager.devices: for d in manager.devices:
if d.address == self.address: if d.address == self.address:
self.is_pairing = False self.is_registering = False
self.manager.disconnect(self.s1) self.manager.disconnect(self.s1)
del(self.s1) del(self.s1)
logger.info(f'{self}: Pairing successful') logger.info(f'{self}: Registration successful')
def terminate(self): def terminate(self):
try: try:
@ -269,7 +269,7 @@ class TuhiKeteDevice(_DBusObject):
class TuhiKeteManager(_DBusObject): class TuhiKeteManager(_DBusObject):
__gsignals__ = { __gsignals__ = {
'pairable-device': 'unregistered-device':
(GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)), (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
} }
@ -279,7 +279,7 @@ class TuhiKeteManager(_DBusObject):
ROOT_PATH) ROOT_PATH)
self._devices = {} self._devices = {}
self._pairable_devices = {} self._unregistered_devices = {}
for objpath in self.property('Devices'): for objpath in self.property('Devices'):
device = TuhiKeteDevice(self, objpath) device = TuhiKeteDevice(self, objpath)
@ -290,15 +290,15 @@ class TuhiKeteManager(_DBusObject):
return [v for k, v in self._devices.items()] return [v for k, v in self._devices.items()]
@GObject.Property @GObject.Property
def pairable_devices(self): def unregistered_devices(self):
return [v for k, v in self._pairable_devices.items()] return [v for k, v in self._unregistered_devices.items()]
@GObject.Property @GObject.Property
def searching(self): def searching(self):
return self.proxy.get_cached_property('Searching') return self.proxy.get_cached_property('Searching')
def start_search(self): def start_search(self):
self._pairable_devices = {} self._unregistered_devices = {}
self.proxy.StartSearch() self.proxy.StartSearch()
def stop_search(self): def stop_search(self):
@ -309,13 +309,13 @@ class TuhiKeteManager(_DBusObject):
e.code != Gio.IOErrorEnum.EXISTS or e.code != Gio.IOErrorEnum.EXISTS or
Gio.dbus_error_get_remote_error(e) != 'org.freedesktop.DBus.Error.ServiceUnknown'): Gio.dbus_error_get_remote_error(e) != 'org.freedesktop.DBus.Error.ServiceUnknown'):
raise e raise e
self._pairable_devices = {} self._unregistered_devices = {}
def terminate(self): def terminate(self):
for dev in self._devices.values(): for dev in self._devices.values():
dev.terminate() dev.terminate()
self._devices = {} self._devices = {}
self._pairable_devices = {} self._unregistered_devices = {}
super(TuhiKeteManager, self).terminate() super(TuhiKeteManager, self).terminate()
def _on_properties_changed(self, proxy, changed_props, invalidated_props): def _on_properties_changed(self, proxy, changed_props, invalidated_props):
@ -328,24 +328,24 @@ class TuhiKeteManager(_DBusObject):
objpaths = changed_props['Devices'] objpaths = changed_props['Devices']
for objpath in objpaths: for objpath in objpaths:
try: try:
d = self._pairable_devices[objpath] d = self._unregistered_devices[objpath]
self._devices[d.address] = d self._devices[d.address] = d
del self._pairable_devices[objpath] del self._unregistered_devices[objpath]
except KeyError: except KeyError:
# if we called Pair() on an existing device it's not in # if we called Register() on an existing device it's not
# pairable devices # in unregistered devices
pass pass
self.notify('devices') self.notify('devices')
def _on_signal_received(self, proxy, sender, signal, parameters): def _on_signal_received(self, proxy, sender, signal, parameters):
if signal == 'SearchStopped': if signal == 'SearchStopped':
self.notify('searching') self.notify('searching')
elif signal == 'PairableDevice': elif signal == 'UnregisteredDevice':
objpath = parameters[0] objpath = parameters[0]
device = TuhiKeteDevice(self, objpath) device = TuhiKeteDevice(self, objpath)
self._pairable_devices[objpath] = device self._unregistered_devices[objpath] = device
logger.debug(f'Found pairable device: {device}') logger.debug(f'Found unregistered device: {device}')
self.emit('pairable-device', device) self.emit('unregistered-device', device)
def __getitem__(self, btaddr): def __getitem__(self, btaddr):
return self._devices[btaddr] return self._devices[btaddr]
@ -374,7 +374,7 @@ class Searcher(Worker):
def __init__(self, manager, args): def __init__(self, manager, args):
super(Searcher, self).__init__(manager) super(Searcher, self).__init__(manager)
self.s1 = self.manager.connect('notify::searching', self._on_notify_search) self.s1 = self.manager.connect('notify::searching', self._on_notify_search)
self.s2 = self.manager.connect('pairable-device', self._on_pairable_device) self.s2 = self.manager.connect('unregistered-device', self._on_unregistered_device)
def run(self): def run(self):
if self.manager.searching: if self.manager.searching:
@ -385,7 +385,7 @@ class Searcher(Worker):
logger.debug('Started searching') logger.debug('Started searching')
for d in self.manager.devices: for d in self.manager.devices:
self._on_pairable_device(self.manager, d) self._on_unregistered_device(self.manager, d)
def stop(self): def stop(self):
if self.manager.searching: if self.manager.searching:
@ -399,8 +399,8 @@ class Searcher(Worker):
if not manager.searching: if not manager.searching:
logger.info('Search cancelled') logger.info('Search cancelled')
def _on_pairable_device(self, manager, device): def _on_unregistered_device(self, manager, device):
logger.info(f'Pairable device: {device}') logger.info(f'Unregistered device: {device}')
class Listener(Worker): class Listener(Worker):
@ -655,7 +655,8 @@ class TuhiKeteShell(cmd.Cmd):
self._workers = [] self._workers = []
def do_devices(self, arg): def do_devices(self, arg):
'''List known devices. These are devices previously paired with the daemon.''' '''List known devices. These are devices previously registered with
the daemon.'''
logger.debug('Listing available devices:') logger.debug('Listing available devices:')
for d in self._manager.devices: for d in self._manager.devices:
print(d) print(d)
@ -840,8 +841,8 @@ class TuhiKeteShell(cmd.Cmd):
def do_search(self, args): def do_search(self, args):
desc = ''' desc = '''
Start/Stop listening for devices that can be paired with the daemon. Start/Stop listening for devices that can be registered with the
The devices must be in pairable mode (blue LED blinking). daemon. The devices must be in registration mode (blue LED blinking).
''' '''
parser = argparse.ArgumentParser(prog='search', parser = argparse.ArgumentParser(prog='search',
description=desc, description=desc,
@ -869,10 +870,10 @@ class TuhiKeteShell(cmd.Cmd):
else: else:
logger.info('Already searching') logger.info('Already searching')
def help_pair(self): def help_register(self):
self.do_pair('-h') self.do_register('-h')
def complete_pair(self, text, line, begidx, endidx): def complete_register(self, text, line, begidx, endidx):
# mark the end of the line so we can match on the number of fields # mark the end of the line so we can match on the number of fields
if line.endswith(' '): if line.endswith(' '):
line += 'm' line += 'm'
@ -880,29 +881,29 @@ class TuhiKeteShell(cmd.Cmd):
completion = [] completion = []
if len(fields) == 2: if len(fields) == 2:
for device in self._manager.pairable_devices + self._manager.devices: for device in self._manager.unregistered_devices + self._manager.devices:
if device.address.startswith(text.upper()): if device.address.startswith(text.upper()):
completion.append(device.address) completion.append(device.address)
return completion return completion
def do_pair(self, args): def do_register(self, args):
if not self._manager.searching and '-h' not in args.split(): if not self._manager.searching and '-h' not in args.split():
print('please call search first') print('please call search first')
return return
desc = ''' desc = '''
Pair the given device. The device must be in pairable mode (blue LED Register the given device. The device must be in registration mode
blinking). (blue LED blinking).
''' '''
parser = argparse.ArgumentParser(prog='pair', parser = argparse.ArgumentParser(prog='register',
description=desc, description=desc,
add_help=False) add_help=False)
parser.add_argument('-h', action='help', help=argparse.SUPPRESS) parser.add_argument('-h', action='help', help=argparse.SUPPRESS)
parser.add_argument('address', metavar='12:34:56:AB:CD:EF', parser.add_argument('address', metavar='12:34:56:AB:CD:EF',
type=TuhiKeteDevice.is_device_address, type=TuhiKeteDevice.is_device_address,
default=None, default=None,
help='the address of the device to pair') help='the address of the device to register')
try: try:
parsed_args = parser.parse_args(args.split()) parsed_args = parser.parse_args(args.split())
@ -918,7 +919,7 @@ class TuhiKeteShell(cmd.Cmd):
if worker.device.address == address: if worker.device.address == address:
self.terminate_worker(worker) self.terminate_worker(worker)
for d in self._manager.devices + self._manager.pairable_devices: for d in self._manager.devices + self._manager.unregistered_devices:
if d.address == address: if d.address == address:
device = d device = d
break break
@ -926,7 +927,7 @@ class TuhiKeteShell(cmd.Cmd):
logger.error(f'{address}: device not found') logger.error(f'{address}: device not found')
return return
device.pair() device.register()
def help_info(self): def help_info(self):
self.do_info('-h') self.do_info('-h')

View File

@ -51,13 +51,13 @@ class TuhiDevice(GObject.Object):
BATTERY_UPDATE_MIN_INTERVAL = 300 BATTERY_UPDATE_MIN_INTERVAL = 300
def __init__(self, bluez_device, config, uuid=None, paired=True): def __init__(self, bluez_device, config, uuid=None, registered=True):
GObject.Object.__init__(self) GObject.Object.__init__(self)
self.config = config self.config = config
self._wacom_device = None self._wacom_device = None
# We need either uuid or paired as false # We need either uuid or registered as false
assert uuid is not None or paired is False assert uuid is not None or registered is False
self.paired = paired self.registered = registered
self._uuid = uuid self._uuid = uuid
self._battery_state = TuhiDevice.BatteryState.UNKNOWN self._battery_state = TuhiDevice.BatteryState.UNKNOWN
self._battery_percent = 0 self._battery_percent = 0
@ -71,12 +71,12 @@ class TuhiDevice(GObject.Object):
self._tuhi_dbus_device = None self._tuhi_dbus_device = None
@GObject.Property @GObject.Property
def paired(self): def registered(self):
return self._paired return self._registered
@paired.setter @registered.setter
def paired(self, paired): def registered(self, registered):
self._paired = paired self._registered = registered
@GObject.Property @GObject.Property
def name(self): def name(self):
@ -98,7 +98,7 @@ class TuhiDevice(GObject.Object):
def dbus_device(self, device): def dbus_device(self, device):
assert self._tuhi_dbus_device is None assert self._tuhi_dbus_device is None
self._tuhi_dbus_device = device self._tuhi_dbus_device = device
self._tuhi_dbus_device.connect('pair-requested', self._on_pair_requested) self._tuhi_dbus_device.connect('register-requested', self._on_register_requested)
self._tuhi_dbus_device.connect('notify::listening', self._on_listening_updated) self._tuhi_dbus_device.connect('notify::listening', self._on_listening_updated)
drawings = self.config.load_drawings(self.address) drawings = self.config.load_drawings(self.address)
@ -140,14 +140,14 @@ class TuhiDevice(GObject.Object):
self._wacom_device.connect('notify::uuid', self._on_uuid_updated, bluez_device) self._wacom_device.connect('notify::uuid', self._on_uuid_updated, bluez_device)
self._wacom_device.connect('battery-status', self._on_battery_status, bluez_device) self._wacom_device.connect('battery-status', self._on_battery_status, bluez_device)
self._wacom_device.start(not self.paired) self._wacom_device.start(not self.registered)
self.pairing_mode = False self.registering_mode = False
def _on_bluez_device_disconnected(self, bluez_device): def _on_bluez_device_disconnected(self, bluez_device):
logger.debug(f'{bluez_device.address}: disconnected') logger.debug(f'{bluez_device.address}: disconnected')
def _on_pair_requested(self, dbus_device): def _on_register_requested(self, dbus_device):
if self.paired: if self.registered:
return return
self.connect_device() self.connect_device()
@ -168,7 +168,7 @@ class TuhiDevice(GObject.Object):
def _on_uuid_updated(self, wacom_device, pspec, bluez_device): def _on_uuid_updated(self, wacom_device, pspec, bluez_device):
self.config.new_device(bluez_device.address, wacom_device.uuid) self.config.new_device(bluez_device.address, wacom_device.uuid)
self.paired = True self.registered = True
def _on_listening_updated(self, dbus_device, pspec): def _on_listening_updated(self, dbus_device, pspec):
self.notify('listening') self.notify('listening')
@ -243,7 +243,7 @@ class Tuhi(GObject.Object):
self._search_device_handler = None self._search_device_handler = None
@classmethod @classmethod
def _is_pairing_device(cls, bluez_device): def _device_in_register_mode(cls, bluez_device):
if bluez_device.vendor_id != WACOM_COMPANY_ID: if bluez_device.vendor_id != WACOM_COMPANY_ID:
return False return False
@ -278,29 +278,29 @@ class Tuhi(GObject.Object):
# if event is set, the device has been 'hotplugged' in the bluez stack # if event is set, the device has been 'hotplugged' in the bluez stack
# so ManufacturerData is reliable. Else, consider the device not in # so ManufacturerData is reliable. Else, consider the device not in
# the pairing mode # the register mode
pairing_device = False register_mode = False
if event: if event:
pairing_device = Tuhi._is_pairing_device(bluez_device) register_mode = Tuhi._device_in_register_mode(bluez_device)
if not pairing_device: if not register_mode:
if uuid is None: if uuid is None:
logger.info(f'{bluez_device.address}: device without config, must be paired first') logger.info(f'{bluez_device.address}: device without config, must be registered first')
return return
logger.debug(f'{bluez_device.address}: UUID {uuid}') logger.debug(f'{bluez_device.address}: UUID {uuid}')
# create the device if unknown from us # create the device if unknown from us
if bluez_device.address not in self.devices: if bluez_device.address not in self.devices:
d = TuhiDevice(bluez_device, self.config, uuid=uuid, paired=not pairing_device) d = TuhiDevice(bluez_device, self.config, uuid=uuid, registered=not register_mode)
d.dbus_device = self.server.create_device(d) d.dbus_device = self.server.create_device(d)
d.connect('notify::listening', self._on_listening_updated) d.connect('notify::listening', self._on_listening_updated)
self.devices[bluez_device.address] = d self.devices[bluez_device.address] = d
d = self.devices[bluez_device.address] d = self.devices[bluez_device.address]
if pairing_device: if register_mode:
d.paired = False d.registered = False
logger.debug(f'{bluez_device.objpath}: call Pair() on device') logger.debug(f'{bluez_device.objpath}: call Register() on device')
elif d.listening: elif d.listening:
d.connect_device() d.connect_device()

View File

@ -41,7 +41,7 @@ INTROSPECTION_XML = '''
<arg name='status' type='i' /> <arg name='status' type='i' />
</signal> </signal>
<signal name='PairableDevice'> <signal name='UnregisteredDevice'>
<arg name='info' type='o' /> <arg name='info' type='o' />
</signal> </signal>
</interface> </interface>
@ -62,7 +62,7 @@ INTROSPECTION_XML = '''
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/> <annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
</property> </property>
<method name='Pair'> <method name='Register'>
<arg name='result' type='i' direction='out'/> <arg name='result' type='i' direction='out'/>
</method> </method>
@ -134,7 +134,7 @@ class TuhiDBusDevice(_TuhiDBus):
handles the DBus bits, communication with the device is done elsewhere. handles the DBus bits, communication with the device is done elsewhere.
''' '''
__gsignals__ = { __gsignals__ = {
'pair-requested': 'register-requested':
(GObject.SignalFlags.RUN_FIRST, None, ()), (GObject.SignalFlags.RUN_FIRST, None, ()),
} }
@ -147,13 +147,13 @@ class TuhiDBusDevice(_TuhiDBus):
self.name = device.name self.name = device.name
self.width, self.height = 0, 0 self.width, self.height = 0, 0
self.drawings = {} self.drawings = {}
self.paired = device.paired self.registered = device.registered
self._listening = False self._listening = False
self._listening_client = None self._listening_client = None
self._dbusid = self._register_object(connection) self._dbusid = self._register_object(connection)
self._battery_percent = 0 self._battery_percent = 0
self._battery_state = device.battery_state self._battery_state = device.battery_state
device.connect('notify::paired', self._on_device_paired) device.connect('notify::registered', self._on_device_registered)
device.connect('notify::battery-percent', self._on_battery_percent) device.connect('notify::battery-percent', self._on_battery_percent)
device.connect('notify::battery-state', self._on_battery_state) device.connect('notify::battery-state', self._on_battery_state)
device.connect('device-error', self._on_device_error) device.connect('device-error', self._on_device_error)
@ -171,12 +171,12 @@ class TuhiDBusDevice(_TuhiDBus):
self.properties_changed({'Listening': GLib.Variant.new_boolean(value)}) self.properties_changed({'Listening': GLib.Variant.new_boolean(value)})
@GObject.Property @GObject.Property
def paired(self): def registered(self):
return self._paired return self._registered
@paired.setter @registered.setter
def paired(self, paired): def registered(self, registered):
self._paired = paired self._registered = registered
@GObject.Property @GObject.Property
def battery_percent(self): def battery_percent(self):
@ -219,10 +219,10 @@ class TuhiDBusDevice(_TuhiDBus):
if interface != self.interface: if interface != self.interface:
return None return None
if methodname == 'Pair': if methodname == 'Register':
# FIXME: we should cache the method invocation here, wait for a # FIXME: we should cache the method invocation here, wait for a
# successful result from Tuhi and then return the value # successful result from Tuhi and then return the value
self._pair() self._register()
result = GLib.Variant.new_int32(0) result = GLib.Variant.new_int32(0)
invocation.return_value(GLib.Variant.new_tuple(result)) invocation.return_value(GLib.Variant.new_tuple(result))
elif methodname == 'StartListening': elif methodname == 'StartListening':
@ -262,13 +262,13 @@ class TuhiDBusDevice(_TuhiDBus):
def _property_write_cb(self): def _property_write_cb(self):
pass pass
def _pair(self): def _register(self):
self.emit('pair-requested') self.emit('register-requested')
def _on_device_paired(self, device, pspec): def _on_device_registered(self, device, pspec):
if self.paired == device.paired: if self.registered == device.registered:
return return
self.paired = device.paired self.registered = device.registered
def _on_battery_percent(self, device, pspec): def _on_battery_percent(self, device, pspec):
self.battery_percent = device.battery_percent self.battery_percent = device.battery_percent
@ -378,7 +378,7 @@ class TuhiDBusServer(_TuhiDBus):
def __init__(self): def __init__(self):
_TuhiDBus.__init__(self, None, BASE_PATH, INTF_MANAGER) _TuhiDBus.__init__(self, None, BASE_PATH, INTF_MANAGER)
self._devices = [] self._devices = []
self._pairable_devices = {} self._unregistered_devices = {}
self._dbus = Gio.bus_own_name(Gio.BusType.SESSION, self._dbus = Gio.bus_own_name(Gio.BusType.SESSION,
BUS_NAME, BUS_NAME,
Gio.BusNameOwnerFlags.NONE, Gio.BusNameOwnerFlags.NONE,
@ -435,7 +435,7 @@ class TuhiDBusServer(_TuhiDBus):
return None return None
if propname == 'Devices': if propname == 'Devices':
return GLib.Variant.new_objv([d.objpath for d in self._devices if d.paired]) return GLib.Variant.new_objv([d.objpath for d in self._devices if d.registered])
elif propname == 'Searching': elif propname == 'Searching':
return GLib.Variant.new_boolean(self.is_searching) return GLib.Variant.new_boolean(self.is_searching)
@ -469,8 +469,8 @@ class TuhiDBusServer(_TuhiDBus):
self.emit('search-start-requested', self._on_search_stop) self.emit('search-start-requested', self._on_search_stop)
for d in self._devices: for d in self._devices:
if not d.paired: if not d.registered:
self._emit_pairable_signal(d) self._emit_unregistered_signal(d)
def _on_name_owner_changed_signal_cb(self, connection, sender, object_path, def _on_name_owner_changed_signal_cb(self, connection, sender, object_path,
interface_name, node, interface_name, node,
@ -500,29 +500,29 @@ class TuhiDBusServer(_TuhiDBus):
self._searching_client = None self._searching_client = None
for dev in self._devices: for dev in self._devices:
if dev.paired: if dev.registered:
continue continue
dev.remove() dev.remove()
self._devices = [d for d in self._devices if d.paired] self._devices = [d for d in self._devices if d.registered]
def cleanup(self): def cleanup(self):
Gio.bus_unown_name(self._dbus) Gio.bus_unown_name(self._dbus)
def create_device(self, device): def create_device(self, device):
dev = TuhiDBusDevice(device, self.connection) dev = TuhiDBusDevice(device, self.connection)
dev.connect('notify::paired', self._on_device_paired) dev.connect('notify::registered', self._on_device_registered)
self._devices.append(dev) self._devices.append(dev)
if not device.paired: if not device.registered:
self._emit_pairable_signal(dev) self._emit_unregistered_signal(dev)
return dev return dev
def _on_device_paired(self, device, param): def _on_device_registered(self, device, param):
objpaths = GLib.Variant.new_array(GLib.VariantType('o'), objpaths = GLib.Variant.new_array(GLib.VariantType('o'),
[GLib.Variant.new_object_path(d.objpath) [GLib.Variant.new_object_path(d.objpath)
for d in self._devices if d.paired]) for d in self._devices if d.registered])
self.properties_changed({'Devices': objpaths}) self.properties_changed({'Devices': objpaths})
def _emit_pairable_signal(self, device): def _emit_unregistered_signal(self, device):
arg = GLib.Variant.new_object_path(device.objpath) arg = GLib.Variant.new_object_path(device.objpath)
self.signal('PairableDevice', arg, dest=self._searching_client[0]) self.signal('UnregisteredDevice', arg, dest=self._searching_client[0])

View File

@ -82,7 +82,7 @@ class WacomWrongModeException(WacomException):
errno = errno.EBADE errno = errno.EBADE
class WacomNotPairedException(WacomException): class WacomNotRegisteredException(WacomException):
errno = errno.EACCES errno = errno.EACCES
@ -239,7 +239,7 @@ class WacomDevice(GObject.Object):
str_b = binascii.hexlify(bytes(data)) str_b = binascii.hexlify(bytes(data))
raise WacomException(f'unexpected data: {str_b}') raise WacomException(f'unexpected data: {str_b}')
if data[0] == 0x07: if data[0] == 0x07:
raise WacomNotPairedException(f'wrong device, please redo pairing') raise WacomNotRegisteredException(f'wrong device, please re-register')
if data[0] == 0x02: if data[0] == 0x02:
raise WacomEEAGAINException(f'unexpected answer: {data[0]:02x}') raise WacomEEAGAINException(f'unexpected answer: {data[0]:02x}')
if data[0] == 0x01: if data[0] == 0x01:
@ -574,7 +574,7 @@ class WacomDevice(GObject.Object):
except WacomEEAGAINException: except WacomEEAGAINException:
logger.warning('no data, please make sure the LED is blue and the button is pressed to switch it back to green') logger.warning('no data, please make sure the LED is blue and the button is pressed to switch it back to green')
def pair_device_slate(self): def register_device_slate(self):
self.register_connection() self.register_connection()
logger.info('Press the button now to confirm') logger.info('Press the button now to confirm')
self.emit('button-press-required') self.emit('button-press-required')
@ -594,7 +594,7 @@ class WacomDevice(GObject.Object):
fw_low = self.get_firmware_version(1) fw_low = self.get_firmware_version(1)
logger.info(f'firmware is {fw_high}-{fw_low}') logger.info(f'firmware is {fw_high}-{fw_low}')
def pair_device_spark(self): def register_device_spark(self):
try: try:
self.check_connection() self.check_connection()
except WacomWrongModeException: except WacomWrongModeException:
@ -619,14 +619,14 @@ class WacomDevice(GObject.Object):
fw_low = self.get_firmware_version(1) fw_low = self.get_firmware_version(1)
logger.info(f'firmware is {fw_high}-{fw_low}') logger.info(f'firmware is {fw_high}-{fw_low}')
def pair_device(self): def register_device(self):
self._uuid = uuid.uuid4().hex[:12] self._uuid = uuid.uuid4().hex[:12]
logger.debug(f'{self.device.address}: pairing device, assigned {self.uuid}') logger.debug(f'{self.device.address}: registering device, assigned {self.uuid}')
if self.is_spark(): if self.is_spark():
self.pair_device_spark() self.register_device_spark()
else: else:
self.pair_device_slate() self.register_device_slate()
logger.info('pairing completed') logger.info('registration completed')
self.notify('uuid') self.notify('uuid')
def run(self): def run(self):
@ -638,19 +638,19 @@ class WacomDevice(GObject.Object):
self._is_running = True self._is_running = True
exception = None exception = None
try: try:
if self._pairing_mode: if self._register_mode:
self.pair_device() self.register_device()
else: else:
self.retrieve_data() self.retrieve_data()
except WacomException as e: except WacomException as e:
logger.error(f'**** Exception: {e} ****') logger.error(f'**** Exception: {e} ****')
exception = e exception = e
finally: finally:
self._pairing_mode = False self._register_mode = False
self._is_running = False self._is_running = False
self.emit('done', exception) self.emit('done', exception)
def start(self, pairing_mode): def start(self, register_mode):
self._pairing_mode = pairing_mode self._register_mode = register_mode
self.thread = threading.Thread(target=self.run) self.thread = threading.Thread(target=self.run)
self.thread.start() self.thread.start()