使用 Chrome Web 藍牙 API 構建藍牙應用

語言: CN / TW / HK

如今,瀏覽器不斷髮展,帶來了新的 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.htmlscript.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_servicedevice_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