android添加蓝牙电量

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android添加蓝牙电量相关的知识,希望对你有一定的参考价值。

参考技术A 1.      HFP 命令AT+IPHONEACCEV

描述:报告耳机的状态变更

发起者:耳机

格式:AT+IPHONEACCEV=[Number of key/value pairs ],[key1 ],[val1 ],[key2

],[val2 ],…

参数:

Number of key/value pairs :接下来参数的数量

key:被报告状态变化的类型

1 =电量等级

2 =暂停状态

val:更改的值

Battery events:0-9之间数字的字符串 A

string value between ‘0’ and ‘9’.

Dock state: 0 = undocked, 1 = docked.

           Example:AT+IPHONEACCEV=1,1,3

2.android  framework 修改点

packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java

BluetoothAssignedNumbers.APPLE可以随便用哪个公司的,但注册广播时要一致就行。

   static

         classInitNative();

         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap();

         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT",BluetoothAssignedNumbers.PLANTRONICS);

         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID",BluetoothAssignedNumbers.GOOGLE);    +VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XAPL",BluetoothAssignedNumbers.APPLE);

 +VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+IPHONEACCEV",BluetoothAssignedNumbers.APPLE);

     

获取蓝牙电量需要向蓝牙发送回复的at命令:

private booleanprocessVendorSpecificAt(String atString)

       log("processVendorSpecificAt - atString = " + atString);

       // Currently we accept only SET type commands.

       int indexOfEqual = atString.indexOf("=");

       if (indexOfEqual == -1)

           Log.e(TAG, "processVendorSpecificAt: command type error in " +atString);

           return false;

       

       String command = atString.substring(0, indexOfEqual);

       Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);

       if (companyId == null)

           Log.e(TAG, "processVendorSpecificAt: unsupported command: " +atString);

           return false;

       

       String arg = atString.substring(indexOfEqual + 1);

       if (arg.startsWith("?"))

           Log.e(TAG, "processVendorSpecificAt: command type error in " +atString);

           return false;

       

       Object[] args = generateArgs(arg);

        + if ("+XAPL".equals(command))

        + processAtXapl(args);

        +

        broadcastVendorSpecificEventIntent(command,

                                          companyId,

                                          BluetoothHeadset.AT_CMD_TYPE_SET,

                                          args,

                                          mCurrentDevice);

       atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);

        return true;



/**

      * Process AT+XAPL AT command

      * @param args command arguments after theequal sign

      * @param device Remote device that hassent this command

      */

     private void processAtXapl(Object[] args)

         if (args.length != 2)

            Log.w(TAG, "processAtXapl()args length must be 2: " + String.valueOf(args.length));

             return;

        

         if (!(args[0] instanceof String) ||!(args[1] instanceof Integer))

             Log.w(TAG, "processAtXapl()argument types not match");

             return;

        

         // feature = 2 indicates that wesupport battery level reporting only

                Log.d("tsq77","+XAPL=iPhone,");

        atResponseStringNative("+XAPL=iPhone," + String.valueOf(2));

    

2.上层app监听广播获取电量

packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSettings.java

在settings中的蓝牙界面中注册广播,然后把电量显示出来。

       //aaron

       IntentFilter filter=new IntentFilter();

       filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);

       //filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY+"."+BluetoothAssignedNumbers.

APPLE);

       filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY+"."+BluetoothAssignedNumbers.

APPLE);

       getActivity().registerReceiver(mIntentReceiver,filter);

       Log.i("a", "registerReceiver  ");

       //end

   private BroadcastReceiver mIntentReceiver = new BroadcastReceiver()

       @Override

       public void onReceive(Context context, Intent intent)

           final String action = intent.getAction();

           if(action.equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT))

                Log.i("a","intent  "+intent);

                String command=intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);

                if("+IPHONEACCEV".equals(command))

                    Object[] args = (Object[])intent.getSerializableExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);

                    if (args.length >= 3&& args[0] instanceof Integer &&((Integer)args[0])*2+1<=args.length)

                        for (inti=0;i<((Integer)args[0]);i++)

                           if(!(args[i*2+1] instanceof Integer) || !(args[i*2+2] instanceof Integer))

                                continue;

                           

                            if(args[i*2+1].equals(1))

                                floatlevel=(((Integer)args[i*2+2])+1)/10.0f;//获取的电量百分比

                                break;

                           

                       

                   

               

           

       

