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:
parent
fc219e4c57
commit
5e82d1b378
97
README.md
97
README.md
|
@ -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.
|
||||||
|
|
|
@ -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')
|
||||||
|
|
50
tuhi/base.py
50
tuhi/base.py
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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])
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue