跳转到内容

能力 · API

Web Bluetooth API

发布于 更新于

有限可用不支持的浏览器: Firefox (Desktop)、Firefox (Android)、Safari (macOS)、Safari (iOS)社区草案

navigator.bluetooth.requestDevice() 弹出一个由浏览器托管的选择器,列出附近匹配所传过滤器的蓝牙低功耗设备,并以用户选中的那一个 BluetoothDevice 兑现。页面随后通过该对象连接设备的 GATT 服务器,读取、写入或订阅特征值,所以 PWA 不需要原生伴侣应用就能与心率带、打印机或灯具通信。

该 API 仅在 Chromium 中实现。Chrome 56 在 Android、ChromeOS 和 macOS 上支持,Chrome 70 加入 Windows,Linux 仍需开启 chrome://flags/#enable-experimental-web-platform-features(实现状态,github.com);Edge 79、Samsung Internet 6.0 与 Opera 43 跟随 Chrome。Firefox 有一个未关闭的跟踪 bug(674737),WebKit 记录了正式的「反对」立场(standards-positions #570),Android WebView 不暴露 navigator.bluetooth。

navigator.bluetooth.requestDevice(options)
navigator.bluetooth.getDevices()
navigator.bluetooth.getAvailability()
device.gatt.connect()
server.getPrimaryService(service)
service.getCharacteristic(characteristic)
characteristic.readValue()
characteristic.writeValueWithResponse(value)
characteristic.startNotifications()

requestDevice() 返回 Promise<BluetoothDevice>;getDevices() 返回 Promise<sequence<BluetoothDevice>>,列出此前已授权给本源的设备且不弹窗,自 Chrome 85 起位于 chrome://flags/#enable-web-bluetooth-new-permissions-backend 之后(BCD api.Bluetooth.getDevices)。getAvailability() 在存在适配器时兑现为 true。Bluetooth 接口标注为 [Exposed=Window, SecureContext]:在 worker 中不可用,非安全源上 navigator.bluetooth 为 undefined。

requestDevice() 接受一个 RequestDeviceOptions 字典。filters 与 acceptAllDevices: true 必须恰好给出一个;选择器只列出至少匹配一个过滤器且不匹配任何排除过滤器的设备。

成员 类型 必填 说明
filters sequence<BluetoothLEScanFilterInit> 与 acceptAllDevices 二选一 匹配任一过滤器的设备会被列出。给出时必须非空。
exclusionFilters sequence<BluetoothLEScanFilterInit> 否 匹配其中任一项的设备即使匹配 filters 也会被隐藏;需要同时给出 filters。Chrome 114。
optionalServices sequence<BluetoothServiceUUID> 否,默认 [] 连接后页面可以访问、但未在某个 services 过滤器中列出的服务。两处都没有的服务会以 SecurityError 拒绝访问。
optionalManufacturerData sequence<unsigned short> 否,默认 [] 页面可以从广播中读取其厂商数据的公司标识符。
acceptAllDevices boolean 与 filters 二选一 列出附近所有设备而不过滤。

每个 BluetoothLEScanFilterInit 至少需要一个成员。

成员 类型 说明
services sequence<BluetoothServiceUUID> 广播的服务;可写 16 位别名(0x180f)、GATT 名称('battery_service')或 128 位 UUID 字符串。必须非空。
name DOMString 精确的设备名,UTF-8 编码不超过 248 字节。
namePrefix DOMString 设备名前缀,非空且 UTF-8 编码不超过 248 字节。
manufacturerData sequence<BluetoothManufacturerDataFilterInit> { companyIdentifier, dataPrefix?, mask? };同一过滤器内标识符不得重复。Chrome 92。
serviceData sequence<BluetoothServiceDataFilterInit> { service, dataPrefix?, mask? }。

getPrimaryService()、getCharacteristic() 及其复数形式接受同样三种写法的 BluetoothServiceUUID 或 BluetoothCharacteristicUUID。

requestDevice() 以下列之一拒绝;TypeError 检查最先执行,在任何无线电活动之前。

异常 条件
TypeError 给了 exclusionFilters 但没有 filters;filters 与 acceptAllDevices: true 同时给出或都没给;filters 或 exclusionFilters 为空数组;某个过滤器没有任何成员;services、manufacturerData 或 serviceData 为空数组;name 或 namePrefix 超过 248 个 UTF-8 字节,或 namePrefix 为空;companyIdentifier 重复;dataPrefix 为空;mask 长度与 dataPrefix 不一致。
SecurityError 文档不被允许使用 bluetooth Permissions Policy 特性;调用时没有瞬时用户激活;或过滤器或 optionalServices 中出现被封禁的服务 UUID。
NotFoundError 选择器在未选中设备的情况下关闭,或没有适配器、没有匹配的设备。

GATT 方法的拒绝方式不同。device.gatt.connect() 在设备超出范围或链路失败时以 NetworkError 拒绝。getPrimaryService() 与 getCharacteristic() 对未经 filters 或 optionalServices 授权的服务或被封禁的 UUID 以 SecurityError 拒绝,gatt.connected 为 false 时以 NetworkError 拒绝,设备没有所请求项时以 NotFoundError 拒绝。readValue() 与 writeValueWithResponse() 对禁止读或写的 UUID 以 SecurityError 拒绝,对超过 512 字节的值以 InvalidModificationError 拒绝,服务或特征值已不存在时以 InvalidStateError 拒绝,设备拒绝该操作时以 NotSupportedError 拒绝,操作中途断连时以 NetworkError 拒绝。这些都列在「另请参阅」所链规范的各方法步骤中。

