如今,浏览器不断发展,带来了新的 API 和连接其他设备的方式,并允许访问比以往更多的功能。其中一种 API 是 Web 蓝牙 API[1]。
在撰写本文时,该 API 仍处于测试阶段,但一旦向公众发布,它将为想要使用蓝牙但不想为每个平台创建原生应用程序的开发人员提供大量机会。
尽管蓝牙 API 仍处于测试阶段,但我们会尝试并制作一个简单的网页,该网页将与我们的手机配对并提供基本详细信息,例如电池百分比、设备名称以及设备提供的基本信息制造商。
我们不会在本教程中使用样式,因为我们只需要了解如何使用 JavaScript 与蓝牙 API 交互。
请记住,并非所有浏览器都支持此 API,您将无法在每部手机上进行测试。某些手机可能不允许获取设备信息。在本教程中,我将使用 Apple iPhone 11,它允许我通过浏览器上的蓝牙获取我的设备信息而不会出现任何问题。
前提
- 一个代码编辑器;我更喜欢 VS Code
- 使用 VS Code 时的实时服务器扩展
- 具有蓝牙功能(或即插即用蓝牙硬件)的笔记本电脑或 PC
- 有蓝牙功能的移动设备(我用的是iPhone 11,你可以用自己的手机试试)
- 对JavaScript有一定的了解
- Chrome Beta 安装在您的 PC 或笔记本电脑上。蓝牙 API 是 Beta 版功能,在 Chrome Beta 版上效果最佳。
请注意,并非所有基于 Chromium 的浏览器(例如 Brave)都支持蓝牙 API。我尝试在 Brave 上使用 API,但发现 Brave 出于安全原因故意禁用了 API。
如果你对代码需要任何帮助,这里是[GitHub仓库](https://github.com/atharvadeosthale/web-bluetooth-phone)。
让我们开始吧
首先,我们需要创建一个文件夹,我们将把它作为一个工作区。一旦你创建了一个文件夹,使用以下命令打开VS Code:
code .
我们将在本教程中使用两个文件;将它们命名为 index.html 和 script.js。在 index.html 中,我们只需要基本布局(只是一个按钮),并将文件链接到我们的 JavaScript 文件。
以下是 index.html 的内容:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Document</title>
- </head>
- <body>
- <button id="getDetails">Get device details</button>
- <div id="details"></div>
- <script src="script.js"></script>
- </body>
- </html>
添加蓝牙功能
让我们从功能开始。转到 script.js 并将 UI 元素存储在变量中,以便我们以后可以访问它们:
- const button = document.getElementById("getDetails");
- const details = document.getElementById("details");
现在,让我们为我们的按钮创建一个点击监听器,这样我们就可以在用户点击按钮时执行我们的操作:
- button.addEventListener("click", async () => {
- try {
- } catch(err) {
- console.error(err);
- alert("An error occured while fetching device details");
- }
- });
我们将该函数设为异步函数,因为它使我们的事情变得更简单,而且我们不需要进行很多回调,使我们的代码看起来更有条理。从现在开始,我们所有的代码都将在 try 块中。
请求蓝牙设备
接下来,让我们通过浏览器请求蓝牙设备:
- // 通过浏览器请求蓝牙设备
- const device = await navigator.bluetooth.requestDevice({
- optionalServices: ["battery_service", "device_information"],
- acceptAllDevices: true,
- });
在上面的代码中,我们通过 navigator.bluetooth 使用了蓝牙 API。在连接到设备之前,我们需要向设备提供有关我们将要访问哪些数据的信息。
我们可以使用目标蓝牙设备上存在的各种服务访问所需的数据。在这种情况下,我们正在与电池和设备信息进行交互,因此我们需要 Battery_service 和 device_information 服务。
一旦用户选择了他想要连接的蓝牙设备,我们就会建立到 GATT 服务器的连接,它为我们提供了对我们之前请求的服务的访问,并将设备名称存储在一个变量中以供以后使用:
- // 连接到 GATT 服务器
- // 我们还在这里获得了蓝牙设备的名称
- let deviceName = device.gatt.device.name;
- const server = await device.gatt.connect();
现在,我们需要从 GATT 服务器单独获取服务,以便我们可以单独访问它们:
- // 通过 GATT 服务器获取我们之前提到的服务
- const batteryService = await server.getPrimaryService("battery_service");
- const infoService = await server.getPrimaryService("device_information");
从设备获取信息
首先,让我们获取目标设备的电池电量。
每个蓝牙设备都有各种服务可以互动。例如,一个移动设备可以有一个电池服务,用于所有电池活动。还可以提供帮助拨打和接听电话的电话服务。不同的设备都有不同的蓝牙服务。
每个服务都有特征,每个特征都有一个值。这个值是一个缓冲区,所以我们需要把它转换成人类可读的形式。
电池电量是一个百分比,因此我们将缓冲区转换为整数:
- // 获取当前电池电量
- const batteryLevelCharacteristic = await batteryService.getCharacteristic(
- "battery_level"
- );
- // 将收到的缓冲区转换为数字
- const batteryLevel = await batteryLevelCharacteristic.readValue();
- const batteryPercent = await batteryLevel.getUint8(0);
readValue() 函数返回一个缓冲区,我们需要将其转换为人类可读的形式。
现在,让我们努力获取更多设备信息。如前所述,每项服务都有一个或多个特征。device_information 服务根据设备的不同可能有相当多的特征,我们无法提取一个特定的特征,因为每个设备都有不同的配置和不同的唯一 ID 来访问数据。因此,我们只需读取本例中的所有特征。
下面的代码就是这样做的:
- // 获取设备信息
- // 我们将从 device_information 中获取所有特征
- const infoCharacteristics = await infoService.getCharacteristics();
- console.log(infoCharacteristics);
- let infoValues = [];
- const promise = new Promise((resolve, reject) => {
- infoCharacteristics.forEach(async (characteristic, index, array) => {
- // Returns a buffer
- const value = await characteristic.readValue();
- console.log(new TextDecoder().decode(value));
- // Convert the buffer to string
- infoValues.push(new TextDecoder().decode(value));
- if (index === array.length - 1) resolve();
- });
- });
我们将 forEach 包装在 Promise 下,因为父级和 forEach 本身是一个异步函数,因此我们需要在继续显示数据之前获取数据。在这里,当我们使用 readValue() 获取值时,我们使用的是 TextDecoder,因为我们知道 device_information 服务中的大部分数据是字符串类型而不是整数。
然后我们将所有数据推送到一个数组中,以便我们可以在 UI 上呈现它,然后在读取所有特征后解析 Promise。
现在,我们只需在屏幕上渲染数据:
- promise.then(() => {
- // 在屏幕上显示所有信息
- // 使用innerHTML
- details.innerHTML = `
- Device Name - ${deviceName}<br />
- Battery Level - ${batteryPercent}%<br />
- Device Information:
- <ul>
- ${infoValues.map((value) => `<li>${value}</li>`).join("")}
- </ul>
- `;
- });
现在,当您在 Chrome Beta 上运行我们的Web应用程序并单击该按钮时,您应该会看到一个连接蓝牙设备的提示,如下所示:
一旦你选择了你的手机(在我的例子中是 Atharva 的 iPhone)并点击配对,你应该会在几秒钟内看到屏幕上的信息,就像这样:
信息是正确的,我截图的时候,我的手机是百分之百开着的。
这里需要注意的一件事是 iPhone 12,1 ,这不是说我有 iPhone12,它是 iPhone 11 的代号,因此,如果您看到一些奇怪的设备名称,您应该知道它可能是代号或制造商提供的其他名称。
你应该使用蓝牙API吗?
这是最重要的问题。此功能在大多数浏览器中处于测试阶段,即使向公众发布,也可能存在一些问题,例如硬件不支持蓝牙。如果您想为某人创建链接其设备的服务,则应牢记这一点。
另一方面,如果你的组织有正确配置了蓝牙的定制系统,你肯定可以为组织创建一个内部Web应用,可以根据他们的需要与蓝牙设备进行互动。
我认为你应该在这个API处于测试阶段时尝试一下,因为一般来说,当它向公众发布时,你就会占到先机。没有多少人会知道如何使用这个API,所以对它的了解可以帮助你获得更多的演出机会。
在测试阶段使用这个的另一个原因是为了挑战自己。当API被发布后,事情可能会变得更容易。但如果你像我一样喜欢玩API测试版,你可能会有一些乐趣,并在这个过程中学习一些新东西。
一旦API向公众发布,就会产生更多的意识,在我看来,越来越多的蓝牙相关服务将在Web上而不是在原生应用程序中进行。这将使这项技术更容易为Web开发者所接受。
原文:https://blog.logrocket.com/build-bluetooth-app-chrome-bluetooth-web-api/
作者:Atharva Deosthale
参考资料
[1]Web 蓝牙 API: https://www.chromestatus.com/feature/5264933985976320