【问题标题】:Python properly and effectively obtaining DBUS object path (e.g. /org/bluez/dev_XX_XX_XX_XX_XX_XX/playerY)Python 正确有效地获取 DBUS 对象路径(例如 /org/bluez/dev_XX_XX_XX_XX_XX_XX/playerY)
【发布时间】:2019-03-04 10:54:31
【问题描述】:

我想获取我正在创建的基于 bluez 的 A2DP 蓝牙播放器的设备路径。我坚持有效地执行播放/暂停/下一个/上一个命令,因为 dbus 可用性和播放器路径会根据您选择的媒体播放器而变化。此外,bluez 有时会决定发送大量无用信息(对我而言),例如播放列表详细信息,这会使我的应用程序处理的负载更大。所以这里的目标是在调用函数时获取/org/bluez/dev_XX_XX_XX_XX_XX_XX/playerY

def update_player():
    manager = dbus.Interface(self.bus.get_object("org.bluez", "/"), "org.freedesktop.DBus.ObjectManager")
    objects = manager.GetManagedObjects()
    player_path = getFromDict(objects,[self.devicepath,"org.bluez.MediaControl1", "Player"])

当我尝试更换媒体播放器,或者 bluez 发送一些日志时(通常是 5% 的时间),dbus org.freedesktop.DBus.ObjectManager 会收到很多信息,这会使 manager.GetManagedObjects() 卡住 10~20 秒。

有没有办法确定 bluez 对象路径而不必接收整个org.freedesktop.DBus.ObjectManager 对象;或者有没有办法限制bluez 发送的消息量。我真的很想有效地获得对象路径。非常感谢任何帮助。

编辑: 虽然我没有在嵌入式系统中测试它,但由于 Partiban 的好建议,我能够使用 InterfacesAdded 和一些正则表达式来匹配我需要的路径。

self.bus.add_signal_receiver(self.objectPathHandler, 
    bus_name="org.bluez",
    dbus_interface="org.freedesktop.DBus.ObjectManager",
    signal_name="InterfacesAdded",
    path_keyword="path")

def objectPathHandler(self, interface, changed, path):
    iface = interface[interface.rfind(".") + 1:]
    #print("InterfacesAdded: {}; changed: {}; path {}".format(iface, changed, path))
    self.playerpath = re.findall('/org/bluez/hci[0-9]/dev_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}/player[0-9]+', iface)[0]
    print "Object path:"
    print self.playerpath

def update_player(self):
    print "Updating player"
    if self.devicepath != "None" and self.playerpath != "None":
    if self.playerpath:
        self.connected = 1
        self.getPlayer (self.playerpath)
        player_properties = self.player.GetAll(PLAYER_IFACE, dbus_interface="org.freedesktop.DBus.Properties")

