API Web Bluetooth позволяет веб-сайтам взаимодействовать с устройствами Bluetooth.
А что, если я скажу вам, что веб-сайты могут обмениваться данными с находящимися поблизости устройствами Bluetooth безопасным и конфиденциальным способом? Таким образом, пульсометры, поющие лампочки и даже черепахи могли бы напрямую взаимодействовать с веб-сайтом.
До сих пор возможность взаимодействия с устройствами Bluetooth была доступна только для приложений, предназначенных для конкретных платформ. Web Bluetooth API призван изменить это и сделать эту возможность доступной и для веб-браузеров.
Прежде чем мы начнём
В этом документе предполагается, что у вас есть базовые знания о том, как работает Bluetooth Low Energy (BLE) и Generic Attribute Profile .
Несмотря на то, что спецификация Web Bluetooth API еще не завершена, авторы спецификации активно ищут заинтересованных разработчиков, готовых протестировать этот API и оставить отзывы о спецификации и реализации .
Часть веб-API Bluetooth доступна в ChromeOS, Chrome для Android 6.0, Mac (Chrome 56) и Windows 10 (Chrome 70). Это означает, что вы сможете запрашивать и подключаться к ближайшим устройствам Bluetooth Low Energy, читать / записывать характеристики Bluetooth, получать уведомления GATT , узнавать о разрыве соединения с устройством Bluetooth и даже читать и записывать дескрипторы Bluetooth . Дополнительную информацию см. в таблице совместимости браузеров на MDN.
Для Linux и более ранних версий Windows включите флаг #experimental-web-platform-features в about://flags .
Доступно для проведения первичных испытаний.
Чтобы получить как можно больше отзывов от разработчиков, использующих Web Bluetooth API в реальных условиях, Chrome ранее добавил эту функцию в Chrome 53 в качестве пробной версии для ChromeOS, Android и Mac.
Судебное разбирательство успешно завершилось в январе 2017 года.
Требования безопасности
Чтобы понять компромиссы в области безопасности, я рекомендую статью Джеффри Ясскина, инженера-программиста из команды Chrome, работающего над спецификацией API Web Bluetooth, под названием «Модель безопасности Web Bluetooth» .
Только HTTPS
Поскольку этот экспериментальный API представляет собой мощную новую функцию, добавленную в веб-среду, он доступен только в защищенных контекстах . Это означает, что вам необходимо разрабатывать приложения с учетом TLS .
Требуется жест пользователя
В целях безопасности обнаружение устройств Bluetooth с помощью navigator.bluetooth.requestDevice должно запускаться жестом пользователя, таким как касание или щелчок мыши. Речь идёт о прослушивании событий pointerup , click и touchend .
button.addEventListener('pointerup', function(event) {
// Call navigator.bluetooth.requestDevice
});
Погрузитесь в код.
API Web Bluetooth в значительной степени основан на промисах JavaScript. Если вы с ними не знакомы, ознакомьтесь с этим отличным руководством по промисам . И ещё одно: () => {} — это стрелочные функции ECMAScript 2015.
Запросить устройства Bluetooth
Эта версия спецификации Web Bluetooth API позволяет веб-сайтам, работающим в роли центрального узла, подключаться к удаленным серверам GATT через соединение BLE. Она поддерживает связь между устройствами, поддерживающими Bluetooth 4.0 или более поздние версии.
Когда веб-сайт запрашивает доступ к находящимся поблизости устройствам с помощью navigator.bluetooth.requestDevice , браузер предлагает пользователю выбрать одно устройство или отменить запрос.
Функция navigator.bluetooth.requestDevice() принимает обязательный объект, определяющий фильтры. Эти фильтры используются для возврата только тех устройств, которые соответствуют некоторым заявленным службам Bluetooth GATT и/или имени устройства.
Фильтр служб
Например, чтобы запросить у устройств Bluetooth рекламу службы Bluetooth GATT Battery Service :
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Если ваша служба Bluetooth GATT не входит в список стандартизированных служб Bluetooth GATT , вы можете указать либо полный UUID Bluetooth, либо его сокращенную 16- или 32-битную форму.
navigator.bluetooth.requestDevice({
filters: [{
services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
}]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Фильтр по имени
Вы также можете запрашивать устройства Bluetooth на основе имени устройства, которое рекламируется, используя ключ name filters, или даже префикс этого имени, используя ключ namePrefix filters. Обратите внимание, что в этом случае вам также потребуется определить необязательный ключ optionalServices , чтобы иметь доступ к любым службам, не включенным в фильтр служб. В противном случае при попытке доступа к ним позже возникнет ошибка.
navigator.bluetooth.requestDevice({
filters: [{
name: 'Francois robot'
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Фильтр данных производителя
Также можно запрашивать данные о Bluetooth-устройствах на основе информации от конкретного производителя, передаваемой с помощью ключа manufacturerData filters. Этот ключ представляет собой массив объектов с обязательным ключом идентификатора компании Bluetooth под названием companyIdentifier . Вы также можете указать префикс данных, который фильтрует данные о производителе от Bluetooth-устройств, начинающихся с этого префикса. Обратите внимание, что вам также потребуется определить optionalServices ключ Services, чтобы иметь доступ к любым службам, не включенным в фильтр служб. В противном случае при попытке доступа к ним возникнет ошибка.
// 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); });
Маска также может использоваться с префиксом данных для сопоставления с некоторыми шаблонами в данных производителя. Подробнее об этом можно узнать в пояснении к фильтрам данных Bluetooth .
Фильтры исключения
Параметр exclusionFilters в navigator.bluetooth.requestDevice() позволяет исключить некоторые устройства из средства выбора в браузере. Его можно использовать для исключения устройств, соответствующих более широкому фильтру, но не поддерживаемых.
// 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); });
Без фильтров
Наконец, вместо filters вы можете использовать ключ acceptAllDevices , чтобы отобразить все находящиеся поблизости устройства Bluetooth. Вам также потребуется определить ключ optionalServices , чтобы иметь доступ к некоторым службам. В противном случае при попытке доступа к ним позже возникнет ошибка.
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Подключитесь к устройству Bluetooth
Итак, что же делать теперь, когда у вас есть BluetoothDevice ? Давайте подключимся к удаленному GATT-серверу Bluetooth, который хранит определения служб и характеристик.
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); });
Прочитать характеристику Bluetooth
Здесь мы подключаемся к GATT-серверу удаленного Bluetooth-устройства. Теперь нам нужно получить основной 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); });
Если вы используете пользовательскую характеристику Bluetooth GATT, вы можете указать в методе service.getCharacteristic либо полный UUID Bluetooth, либо его сокращенную 16- или 32-битную форму.
Обратите внимание, что вы также можете добавить обработчик события 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);
}
Запись в характеристику Bluetooth
Запись в характеристику Bluetooth 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); });
Получайте уведомления ГАТТ
Теперь давайте посмотрим, как получать уведомления об изменении параметра измерения частоты сердечных сокращений на устройстве:
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 .
Отключитесь от устройства Bluetooth
Для улучшения пользовательского опыта вы можете отслеживать события отключения и предлагать пользователю повторно подключиться:
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() для отключения вашего веб-приложения от устройства Bluetooth. Это активирует существующие обработчики событий gattserverdisconnected . Обратите внимание, что это НЕ остановит связь с устройством Bluetooth, если другое приложение уже взаимодействует с этим устройством Bluetooth. Для более подробного ознакомления посмотрите примеры Device Disconnect и Automatic Reconnect .
Чтение и запись в дескрипторы Bluetooth.
Дескрипторы Bluetooth GATT — это атрибуты, описывающие значение характеристики. Их можно считывать и записывать аналогично характеристикам Bluetooth 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); });
Примеры, демонстрации и практические занятия по программированию.
Все приведенные ниже примеры веб-приложений Bluetooth были успешно протестированы. Чтобы в полной мере оценить возможности этих примеров, я рекомендую установить [приложение BLE Peripheral Simulator для Android], которое имитирует работу BLE-периферийного устройства с функциями измерения заряда батареи, частоты сердечных сокращений или температуры тела.
Начинающий
- Информация об устройстве — получение основных сведений об устройстве, работающем по протоколу BLE.
- Уровень заряда батареи — получение информации о состоянии батареи от BLE-устройства, передающего информацию о состоянии батареи.
- Сброс энергии — сброс энергии, затраченной устройством BLE, передающим данные о частоте сердечных сокращений.
- Характеристики устройства — отображение всех свойств конкретной характеристики устройства BLE.
- Уведомления — запуск и остановка уведомлений о характеристиках устройства BLE.
- Отключение устройства — отключитесь от BLE-устройства и получите уведомление об отключении после подключения к нему.
- Получение характеристик — получение всех характеристик рекламируемой услуги от устройства BLE.
- Получение дескрипторов — получение всех дескрипторов характеристик рекламируемой услуги от BLE-устройства.
- Фильтр данных производителя — получение основной информации об устройстве BLE, соответствующей данным производителя.
- Фильтры исключения — получение основной информации об устройстве BLE, оснащенном базовыми фильтрами исключения.
Объединение нескольких операций
- Характеристики GAP — получите все характеристики GAP для устройства BLE.
- Характеристики информации об устройстве — получите все характеристики информации об устройстве BLE.
- Потеря связи — установите характеристику уровня оповещения для BLE-устройства (readValue и writeValue).
- Откройте для себя сервисы и характеристики — узнайте обо всех доступных основных сервисах и их характеристиках с помощью устройства BLE.
- Автоматическое переподключение — повторное подключение к отключенному BLE-устройству с использованием алгоритма экспоненциальной задержки.
- Изменение значения характеристики — считывайте уровень заряда батареи и получайте уведомления об изменениях от устройства BLE.
- Чтение дескрипторов — считывание всех дескрипторов характеристик сервиса с BLE-устройства.
- Запись дескриптора — запись в дескриптор «Характеристика описания пользователя» на устройстве BLE.
Также ознакомьтесь с нашими тщательно отобранными демонстрационными примерами Web Bluetooth и официальными учебными материалами Web Bluetooth Codelabs .
Библиотеки
- web-bluetooth-utils — это npm-модуль, добавляющий в API ряд удобных функций.
- В Nobene , самом популярном модуле Node.js BLE Central, доступен плагин Web Bluetooth API shim. Это позволяет упаковывать Nobene в webpack/browserify без необходимости использования WebSocket-сервера или других плагинов.
- angular-web-bluetooth — это модуль для Angular , который абстрагирует весь шаблонный код, необходимый для настройки Web Bluetooth API.
Инструменты
- Get Started with Web Bluetooth — это простое веб-приложение, которое сгенерирует весь необходимый JavaScript-код для взаимодействия с Bluetooth-устройством. Введите имя устройства, службу, характеристику, определите его свойства, и всё готово.
- Если вы уже являетесь разработчиком Bluetooth-приложений, плагин Web Bluetooth Developer Studio также сгенерирует JavaScript-код Web Bluetooth для вашего Bluetooth-устройства.
Советы
В браузере Chrome по about://bluetooth-internals доступна страница « Внутреннее устройство Bluetooth» , где вы можете проверить всю информацию о находящихся поблизости устройствах Bluetooth: состояние, службы, характеристики и описания.

