Skip to content

Commit 3856ee3

Browse files
authored
Merge pull request #4 from kawasaki/profile
Thank you!
2 parents e1d4dca + 4082135 commit 3856ee3

2 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# SPDX-License-Identifier: LGPL-2.1-or-later
2+
# Copyright (C) 2022 Shin'ichiro Kawasaki <kawasaki@juno.dti.ne.jp>
3+
4+
# This example shows how to connect to Bluetooth devices using BlueZ D-Bus
5+
# Profile API [*]. The API provides Profile Manager interface and Profile
6+
# interface. When the application request BlueZ to connect to a device, BlueZ
7+
# open a file descriptor to send and receive to the device and pass the file
8+
# descriptor to the application. To receive the file descriptor, the
9+
# application prepares a D-Bus object with Profile interface. Steps to take
10+
# are as follows:
11+
#
12+
# 1) Create Profile object with new_connection() call back
13+
# 2) Register the Profile object through Profile Manager
14+
# 3) Request connect to the Bluetooth device with Device API
15+
# 4) BlueZ calls new_connect() for the Profile object with a file descriptor
16+
# 5) In new_connect(), duplicate the file descriptor for communication
17+
# 6) Write the file descriptor to send data to the Bluetooth device, or
18+
# read the file descriptor to receive data from the Bluetooth device
19+
#
20+
# In this example, connect to a Bluetooth RFCOMM device with Serial Port
21+
# service.
22+
23+
from __future__ import annotations
24+
25+
import sys
26+
from asyncio import run, sleep
27+
from os import dup, fdopen
28+
from typing import Any, Dict, Tuple
29+
30+
from sdbus import dbus_method_async_override, sd_bus_open_system
31+
32+
from sdbus_async.bluez.device_api import DeviceInterfaceAsync
33+
from sdbus_async.bluez.profile_api import (
34+
ProfileInterfaceAsync,
35+
ProfileManagerInterfaceAsync,
36+
)
37+
38+
39+
def usage() -> None:
40+
print(f"Usage: {sys.argv[0]} DEV_ADDR BYTE1 [BYTE2...]")
41+
print("\tConnect to serial port service of the Bluetooth RFCOMM device")
42+
print("\tspecified with DEV_ADDR and send specified bytes to the device.")
43+
print("\t\tDEV_ADDR: e.g. 01:23:45:67:89:AB")
44+
print("\t\tBYTE1 [BYTE2...]: e.g. 0x02 0x00 0x01 0x9B")
45+
exit(1)
46+
47+
48+
if len(sys.argv) < 3:
49+
usage()
50+
51+
DEV_ADDR = sys.argv[1]
52+
DEV_PATH = '/org/bluez/hci0/dev_' + DEV_ADDR.replace(":", "_")
53+
BYTES_TO_SEND = [int(x, 0) for x in sys.argv[2:]]
54+
# Serial port service UUID
55+
SERVICE_UUID = '00001101-0000-1000-8000-00805f9b34fb'
56+
57+
58+
class RFCOMMProfileAsync(ProfileInterfaceAsync):
59+
60+
@dbus_method_async_override()
61+
async def release(
62+
self,
63+
) -> None:
64+
return
65+
66+
@dbus_method_async_override()
67+
async def new_connection(
68+
self,
69+
device: str,
70+
fd: int,
71+
fd_properties: Dict[str, Tuple[str, Any]],
72+
) -> None:
73+
"""Receive the file descriptor that BlueZ prepared to communicate with
74+
the Serial Port service of the RFCOMM Bluetooth Device. Duplicate the
75+
file descriptor since it will not be valid after this function call."""
76+
print(f"Connecting to device: {device}")
77+
self.fd = dup(fd)
78+
79+
@dbus_method_async_override()
80+
async def request_disconnection(
81+
self,
82+
device: str,
83+
) -> None:
84+
print(f"Disconnecting device: {device}")
85+
86+
87+
async def main() -> None:
88+
89+
# connect to D-Bus
90+
dbus = sd_bus_open_system()
91+
profile_manager = ProfileManagerInterfaceAsync()
92+
profile_manager._connect('org.bluez', '/org/bluez', bus=dbus)
93+
device = DeviceInterfaceAsync()
94+
device._connect('org.bluez', DEV_PATH, bus=dbus)
95+
96+
# Create RFCOMM profile object
97+
export_object = RFCOMMProfileAsync()
98+
export_object.export_to_dbus('/org/test', dbus)
99+
100+
# Register the RFCOMM profile object
101+
await profile_manager.register_profile('/org/test',
102+
SERVICE_UUID,
103+
{'Name': ('s', 'foo'),
104+
'Role': ('s', "client"),
105+
'Service': ('s', SERVICE_UUID),
106+
'Channel': ('i', 0),
107+
'AutoConnect': ('b', True),
108+
})
109+
110+
# Connect to serial port service
111+
try:
112+
await device.connect_profile(SERVICE_UUID)
113+
except NotImplementedError as e:
114+
print(e)
115+
return
116+
except Exception as e:
117+
print(e)
118+
print(f"failed to connect: {e}")
119+
return
120+
121+
# Send and receive data
122+
fp = fdopen(export_object.fd, mode='r+b', buffering=0, newline=None)
123+
124+
msg = bytes(BYTES_TO_SEND)
125+
print("Sending bytes: ", msg)
126+
fp.write(msg)
127+
128+
await sleep(1)
129+
130+
received_bytes = fp.read()
131+
print("Received bytes: ", received_bytes)
132+
133+
# Clean up
134+
await device.disconnect()
135+
await profile_manager.unregister_profile('/org/test')
136+
137+
run(main())

β€Žsdbus_async/bluez/profile_api.pyβ€Ž

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,34 @@ async def unregister_profile(
4848
profile_path: str,
4949
) -> None:
5050
raise NotImplementedError
51+
52+
class ProfileInterfaceAsync(
53+
DbusInterfaceCommonAsync,
54+
interface_name='org.bluez.Profile1',
55+
):
56+
57+
@dbus_method_async()
58+
async def release(
59+
self,
60+
) -> None:
61+
raise NotImplementedError
62+
63+
@dbus_method_async(
64+
input_signature='oha{sv}',
65+
)
66+
async def new_connection(
67+
self,
68+
device: str,
69+
fd: int,
70+
fd_properties: Dict[str, Tuple[str, Any]],
71+
) -> None:
72+
raise NotImplementedError
73+
74+
@dbus_method_async(
75+
input_signature='o',
76+
)
77+
async def request_disconnection(
78+
self,
79+
device: str,
80+
) -> None:
81+
raise NotImplementedError

0 commit comments

Comments
 (0)