;

在 Linux 上检查连接的蓝牙设备的电池电量

【中文标题】在 Linux 上检查连接的蓝牙设备的电池电量【英文标题】:Check battery level of connected bluetooth device on linux 【发布时间】:2018-08-11 05:32:49 【问题描述】:

如何查看已连接蓝牙设备的电池电量?该设备在 Android 上显示电池电量,因此我假设该设备支持GATT-based Battery Service。但是,通过在 bluetoothctl 中输入“menu gatt”,然后使用“list-attributes [dev]”列出设备的 GATT 属性,什么都没有显示。

similar question was posted to SO 但 OP 似乎找到了一个对我不起作用的解决方案。当我在 bluetoothctl 中运行“info [dev]”时,我看不到电池服务的 UUID。

我更喜欢在命令行上运行且与发行版无关的解决方案。

请让我知道是否应该将这个问题发布在 SuperUser 上。

【问题讨论】:

我们在谈论什么类型的设备?电池电量可以通过蓝牙通过许多不同的配置文件报告,而不仅仅是通过 LE GATT 服务...您可以使用 BLE explore 应用程序等来浏览 GATT 服务并确认它实际支持的内容吗? 你能推荐一个BLE探索应用程序吗?有没有办法通过命令行工具(例如 bluetoothctl)知道连接了什么类型的设备? 我检查/sys/class/power_supply的内容,如this other answer中所述。 @DamianNadales my /sys/class/power_supply 仅包含 ACBAT0 如何访问 A2DP 蓝牙配置文件?如何通过该配置文件检查电池信息是否可用? 【参考方案1】:

对我来说,在终端中运行它是有效的:

upower --dump

【讨论】:

感谢 Yash 的建议。不幸的是,这不适用于我的设置。只有笔记本电脑的电池battery_BAT0、线路电源line_power_AC和神秘的/org/freedesktop/UPower/devices/DisplayDevice设备出现在输出中。 非常适合我!【参考方案2】:

自 Bluez v5.48 以来,您在 GATT 特征列表中看不到电池电量,因为此特定 GATT 特征已移至 DBUS org.bluez.Battery1 interface。

从命令行:

    使用bluetoothctl 连接到您的目标 BLE 设备 然后通过运行请求 DBUS:dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0/dev_&lt;mac_address_of_your_ble_peripheral&gt; org.freedesktop.DBus.Properties.Get string:"org.bluez.Battery1" string:"Percentage"

以我的 BLE 外围设备为例,其 MAC 地址为 C3:41:A6:C8:93:42

$ dbus-send --print-reply=literal --system --dest=org.bluez \
    /org/bluez/hci0/dev_C3_41_A6_C8_93_42 org.freedesktop.DBus.Properties.Get \
    string:"org.bluez.Battery1" string:"Percentage"
   variant       byte 94

注意:您可能会使用 Bluez DBUS API 扫描并连接到您的设备。

【讨论】:

按照这些说明操作时,我收到此错误$ dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0/dev_E9_09_EF_A6_24_70 org.freedesktop.DBus.Properties.Get string:"org.bluez.Battery1" string:"Percentage" Error org.freedesktop.DBus.Error.InvalidArgs: No such interface 'org.bluez.Battery1' 您使用的是哪个“bluez”版本? 我刚刚尝试了 Bluez 来源的 5.50,它也适用于我。您确定您的设备公开电池服务吗?你能运行这个命令吗:dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0/dev_E9_09_EF_A6_24_70 org.freedesktop.DBus.Introspectable.Introspect 嗨,我和@Ricardo 有同样的问题,我在 Archlinux 上使用 bluez 5.50-6,我也没有org.bluez.Battery1 接口;我的内省输出是gist.github.com/Terseus/d78e6ca711cef914e52bffd757d40c5b @Terseus,和里卡多一样。您的设备使用 A2DP(高级音频分发配置文件),您的电池信息可能可以通过此配置文件访问。看我的评论:***.com/questions/49078659/…【参考方案3】:

