Fix BLE companion transport reliability
This commit is contained in:
@@ -21,8 +21,6 @@ DBUS_PROPERTIES = "org.freedesktop.DBus.Properties"
|
||||
GATT_MANAGER = "org.bluez.GattManager1"
|
||||
GATT_SERVICE = "org.bluez.GattService1"
|
||||
GATT_CHARACTERISTIC = "org.bluez.GattCharacteristic1"
|
||||
AD_MANAGER = "org.bluez.LEAdvertisingManager1"
|
||||
ADVERTISEMENT = "org.bluez.LEAdvertisement1"
|
||||
|
||||
SERVICE_UUID = "6c7a0001-7c6d-4f74-9bb0-c5a4e5efc001"
|
||||
RX_UUID = "6c7a0002-7c6d-4f74-9bb0-c5a4e5efc001"
|
||||
@@ -103,19 +101,30 @@ class Characteristic(dbus.service.Object):
|
||||
|
||||
class TxCharacteristic(Characteristic):
|
||||
def __init__(self, bus, service):
|
||||
super().__init__(bus, 1, TX_UUID, ["notify"], service)
|
||||
super().__init__(bus, 1, TX_UUID, ["indicate"], service)
|
||||
self.notifying = False
|
||||
self.queue = deque()
|
||||
self.sending = False
|
||||
|
||||
@dbus.service.method(GATT_CHARACTERISTIC, in_signature="a{sv}")
|
||||
def StartNotify(self, _options):
|
||||
@dbus.service.method(GATT_CHARACTERISTIC)
|
||||
def StartNotify(self):
|
||||
self.notifying = True
|
||||
if self.queue and not self.sending:
|
||||
self.sending = True
|
||||
GLib.idle_add(self._send_next)
|
||||
|
||||
@dbus.service.method(GATT_CHARACTERISTIC, in_signature="a{sv}")
|
||||
def StopNotify(self, _options):
|
||||
@dbus.service.method(GATT_CHARACTERISTIC)
|
||||
def StopNotify(self):
|
||||
self.notifying = False
|
||||
self.queue.clear()
|
||||
self.sending = False
|
||||
|
||||
@dbus.service.method(GATT_CHARACTERISTIC)
|
||||
def Confirm(self):
|
||||
if self.queue:
|
||||
GLib.idle_add(self._send_next)
|
||||
else:
|
||||
self.sending = False
|
||||
|
||||
def send(self, message):
|
||||
encoded = (json.dumps(message, separators=(",", ":")) + "\n").encode("utf-8")
|
||||
@@ -123,7 +132,7 @@ class TxCharacteristic(Characteristic):
|
||||
self.queue.append(encoded[offset:offset + NOTIFY_CHUNK_BYTES])
|
||||
if not self.sending:
|
||||
self.sending = True
|
||||
GLib.timeout_add(20, self._send_next)
|
||||
GLib.idle_add(self._send_next)
|
||||
|
||||
def _send_next(self):
|
||||
if not self.notifying or not self.queue:
|
||||
@@ -132,7 +141,7 @@ class TxCharacteristic(Characteristic):
|
||||
chunk = self.queue.popleft()
|
||||
value = dbus.Array([dbus.Byte(byte) for byte in chunk], signature="y")
|
||||
self.PropertiesChanged(GATT_CHARACTERISTIC, {"Value": value}, [])
|
||||
return bool(self.queue)
|
||||
return False
|
||||
|
||||
|
||||
class RxCharacteristic(Characteristic):
|
||||
@@ -216,26 +225,10 @@ def call_api(method, path, body, token):
|
||||
raise ProtocolError("PI_SERVICE_UNAVAILABLE", "The Pi companion service is unavailable") from error
|
||||
|
||||
|
||||
class Advertisement(dbus.service.Object):
|
||||
def __init__(self, bus):
|
||||
self.path = "/org/pi_car_companion/advertisement0"
|
||||
super().__init__(bus, self.path)
|
||||
|
||||
@dbus.service.method(DBUS_PROPERTIES, in_signature="s", out_signature="a{sv}")
|
||||
def GetAll(self, interface):
|
||||
if interface != ADVERTISEMENT:
|
||||
raise InvalidArgs()
|
||||
return {"Type": "peripheral", "ServiceUUIDs": dbus.Array([SERVICE_UUID], signature="s"), "LocalName": "Pi Car"}
|
||||
|
||||
@dbus.service.method(ADVERTISEMENT)
|
||||
def Release(self):
|
||||
pass
|
||||
|
||||
|
||||
def find_adapter(bus):
|
||||
objects = dbus.Interface(bus.get_object(BLUEZ, "/"), DBUS_OM).GetManagedObjects()
|
||||
for path, interfaces in objects.items():
|
||||
if GATT_MANAGER in interfaces and AD_MANAGER in interfaces:
|
||||
if GATT_MANAGER in interfaces:
|
||||
return path
|
||||
raise RuntimeError("No Bluetooth LE adapter with GATT server support was found")
|
||||
|
||||
@@ -253,10 +246,22 @@ def main():
|
||||
service.add_characteristic(rx)
|
||||
service.add_characteristic(tx)
|
||||
application.add_service(service)
|
||||
advertisement = Advertisement(bus)
|
||||
dbus.Interface(adapter, GATT_MANAGER).RegisterApplication(application.path, {}, reply_handler=lambda: None, error_handler=lambda error: (_ for _ in ()).throw(error))
|
||||
dbus.Interface(adapter, AD_MANAGER).RegisterAdvertisement(advertisement.path, {}, reply_handler=lambda: None, error_handler=lambda error: (_ for _ in ()).throw(error))
|
||||
GLib.MainLoop().run()
|
||||
loop = GLib.MainLoop()
|
||||
registration_errors = []
|
||||
|
||||
def registration_failed(error):
|
||||
registration_errors.append(error)
|
||||
loop.quit()
|
||||
|
||||
dbus.Interface(adapter, GATT_MANAGER).RegisterApplication(
|
||||
application.path,
|
||||
{},
|
||||
reply_handler=lambda: None,
|
||||
error_handler=registration_failed,
|
||||
)
|
||||
loop.run()
|
||||
if registration_errors:
|
||||
raise registration_errors[0]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user