Atharva Deosthale Web Developer and Designer | JavaScript = ❤ | MERN Stack Developer

Build a Bluetooth app with the Chrome Web Bluetooth API

5 min read 1640

Nowadays, browsers are evolving, bringing new APIs and ways to connect to other devices and allowing access to more functionality than they ever did before. One such API is the Web Bluetooth API.

This API is still in beta as of this writing, but once this gets released to the public, it will open a whole lot of opportunities for developers who want to use Bluetooth but don’t want to create a native application for each platform.

Even though the Bluetooth API is still in beta, we will try it out and make a simple web page that will pair with our phone and give us basic details such as the battery percentage, name of the device, and basic information provided by the device manufacturer.

We won’t be using styles in this tutorial because we just need to understand how to interact with the Bluetooth API with JavaScript.

Remember, not all browsers support this API, and you won’t be able to test this with every phone. Some phones might not allow fetching device information. In this tutorial, I will be using an Apple iPhone 11, which allows me to fetch my device information through Bluetooth on my browser without any problems.

Requirements

  • A code editor; I prefer VS Code
  • Live server extension if you’re using VS Code
  • Laptop or PC with Bluetooth capabilities (or plug-and-play Bluetooth hardware)
  • A mobile device with Bluetooth capabilities (I use an iPhone 11, you can try with your own phone)
  • Working knowledge of JavaScript
  • Chrome Beta installed on your PC or laptop. The Bluetooth API is a beta feature and will work best on Chrome Beta

Please note that not all Chromium-based browsers, such as Brave, support the Bluetooth API. I tried using the API on Brave but discovered that Brave disabled the API on purpose for security reasons.

If you need any help with the code, here’s the GitHub repository.

Let’s get started

First, we need to create a folder which we will be using as a workspace. Once you create a folder, open VS Code using the following command:

code .

We will be working with two files in this tutorial; name them index.html and script.js. In index.html we just need the basic layout (just a button), and link the file to our JavaScript file.

Here are the contents of index.html:

We made a custom demo for .
No really. Click here to check it out.

<!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>

Adding Bluetooth functionality

Let’s start with the functionality. Go to script.js and store the UI elements in variables so that we can access them later:

const button = document.getElementById("getDetails");
const details = document.getElementById("details");

Now, let’s create a click listener for our button so that we can perform our operations once user clicks the button:

button.addEventListener("click", async () => {
  try {
  } catch(err) {
    console.error(err);
    alert("An error occured while fetching device details");
  }
});

We made the function an async function because it makes things simpler for us and we don’t need to make many callbacks, making our code look more organized. From now on, all of our code will be inside the try block.

Requesting a Bluetooth device

Next, let’s work on requesting a Bluetooth device through the browser:

// Request the Bluetooth device through browser
const device = await navigator.bluetooth.requestDevice({
  optionalServices: ["battery_service", "device_information"],
  acceptAllDevices: true,
});

In the above code, we used the Bluetooth API through navigator.bluetooth. Before connecting to a device, we need to provide the device with information about what data we are going to access.

We can access the required data using various services present on the target Bluetooth device. In this case, we are interacting with battery and device information, so we need the battery_service and device_information services.

Once the user selects the Bluetooth device he wants to connect, we establish a connection to the GATT server, which provides us access to the services which we requested earlier, and also stores the device name in a variable for later use:

// Connect to the GATT server
// We also get the name of the Bluetooth device here
let deviceName = device.gatt.device.name;
const server = await device.gatt.connect(); 

Now, we need to get the services individually from the GATT server so that we can access them individually:

// Getting the services we mentioned before through GATT server
const batteryService = await server.getPrimaryService("battery_service");
const infoService = await server.getPrimaryService("device_information");

Fetching information from the device

First, let’s work on fetching the battery level of the target device.

Every Bluetooth device has various services to interact with. For example, a mobile device can have a battery service for all battery activities. There can also be a phone service that helps make and receive calls. Different devices all have different Bluetooth services.

Each service has characteristics, and each characteristic has a value. This value is a buffer, so we need to convert it into human-readable form.

Battery level is a percentage so we will convert the buffer to integer:

// Getting the current battery level
const batteryLevelCharacteristic = await batteryService.getCharacteristic(
  "battery_level"
);
// Convert recieved buffer to number
const batteryLevel = await batteryLevelCharacteristic.readValue();
const batteryPercent = await batteryLevel.getUint8(0);

The readValue() function returns us a buffer which we need to convert into human readable form.

Now, let’s work on getting more device information. As said before, each service has one or more characteristics. The device_information services may have quite a lot of characteristics depending on the device, and we cannot extract a specific one because every device has different configurations and different unique IDs to access data. So we simply read all the characteristics in this case.

The following code does just that:

// Getting device information
// We will get all characteristics from 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();
  });
});

We wrapped the forEach under a Promise because the parent and the forEach itself is an asynchronous function, so we need to fetch the data before moving on to displaying it.

Here, when we get the value using readValue(), we are using TextDecoder because we know that most of the data in the device_information service is a string type and not an integer.

We then push all the data into an array so that we can render it on the UI, then resolve the Promise once all characteristics are read.

Now, we simply render the data out on the screen:

promise.then(() => {
  // Display all the information on the screen
  // use innerHTML
  details.innerHTML = `
    Device Name - ${deviceName}<br />
    Battery Level - ${batteryPercent}%<br />
    Device Information:
    <ul>
      ${infoValues.map((value) => `<li>${value}</li>`).join("")}
    </ul> 
  `;
});

Now when you run our web app on Chrome Beta and click on the button, you should see a prompt to connect to a Bluetooth device just like this:

Bluetooth pairing screen

Once you select your phone (in my case it’s Atharva’s iPhone) and hit pair, you should see the information on your screen in a few seconds just like this:

Screenshot of paired Bluetooth device battery level information

The information is correct, my phone was on 100 percent when I took the screenshot.

One thing to note here is that iPhone 12,1 doesn’t mean I have an iPhone 12. iPhone 12,1 is the code name for iPhone 11. So if you see some weird name for your device, you should know that it might be the codename or something else from the manufacturer.

Should you use the Bluetooth API?

This is the most important question. This feature is in beta for most browsers, and even when it comes out to the public, there may be some issues, such as the hardware not supporting Bluetooth. If you want to create a service for someone to link their device, you should keep this in mind.

On the other hand, if your organization has custom systems with Bluetooth configured properly, you can definitely create an internal web app for the organization that can interact with Bluetooth devices as per their needs.

I think you should try this API out while it is in beta because it generally gives you an upper hand when it releases to the public. Not many people will be aware about how to use this API, so knowledge of it can help you land more gigs.

Another reason to use this while in beta would be to challenging yourself. When the API gets released, things might get easier. But if you are someone like me who loves to play around with API betas, you might have some fun and learn something new in the process.

Once the API is released to the public, more awareness will be created and, in my opinion, more and more Bluetooth-related services will take place on the web rather than in native applications. This will make this technology more accessible to web developers.

What’s next?

I’d highly recommend reading the target device documentation for help. Not every device has the same services; some might have custom services with custom IDs.

For practice, I recommend figuring out what else can you extract from your phone using the Bluetooth API.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Atharva Deosthale Web Developer and Designer | JavaScript = ❤ | MERN Stack Developer

Leave a Reply