在 Windows 中列出来自 CPython 的已连接 BLE 设备

Listing connected BLE devices from CPython in Windows

我正在将 Linux 上的 Python 代码 运行 移植到 Windows,并且需要实现 2 个简单的特定于平台的功能(通过 [=11 处理) =] 在 Linux 下调用):

  1. 列出所有当前连接的 BLE 设备
    • 例如list_connected()[('F9:18:AF:E7:9D:40','HeadPhones'),('aa:93:88:1e:03','SmartWatch')]
  2. 强制断开连接的设备
    • 例如disconnect('F9:18:AF:E7:9D:40',timeout=2)

由于 bleak,其余 BLE 功能已经跨平台。

我只有很少的 Windows 经验,尤其是最近的 API。我环顾四周,不明白 WinRT(Windows 运行时 API)是 UWP(通用 Windows 平台)的一部分,可以从 CPython 通过 pythonnetwinrt 包;这是正确的实施方式吗?

感谢提示。

以下是列出和断开设备的主要代码片段(完整代码已通过电子邮件发送给 OP)。我没有包括异常处理和从异步到同步的包装,因为这是 Python 特有的,对其他人来说可能并不那么重要。

import winrt.windows.foundation as wf
import winrt.windows.devices.bluetooth as bt
import winrt.windows.devices.enumeration as denum

import asyncio # to support async / await because winrt doesn't have sync versions of BLE functions

# the naming in the Python wrapper looks a bit messy, underscores mixed up with PascalCase
# another caveat is that we have to call the specific override with the second argument `[]`
# otherwise Python winrt wrapper gets confused with multiple versions of find_all_async
dev_infos = await denum.DeviceInformation.find_all_async(bt.BluetoothLEDevice.get_device_selector(), [])

id_name_pairs = []
    
for dev_info in dev_infos:
    # getting the device itself to access it's "connected" status
    le_device = await bt.BluetoothLEDevice.from_id_async(dev_info.id);
    
    if le_device is None: # enumerator sometimes has stale devices; this can also happen when you unplug BLE dongle
        continue
        
    # in status context, Microsoft calls it "CONNECTED", but later, when disconnecting, it needs "unpair"
    # and there is no any "disconnect" function. Microsoft is messy in this regard
    if le_device.connection_status == bt.BluetoothConnectionStatus.CONNECTED:
        id_name_pairs.append((le_device.device_id, le_device.name));


重要 - Microsoft 通过其完整 ID 而不是 BLE 地址来识别 BLE 设备。如果我们希望稍后能够断开设备,我们必须知道 ID。如果我们不将 ID 存储在任何地方并按地址断开连接,我们将不得不再次 运行 枚举并按地址查找 ID。

从设备 ID 中轻松提取 BLE 地址:

addressed_devices = list(map(lambda dev: (dev[0][-17:], dev[1]), id_name_pairs))

要断开已知 ID 的设备:

le_device = await bt.BluetoothLEDevice.from_id_async(dev_id)
await le_device.device_information.pairing.unpair_async()

要求:Windows10,2018 年 10 月更新或更高版本,Python for Windows,版本 3.7 或更高版本,pip,版本 19 或更高版本。

使用 pip install winrt 获取 Python winrt 包装器库。

总而言之,一项乍一看似乎微不足道的任务,由于 .NET / Python 命名和覆盖差异以及 Microsoft 的 pairing/connection 导致了一些 head-scratching执行;很容易与不能使用 BLE 配对信息的经典蓝牙 BR/EDR API 混淆。