Linux の Bluetooth ドライバ bluez には、色々なツールが付属しているが、対話的に使う正式な方法は bluetoothctl コマンドで、プログラム的には bluetoothd の提供する DBus API を使うのが正しい方法らしい。なので BT の動作を確認するために send-dbus を使ってみた。
まず、BT Adapter があるかどうか調べる。dbus-send
がターミナルで dbus と対話するコマンドだが、オプションの意味は以下の通り。
-
--print-reply
: 応答を待って返す。=literal
を付けると応答をそのまま表示するので整形したりするのに便利。--type=method_call
が指定されたとみなされる。 -
--system
: DBus には全ユーザ共通の system bus と各ユーザごとの session bus という二つのバスがある。bluez は system bus しかないので --system を指定する。 -
--dest=org.bluez
: 接続先 -
/org/bluez
: オブジェクトパス -
org.freedesktop.DBus.Introspectable.Introspect
: インタフェース.メンバ (メソッドやシグナルの名前) -
| xmllint --format -
: 読みやすく整形します。
オブジェクト指向的に考えると、接続先とオブジェクトパスの組み合わせでレシーバを指定して、インタフェース.メンバがメッセージという事になる。
# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect | xmllint --format -
<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="xml" type="s" direction="out"/>
</method>
</interface>
<interface name="org.bluez.AgentManager1">
<method name="RegisterAgent">
<arg name="agent" type="o" direction="in"/>
<arg name="capability" type="s" direction="in"/>
</method>
<method name="UnregisterAgent">
<arg name="agent" type="o" direction="in"/>
</method>
<method name="RequestDefaultAgent">
<arg name="agent" type="o" direction="in"/>
</method>
</interface>
<interface name="org.bluez.ProfileManager1">
<method name="RegisterProfile">
<arg name="profile" type="o" direction="in"/>
<arg name="UUID" type="s" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<method name="UnregisterProfile">
<arg name="profile" type="o" direction="in"/>
</method>
</interface>
<node name="hci0"/>
</node>
最後の <node name="hci0"/>
が BT Adapter を表す。
ここで使ったメソッド org.freedesktop.DBus.Introspectable.Introspect は DBus で共通に使われるメソッドで、オブジェクトパスの内容を調べるのに使う。この応答を見ると、/org/bluez/
では org.bluez.AgentManager1
などのインタフェースが使えたり、子要素 hci0
がある事が分かる。API については bluez のドキュメントに解説がある。例えば org.bluez.AgentManager1
なら agent-api.txt に書いてある。
次に、ペアリングされたデバイスを探す。device-api.txt によると、デバイスを表すオブジェクトは /org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
に出てくるので /org/bluez/hci0
を Introspect する。
# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Introspectable.Introspect | xmllint --format -
...
<node name="dev_B8_8A_60_E6_20_61"/>
<node name="dev_D4_0B_1A_F2_FE_DC"/>
</node>
ここで表示されるデバイスのうち、ペアリングされているかどうかを示す情報は Properties として実装されている
boolean Paired [readonly]
ので、とりあえず全部のプロパティを表示する org.freedesktop.DBus.Properties.GetAll を使う。
# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0/dev_D4_0B_1A_F2_FE_DC org.freedesktop.DBus.Properties.GetAll string:org.bluez.Device1
array [
dict entry(
Address variant D4:0B:1A:F2:FE:DC )
dict entry(
Name variant M00213 )
dict entry(
Alias variant M00213 )
dict entry(
Class variant uint32 5898764
)
dict entry(
Icon variant phone )
dict entry(
Paired variant boolean true
)
...
これで、このデバイスが接続中である事が分かる。DBus.Properties.html によると、GetAll を使うには引数としてインターフェース名を指定する。Introspect すると分かるが、dev_XX_XX_XX_XX_XX_XX
で使えるインタフェースには org.freedesktop.DBus.Introspectable、org.bluez.Device1、org.freedesktop.DBus.Properties、org.bluez.Network1、org.bluez.MediaControl1 があってそれぞれにプロパティがあるので、必要なインタフェースを指定しなくてはならない。
ちなみに dbus-send の引数を指定する時は string:org.bluez.Device1
のように型を指定する。
実際のプログラムでは GetAll では無く freedesktop.DBus.Properties.Get でプロパティの一つを取ってくる機会が多いと思う。
# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0/dev_D4_0B_1A_F2_FE_DC org.freedesktop.DBus.Properties.Get string:org.bluez.Device1 string:Paired
variant boolean true
参考
-
The upcoming Management Interface
- なぜ hci socket では無く bluetoothd を使うようになったかが書いてある。
- dbus-send(1)
- bluez.git root/doc
- D-Bus のはなし