keyboard_backspace wifi_off

Web Bluetooth API

This feature is not (yet) supported on your device

The Web Bluetooth API enables apps to connect to Bluetooth Low Energy (BLE) devices and read values from or write values to it.

Demo

Connect to a nearby Bluetooth Low Energy device to explore its battery, device information, heart rate, temperature and humidity. Available readings depend on the device. Sensor values update live when the device supports notifications.

Turn on your device and make it available for a BLE connection, then tap “Scan”. Appearing in the picker does not guarantee readable data. Bluetooth Classic devices and proprietary services are not supported by this demo.

Scan to choose a device.

No device connected

Battery

—

Not connected

Heart rate

—

Not connected

Temperature

—

Not connected

Humidity

—

Not connected

Manufacturer

—

Not connected

Model

—

Not connected

Firmware

—

Not connected

if('bluetooth' in navigator) { const scan = document.querySelector('#scan'); const disconnect = document.querySelector('#disconnect'); const deviceName = document.querySelector('#bluetooth-device'); const status = document.querySelector('#bluetooth-status'); const output = document.querySelector('[data-bluetooth-value="battery"]'); let device; let characteristic; let listener; const onDisconnected = () => { characteristic?.removeEventListener('characteristicvaluechanged', listener); device?.removeEventListener('gattserverdisconnected', onDisconnected); device = undefined; characteristic = undefined; listener = undefined; deviceName.textContent = 'No device connected'; output.textContent = '—'; disconnect.disabled = true; status.textContent = 'Disconnected. Scan to connect again.'; }; const withTimeout = async (operation, description) => { let timer; try { return await Promise.race([ operation, new Promise((resolve, reject) => { timer = setTimeout(() => { reject(new DOMException(description + ' timed out after 30 seconds', 'TimeoutError')); }, 30000); }) ]); } finally { clearTimeout(timer); } }; disconnect.disabled = true; disconnect.addEventListener('click', () => device?.gatt.disconnect()); scan.addEventListener('click', async () => { scan.disabled = true; disconnect.disabled = true; let selected; try { status.textContent = 'Choose a Bluetooth Low Energy device.'; selected = await navigator.bluetooth.requestDevice({ acceptAllDevices: true, optionalServices: ['battery_service'] }); if(device) { device.gatt.disconnect(); onDisconnected(); } device = selected; device.addEventListener('gattserverdisconnected', onDisconnected); deviceName.textContent = selected.name || 'Unnamed Bluetooth device'; output.textContent = '—'; status.textContent = 'Connecting…'; const server = await withTimeout(selected.gatt.connect().then(server => { // Release a late connection unless a new attempt is using the same device. if(device !== selected) server.disconnect(); return server; }), 'Connecting'); if(device !== selected || !server.connected) return; status.textContent = 'Connected. Checking battery service…'; const service = await withTimeout(server.getPrimaryService('battery_service'), 'Finding battery service'); if(device !== selected || !server.connected) return; const battery = await withTimeout(service.getCharacteristic('battery_level'), 'Finding battery level'); if(device !== selected || !server.connected) return; characteristic = battery; const update = value => { if(device !== selected || !server.connected) return; try { const level = value.getUint8(0); if(level > 100) throw new Error('Invalid battery level'); output.textContent = level + '%'; } catch(err) { console.error('Battery value:', err); output.textContent = 'Unavailable'; } }; const messages = []; if(battery.properties.read) { try { update(await withTimeout(battery.readValue(), 'Reading battery level')); messages.push('Battery read'); } catch(err) { if(err.name === 'TimeoutError') throw err; console.error('Battery read failed:', err); messages.push('Battery read failed'); } } if(device !== selected || !server.connected) return; if(battery.properties.notify || battery.properties.indicate) { listener = event => update(event.target.value); battery.addEventListener('characteristicvaluechanged', listener); try { await withTimeout(battery.startNotifications(), 'Starting battery updates'); messages.push('Live updates enabled'); } catch(err) { if(err.name === 'TimeoutError') throw err; console.error('Battery notifications unavailable:', err); messages.push('Live updates unavailable'); } } if(device === selected && server.connected) { status.textContent = messages.join(' · ') || 'Battery reading and live updates are not supported.'; } } catch(err) { if(selected && device === selected && selected.gatt.connected && err.name === 'NotFoundError') { status.textContent = 'Connected. Battery service or battery level is not provided by this device.'; } else { console.error('Bluetooth:', err); if(selected && device === selected) { selected.gatt.disconnect(); onDisconnected(); } status.textContent = err.name === 'NetworkError' && err.message === 'Unsupported device.' ? 'This device entry cannot establish a BLE connection. If another entry has the same name, try that one.' : !selected && err.name === 'NotFoundError' ? 'No device selected.' : err.message; } } finally { scan.disabled = false; disconnect.disabled = !device?.gatt.connected; } }); }

Documentation

Web Bluetooth API on MDN

Browser support

Web Bluetooth API on caniuse.com

What PWA Can Do Today A showcase of what is possible with Progressive Web Apps today