首页
/ Puppeteer 详解 BluetoothManufacturerData:Web Bluetooth 模拟中的厂商数据接口

Puppeteer 详解 BluetoothManufacturerData:Web Bluetooth 模拟中的厂商数据接口

2026-09-05 12:19:30作者:柯茵沙

本篇围绕 Puppeteer API 文档中的 BluetoothManufacturerData 接口展开,讲解这一"模拟蓝牙外设厂商数据"类型的两个核心字段(keydata)的含义、取值来源,以及它如何被组合进 PreconnectedPeripheral,经由 page.bluetooth.simulatePreconnectedPeripheral() 下发到 Chrome(CDP)或 BiDi 会话,最终驱动 navigator.bluetooth.requestDevice() 设备选择提示框。读完本文,你将能够独立构造合法的蓝牙外设模拟配置,并理解其底层 CDP/BiDi 指令链路与浏览器上下文级别的状态限制。

接口定位:BluetoothManufacturerData 是什么

BluetoothManufacturerData 是一个用于模拟(模拟出的)蓝牙外设(peripheral)厂商数据的 TypeScript 接口。官方定义如下(见 BluetoothEmulation.ts 源码):

/**
 * @public
 * Represents the simulated bluetooth peripheral's manufacturer data.
 */
export interface BluetoothManufacturerData {
  /**
   * The company identifier, as defined by the
   * {@link https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/|Bluetooth SIG}.
   */
  key: number;
  /**
   * The manufacturer-specific data as a base64-encoded string.
   */
  data: string;
}

在真实的 BLE(低功耗蓝牙)广播数据中,"厂商特定数据(Manufacturer Specific Data)"由两部分组成:一个 2 字节的公司标识符(Company Identifier),以及紧随其后的厂商自定义负载。Puppeteer 的该接口正是按这一结构建模:key 对应公司标识符,data 对应厂商自定义负载。整个接口属于 BluetoothEmulation 能力族(emulateAdapter / simulatePreconnectedPeripheral / disableEmulation),并在源码注释中标记为 @public@experimental(实验性 API)。

属性详解:key 与 data

对应官方文档 puppeteer.bluetoothmanufacturerdata.md 中的属性表,完整继承其字段定义如下:

属性 修饰符 类型 说明 默认值
data string 厂商特定数据,以 base64 编码的字符串表示
key number 公司标识符,按 Bluetooth SIG 的 Assigned Numbers(Company Identifiers)注册表定义

key:Bluetooth SIG 公司标识符

key 必须取 Bluetooth SIG 官方"Assigned Numbers — Company Identifiers"注册表中的编号(例如注册表中 0x004C/76 对应 Apple、0x0005/5 对应 Intel 等公开条目,完整清单以注册表为准)。它是 number 类型,即十进制整数;测试用例 bluetooth-emulation.test.ts 中使用的示例值为 17。在 Web Bluetooth 语义下,页面代码可以通过 device.gatt 或广播数据过滤匹配该标识来识别特定厂商的外设,因此模拟时填写正确的 key 对行为保真很重要。

data:base64 编码的厂商负载

data厂商自定义负载的 base64 编码字符串,而非原始字节或十六进制串。这一点在写测试或模拟配置时容易踩坑:需要先把二进制负载编码为 base64 再传入。

以仓库测试与官方文档示例中反复出现的 'AP8BAX8=' 为例,其 base64 解码后恰好是 5 个字节:

'AP8BAX8='  ->  0x00 0xFF 0x01 0x01 0x5F

这是一个典型的小型厂商数据负载(末尾 0x5F 对应 ASCII 字符 "Some Name" 片段中常见的演示数据)。这提示读者:data 字段的长度与内容完全由被测 Web 应用对该厂商数据的解析逻辑决定,模拟时应以页面期望的字节序列为准,base64 只是传输编码形式。

使用位置:作为 PreconnectedPeripheral 的组成部分

BluetoothManufacturerData 并不是直接暴露给 page 的,而是作为 PreconnectedPeripheral(待模拟的蓝牙外设)的一个字段数组存在。完整的周边设备类型定义见 BluetoothEmulation.ts

/**
 * @public
 * A bluetooth peripheral to be simulated.
 */
export interface PreconnectedPeripheral {
  address: string;
  name: string;
  manufacturerData: BluetoothManufacturerData[];
  knownServiceUuids: string[];
}