【问题讨论】:

    标签: python dictionary dbus bluez freedesktop.org


    【解决方案1】:

    您不应该每次都使用org.freedesktop.DBus.ObjectManager.GetManagedObjects 来获取对象路径。此GetManagedObjects 旨在获取现有或以前可用的接口以及应用程序启动时的详细信息。

    例如,假设 Bluez 已启动并连接了 1 个终端设备。稍后您的应用程序启动,在您的应用程序的初始化/启动期间,您可能需要获取所有可用/连接的设备,因此您可以使用GetManagedObjects 来获取它。

    为了在运行时创建接口、对象路径,您应该依赖对象管理器的InterfacesAddedInterfacesRemoved 信号。

    我在 python 中没有示例,但以下 C 中的示例通常使用 StartDiscovery 并使用信号监视新设备。因此,您可以使用信号来适应 python 中的类似示例。下面的示例只是为了清楚起见(有关此示例的更多详细信息,请参见Linumiz)。

    /*
     * bluez_adapter_scan.c - Scan for bluetooth devices
     *  - This example scans for new devices after powering the adapter, if any devices
     *    appeared in /org/hciX/dev_XX_YY_ZZ_AA_BB_CC, it is monitered using "InterfaceAdded"
     *    signal and all the properties of the device is printed
     *  - Scanning continues to run until any device is disappered, this happens after 180 seconds
     *    automatically if the device is not used.
     * gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_scan ./bluez_adapter_scan.c `pkg-config --libs glib-2.0 gio-2.0`
     */
    #include <glib.h>
    #include <gio/gio.h>
    
    GDBusConnection *con;
    static void bluez_property_value(const gchar *key, GVariant *value)
    {
        const gchar *type = g_variant_get_type_string(value);
    
        g_print("\t%s : ", key);
        switch(*type) {
            case 'o':
            case 's':
                g_print("%s\n", g_variant_get_string(value, NULL));
                break;
            case 'b':
                g_print("%d\n", g_variant_get_boolean(value));
                break;
            case 'u':
                g_print("%d\n", g_variant_get_uint32(value));
                break;
            case 'a':
            /* TODO Handling only 'as', but not array of dicts */
                if(g_strcmp0(type, "as"))
                    break;
                g_print("\n");
                const gchar *uuid;
                GVariantIter i;
                g_variant_iter_init(&i, value);
                while(g_variant_iter_next(&i, "s", &uuid))
                    g_print("\t\t%s\n", uuid);
                break;
            default:
                g_print("Other\n");
                break;
        }
    }
    
    static void bluez_device_appeared(GDBusConnection *sig,
                    const gchar *sender_name,
                    const gchar *object_path,
                    const gchar *interface,
                    const gchar *signal_name,
                    GVariant *parameters,
                    gpointer user_data)
    {
        (void)sig;
        (void)sender_name;
        (void)object_path;
        (void)interface;
        (void)signal_name;
        (void)user_data;
    
        GVariantIter *interfaces;
        const char *object;
        const gchar *interface_name;
        GVariant *properties;
    
        g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
        while(g_variant_iter_next(interfaces, "{&s@a{sv}}", &interface_name, &properties)) {
            if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
                g_print("[ %s ]\n", object);
                const gchar *property_name;
                GVariantIter i;
                GVariant *prop_val;
                g_variant_iter_init(&i, properties);
                while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
                    bluez_property_value(property_name, prop_val);
                g_variant_unref(prop_val);
            }
            g_variant_unref(properties);
        }
        return;
    }
    
    #define BT_ADDRESS_STRING_SIZE 18
    static void bluez_device_disappeared(GDBusConnection *sig,
                    const gchar *sender_name,
                    const gchar *object_path,
                    const gchar *interface,
                    const gchar *signal_name,
                    GVariant *parameters,
                    gpointer user_data)
    {
        (void)sig;
        (void)sender_name;
        (void)object_path;
        (void)interface;
        (void)signal_name;
    
        GVariantIter *interfaces;
        const char *object;
        const gchar *interface_name;
        char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
    
        g_variant_get(parameters, "(&oas)", &object, &interfaces);
        while(g_variant_iter_next(interfaces, "s", &interface_name)) {
            if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
                int i;
                char *tmp = g_strstr_len(object, -1, "dev_") + 4;
    
                for(i = 0; *tmp != '\0'; i++, tmp++) {
                    if(*tmp == '_') {
                        address[i] = ':';
                        continue;
                    }
                    address[i] = *tmp;
                }
                g_print("\nDevice %s removed\n", address);
                g_main_loop_quit((GMainLoop *)user_data);
            }
        }
        return;
    }
    
    static void bluez_signal_adapter_changed(GDBusConnection *conn,
                        const gchar *sender,
                        const gchar *path,
                        const gchar *interface,
                        const gchar *signal,
                        GVariant *params,
                        void *userdata)
    {
        (void)conn;
        (void)sender;
        (void)path;
        (void)interface;
        (void)userdata;
    
        GVariantIter *properties = NULL;
        GVariantIter *unknown = NULL;
        const char *iface;
        const char *key;
        GVariant *value = NULL;
        const gchar *signature = g_variant_get_type_string(params);
    
        if(g_strcmp0(signature, "(sa{sv}as)") != 0) {
            g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
            goto done;
        }
    
        g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
        while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
            if(!g_strcmp0(key, "Powered")) {
                if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
                    g_print("Invalid argument type for %s: %s != %s", key,
                            g_variant_get_type_string(value), "b");
                    goto done;
                }
                g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
            }
            if(!g_strcmp0(key, "Discovering")) {
                if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
                    g_print("Invalid argument type for %s: %s != %s", key,
                            g_variant_get_type_string(value), "b");
                    goto done;
                }
                g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
            }
        }
    done:
        if(properties != NULL)
            g_variant_iter_free(properties);
        if(value != NULL)
            g_variant_unref(value);
    }
    
    static int bluez_adapter_call_method(const char *method)
    {
        GVariant *result;
        GError *error = NULL;
    
        result = g_dbus_connection_call_sync(con,
                             "org.bluez",
                        /* TODO Find the adapter path runtime */
                             "/org/bluez/hci0",
                             "org.bluez.Adapter1",
                             method,
                             NULL,
                             NULL,
                             G_DBUS_CALL_FLAGS_NONE,
                             -1,
                             NULL,
                             &error);
        if(error != NULL)
            return 1;
    
        g_variant_unref(result);
        return 0;
    }
    
    static int bluez_adapter_set_property(const char *prop, GVariant *value)
    {
        GVariant *result;
        GError *error = NULL;
    
        result = g_dbus_connection_call_sync(con,
                             "org.bluez",
                             "/org/bluez/hci0",
                             "org.freedesktop.DBus.Properties",
                             "Set",
                             g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
                             NULL,
                             G_DBUS_CALL_FLAGS_NONE,
                             -1,
                             NULL,
                             &error);
        if(error != NULL)
            return 1;
    
        g_variant_unref(result);
        return 0;
    }
    
    int main(void)
    {
        GMainLoop *loop;
        int rc;
        guint prop_changed;
        guint iface_added;
        guint iface_removed;
    
        con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
        if(con == NULL) {
            g_print("Not able to get connection to system bus\n");
            return 1;
        }
    
        loop = g_main_loop_new(NULL, FALSE);
    
        prop_changed = g_dbus_connection_signal_subscribe(con,
                            "org.bluez",
                            "org.freedesktop.DBus.Properties",
                            "PropertiesChanged",
                            NULL,
                            "org.bluez.Adapter1",
                            G_DBUS_SIGNAL_FLAGS_NONE,
                            bluez_signal_adapter_changed,
                            NULL,
                            NULL);
    
        iface_added = g_dbus_connection_signal_subscribe(con,
                                "org.bluez",
                                "org.freedesktop.DBus.ObjectManager",
                                "InterfacesAdded",
                                NULL,
                                NULL,
                                G_DBUS_SIGNAL_FLAGS_NONE,
                                bluez_device_appeared,
                                loop,
                                NULL);
    
        iface_removed = g_dbus_connection_signal_subscribe(con,
                                "org.bluez",
                                "org.freedesktop.DBus.ObjectManager",
                                "InterfacesRemoved",
                                NULL,
                                NULL,
                                G_DBUS_SIGNAL_FLAGS_NONE,
                                bluez_device_disappeared,
                                loop,
                                NULL);
    
        rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
        if(rc) {
            g_print("Not able to enable the adapter\n");
            goto fail;
        }
    
        rc = bluez_adapter_call_method("StartDiscovery");
        if(rc) {
            g_print("Not able to scan for new devices\n");
            goto fail;
        }
    
        g_main_loop_run(loop);
        rc = bluez_adapter_call_method("StopDiscovery");
        if(rc)
            g_print("Not able to stop scanning\n");
        g_usleep(100);
    
        rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
        if(rc)
            g_print("Not able to disable the adapter\n");
    fail:
        g_dbus_connection_signal_unsubscribe(con, prop_changed);
        g_dbus_connection_signal_unsubscribe(con, iface_added);
        g_dbus_connection_signal_unsubscribe(con, iface_removed);
        g_object_unref(con);
        return 0;
    }
    

    在这个使用StartDiscovery 扫描设备的示例中,我使用了InterfaceRemovedInterfaceAdded 这两个信号来进行演示。因此,当/org/hciX/ 上出现新设备时,会调用bluez_device_appeared 并以相同的方式进行删除。

    如果您连接了多个蓝牙适配器,您可以通过指定适配器路径(例如 /org/bluez/hciX)在 g_dbus_connection_signal_subscribe 下过滤它们。

    所有基于 DBUS 的守护进程都使用信号来通知总线上的客户端,因此我们在总线上看到很多消息。所以我们需要根据确切的需要订阅。此过滤器在 dbus 守护程序级别应用并转发消息。

    添加MediaControl1 bluez 接口已过时且不推荐使用。所有新应用程序都应使用MediaPlayer 定义的here

    【讨论】:

    • 谢谢,您提供的信息指导我使用 InterfacesAdded,检查我的编辑
    猜你喜欢
    • 1970-01-01
    • 2018-05-19
    • 2015-07-26
    • 1970-01-01
    • 1970-01-01
    • 2020-04-23
    • 1970-01-01
    • 2012-01-21
    • 2010-12-09
    相关资源
    最近更新 更多