Также рекомендую ознакомиться с официальной страницей «Как сообщать об ошибках Web Bluetooth», поскольку отладка Bluetooth иногда может быть сложной.
Что дальше?
Чтобы узнать, какие части Web Bluetooth API в настоящее время разрабатываются, сначала проверьте статус реализации в конкретном браузере и на конкретной платформе .
Хотя проект ещё не завершен, вот небольшой предварительный обзор того, чего можно ожидать в ближайшем будущем:
- Сканирование ближайших BLE-объявлений будет выполняться с помощью
navigator.bluetooth.requestLEScan(). - Новое событие
serviceaddedбудет отслеживать вновь обнаруженные службы Bluetooth GATT, а событиеserviceremoved— удаленные. Новое событиеservicechangedбудет срабатывать при добавлении или удалении любой характеристики и/или дескриптора из службы Bluetooth GATT.
Показать поддержку API
Планируете ли вы использовать Web Bluetooth API? Ваша публичная поддержка помогает команде Chrome расставлять приоритеты в разработке новых функций и показывает другим разработчикам браузеров, насколько важно их поддерживать.
Отправьте твит @ChromiumDev , используя хэштег #WebBluetooth и расскажите, где и как вы его используете.
Ресурсы
- Stack Overflow
- Статус функций Chrome
- Ошибки реализации Chrome
- Спецификация Web Bluetooth
- Вопросы по спецификациям на GitHub
- Приложение-симулятор BLE-периферийных устройств
Благодарности
Благодарим Кейси Баскес за рецензию.