每个示例都先检测 navigator.bluetooth,并给出没有它时页面的行为;前两个因激活规则必须由 click 之类的处理函数触发。

按标准的 Battery Service 过滤,选择器只显示广播该服务的设备;连接后读取单字节的 Battery Level 特征值。API 缺失时函数返回 null,界面说明该浏览器无法访问蓝牙设备。

async function readBatteryLevel() {
if (!('bluetooth' in navigator)) return null;
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['battery_service'] }],
});
const server = await device.gatt.connect();
const service = await server.getPrimaryService('battery_service');
const characteristic = await service.getCharacteristic('battery_level');
const value = await characteristic.readValue();
return value.getUint8(0);
}
document.querySelector('#battery').addEventListener('click', async () => {
const output = document.querySelector('#battery-output');
try {
const level = await readBatteryLevel();
output.textContent = level === null
? '此浏览器无法访问蓝牙设备,请直接在设备上查看。'
: `电量:${level}%`;
} catch (err) {
if (err.name === 'NotFoundError') return; // 选择器被关闭
output.textContent = `${err.name}: ${err.message}`;
}
});

'battery_service' 与 'battery_level' 是 GATT 分配名称,浏览器将其映射为 0x180F 与 0x2A19;厂商自定义服务用 128 位 UUID 字符串,用法相同。

startNotifications() 之后,通知以 characteristicvaluechanged 事件到达。设备随时可能走出范围,所以在设备上监听 gattserverdisconnected,并提供重连:对同一个对象再次调用 gatt.connect(),不需要重新弹出选择器。

async function watchHeartRate(onBeat, onLost) {
if (!('bluetooth' in navigator)) {
onLost('此浏览器不提供蓝牙访问');
return;
}
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['heart_rate'] }],
});
device.addEventListener('gattserverdisconnected', () => onLost('设备已断开'));
const server = await device.gatt.connect();
const service = await server.getPrimaryService('heart_rate');
const measurement = await service.getCharacteristic('heart_rate_measurement');
measurement.addEventListener('characteristicvaluechanged', (event) => {
const data = event.target.value;
const is16Bit = data.getUint8(0) & 0x1;
onBeat(is16Bit ? data.getUint16(1, true) : data.getUint8(1));
});
await measurement.startNotifications();
}

Heart Rate Measurement 的第一个字节是标志位;bit 0 表示随后的心率值是 8 位还是 16 位,所以处理函数在读取前要分支。

显示配对按钮前先检查有没有适配器

Section titled “显示配对按钮前先检查有没有适配器”

getAvailability() 告诉页面这台机器是否有蓝牙无线电,这样可以在没有适配器的桌面机上隐藏「连接」按钮,而不是让用户撞上 NotFoundError。它不弹窗,也不需要用户激活。

async function bluetoothUiState() {
if (!('bluetooth' in navigator)) return 'unsupported';
const available = await navigator.bluetooth.getAvailability();
return available ? 'ready' : 'no-adapter';
}
const state = await bluetoothUiState();
document.querySelector('#connect').hidden = state !== 'ready';
document.querySelector('#bluetooth-note').textContent = {
unsupported: '请在桌面或 Android 设备上使用 Chrome 或 Edge 进行蓝牙连接。',
'no-adapter': '此设备上未找到蓝牙适配器。',
ready: '',
}[state];

页面打开期间可用性可能变化(插入 USB 适配器、关闭蓝牙);navigator.bluetooth 上的 availabilitychanged 事件会报告新值。

规范

规范状态
Web Bluetooth API(网页蓝牙)社区草案
Web Bluetooth: requestDevice() methodWHATWG 现行标准
Web Bluetooth: RequestDeviceOptions dictionaryWHATWG 现行标准
  • 图例
  • 支持
  • 部分支持
  • 需开启标志
  • 不支持
  • 未知
浏览器 / 平台支持版本置信度来源备注
Chrome (Desktop)支持56高来源—
Chrome (Android)支持56高来源1
Edge (Desktop)支持79高来源2
Firefox (Desktop)不支持—高来源3
Firefox (Android)不支持—高来源45
Safari (macOS)不支持—高来源6
Safari (iOS)不支持—高来源78
Samsung Internet支持6.0高来源9
WebView (Android)不支持—高来源10
Opera支持43高来源11
  1. 由 browser-compat-data 镜像自 Chrome 的数据推导。
  2. 由 browser-compat-data 镜像自 Chrome 的数据推导。
  3. 实现跟踪:https://bugzil.la/674737。
  4. 实现跟踪:https://bugzil.la/674737。
  5. 由 browser-compat-data 镜像自 Firefox 的数据推导。
  6. 实现跟踪:https://webkit.org/b/101034。
  7. 实现跟踪:https://webkit.org/b/101034。
  8. 由 browser-compat-data 镜像自 Safari 的数据推导。
  9. 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
  10. browser-compat-data 未记录 WebView Android 的支持。
  11. 镜像 Chrome 的实现(BCD `opera: mirror`):Linux 上有支持,但默认未启用。

源数据: /compatibility/web-bluetooth.json · 全球使用占比: 74 % (StatCounter 2026-05)

来源: 规范 · MDN · 最近核验 2026-10-03 · 置信度: 高 (由来源计算)