What if I told you websites could communicate with nearby Bluetooth devices in a secure and privacy-preserving way? This way, heart rate monitors, singing lightbulbs and turtles could interact directly with a website.
Before we start
This article assumes you have some basic knowledge of how Bluetooth Low Energy (BLE) and the Generic Attribute Profile (GATT) work.
Even though the Web Bluetooth API specification is not finalized yet, the Chrome Team is actively looking for enthusiastic developers (I mean you) to try out this work-in-progress API and give feedback on the spec and feedback on the implementation.
Web Bluetooth API is at the time of writing partially implemented in Chrome OS
M45 behind an experimental flag. Go to chrome://flags/#enable-web-bluetooth
,
enable the highlighted flag, restart Chrome and you should be able to
scan for and connect to
nearby Bluetooth devices and
read/write
Bluetooth characteristics.
Security Requirements
HTTPS Only
Because the Web Bluetooth API is a powerful new feature added to the Web, Google Chrome aims to make it available only to secure contexts. This means you’ll need to build with TLS in mind.
During development you’ll be able to play with Web Bluetooth through http://localhost by using some tools like the Chrome Dev Editor, but to deploy it on a site you’ll need to have HTTPS setup on your server. I personally enjoy GitHub Pages for demo purposes. To add HTTPS to your server you’ll need to get a TLS certificate and set it up. Be sure to check out Security with HTTPS article for best practices there.
User Gesture Required
As a security feature, discovering nearby Bluetooth devices with
navigator.bluetooth.requestDevice
must be called via a user gesture
like a touch or mouse click.
Get into the Code
The Web Bluetooth API relies heavily on JavaScript
Promises.
If you’re not familiar with them, check out this great
Promises tutorial. One
more thing, () => {}
are simply ECMAScript 2015 Arrow
functions
– they have a shorter syntax compared to function expressions and lexically
bind the this
value.
Scan for Bluetooth Devices
This version of the Web Bluetooth API specification allows websites, running in the Central role, to connect to remote GATT Servers over a BLE connection. It supports communication among devices that implement Bluetooth 4.0 or later.
When a website requests access to nearby BLE devices using
navigator.bluetooth.requestDevice
, Google Chrome will prompt user with a
device chooser where he can pick one device or simply cancel the request. At
the time of writing though, the device chooser hasn’t been implemented yet.
Only the first device that matches filters will be returned.
The navigator.bluetooth.requestDevice
function takes a mandatory Object that
defines Bluetooth GATT service filters. These filters are used to return
only devices that advertise the selected services.
For instance, scanning for Bluetooth devices advertising the Bluetooth GATT Battery Service is this simple:
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {...})
.catch(error => { console.log(error); });
If your Bluetooth GATT Service is not on the list of the standardized Bluetooth GATT services though, you may provide either the full Bluetooth UUID or a short 16- or 32-bit form.
navigator.bluetooth.requestDevice({
filters: [{
services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
}]
})
.then(device => {...})
.catch(error => { console.log(error); });
Connect to a Bluetooth Device
So what do you do now that you have a BluetoothDevice
returned from
navigator.bluetooth.requestDevice
’s Promise? Let’s connect to the Bluetooth
remote GATT Server which holds the
service and characteristic definitions.
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {
// Human-readable name of the device.
console.log(device.name);
// Indicates whether or not the device is paired with the system.
console.log(device.paired);
// Filtered UUIDs of GATT services the website origin has access to.
console.log(device.uuids);
// Attempts to connect to remote GATT Server.
return device.connectGATT();
})
.then(server => {...})
.catch(error => { console.log(error); });
Read a Bluetooth Characteristic
Here we are connected to the GATT Server of the remote Bluetooth device. Now we want to get a Primary GATT Service and read a characteristic that belongs to this service. Let’s try, for instance, to read the current charge level of the device’s battery.
In the example below, battery_level
is the standardized
Battery Level
Characteristic.
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => device.connectGATT())
.then(server => {
// Getting Battery Service...
return server.getPrimaryService('battery_service');
})
.then(service => {
if (!service) throw 'Battery Service not found';
// Getting Battery Level Characteristic...
return service.getCharacteristic('battery_level');
})
.then(characteristic => {
// Reading Battery Level...
return characteristic.readValue();
})
.then(buffer => {
var data = new DataView(buffer);
console.log('Battery percentage is ' + data.getUint8(0));
})
.catch(error => { console.log(error); });
If you use a custom Bluetooth GATT characteristic, you may provide either the
full Bluetooth UUID or a short 16- or 32-bit form to service.getCharacteristic
.
Write to a Bluetooth Characteristic
Writing to a Bluetooth GATT Characteristic is as easy as reading it. This time, let’s reset the value of the Energy Expended field in the Heart Rate Measurement characteristic to 0 on a heart rate monitor device.
I promise there is no magic here. It’s all explained in the Heart Rate Control Point Characteristic page.
navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.connectGATT())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => {
if (!service) throw 'Heart Rate Service not found';
return service.getCharacteristic('heart_rate_control_point');
})
.then(characteristic => {
// Writing 1 is the signal to reset energy expended.
var resetEnergyExpended = new Uint8Array([1]);
return characteristic.writeValue(resetEnergyExpended);
})
.then(() => {
console.log('Energy expended has been reset.');
})
.catch(error => { console.log(error); });
Samples
The samples below have been tested on Chrome OS M45 with the Web Bluetooth flag enabled. To enjoy these samples to their fullest, I recommend you install the BLE Peripheral Simulator Android App which simulates a BLE Peripheral with a Battery Service or a Heart Rate Service.
- Battery Level - https://googlechrome.github.io/samples/web-bluetooth/battery-level.html
- Reset Energy - https://googlechrome.github.io/samples/web-bluetooth/reset-energy.html
- Device Info - https://googlechrome.github.io/samples/web-bluetooth/device-info.html
Dev Tips
A Bluetooth Console is available in Chrome OS developer shell. Press [ Ctrl ] [
Alt ] [ T ] to open a browser tab terminal and use the bt_console
command to
start poking around your bluetooth settings. The help
command will give you a
list of all available commands.
Resetting the first device resolved by navigator.bluetooth.requestDevice
can
be done in two ways:
- Restart Chrome OS.
- Enter
remove 01:23:45:67:89:01
in the Bluetooth Console where01:23:45:67:89:01
is the Bluetooth address of the first device.
What’s next
As the Web Bluetooth API implementation is not complete yet, here’s a sneak peek of what to expect in the coming months:
- Bluetooth GATT Characteristics will support the
startNotifications
andstopNotifications
functions to subscribe to notifications from Bluetooth Devices. - A new
serviceadded
event will track newly discovered Bluetooth GATT Services whileserviceremoved
event will track removed ones. A newservicechanged
event will fire when any characteristic and/or descriptor gets added or removed from the Bluetooth GATT Service. - A Promise to detect if Bluetooth is available on the platform will be added to improve user experience.
At the time of writing, Chrome OS M45 is the most advanced platform as the low level work has been done already. Android and Mac OS are under active development. Windows 8.1+, Linux, and iOS will be supported as much as feasible by the platforms.
Resources
- Web Bluetooth Spec: https://webbluetoothcg.github.io/web-bluetooth
- Chrome Feature Status: https://www.chromestatus.com/feature/5264933985976320
- Spec Issues: https://github.com/WebBluetoothCG/web-bluetooth/issues
- Implementation Bugs: http://crbug.com/?q=type=Bug%20label:Cr-Blink-Bluetooth
- BLE Peripheral Simulator App: https://github.com/WebBluetoothCG/ble-test-peripheral-android
- Google+ Community: https://plus.google.com/communities/108953318610326025178