(此答案特定于耳机/耳机)

我已经使用 clst 的答案中的 Python 程序有一段时间了,虽然它有效,但它需要我连接,然后断开连接并再次运行它。如果我正确理解了这个问题,那是因为 只有一个程序可以打开一个套接字与蓝牙设备通信,所以它最终会与 PulseAudio 争吵。

我最近发现了hsphfpd。

hsphfpd 是带有一些原型实现的规范,用于连接 在 Linux 操作系统上具有 HSP 和 HFP 配置文件的蓝牙设备。

基本上,由于一次只有一个程序可以与耳机通信,并且在音频服务器中实现电池电量报告或在电源管理软件中实现音频是没有意义的,因此它将该功能移至外部守护程序.这样一来,PulseAudio 和其他任何东西都可以同时使用耳机。有一个version of PulseAudio 修补以使用 hsphfpd。尽管它们仍然是原型,但它们似乎工作得很好。

hsphfpd 通过 DBus 报告电池状态(和其他内容),因此要从命令行获取它,您可以这样做

dbus-send --system --dest=org.hsphfpd --print-reply /org/hsphfpd/hci0/dev_XX_XX_XX_XX_XX_XX/hsp_hs org.freedesktop.DBus.Properties.Get string:org.hsphfpd.Endpoint string:BatteryLevel

甚至从程序中调用它。

如果您使用 Arch Linux,这两个都可以在 AUR 中使用。

【讨论】:

当运行 dbus-send --system --dest=org.hsphfpd --print-reply /org/hsphfpd/hci0/dev_XX_XX_XX_XX_XX_XX/hsp_hs org.freedesktop.DBus.Properties.Get string:org.hsphfpd.Endpoint string:BatteryLevel 我得到 method return time=1606703580.141858 sender=:1.3546 -&gt; destination=:1.3550 serial=44 reply_serial=2 variant int16 -1 @Ricardo 你可以试试hfp_hfhfp_ag 而不是hsp_hs。如果这不起作用,请打开 pavucontrol 并切换到 HFP 或 HSP(即使他们说不可用),然后再试一次。在此之后,您可以切换回您正在使用的任何配置文件,否则您将遇到质量非常低的音频。如果这仍然不起作用,请尝试使用QDBusViewer 检查org.hsphfpd 服务并找到您可以使用的任何替代路径。如果你设法让它工作,请告诉我。 当我使用hfp_hf 时,我得到variant int16 40。我猜 40 表示剩余电量为 40%。不确定如何验证,因为新设置使接受答案中的程序无法连接到设备。也许我会等着看这个数字是否会随着使用而下降。谢谢您的帮助!使用qdbus --system org.hsphfpd 时,我看到hfp_hfhsp_hs 都可用于此设备。 @Ricardo 没错。据我所知, hsphfpd 使用来自接受答案的脚本中的类似方法,因此值应该相同。此外,您还可以连接到PropertiesChanged 信号,以便在电池电量发生变化时得到通知。请注意,它仅在 Connected 属性为 true 时更新。 酷。通过给耳机充电,这个数字增加到了 60。我认为它可能只对 10% 的增加和减少敏感。非常感谢!这个答案很有用。【参考方案4】:

这可能有点晚了,但对我来说,这个 Python 项目运行良好:

https://github.com/TheWeirdDev/Bluetooth_Headset_Battery_Level

我只需要将我的无名 X5 耳机的第 57 行端口更改为 3。如果它挂起或出现“连接被拒绝”错误,请尝试不同的端口。

Python 程序通过 RFCOMM 使用 AT 命令,并且应该在 Pulseaudio 使用 A2DP 接收器(我的重新连接)时工作。需要 Python 3,因为 2 没有 BT-Serial 套接字。 Windows 可能无法工作,因为它缺少 bluez。它基本上和这里的 Pulseaudio hack 做同样的事情:https://***.com/a/56390625/920122