各字段的语义可参照 PreconnectedPeripheral 接口文档

字段 类型 说明
address string 模拟外设的蓝牙地址,示例为 '09:09:09:09:09:09'
name string 设备名称,示例为 'SOME_NAME'
manufacturerData BluetoothManufacturerData[] 一个或多个厂商数据条目,即本文主角接口
knownServiceUuids string[] 该外设已知的 GATT 服务 UUID 列表

其中 manufacturerData数组类型,意味着同一台模拟外设可以携带多条不同公司标识(key)的厂商数据,逐条独立编码 data

完整实战流程:从 emulateAdapter 到 disableEmulation

官方 BluetoothEmulation 文档BluetoothEmulation.ts 源码注释中给出了同一套端到端示例,完整保留如下:

// 1. 先把模拟的蓝牙适配器置为"开机"状态(模拟的前提)
await page.bluetooth.emulateAdapter('powered-on');

// 2. 注入一台"已预连接"的模拟外设,携带厂商数据
await page.bluetooth.simulatePreconnectedPeripheral({
  address: '09:09:09:09:09:09',
  name: 'SOME_NAME',
  manufacturerData: [
    {
      key: 17,
      data: 'AP8BAX8=',
    },
  ],
  knownServiceUuids: ['12345678-1234-5678-9abc-def123456789'],
});

// 3. 用完后关闭模拟,恢复真实行为
await page.bluetooth.disableEmulation();

关键说明:

  • emulateAdapter(state, leSupported?) 是模拟的前置条件(Required for bluetooth simulations),对应 Web Bluetooth 规范的 bluetooth.simulateAdapter 命令。state 的合法取值由 AdapterState 类型约束(源码定义):

    export type AdapterState = 'absent' | 'powered-off' | 'powered-on';
    

    第二参数 leSupported 标记适配器是否支持低功耗蓝牙,两个实现中默认值均为 true

  • simulatePreconnectedPeripheral() 对应规范中的 bluetooth.simulatePreconnectedPeripheral(见方法文档),把上表四字段的外设"预连接"进模拟环境。

  • disableEmulation() 对应 bluetooth.disableSimulation,用于清理模拟状态。

这三个方法均挂载在 Page 实例上:从 Page.ts 源码结构看,Page 通过抽象访问器 abstract get bluetooth(): BluetoothEmulation 暴露该能力,CDP 与 BiDi 两套后端各自提供实现。

底层实现:CDP 与 BiDi 双后端的指令链路

Puppeteer 对 BluetoothManufacturerData 的消费并不在 JS 层做校验或转换,而是整体透传给浏览器协议。两个后端的实现可以对照阅读:

CDP 后端(Chromium DevTools Protocol)

CdpBluetoothEmulation 通过 Connection.send 发送 CDP 命令。值得注意的是 emulateAdapter 的实现细节:

async emulateAdapter(state: AdapterState, leSupported = true): Promise<void> {
  // Bluetooth spec requires overriding the existing adapter (step 6). From the CDP
  // perspective, it means disabling the emulation first.
  await this.#connection.send('BluetoothEmulation.disable');
  await this.#connection.send('BluetoothEmulation.enable', {
    state,
    leSupported,
  });
}

源码注释指出:Web Bluetooth 规范(bluetooth.simulateAdapter 命令的第 6 步)要求覆盖已存在的模拟适配器,因此在 CDP 侧必须先发 BluetoothEmulation.disable 再发 BluetoothEmulation.enable。而 simulatePreconnectedPeripheral 则是把整个 preconnectedPeripheral 对象(含 manufacturerData 数组)原样作为 BluetoothEmulation.simulatePreconnectedPeripheral 命令的参数发送——keydata 保持 JS 侧的类型不做任何变换。

BiDi 后端(WebDriver BiDi)

BidiBluetoothEmulation 发送的是 BiDi 域命令,且显式携带 context(浏览器上下文)标识

await this.#session.send('bluetooth.simulatePreconnectedPeripheral', {
  context: this.#contextId,
  address: preconnectedPeripheral.address,
  name: preconnectedPeripheral.name,
  manufacturerData: preconnectedPeripheral.manufacturerData,
  knownServiceUuids: preconnectedPeripheral.knownServiceUuids,
});

