Типичная ситуация: у вас есть BLE-устройство — пульсометр, умная лампа или промышленный датчик. Производитель даёт только мобильное приложение. Вы хотите, чтобы пользователи управляли устройством прямо с сайта, без установки софта. Web Bluetooth API решает эту задачу, но требует аккуратной работы с протоколом, переподключениями и обработкой ошибок. За 5 лет мы провели более 20 интеграций с BLE-устройствами: от фитнес-браслетов до медицинских мониторов. Экономия на разработке мобильного приложения может достигать 70%, а время выхода на рынок сокращается в 3 раза. Хотите подключить BLE-устройство к сайту? Оценим проект за один день — напишите нам.
Какие BLE-устройства можно подключить к сайту?
Подключаемся к пульсометрам, термометрам, лампам, контроллерам — любым устройствам с Bluetooth Low Energy. Пользователь открывает страницу, нажимает «Подключить», выбирает устройство из списка — и сайт начинает принимать показания датчика, менять яркость или включать мотор. Главное — разобраться с сервисами и характеристиками GATT. Если документация есть, интеграция занимает дни. Если нет — мы реверсим протокол через сниффер.
Почему Web Bluetooth API — альтернатива мобильному приложению?
Внедрение через браузер втрое быстрее нативной разработки и не требует публикации в магазинах. Пользователь не скачивает приложение — просто переходит по ссылке. Обновления применяются мгновенно на сервере, без версионирования. Для компании это снижает порог входа и экономит до 70% на поддержке двух платформ.
Как обеспечить стабильное подключение после разрыва?
Критично для устройств, требующих непрерывного потока данных. Мы реализуем автоматическое переподключение с экспоненциальной задержкой: первая попытка через 1 секунду, вторая через 2, третья через 4. После трёх неудач выводим уведомление. Ниже пример кода на TypeScript.
Подключение к устройству
interface BluetoothDevice {
name: string
gatt: BluetoothRemoteGATTServer
}
async function connectToDevice(serviceUUID: string): Promise<BluetoothRemoteGATTServer> {
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: [serviceUUID] }],
})
console.log(`Подключаемся к: ${device.name}`)
device.addEventListener('gattserverdisconnected', () => {
console.log('Устройство отключилось')
})
const server = await device.gatt!.connect()
return server
}
Чтение данных: пульсометр
class HeartRateMonitor {
private server: BluetoothRemoteGATTServer | null = null
private characteristic: BluetoothRemoteGATTCharacteristic | null = null
async connect() {
this.server = await connectToDevice('heart_rate')
const service = await this.server.getPrimaryService('heart_rate')
this.characteristic = await service.getCharacteristic('heart_rate_measurement')
await this.characteristic.startNotifications()
this.characteristic.addEventListener(
'characteristicvaluechanged',
this.handleHeartRateMeasurement.bind(this)
)
}
private handleHeartRateMeasurement(event: Event) {
const value = (event.target as BluetoothRemoteGATTCharacteristic).value!
const flags = value.getUint8(0)
let heartRate: number
if (flags & 0x01) {
heartRate = value.getUint16(1, true)
} else {
heartRate = value.getUint8(1)
}
const rrIntervals: number[] = []
if (flags & 0x10) {
for (let i = 2; i + 1 < value.byteLength; i += 2) {
rrIntervals.push(value.getUint16(i, true) / 1024 * 1000)
}
}
console.log(`ЧСС: ${heartRate} уд/мин, RR: [${rrIntervals.join(', ')}] мс`)
}
async disconnect() {
await this.characteristic?.stopNotifications()
this.server?.disconnect()
}
}
Запись данных: управление умной лампой
class SmartLightController {
private server: BluetoothRemoteGATTServer | null = null
private controlChar: BluetoothRemoteGATTCharacteristic | null = null
private readonly SERVICE_UUID = '00010203-0405-0607-0809-0a0b0c0d1910'
private readonly CONTROL_UUID = '00010203-0405-0607-0809-0a0b0c0d2b11'
async connect() {
this.server = await connectToDevice(this.SERVICE_UUID)
const service = await this.server.getPrimaryService(this.SERVICE_UUID)
this.controlChar = await service.getCharacteristic(this.CONTROL_UUID)
}
async setColor(r: number, g: number, b: number) {
const command = new Uint8Array([
0x33, 0x05, 0x02,
r, g, b,
0x00, 0x00, 0x00,
r ^ g ^ b,
])
await this.controlChar!.writeValueWithResponse(command)
}
async setBrightness(level: number) {
const command = new Uint8Array([
0x33, 0x04,
Math.round(level * 2.55),
0x00,
])
await this.controlChar!.writeValueWithoutResponse(command)
}
async readBatteryLevel(): Promise<number> {
const service = await this.server!.getPrimaryService('battery_service')
const char = await service.getCharacteristic('battery_level')
const value = await char.readValue()
return value.getUint8(0)
}
}
React-хук для управления подключением
function useBluetooth() {
const [device, setDevice] = useState<BluetoothRemoteGATTServer | null>(null)
const [isConnected, setIsConnected] = useState(false)
const [isSupported] = useState(() => 'bluetooth' in navigator)
const [error, setError] = useState<string | null>(null)
const connect = useCallback(async (serviceUUID: string) => {
try {
setError(null)
const server = await connectToDevice(serviceUUID)
setDevice(server)
setIsConnected(true)
} catch (err) {
if ((err as Error).name === 'NotFoundError') {
setError('Устройство не выбрано')
} else {
setError((err as Error).message)
}
}
}, [])
const disconnect = useCallback(() => {
device?.disconnect()
setDevice(null)
setIsConnected(false)
}, [device])
return { device, isConnected, isSupported, error, connect, disconnect }
}
Переподключение после разрыва
device.addEventListener('gattserverdisconnected', async () => {
let retries = 0
while (retries < 3) {
await new Promise((r) => setTimeout(r, 1000 * (retries + 1)))
try {
await device.gatt!.connect()
await resubscribeCharacteristics()
console.log('Переподключено')
return
} catch {
retries++
}
}
console.error('Не удалось переподключиться')
})
Сравнение: Web Bluetooth API vs нативное приложение
| Критерий | Web Bluetooth API | Нативное приложение |
|---|---|---|
| Время разработки | 1–2 недели | 2–3 месяца |
| Доставка | Ссылка | Магазины приложений |
| Обновления | Мгновенно | Ручные, через store |
| Платформы | Chrome/Edge | iOS, Android |
| Сложность | Средняя | Высокая |
| Стоимость | Индивидуально | Индивидуально |
Типичные ошибки при интеграции BLE
Устройство не отображается при сканировании. Убедитесь, что сайт открыт по HTTPS, запросите разрешение на использование Bluetooth. Причина часто в отсутствии optionalServices. Разрыв соединения при переходе фона. Используйте navigator.wakeLock или переподключайтесь автоматически. Некорректный парсинг данных. Проверяйте битовые флаги, порядок байтов (little-endian).
Какие браузеры поддерживают Web Bluetooth?
На сегодня Web Bluetooth API работает в Chrome 70+, Edge 79+ и Chrome Android. Firefox и Safari не поддерживают. Если ваша аудитория использует эти браузеры, можно предложить обходные пути: Native Messaging, PWA с Service Worker или гибридное приложение. Для большинства B2B-сценариев Chrome — стандартный выбор.
План работ: этапы интеграции
| Этап | Длительность |
|---|---|
| Анализ документации и реверс-инжиниринг | 1–3 дня |
| Проектирование архитектуры и UI | 1–2 дня |
| Реализация подключения и обработка ошибок | 2–4 дня |
| Тестирование на реальном устройстве | 1–2 дня |
| Деплой и мониторинг | 0.5–1 день |
Что входит в работу
- Полная документация по протоколу и коду интеграции.
- Доступ к репозиторию с исходным кодом и инструкцией по развёртыванию.
- Обучение вашей команды: как поддерживать и расширять интеграцию.
- Поддержка в течение 30 дней после запуска (исправление багов, консультации).
Как подключить BLE-устройство: пошаговая инструкция
- Проверьте поддержку браузера:
if ('bluetooth' in navigator). - Запросите устройство:
navigator.bluetooth.requestDevice({ filters: [...] }). - Подключитесь к GATT-серверу:
device.gatt.connect(). - Получите нужный сервис и характеристику.
- Начните читать или записывать данные.
- Обработайте отключение: подпишитесь на событие
gattserverdisconnected.
Что такое GATT-сервисы и характеристики?
GATT (Generic Attribute Profile) — протокол, определяющий структуру данных BLE-устройства. Сервис — это группа характеристик, например, сервис «Батарея». Характеристика — конкретный параметр, например, уровень заряда. Каждый сервис и характеристика имеют уникальный UUID.Сроки и стоимость
Срок интеграции с задокументированным устройством — от 3 до 5 дней. Для устройств без документации (реверс протокола) — от 7 до 12 дней. Стоимость рассчитывается индивидуально, исходя из сложности протокола и необходимости разработки UI. Оценим ваш проект в течение рабочего дня — просто напишите нам. Получите консультацию по интеграции BLE уже сегодня.
Подробнее о технологии: Web Bluetooth API и Bluetooth Low Energy.







