diff --git a/tuhi.py b/tuhi.py
index 52bb9a3..fd7afaa 100755
--- a/tuhi.py
+++ b/tuhi.py
@@ -82,13 +82,19 @@ class TuhiDevice(GObject.Object):
self._wacom_device.connect('drawing', self._on_drawing_received)
self._wacom_device.connect('done', self._on_fetching_finished, bluez_device)
self.drawings = []
+ self.pairing_mode = False
bluez_device.connect('connected', self._on_bluez_device_connected)
bluez_device.connect('disconnected', self._on_bluez_device_disconnected)
+ self._bluez_device = bluez_device
+
+ def connect_device(self):
+ self._bluez_device.connect_device()
def _on_bluez_device_connected(self, bluez_device):
logger.debug('{}: connected'.format(bluez_device.address))
- self._wacom_device.start()
+ self._wacom_device.start(self.pairing_mode)
+ self.pairing_mode = False
def _on_bluez_device_disconnected(self, bluez_device):
logger.debug('{}: disconnected'.format(bluez_device.address))
@@ -140,6 +146,7 @@ class Tuhi(GObject.Object):
self.server.connect('bus-name-acquired', self._on_tuhi_bus_name_acquired)
self.server.connect('pairing-start-requested', self._on_start_pairing_requested)
self.server.connect('pairing-stop-requested', self._on_stop_pairing_requested)
+ self.server.connect('pair-device-requested', self._on_pair_device_requested)
self.bluez = BlueZDeviceManager()
self.bluez.connect('device-added', self._on_bluez_device_added)
self.bluez.connect('device-updated', self._on_bluez_device_updated)
@@ -165,6 +172,13 @@ class Tuhi(GObject.Object):
self.bluez.stop_discovery()
self._pairable_device_handler = None
+ def _on_pair_device_requested(self, dbusserver, bluez_device):
+ tuhi_dbus_device = self.server.create_device(bluez_device)
+ d = TuhiDevice(bluez_device, tuhi_dbus_device)
+ d.pairing_mode = True
+ self.devices[bluez_device.address] = d
+ d.connect_device()
+
@classmethod
def _is_pairing_device(cls, bluez_device):
if bluez_device.vendor_id != WACOM_COMPANY_ID:
diff --git a/tuhi/dbusserver.py b/tuhi/dbusserver.py
index 37cb4de..f1f9b1a 100755
--- a/tuhi/dbusserver.py
+++ b/tuhi/dbusserver.py
@@ -11,6 +11,7 @@
# GNU General Public License for more details.
#
+import os
import logging
from gi.repository import GObject, Gio, GLib
@@ -32,6 +33,11 @@ INTROSPECTION_XML = """
+
+
+
+
+
@@ -39,7 +45,6 @@ INTROSPECTION_XML = """
-
@@ -157,6 +162,11 @@ class TuhiDBusServer(GObject.Object):
(GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
"pairing-stop-requested":
(GObject.SIGNAL_RUN_FIRST, None, ()),
+ # Signal arguments:
+ # address
+ # string of the Bluetooth device address
+ "pair-device-requested":
+ (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
}
def __init__(self):
@@ -199,6 +209,10 @@ class TuhiDBusServer(GObject.Object):
elif methodname == 'StopPairing':
self._stop_pairing()
invocation.return_value()
+ elif methodname == 'Pair':
+ result = self._pair(args[0])
+ result = GLib.Variant.new_int32(result)
+ invocation.return_value(GLib.Variant.new_tuple(result))
def _property_read_cb(self, connection, sender, objpath, interface, propname):
if interface != INTF_MANAGER:
@@ -226,6 +240,19 @@ class TuhiDBusServer(GObject.Object):
self._is_pairing = False
self.emit("pairing-stop-requested")
+ def _pair(self, address):
+ if not self._is_pairing:
+ return os.errno.ECONNREFUSED
+
+ if address not in self._pairable_devices:
+ return os.errno.ENODEV
+
+ self.emit('pair-device-requested', self._pairable_devices[address])
+
+ # FIXME: we should cache the method invocation here, wait for a
+ # successful result from Tuhi and then return the value
+ return 0
+
def _on_pairing_stop(self, status):
"""
Called by whoever handles the pairing-start-requested signal
diff --git a/tuhi/wacom.py b/tuhi/wacom.py
index 9987836..c6b69ca 100644
--- a/tuhi/wacom.py
+++ b/tuhi/wacom.py
@@ -97,6 +97,10 @@ class WacomEEAGAINException(WacomException):
pass
+class WacomWrongModeException(WacomException):
+ pass
+
+
class WacomNotPairedException(WacomException):
pass
@@ -241,6 +245,8 @@ class WacomDevice(GObject.Object):
raise WacomNotPairedException(f"wrong device, please redo pairing")
if data[0] == 0x02:
raise WacomEEAGAINException(f"unexpected answer: {data[0]:02x}")
+ if data[0] == 0x01:
+ raise WacomWrongModeException(f"wrong device mode")
def send_nordic_command_sync(self,
command,
@@ -571,6 +577,57 @@ class WacomDevice(GObject.Object):
except WacomEEAGAINException:
logger.warning("no data, please make sure the LED is blue and the button is pressed to switch it back to green")
+ def register_device_slate(self):
+ self.register_connection()
+ logger.info("Press the button now to confirm")
+ data = self.wait_nordic_data([0xe4, 0xb3], 10)
+ if data.opcode == 0xb3:
+ # generic ACK
+ self.check_ack(data)
+ self.set_time()
+ self.read_time()
+ self.ec_command()
+ self.bb_command()
+ w = self.get_dimensions('width')
+ h = self.get_dimensions('height')
+ if self.width != w or self.height != h:
+ logger.error(f'Uncompatible dimensions: {w}x{h}')
+ fw_high = self.get_firmware_version(0)
+ fw_low = self.get_firmware_version(1)
+ logger.info(f'firmware is {fw_high}-{fw_low}')
+ logger.info("pairing completed")
+
+ def register_device_spark(self):
+ try:
+ self.check_connection()
+ except WacomWrongModeException:
+ # this is expected
+ pass
+ self.send_nordic_command(command=0xe3,
+ arguments=[0x01])
+ logger.info("Press the button now to confirm")
+ # Wait for the button confirmation event, or any error
+ data = self.wait_nordic_data([0xe4, 0xb3], 10)
+ if data.opcode == 0xb3:
+ # generic ACK
+ self.check_ack(data)
+ self.send_nordic_command_sync(command=0xe5,
+ arguments=None,
+ expected_opcode=0xb3)
+ self.set_time()
+ self.read_time()
+ self.bb_command()
+ fw_high = self.get_firmware_version(0)
+ fw_low = self.get_firmware_version(1)
+ logger.info(f'firmware is {fw_high}-{fw_low}')
+ logger.info("pairing completed")
+
+ def register_device(self):
+ if self.is_slate():
+ self.register_device_slate()
+ else:
+ self.register_device_spark()
+
def run(self):
if self._is_running:
logger.error('{}: already synching, ignoring this request'.format(self.device.address))
@@ -579,11 +636,16 @@ class WacomDevice(GObject.Object):
logger.debug('{}: starting'.format(self.device.address))
self._is_running = True
try:
- self.retrieve_data()
+ if self._pairing_mode:
+ self.register_device()
+ else:
+ self.retrieve_data()
finally:
+ self._pairing_mode = False
self._is_running = False
self.emit("done")
- def start(self):
+ def start(self, pairing_mode):
+ self._pairing_mode = pairing_mode
self.thread = threading.Thread(target=self.run)
self.thread.start()