从源码结构看,两个后端对 manufacturerData 都是零加工透传,协议层面的 base64 编码与 key 取值合法性校验由浏览器端负责。这提示使用者:如果 data 不是合法 base64 或字段结构不完整,错误会在协议层/浏览器侧暴露,而不是在 Puppeteer 侧提前拦截。

测试佐证:厂商数据如何驱动真实设备选择提示

仓库内置的 bluetooth-emulation.test.ts 提供了该接口在真实浏览器中的完整验证链路,其模拟外设配置与官方示例完全一致:

const SIMULATED_PERIPHERAL = {
  address: '09:09:09:09:09:09',
  name: DEVICE_NAME, // 'SOME_NAME'
  manufacturerData: [
    {
      key: 17,
      data: 'AP8BAX8=',
    },
  ],
  knownServiceUuids: ['12345678-1234-5678-9abc-def123456789'],
};

测试的关键要点:

  1. 启动参数:通过 setupSeparateTestBrowserHooks 传入 --enable-features=WebBluetoothNewPermissionsBackend--enable-features=WebBluetooth,并设置 acceptInsecureCerts: true,在 HTTPS 测试服务器页面上执行(Web Bluetooth 要求安全上下文)。
  2. 触发页面 API:在页面内调用 navigator.bluetooth.requestDevice({ acceptAllDevices: true, optionalServices: [] }),浏览器弹出设备选择提示框。
  3. Puppeteer 侧接管page.waitForDevicePrompt() 等待提示框,然后可以 cancel()(此时页面侧的 requestDevice 应 reject),或 select(devicePrompt.devices[0]) 选中模拟设备(此时页面拿到 device.name === 'SOME_NAME')。

这说明携带 manufacturerData 的模拟外设会真实出现在 Web Bluetooth 的设备选择列表中,key/data 构成了页面端过滤与识别依据。

作用域与注意事项

  • 实验性 APIBluetoothManufacturerData 及其所属的 BluetoothEmulation 三个方法在源码中均标注 @experimental,接口签名可能随版本演进变化,用于生产自动化前建议锁定版本并关注变更日志。
  • 模拟状态绑定在浏览器上下文而非页面:官方文档(BluetoothEmulation Remarks)明确说明——Web Bluetooth 规范要求模拟适配器按顶层可导航单元(top-level navigable)隔离,但 Chromium 当前的实现把蓝牙模拟绑定到**浏览器上下文(browser context)**上。因此,同一浏览器上下文内的不同页面暴露出的蓝牙模拟状态会相互干扰。从源码结构看,BiDi 实现按 contextId 发送命令,CDP 实现则直接走浏览器级 Connection,两种路径都体现了这一上下文级作用域。若需要相互独立的模拟状态,建议使用独立的浏览器上下文。
  • 执行顺序:先 emulateAdapter('powered-on')simulatePreconnectedPeripheral,最后 disableEmulation 清理;跳过适配器模拟直接注入外设的行为未受支持。

相关文件索引

内容 路径
本接口官方 API 文档 docs/api/puppeteer.bluetoothmanufacturerdata.md
接口类型定义(BluetoothManufacturerData / PreconnectedPeripheral / BluetoothEmulation packages/puppeteer-core/src/api/BluetoothEmulation.ts
CDP 后端实现 packages/puppeteer-core/src/cdp/BluetoothEmulation.ts
BiDi 后端实现 packages/puppeteer-core/src/bidi/BluetoothEmulation.ts
端到端测试用例 test/src/bluetooth-emulation.test.ts
外设接口文档 docs/api/puppeteer.preconnectedperipheral.md
BluetoothEmulation 能力文档(含完整示例) docs/api/puppeteer.bluetoothemulation.md

小结

BluetoothManufacturerData 虽只是一个双字段接口(key: number + data: string),却是 Puppeteer 蓝牙模拟中承载"厂商身份"的关键载体:key 对齐 Bluetooth SIG 公司标识符注册表,data 以 base64 承载厂商自定义字节。它与 addressnameknownServiceUuids 共同构成 PreconnectedPeripheral,经 CDP 的 BluetoothEmulation.simulatePreconnectedPeripheral 或 BiDi 的 bluetooth.simulatePreconnectedPeripheral 命令透传给浏览器,使无硬件环境的自动化测试也能完整演练 requestDevice() 的设备选择、取消与选中流程。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384