You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python在Windows系统中编程连接蓝牙设备?

Windows下Python实现等效系统UI的蓝牙连接方案

你判断的方向是对的,这类和系统蓝牙行为完全一致的操作必须调用原生Win32蓝牙API实现,BluetoothAuthenticateDevice确实是配对认证环节的核心接口,但只调用这一个接口无法实现完整的连接效果——系统UI点击「连接」的动作实际会串行走完设备枚举、配对认证、服务启用三个流程,缺任意一个环节都达不到和手动操作一致的结果。

前置准备

  • 安装依赖库:执行pip install pywin32,用于调用Win32系统接口
  • 运行要求:所有蓝牙相关API调用必须以管理员权限启动Python进程,否则会返回权限不足错误
  • 提前获取目标设备的MAC地址,格式为XX:XX:XX:XX:XX:XX,可在Windows蓝牙设置的设备属性页查看

核心API调用逻辑

系统手动连接蓝牙的内部调用链路如下,写代码时必须完整覆盖:

  • 第一步:调用BluetoothFindFirstRadio获取本地蓝牙适配器的有效句柄,这是所有后续蓝牙API调用的必填参数,直接传空值会直接调用失败
  • 第二步:调用BluetoothAuthenticateDevice触发配对认证,该接口的行为和系统弹出的标准配对流程完全一致:支持无密码自动配对、弹出PIN码输入框、自动匹配固定PIN等所有系统原生支持的认证场景
  • 第三步:调用BluetoothSetServiceState启用目标设备对应的核心服务(比如蓝牙键鼠的HID服务、蓝牙耳机的A2DP音频服务),这一步是真正建立连接的动作,跳过的话设备只会停留在「已配对未连接」状态,和手动操作效果不一致

可直接运行的实现代码

import ctypes
from ctypes import wintypes

# 加载系统蓝牙相关API库
bthprops = ctypes.WinDLL('bthprops.cpl', use_last_error=True)

# 常用蓝牙服务GUID常量
BLUETOOTH_SERVICE_HID = 0x00001124       # 蓝牙键鼠等输入设备
BLUETOOTH_SERVICE_A2DP_SINK = 0x0000110B # 蓝牙耳机音频播放
BLUETOOTH_SERVICE_A2DP_SOURCE = 0x0000110A # 蓝牙音频采集
BLUETOOTH_SERVICE_CONNECT_FLAG = 0x00000001 | 0x00000002 # 认证+连接标志

# 蓝牙API所需结构体定义
class BLUETOOTH_ADDRESS(ctypes.Structure):
    _fields_ = [('ullLong', ctypes.c_ulonglong)]

class BLUETOOTH_FIND_RADIO_PARAMS(ctypes.Structure):
    _fields_ = [('dwSize', wintypes.DWORD)]

class SYSTEMTIME(ctypes.Structure):
    _fields_ = [
        ('wYear', wintypes.WORD), ('wMonth', wintypes.WORD),
        ('wDayOfWeek', wintypes.WORD), ('wDay', wintypes.WORD),
        ('wHour', wintypes.WORD), ('wMinute', wintypes.WORD),
        ('wSecond', wintypes.WORD), ('wMilliseconds', wintypes.WORD)
    ]

class BLUETOOTH_DEVICE_INFO(ctypes.Structure):
    _fields_ = [
        ('dwSize', wintypes.DWORD),
        ('Address', BLUETOOTH_ADDRESS),
        ('ulClassofDevice', wintypes.ULONG),
        ('fConnected', wintypes.BOOL),
        ('fRemembered', wintypes.BOOL),
        ('fAuthenticated', wintypes.BOOL),
        ('stLastSeen', SYSTEMTIME),
        ('stLastUsed', SYSTEMTIME),
        ('szName', ctypes.c_wchar * 248)
    ]

def mac_to_btaddr(mac_str: str) -> BLUETOOTH_ADDRESS:
    """将XX:XX:XX:XX:XX:XX格式的MAC地址转换为API要求的地址结构"""
    addr_bytes = bytes.fromhex(mac_str.replace(':', ''))[::-1]
    bt_addr = BLUETOOTH_ADDRESS()
    bt_addr.ullLong = int.from_bytes(addr_bytes, 'little')
    return bt_addr

def connect_bt_device(target_mac: str, service_guid: int):
    # 初始化本地蓝牙适配器句柄
    radio_params = BLUETOOTH_FIND_RADIO_PARAMS()
    radio_params.dwSize = ctypes.sizeof(radio_params)
    h_radio = wintypes.HANDLE()
    h_radio_find = bthprops.BluetoothFindFirstRadio(ctypes.byref(radio_params), ctypes.byref(h_radio))
    if h_radio_find == wintypes.INVALID_HANDLE_VALUE:
        raise ctypes.WinError(ctypes.get_last_error(), "未检测到可用蓝牙适配器")
    
    try:
        # 初始化目标设备信息结构
        dev_info = BLUETOOTH_DEVICE_INFO()
        dev_info.dwSize = ctypes.sizeof(dev_info)
        dev_info.Address = mac_to_btaddr(target_mac)

        # 触发配对认证,和系统UI配对逻辑完全一致
        auth_ret = bthprops.BluetoothAuthenticateDevice(
            None,  # 不需要自定义父窗口时传None,系统自动弹出标准配对框
            h_radio,
            ctypes.byref(dev_info),
            None,
            0
        )
        if auth_ret != 0:
            err = ctypes.get_last_error()
            # 错误码1244代表设备已完成配对,不属于致命错误
            if err != 1244:
                raise ctypes.WinError(err, "设备配对失败")
        
        # 启用对应服务,完成最终连接
        svc_ret = bthprops.BluetoothSetServiceState(
            h_radio,
            ctypes.byref(dev_info),
            ctypes.byref(ctypes.c_ulonglong(service_guid)),
            BLUETOOTH_SERVICE_CONNECT_FLAG
        )
        if svc_ret != 0:
            raise ctypes.WinError(ctypes.get_last_error(), "设备服务连接失败")
    finally:
        bthprops.BluetoothFindRadioClose(h_radio_find)
        ctypes.windll.kernel32.CloseHandle(h_radio)

if __name__ == "__main__":
    # 替换为目标设备的MAC地址
    TARGET_MAC = "12:34:56:78:9A:BC"
    # 根据设备类型替换为对应服务GUID
    connect_bt_device(TARGET_MAC, BLUETOOTH_SERVICE_A2DP_SINK)
    print("蓝牙设备连接完成")

常见调用失败排查

  • 传参错误:很多人调用BluetoothAuthenticateDevice失败是因为没有先获取本地蓝牙适配器句柄,直接传空值作为第二个参数,会直接返回参数无效错误
  • 权限问题:非管理员进程调用蓝牙API会返回错误码5(拒绝访问),必须提权运行
  • 服务匹配错误:不同类型的设备需要启用对应的服务GUID,传错GUID会导致连接完成后设备无法正常使用(比如蓝牙耳机只连HID服务就不会出音频输出)
  • 静默配对需求:如果已知设备固定PIN码(比如老款设备默认PIN为0000或1234),可以传入BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS结构配置PIN码,实现无弹窗静默配对,行为和系统自动连接已知设备完全一致

内容的提问来源于stack exchange,提问作者JustOscarJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 19:56:49