BluezをD-busから使う LE advertise

BlueZをD-Busから使う方法について

LEのAdvertiseする方法を調べました。

# Bluezのバージョンについて
* D-Bus から LE advertisetをするには5.30以降が必要です。下記は5.40で確認しました。

# leadv.py
* Bluezのexample-advertisementを参考にしました。

```py:leadv.py
#!/usr/bin/python

import dbus
import dbus.mainloop.glib
import dbus.service
import gobject as GObject

mainloop = None

BLUEZ_SERVICE_NAME = 'org.bluez'
LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'

LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'

class FixedAdvertisement(dbus.service.Object):
    PATH_BASE = '/org/bluez/example/fixedadvertisement'

    def __init__(self, bus, index, advertising_type):
        self.path = self.PATH_BASE + str(index)
        self.bus = bus
        self.ad_type = advertising_type
        dbus.service.Object.__init__(self, bus, self.path)

    def get_path(self):
        return dbus.ObjectPath(self.path)

    @dbus.service.method(DBUS_PROP_IFACE,
                         in_signature='s',
                         out_signature='a{sv}')
    def GetAll(self, interface):
        if interface != LE_ADVERTISEMENT_IFACE:
            raise InvalidArgsException()

        properties = dict()
        properties['Type'] = self.ad_type
        return properties

    @dbus.service.method(LE_ADVERTISEMENT_IFACE,
                         in_signature='',
                         out_signature='')
    def Release(self):
        print '%s: Released!' % self.path

def register_ad_cb():
    print 'Advertisement registered'

def register_ad_error_cb(error):
    print 'Failed to register advertisement: ' + str(error)
    mainloop.quit()

def find_adapter(bus):
    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
                               DBUS_OM_IFACE)
    objects = remote_om.GetManagedObjects()

    for o, props in objects.iteritems():
        if LE_ADVERTISING_MANAGER_IFACE in props:
            return o

    return None

def main():
    global mainloop

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus = dbus.SystemBus()

    adapter = find_adapter(bus)
    if not adapter:
        print 'LEAdvertisingManager1 interface not found'
        return

    ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
                                LE_ADVERTISING_MANAGER_IFACE)

    ad_manager.RegisterAdvertisement(FixedAdvertisement(bus, 0, 'broadcast').get_path(), {},
                                     reply_handler=register_ad_cb,
                                     error_handler=register_ad_error_cb)

    mainloop = GObject.MainLoop()
    mainloop.run()

if __name__ == '__main__':
    main()
```

* FixedAdvertisementクラスはアドバタイズデータのクラスです。
 * サービスデーモンから呼ばれるので、GetAll()とRemove()のメソッドをクラスに用意する必要があります。 
 * GetAll()では、アドバタイズデータをpropertisとして返します。
 * Remove()はアドバタイズを停止するときに呼ばれれます。
 * GetAll()とRemove()は定義する際に@dbus.service.methodのおまじないが必要です。
* register_ad_cb()とregister_ad_error_cb()はRegisterAdvertisement()で登録するコールバック関数です。
* find_adapter()はAdapterを探して、LE_ADVERTISING_MANAGER_IFACEの有無からLE advertiseが使えるか判定します。
* メイン
 * mainloopのおまじない
 * busとadapterの取得
 * アドバタイズマネージャの取得
 *  FixedAdvertisementを生成して、RegisterAdvertisement()に登録する
 *  メインループ

# 動作例
* アドバタイズはスマートフォン等のアプリで確認してください。

```
pi@raspberrypi: $ ./leadv.py
Advertisement registered
```

# アドバタイズで指定できるパラメータ
* Type : broadcast or peripheral
* ServiceUUIDs
* ManufacturerData
* SolicitUUIDs
* ServiceData
* IncludeTxPower


# 参考
* Bluez の example-advertisement https://git.kernel.org/cgit/bluetooth/bluez.git/tree/test/example-advertisement
* Bluez の advertising-api.txt https://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/advertising-api.txt