如果您想查看交换的命令,请尝试我的调试分支:https://github.com/clst/Bluetooth_Headset_Battery_Level

【讨论】:

嘿@clst,谢谢你的回答!看起来很有希望!您的意思是原始代码第 56 行中的端口和您的 fork 中的 58?那个说s.connect((BT_ADDRESS, 3))的?我尝试了从 1 到 11 的所有数字,但没有成功。我应该继续尝试更多的端口号吗?您知道端口可能位于的数字范围是多少吗? 我再次尝试,这次是在使用bluetoothctl 与设备断开连接后。 它有效! :) 我正在使用你的 fork 和端口号 3。唯一需要注意的是,我不能在听音乐时使用它,因为我必须断开与设备的连接才能使用 python 脚本。 是的,这些端口号是特定于设备的,没有通用做法,因此您必须尝试它们。如果 RFCOMM 在设备执行其他操作(如 A2DP)时工作,则也是设备特定的。在有人编写强大的自动检测系统之前,这是我们拥有的最好的:) 谢谢!捐给你几美元的比特币现金 :) 我相信原作者会很感激的 :) 你可以尝试通过 github 联系他们。我不确定 TheWeirdDev 是否​​知道这个 SO 问题...【参考方案5】:

默认情况下,Bluez 会“隐藏”电池服务 UUID。这是因为在蓝牙启动时加载了一个“电池插件”。

如果您不希望激活电池插件并使电池服务 UUID 再次对 bluetoothctl 或任何其他应用程序可见,则将 bluetoothd 的启动命令更改为:'bluetoothd -P battery'。这将确保未加载电池插件。在 Raspberry Pi 上,bluetooth.service 位于 /lib/systemd/system/bluetooth.service 中,因此您需要在该文件中进行更改。

【讨论】:

请不要直接修改打包好的单元文件(/lib/usr/lib下的)。 Systemd 提供了systemctl edit 接口,用于以不会导致包管理器出现问题的方式修改单元。【参考方案6】:

正如上面@OlivierM 所说,UUID 由蓝牙过滤。您可以通过从src/gatt-client.c 中的export_service() 函数中删除以下内容来撤消该操作并像任何其他服务特征一样导出UUID

if (gatt_db_service_get_claimed(attr))
     return;

【讨论】:

【参考方案7】:

这是一种通过带有一些hack 的pulseaudio 日志获取电池电量的方法。 我的蓝牙耳机使用专有的 Apple HFP AT 命令,HFP/A2DP 协议由 pulseaudio 直接处理。似乎获得这些值的唯一方法是通过脉冲。

【讨论】:

感谢 Vasily 并为长时间的延迟感到抱歉。你知道是否有办法从命令行发送那些 AT 命令(例如使用dbus-send)来获取电池电量? 您可以自己构建 pulseaudio 并应用上述补丁。您可以对其进行修改:例如,将 pa_log_notice 替换为 /tmp 中某个管道的输出,并让应用程序监视该管道。我选择了这种方式。为我工作。该代码仅在设备配对时触发一次。您可以将其放在音量更改功能上。我认为有一种方法可以将 dbus 侦听器添加到 pulseaudio 中的特定命令,这样它就会触发 PA 发送那些 AT 命令,但这对我来说太复杂了。 我明白了。谢谢。我宁愿不维护一个pulseaudio的分支。【参考方案8】:

这是一个很好的问题,在目前可用的开发和工具之前。

简短回答(2018 年 10 月)

你必须自己写!它不会是候机楼里的一个班轮。我打算用 Python 自己写这个,但是 C 有更多的文档,所以如果你对 C 很熟练,那就去吧。

