Bluetooth mesh blues
2019
Introduction to BlueZ mesh, via Python/asyncio
Operating System
App
Kernel
User
Some syscalls require
privileged acess!
Service
Service
Service
Operating System
App
App
App
API
API
API
Root
Message bus
Service
Service
Service
Operating System
App
App
App
API
API
API
Message bus
BlueZ
org.bluez.mesh
/org/bluez/mesh
/org/bluez/mesh/node<uuid>
org.bluez.mesh.Network1
org.bluez.mesh.Node1
org.bluez.mesh.Management1
Message bus
App
<unnamed>
/com/silvair/app
/com/silvair/app/element<n>
org.bluez.mesh.Application1
org.bluez.mesh.ProvisionAgent1
org.bluez.mesh.Element1
BlueZ
App
Provisioner
Join "/com/silvair/app", <uuid>
JoinComplete <token>
Provisioning
Attach <token>
GetManagedObjects
Send ...
<configuration>
BlueZ
App
Provisioner
ImportLocalNode ...
<token>
Attach <token>
GetManagedObjects
Send ...
<configuration>
BlueZ
App
Provisioner
class Application:
def __init__(self):
self.bus = ravel.system_bus(managed_objects=True)
self.path = "/com/silvair/app"
self.bus.register(self.path, interface=ApplicationInterface(self))
self.bus.object_added(self.path)
@ravel.interface(ravel.ITERFACE_SERVER, name='org.bluez.mesh.Application1')
class ApplicationInterface:
def __init__(self, application):
self.application = application
@ravel.method(name='JoinComplete',
in_signature='t', out_signature='')
def join_complete(self, token):
return self.application.join_complete(token)
# ...
BlueZ
App
Provisioner
class Element:
def __init__(self, application, index):
self.path = '%s/element%02d' % (application.path, index)
# ...
class Application:
def __init__(self):
# ...
self.element = Element(self, index=0)
self.bus.register(self.element.path, interface=ElementInterface(self.element)
self.bus.object_added(path)
@ravel.interface(ravel.ITERFACE_SERVER, name='org.bluez.mesh.Element1')
class ElementInterface:
def __init__(self, element):
self.element = element
@ravel.method(name='MessageReceived',
in_signature='qqbay', out_signature='')
def message_received(self, source, application_key, subscription, data):
return self.element.message_received(source, application_key,
subscription, bytes(data))
BlueZ
App
Provisioner
class Application:
def __init__(self):
self.mesh_service = self.bus['org.bluez.mesh']
self.token = ...
async def connect(self):
network_object = self.mesh_service['/org/bluez/mesh']
network_interface = await network_object \
.get_async_interface('org.bluez.mesh.Network1')
node_path, configuration = \
await network_interface.Attach(self.PATH, token)
node_object = self.mesh_service[node_path]
self.node_interface = await node_object \
.get_async_interface('org.bluez.mesh.Node1')
BlueZ
App
Provisioner
class Application:
async def send(self):
await self.node_interface.Send(self.element.path,
destination=0x0042,
application_key=0,
bytes.fromhex('800501'))
BlueZ
App
Provisioner
class Element:
def message_received(self, src, app_key, sub, msg):
self.application.message_received(self, src, app_key, sub, msg)
class Application:
def __init__(self):
self.message_callbacks = defaultdict(set)
def message_received(self, element, src, app_key, sub, msg):
opcode, params = parse(msg)
for cb in self.message_callbacks(opcode):
cb(element, src, app_key, sub, params)
BlueZ
App
Provisioner
class Application:
async def attention(self):
status = asyncio.Future()
def message_received(element, src, app_key, sub, params):
if element.path != self.element.path or src != 0x0042 or app_key != 0:
return
status.set_result(params)
opcode = 0x8007
self.message_callbacks[].add(message_received)
await self.node_interface.Send(self.element.path,
destination=0x0042,
application_key=0,
bytes.fromhex('800501'))
params = await status
Thank you
Questions?