Web Bluetooth API 允许网站与蓝牙设备通信。
如果我告诉你网站可以与附近的蓝牙设备通信,该怎么办? 安全、可保护隐私的方式?这样,心率监测器就会开始唱歌 灯泡,甚至海龟都可以直接与网站互动。
到目前为止,用户可以与蓝牙设备互动 。Web Bluetooth API 旨在改变这种情况, 也会将其引入网络浏览器
开始前须知
本文档假定您对低功耗蓝牙的工作原理有基本的了解 能耗 (BLE) 和通用属性配置文件可正常运作。
虽然 Web Bluetooth API 规范尚未最终确定,该规范 作者正在积极寻找有热情的开发者试用此 API 并 提供有关规范的反馈和有关实施的反馈。
Web Bluetooth API 的部分功能可在 ChromeOS、Chrome(Android 版)中使用 6.0、Mac (Chrome 56) 和 Windows 10 (Chrome 70)。也就是说,您应该能够 请求并连接附近的蓝牙低功耗设备; 读取/写入蓝牙特征,接收 GATT 通知,了解 当蓝牙设备断开连接时,甚至读取和写入 蓝牙描述符。如需了解详情,请参阅 MDN 的浏览器兼容性表格 信息。
对于 Linux 和更早版本的 Windows,请启用
about://flags
中的 #experimental-web-platform-features
标志。
可用于源试用
为了从使用 Web 应用的开发者那里获得尽可能多的反馈 Bluetooth API,Chrome 之前已在 Chrome 中添加此功能 53 作为 ChromeOS、Android 和 Mac 的源试用版。
试用期已于 2017 年 1 月成功完成。
安全性要求
要了解其安全方面的权衡,建议您参考网络蓝牙安全性 Chrome 团队的软件工程师 Jeffrey Yasskin 发布的模型帖子, 制定 Web Bluetooth API 规范。
仅限 HTTPS
由于这个实验性 API 是添加到 Web 中的强大新功能, 仅可用于安全上下文。也就是说,您需要使用 考虑使用 TLS。
需要用户手势
作为一项安全功能,
navigator.bluetooth.requestDevice
必须通过用户手势触发,例如
通过触摸或鼠标点击操作。我们要说的是
pointerup
、click
和 touchend
事件。
button.addEventListener('pointerup', function(event) {
// Call navigator.bluetooth.requestDevice
});
深入了解代码
Web Bluetooth API 在很大程度上依赖于 JavaScript Promise。如果您没有
请参阅这个精彩的 Promise 教程。还有一点
() => {}
是 ECMAScript 2015 箭头函数。
请求蓝牙设备
此版本的 Web Bluetooth API 规范允许运行 它 支持在实现蓝牙 4.0 或更高版本的设备之间通信。
当某个网站使用
navigator.bluetooth.requestDevice
时,浏览器提示用户使用设备
选择器,他们可以选择一台设备或取消请求。
navigator.bluetooth.requestDevice()
函数接受一个必需的对象,
定义了过滤器。这些过滤条件用于仅返回与某些条件匹配的设备
通告的蓝牙 GATT 服务和/或设备名称。
服务过滤条件
例如,请求通告蓝牙 GATT 的蓝牙设备 电池服务:
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });
如果您的蓝牙 GATT 服务不在标准化蓝牙 GATT 服务,您可以提供完整的蓝牙 UUID 或简短的蓝牙 UUID 16 位或 32 位形式。
navigator.bluetooth.requestDevice({
filters: [{
services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
}]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
名称过滤条件
您还可以根据所通告的设备名称请求蓝牙设备
替换为 name
过滤条件键,甚至是带有 namePrefix
的此名称前缀
过滤条件键。请注意,在这种情况下,您还需要定义
optionalServices
密钥,即可访问未包含在
服务过滤条件。如果不这样做,之后当您尝试访问时将收到错误消息
。
navigator.bluetooth.requestDevice({
filters: [{
name: 'Francois robot'
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
制造商数据过滤器
也可以根据制造商来请求蓝牙设备
使用 manufacturerData
过滤条件键通告的特定数据。此钥匙
是对象的数组,其中包含必需的蓝牙公司标识符键,该键名为
companyIdentifier
。您还可以提供一个数据前缀
制造商数据。请注意,您将
还需要定义optionalServices
键才能访问任何服务
未包含在服务过滤器中。如果不进行此设置,
访问它们。
// Filter Bluetooth devices from Google company with manufacturer data bytes
// that start with [0x01, 0x02].
navigator.bluetooth.requestDevice({
filters: [{
manufacturerData: [{
companyIdentifier: 0x00e0,
dataPrefix: new Uint8Array([0x01, 0x02])
}]
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
掩码也可以与数据前缀一起使用,以匹配 制造商数据。如需了解相关信息,请参阅蓝牙数据过滤器说明 。
排除过滤条件
通过 navigator.bluetooth.requestDevice()
中的 exclusionFilters
选项,
您将某些设备从浏览器选择器中排除。它可用于排除
匹配范围更广但不受支持的设备。
// Request access to a bluetooth device whose name starts with "Created by".
// The device named "Created by Francois" has been reported as unsupported.
navigator.bluetooth.requestDevice({
filters: [{
namePrefix: "Created by"
}],
exclusionFilters: [{
name: "Created by Francois"
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
不使用滤镜
最后,您可以使用 acceptAllDevices
键(而不是 filters
)显示所有
附近的蓝牙设备。您还需要定义 optionalServices
密钥才能访问某些服务。如果不这样做,稍后您会收到错误消息
。
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
连接到蓝牙设备
那么,您有了 BluetoothDevice
后要怎么做?让我们连接到
包含服务和特征的蓝牙远程 GATT 服务器
定义。
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {
// Human-readable name of the device.
console.log(device.name);
// Attempts to connect to remote GATT Server.
return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });
读取蓝牙特性
这里我们连接了远程蓝牙设备的 GATT 服务器。现在,我们 希望获取主要 GATT 服务并读取属于 。例如,我们来尝试读取 电池。
在前面的示例中,battery_level
是标准化电池电量
特征。
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => device.gatt.connect())
.then(server => {
// Getting Battery Service…
return server.getPrimaryService('battery_service');
})
.then(service => {
// Getting Battery Level Characteristic…
return service.getCharacteristic('battery_level');
})
.then(characteristic => {
// Reading Battery Level…
return characteristic.readValue();
})
.then(value => {
console.log(`Battery percentage is ${value.getUint8(0)}`);
})
.catch(error => { console.error(error); });
如果您使用自定义蓝牙 GATT 特征,则可以提供
完整的蓝牙 UUID 或简短的 16 位或 32 位形式,
service.getCharacteristic
。
请注意,您也可以将characteristicvaluechanged
事件监听器添加到
特征来处理读取其值的操作。请参阅阅读特征
值更改示例,了解如何有选择地处理即将推出的 GATT
通知。
…
.then(characteristic => {
// Set up event listener for when characteristic value changes.
characteristic.addEventListener('characteristicvaluechanged',
handleBatteryLevelChanged);
// Reading Battery Level…
return characteristic.readValue();
})
.catch(error => { console.error(error); });
function handleBatteryLevelChanged(event) {
const batteryLevel = event.target.value.getUint8(0);
console.log('Battery percentage is ' + batteryLevel);
}
写入蓝牙特征
写入蓝牙 GATT 特性就像读取它一样简单。这一次, 让我们用心率控制点来重置消耗能量的值 字段设置为 0。
我保证这里没有魔法。有关详情,请参阅心率控制 “点特征”页面。
navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_control_point'))
.then(characteristic => {
// Writing 1 is the signal to reset energy expended.
const resetEnergyExpended = Uint8Array.of(1);
return characteristic.writeValue(resetEnergyExpended);
})
.then(_ => {
console.log('Energy expended has been reset.');
})
.catch(error => { console.error(error); });
接收 GATT 通知
现在,我们来看看当心率测量 特征变化:
navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_measurement'))
.then(characteristic => characteristic.startNotifications())
.then(characteristic => {
characteristic.addEventListener('characteristicvaluechanged',
handleCharacteristicValueChanged);
console.log('Notifications have been started.');
})
.catch(error => { console.error(error); });
function handleCharacteristicValueChanged(event) {
const value = event.target.value;
console.log('Received ' + value);
// TODO: Parse Heart Rate Measurement value.
// See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
}
通知示例向您展示了如何使用
stopNotifications()
,并正确移除已添加的 characteristicvaluechanged
事件监听器。
断开与蓝牙设备的连接
为了提供更好的用户体验,您可能需要监听断开连接事件 并邀请用户重新连接:
navigator.bluetooth.requestDevice({ filters: [{ name: 'Francois robot' }] })
.then(device => {
// Set up event listener for when device gets disconnected.
device.addEventListener('gattserverdisconnected', onDisconnected);
// Attempts to connect to remote GATT Server.
return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });
function onDisconnected(event) {
const device = event.target;
console.log(`Device ${device.name} is disconnected.`);
}
您也可以调用 device.gatt.disconnect()
断开您的 Web 应用与
蓝牙设备。此操作会触发现有的 gattserverdisconnected
事件
监听器。请注意,如果其他蓝牙设备与蓝牙设备通信,
应用程序已在与蓝牙设备通信。请查看设备
断开示例和自动重新连接示例以深入了解。
对蓝牙描述符执行读写操作
蓝牙 GATT 描述符是描述特征值的属性。 您可以采用与蓝牙 GATT 类似的方式读取和写入它们 特征。
例如,我们来看看如何解读用户测量的 设备健康温度计的时间间隔。
在下面的示例中,health_thermometer
是健康温度计服务。
measurement_interval
测量间隔特性
gatt.characteristic_user_description
在有特征的用户描述
描述符。
navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => descriptor.readValue())
.then(value => {
const decoder = new TextDecoder('utf-8');
console.log(`User Description: ${decoder.decode(value)}`);
})
.catch(error => { console.error(error); });
现在,我们已阅读了有关 设备健康温度计,我们来看看如何更新温度计,并编写自定义 值。
navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => {
const encoder = new TextEncoder('utf-8');
const userDescription = encoder.encode('Defines the time between measurements.');
return descriptor.writeValue(userDescription);
})
.catch(error => { console.error(error); });
示例、演示和 Codelab
以下所有网络蓝牙示例均已成功测试。为了欣赏这些 建议您安装 [BLE 外设模拟器], Android 应用],用于模拟具有电池服务和心率的 BLE 外围设备 服务或健康温度计服务
新手
- 设备信息 - 从 BLE 设备中检索基本设备信息。
- 电池电量 - 从通告电池信息的 BLE 设备中检索电池信息。
- 重置能量 - 重置 BLE 设备通告心率消耗的能量。
- 特征属性 - 显示 BLE 设备中特定特征的所有属性。
- 通知 - 开始和停止来自 BLE 设备的特征通知。
- 设备断开连接 - BLE 设备在连接后断开,当 BLE 设备断开连接时,您会收到相关通知。
- 获取特征 - 从 BLE 设备获取所通告服务的所有特征。
- 获取描述符 - 获取所有特征BLE 设备中通告服务的描述符。
- 制造商数据过滤器 - 从 BLE 设备中检索与制造商数据匹配的基本设备信息。
- 排除过滤器 - 从具有基本排除过滤器的 BLE 设备中检索基本设备信息。
组合多项操作
- GAP 特性 - 获取 BLE 设备的所有 GAP 特性。
- 设备信息特征 - 获取 BLE 设备的所有设备信息特征。
- 链路损失 - 设置 BLE 设备的警报等级特征(readValue 和 writeValue)。
- 探索服务和特征 - 通过 BLE 设备发现所有可访问的主要服务及其特性。
- 自动重新连接 - 使用指数退避算法重新连接到已断开连接的 BLE 设备。
- 读取特性值已更改 - 读取电池电量,并在 BLE 设备发生变化时收到通知。
- 读取描述符 - 从 BLE 设备中读取服务的所有特征描述符。
- 写入描述符 - 写入描述符“特征用户描述”实现这个目标
您还可以查看我们精选的 Web 蓝牙演示和官方的 Web 蓝牙 Codelab。
库
- web-bluetooth-utils 是一个 npm 模块,它添加了一些便捷函数 该 API。
- Web Bluetooth API shim 可在最受欢迎的 Node.js BLE noble 中实现 核心模块中这样,您无需编写任何代码 适用于 WebSocket 服务器或其他插件。
- angular-web-bluetooth 是 Angular 的一个模块,可以抽象化所有 配置 Web Bluetooth API 所需的样板文件。
工具
- 网络蓝牙入门是一款简单的网络应用,能生成所有 开始与蓝牙设备交互的 JavaScript 样板代码。 输入设备名称、服务和特征,定义其属性并 一切正常。
- 如果您已经是蓝牙开发者,请访问 Web Bluetooth Developer Studio 插件还会为您的移动设备生成网络蓝牙 JavaScript 代码。 蓝牙设备。
提示
Chrome 中提供了一个蓝牙内部功能页面,网址为:
about://bluetooth-internals
,以便您可以检查关于附近地点的所有信息
蓝牙设备:状态、服务、特征和描述符。
![用于在 Chrome 中调试蓝牙的内部页面的屏幕截图](https://developer.chrome.google.cn/static/docs/capabilities/bluetooth/image/screenshot-the-internal-acdfb455a1a38.jpg?authuser=9&hl=sk)
我还建议您查看官方的如何提交网络蓝牙错误 因为有时调试蓝牙可能很困难。
后续步骤
请先查看浏览器和平台实现状态,以了解哪些部分 的 Web Bluetooth API 功能目前正在实现中。
虽然目前还不够完善,但您可以抢先了解 未来:
- 扫描附近的 BLE 广告
navigator.bluetooth.requestLEScan()
会发生哪些变化。 - 新的
serviceadded
事件将跟踪新发现的蓝牙 GATT 服务 而serviceremoved
事件则会跟踪已删除的事件。全新servicechanged
事件会在用户添加了任何特征和/或描述符时触发, 从蓝牙 GATT 服务中移除的内容。
表示对 API 的支持
您打算使用 Web Bluetooth API 吗?您的公开支持对 Chrome 团队有所帮助 确定功能的优先级,并向其他浏览器供应商展示支持这些功能的重要性。
使用 # 标签向 @ChromiumDev 发送推文
#WebBluetooth
并告诉我们您使用它的地点和方式。
资源
致谢
感谢 Kayce Basques 审核本文。 主打图片由美国博尔德的 SparkFun Electronics 提供。