Інтеграція Web Bluetooth API: як підключити BLE-пристрої до сайту
Типова ситуація: у вас є 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.







