我有一个python3脚本,它使用老式蓝牙成功地打开了到服务器的RFCOMM套接字。我正在尝试使用dbus来完成同样的事情,这就是我读到的你现在应该在Linux上使用蓝牙的方式。(这是对用C编写的Linux应用程序进行重大更改的概念证明。(
当我运行下面的脚本时,我会看到:
connecting...
ex from ConnectProfile(): g-io-error-quark: GDBus.Error:org.bluez.Error.NotAvailable: Operation currently not available (36)
onPropertiesChanged( org.bluez.Device1 {'Connected': True} [] )
onPropertiesChanged( org.bluez.Device1 {'ServicesResolved': True} [] )
onPropertiesChanged( org.bluez.Device1 {'ServicesResolved': False, 'Connected': False} [] )
请注意,属性更改发生在对ConnectProfile的调用失败之后。我看到一些建议,我应该从属性更改回调内部打开RFCOMM套接字,利用连接打开的时机。但是服务器端(我在github上使用了出色的bluez-rfcomm示例(dbus/bluez负责创建套接字:您只需获得一个文件描述符。我希望ConnectProfile也能类似地工作,但找不到任何示例。
我应该如何修改我的new_style((函数,以便它给我一个工作套接字?
谢谢,
--Eric
#!/usr/bin/env python3
# for new_style()
from pydbus import SystemBus
from gi.repository import GLib
# for old_style()
import bluetooth
PROFILE = 'b079b640-35fe-11e5-a432-0002a5d5c51b'
ADDR = 'AA:BB:CC:DD:EE:FF'
# Works fine. But you're supposed to use dbus these days
def old_style():
service_matches = bluetooth.find_service(uuid=PROFILE, address=ADDR)
if len(service_matches):
first_match = service_matches[0]
port = first_match['port']
host = first_match['host']
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.connect((host, port))
while True:
data = input()
if not data:
break
sock.send(data)
sock.close()
# Does not work. First an exception fires:
# g-io-error-quark: GDBus.Error:org.bluez.Error.NotAvailable: Operation currently not available (36)
# then onPropertiesChanged lists stuff -- after the failure, not during the connection attempt.
def new_style():
nucky = SystemBus().get('org.bluez', '/org/bluez/hci0/dev_' + ADDR.replace(':', '_'))
# Callback: (s, a{sv}, as)
nucky.onPropertiesChanged = lambda p1, p2, p3: print('onPropertiesChanged(', p1, p2, p3, ')')
def try_connect():
print('connecting...')
try:
nucky.ConnectProfile(PROFILE)
except Exception as ex:
print('ex from ConnectProfile():', ex)
GLib.timeout_add( 250, try_connect )
GLib.MainLoop().run()
if False:
old_style()
else:
new_style()
(稍后添加(
让我澄清一下我的问题。在一个Linux盒子上,我正在运行一个bluez-rfcomm示例服务器,我将其修改为使用自定义服务UUID。它可能会创建一个服务记录,但在客户端(Android(,这三行Java足以连接到它的套接字(假设服务器有蓝牙mac AA:BB:CC:DD:EE:FF,并且两者配对(:
BluetoothDevice remote = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( "AA:BB:CC:DD:EE:FF" );
BluetoothSocket socket = remote.createRfcommSocketToServiceRecord( MY_SERVICE_UUID );
socket.connect();
有没有一种方法可以在Linux上使用dbus/bluez来实现这一点,这种方法非常简单?我假设Device1/ConnectProfile(UUID(是我想要的——它与createRfcommSocketToServiceRecord((是一样的——但这种假设可能完全错误!这是否应该在使用blues/dbus的Linux中实现?还是应该坚持旧的方法?
谢谢,很抱歉最初的问题很含糊。
--Eric
下面的代码用于获取并使用rfcomm套接字连接到由UUID指定的远程服务。我接受了ukBaz的答案,包括了我所需要的一切,但我没有足够的背景知识来立即理解它。我说调用ConnectProfile((是正确的开始方式,但遗漏了两件事:
- 在调用端提供配置文件是必要的,原因有两个。首先,它提供了一个回调,您可以通过它来获取套接字。但是如果没有它——特别是没有NewConnection方法——连接就会失败(ConnectProfile((返回一个错误。(
- 我需要在后台线程上调用ConnectProfile((。回调将出现在glib循环的主线程上,因此在连接成功或失败之前不会返回的ConnectProfile((不能阻塞该线程
不同的蓝牙连接类型可能需要细微的不同机制,但对于RFCOMM套接字连接来说,这是可行的。
#!/usr/bin/env python3
import socket, threading
from pydbus import SystemBus
from gi.repository import GLib
import dbus, dbus.service, dbus.mainloop.glib
CUSTOM_UUID = 'b079b640-35fe-11e5-a432-0002a5d5c51b'
ADDR = 'AA:BB:CC:DD:EE:FF'
PATH = '/org/neednt/match/remote'
class Profile(dbus.service.Object):
@dbus.service.method("org.bluez.Profile1",
in_signature="oha{sv}", out_signature="")
def NewConnection(self, path, fd, properties):
None
print('NewConnection: fd:', fd);
try:
self.socket = socket.socket(fileno=fd.take())
print('got socket:', self.socket)
self.socket.send(b"You there?")
except Exception as ex:
print('ex:', ex)
def connect_thread_main():
print('connect_thread_main()...')
SystemBus().get('org.bluez', '/org/bluez/hci0/dev_' + ADDR.replace(':', '_'))
.ConnectProfile(CUSTOM_UUID)
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
Profile(bus, PATH) # added by side-effect apparently
dbus.Interface(bus.get_object("org.bluez","/org/bluez"),
"org.bluez.ProfileManager1")
.RegisterProfile(PATH, CUSTOM_UUID, {})
threading.Thread(target=connect_thread_main).start()
GLib.MainLoop().run()
有一个很好的博客(如果现在有点旧的话(比较pybluez和使用Python3套接字:https://blog.kevindoran.co/bluetooth-programming-with-python-3/
如果您想使用BlueZ D-Bus API,那么关键文档是:https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/profile-api.txt
BlueZ示例位于:https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/test-profile
使用pydbus创建它时会遇到一些问题,如中所述:https://github.com/LEW21/pydbus/issues/54