长答案,但它更像是一个推荐的起点:

    Tony D:https://youtu.be/5fQR2PHMDWE?t=4644 设法使用bluetoothctl 读取属性并将数据发送到蓝牙设备。一定要查看视频信息,你会发现很好的链接和参考:https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt Szymon Janc:https://youtu.be/VMDyebKT5c4 LINUX 蓝牙堆栈的开发者和贡献者

    肯定看看这个问题是如何在移动设备上回答的。对于 Android,它是 BAS(电池服务):https://android.stackexchange.com/questions/106073/displaying-bluetooth-gadgets-battery-status-on-the-phone

【讨论】:

我检查了第一个视频,那个人在bluetoothctl 中使用了list-attributes,在我的情况下没有显示任何内容。这就是为什么我在想也许有一种非 GATT 方法来检查电池状态?我也检查了其他链接,除了 Szymon Janc 的演讲有点太长了。如果您成功编写了读取 BLE 设备电池电量的程序,请告诉我。 在运行bluetoothctl 时不要忘记使用sudo。但是,是的,今天在 Linux 中确实没有一种舒适的方法可以做到这一点。这也是这个 python 项目的动机:github.com/peplin/pygatt#motivation由于没有时间,我不会进一步研究这个主题。如果您决定继续解决此问题的路径,请使用上述 git 项目和示例代码。 github.com/peplin/pygatt#example-use 明年我可能只能再次提供帮助。祝你好运! 感谢@VeRo 的帮助!我不知道我应该以 root 身份运行 bluetoothctl(使用 sudo)。我试过了,当我在连接到设备后执行list-attributes 时仍然没有任何显示。不知道应该做什么来获得那里列出的属性,或者如果设备没有列出任何 gatt 属性可以做什么。干杯! 更新者(内核开发者)something has to register the battery with the power-supply subsystem using power_supply_register (or the variant prefixed with devm_). For the BT HID devices that happens in drivers/hid/hid-input.c and is based on the HID protocol. Other BT devices do not use HID protocol and need their own handler. AFAIK for other device types the highlevel protocols are implemented in userspace/bluez. That would require something like uinput for power-supply, so that bluez can feed battery information back into the kernel. AFAIK nobody is currently working on that.【参考方案9】:

在 bluez 版本中,您使用的 Gatt 属性可能是实验性的。如果是这样,您需要通过 -E 关键字运行 bluetoothd 守护程序来启用实验性特性 像“/usr/libexec/bluetooth/bluetoothd -E” 这对我有用。

【讨论】:

当您执行“systemctl start bluetooth”时,bluetoothd 守护进程由 systemd 运行,对吗?如何告诉 systemd 使用 -E 参数?我有 bluez 5.48 版 在执行“systemctl start bluetooth”命令时,您正在调用 bluetooth.service,在您的主目录中搜索此服务。在该服务中,您可以在调用 bluetoothd 的行中添加 -E 参数.它是 bluez 包的一部分。该行看起来像这样 "ExecStart=/usr/libexec/bluetooth/bluetoothd" 添加 -E 参数到它的末尾 "ExecStart=/usr/libexec/bluetooth/bluetoothd - E"。 当您使用 bluetoothctl 应用程序连接设备时添加此关键字后,它将列出您的蓝牙设备支持的服务。从中您可以选择“电池级别”服务的属性并使用读取命令获取值。也有直接获取电池电量的方法,通过usinf dbus-send一个用来发送dbus命令的utilit。 非常感谢您的帮助。我按照您的建议更改了行,然后执行了“systemctl deamon-reload”,然后执行了“systemctl restart bluetooth”(均以root身份)。然后启动 bluetoothctl,连接设备,“menu gatt”,“list-attributes [dev]”......但仍然没有。任何想法可能会发生什么? sudo ls /var/lib/bluetooth/40:xx:xx:xx:xx:xx/E9:xx:xx:xx:xx:xx/ 显示其中只有文件info,但没有文件attributes

以上是关于android添加蓝牙电量的主要内容,如果未能解决你的问题,请参考以下文章

电池电量低于 40% 时是不是可以关闭蓝牙?

在 Linux 上检查连接的蓝牙设备的电池电量

Android 蓝牙 LE 通知的问题

Android移动客户端性能测试浅谈——电量

Android移动客户端性能测试浅谈——电量

Android BLE 蓝牙编程