Інтеграція 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.







