Kommunikation mit Bluetooth-Geräten über JavaScript

Über die Web Bluetooth API können Websites mit Bluetooth-Geräten kommunizieren.

François Beaufort
François Beaufort

Was wäre, wenn Websites sicher und datenschutzfreundlich mit Bluetooth-Geräten in der Nähe kommunizieren könnten? So könnten Herzfrequenzmesser, singende Glühbirnen und sogar Schildkröten direkt mit einer Website interagieren.

Bisher war die Interaktion mit Bluetooth-Geräten nur für plattformspezifische Apps möglich. Die Web Bluetooth API soll dies ändern und die Funktion auch in Webbrowsern verfügbar machen.

Bevor es losgeht

In diesem Dokument wird davon ausgegangen, dass Sie Grundkenntnisse über die Funktionsweise von Bluetooth Low Energy (BLE) und dem Generic Attribute Profile haben.

Obwohl die Web Bluetooth API-Spezifikation noch nicht abgeschlossen ist, suchen die Autoren der Spezifikation aktiv nach begeisterten Entwicklern, die diese API ausprobieren und Feedback zur Spezifikation und Feedback zur Implementierung geben.

Eine Teilmenge der Web Bluetooth API ist in ChromeOS, Chrome für Android 6.0, Mac (Chrome 56) und Windows 10 (Chrome 70) verfügbar. Das bedeutet, dass Sie in der Lage sein sollten, Anfragen zu stellen und Verbindungen zu Bluetooth Low Energy-Geräten in der Nähe herzustellen, Bluetooth-Merkmale zu lesen/zu schreiben, GATT-Benachrichtigungen zu empfangen, zu erfahren, wann ein Bluetooth-Gerät getrennt wird, und sogar Bluetooth-Deskriptoren zu lesen und zu schreiben. Weitere Informationen finden Sie in der Tabelle Browser compatibility (Browserkompatibilität) von MDN.

Aktivieren Sie für Linux und frühere Windows-Versionen das Flag #experimental-web-platform-features in about://flags.

Für Ursprungstests verfügbar

Um so viel Feedback wie möglich von Entwicklern zu erhalten, die die Web Bluetooth API in der Praxis verwenden, hat Chrome diese Funktion bereits in Chrome 53 als Ursprungstest für ChromeOS, Android und Mac hinzugefügt.

Die Testphase wurde im Januar 2017 erfolgreich abgeschlossen.

Sicherheitsanforderungen

Wenn Sie mehr über die Sicherheitsrisiken erfahren möchten, empfehle ich Ihnen den Beitrag Web Bluetooth Security Model von Jeffrey Yasskin, einem Softwareentwickler im Chrome-Team, der an der Web Bluetooth API-Spezifikation arbeitet.

Nur HTTPS

Da diese experimentelle API eine leistungsstarke neue Funktion ist, die dem Web hinzugefügt wurde, ist sie nur für sichere Kontexte verfügbar. Das bedeutet, dass Sie TLS bei der Entwicklung berücksichtigen müssen.

Nutzergeste erforderlich

Als Sicherheitsfunktion muss die Suche nach Bluetooth-Geräten mit navigator.bluetooth.requestDevice durch eine Nutzeraktion wie eine Berührung oder einen Mausklick ausgelöst werden. Es geht darum, auf pointerup-, click- und touchend-Ereignisse zu warten.

button.addEventListener('pointerup', function(event) {
  // Call navigator.bluetooth.requestDevice
});

Code aufrufen

Die Web Bluetooth API basiert stark auf JavaScript-Promise-Objekten. Wenn Sie damit nicht vertraut sind, sehen Sie sich dieses Promises-Tutorial an. Noch etwas: () => {} sind ECMAScript 2015-Pfeilfunktionen.

Bluetooth-Geräte anfordern

Diese Version der Web Bluetooth API-Spezifikation ermöglicht es Websites, die in der Rolle „Central“ ausgeführt werden, über eine BLE-Verbindung eine Verbindung zu Remote-GATT-Servern herzustellen. Es unterstützt die Kommunikation zwischen Geräten, die Bluetooth 4.0 oder höher implementieren.

Wenn eine Website mit navigator.bluetooth.requestDevice Zugriff auf Geräte in der Nähe anfordert, wird dem Nutzer im Browser eine Geräteauswahl angezeigt, in der er ein Gerät auswählen oder die Anfrage abbrechen kann.

Aufforderung für Bluetooth-Geräte.

Für die Funktion navigator.bluetooth.requestDevice() ist ein obligatorisches Objekt erforderlich, das Filter definiert. Mit diesen Filtern werden nur Geräte zurückgegeben, die mit einigen beworbenen Bluetooth GATT-Diensten und/oder dem Gerätenamen übereinstimmen.

Filter für Dienste

Wenn Sie beispielsweise Bluetooth-Geräte anfordern möchten, die den Bluetooth GATT Battery Service (Bluetooth-GATT-Akkudienst) bewerben:

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Wenn Ihr Bluetooth-GATT-Dienst nicht in der Liste der standardisierten Bluetooth-GATT-Dienste enthalten ist, können Sie entweder die vollständige Bluetooth-UUID oder eine kurze 16- oder 32-Bit-Form angeben.

navigator.bluetooth.requestDevice({
  filters: [{
    services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
  }]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Namensfilter

Sie können auch Bluetooth-Geräte basierend auf dem Gerätenamen anfordern, der mit dem Filterschlüssel name beworben wird, oder sogar ein Präfix dieses Namens mit dem Filterschlüssel namePrefix. In diesem Fall müssen Sie auch den optionalServices-Schlüssel definieren, um auf Dienste zugreifen zu können, die nicht in einem Dienstfilter enthalten sind. Andernfalls erhalten Sie später eine Fehlermeldung, wenn Sie versuchen, darauf zuzugreifen.

navigator.bluetooth.requestDevice({
  filters: [{
    name: 'Francois robot'
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Herstellerdatenfilter

Es ist auch möglich, Bluetooth-Geräte basierend auf den herstellerspezifischen Daten anzufordern, die mit dem Schlüssel manufacturerData beworben werden. Dieser Schlüssel ist ein Array von Objekten mit dem obligatorischen Schlüssel Bluetooth-Unternehmenskennung mit dem Namen companyIdentifier. Sie können auch ein Datenpräfix angeben, mit dem Herstellerdaten von Bluetooth-Geräten gefiltert werden, die damit beginnen. Sie müssen auch den optionalServices-Schlüssel definieren, um auf Dienste zugreifen zu können, die nicht in einem Dienstfilter enthalten sind. Andernfalls erhalten Sie später eine Fehlermeldung, wenn Sie versuchen, darauf zuzugreifen.

// 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); });

Eine Maske kann auch mit einem Datenpräfix verwendet werden, um einige Muster in Herstellerdaten abzugleichen. Weitere Informationen zu Bluetooth-Datenfiltern

Ausschlussfilter

Mit der Option exclusionFilters in navigator.bluetooth.requestDevice() können Sie einige Geräte aus der Browserauswahl ausschließen. Damit können Geräte ausgeschlossen werden, die einem umfassenderen Filter entsprechen, aber nicht unterstützt werden.

// 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); });

Keine Filter

Anstelle von filters können Sie auch die Taste acceptAllDevices verwenden, um alle Bluetooth-Geräte in der Nähe anzuzeigen. Außerdem müssen Sie den optionalServices-Schlüssel definieren, um auf einige Dienste zugreifen zu können. Andernfalls erhalten Sie später eine Fehlermeldung, wenn Sie versuchen, darauf zuzugreifen.

navigator.bluetooth.requestDevice({
  acceptAllDevices: true,
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Herstellen von Verbindungen zu Bluetooth-Geräten

Was können Sie jetzt mit Ihrem BluetoothDevice tun? Wir stellen eine Verbindung zum GATT-Server der Bluetooth-Fernbedienung her, der die Dienst- und Merkmalsdefinitionen enthält.

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-Merkmal lesen

Hier stellen wir eine Verbindung zum GATT-Server des Remote-Bluetooth-Geräts her. Jetzt möchten wir einen primären GATT-Dienst abrufen und ein Merkmal lesen, das zu diesem Dienst gehört. Versuchen wir beispielsweise, den aktuellen Akkustand des Geräts zu lesen.

Im folgenden Beispiel ist battery_level das standardisierte Merkmal für den Akkustand.

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); });

Wenn Sie ein benutzerdefiniertes Bluetooth-GATT-Merkmal verwenden, können Sie entweder die vollständige Bluetooth-UUID oder eine kurze 16- oder 32-Bit-Form für service.getCharacteristic angeben.

Sie können auch einen characteristicvaluechanged-Event-Listener für ein Merkmal hinzufügen, um das Lesen seines Werts zu verarbeiten. Im Beispiel für das Lesen von Änderungen des charakteristischen Werts erfahren Sie, wie Sie optional auch anstehende GATT-Benachrichtigungen verarbeiten können.


.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);
}

In ein Bluetooth-Merkmal schreiben

Das Schreiben in eine Bluetooth-GATT-Charakteristik ist genauso einfach wie das Lesen. Dieses Mal verwenden wir den Herzfrequenz-Kontrollpunkt, um den Wert des Felds „Verbrannte Kalorien“ auf einem Gerät mit Herzfrequenzmesser auf 0 zurückzusetzen.

Ich verspreche dir, dass es hier keine Magie gibt. Das wird alles auf der Seite Charakteristik des Herzfrequenz-Kontrollpunkts beschrieben.

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); });

GATT-Benachrichtigungen erhalten

Sehen wir uns nun an, wie du benachrichtigt wirst, wenn sich die Eigenschaft Herzfrequenzmessung auf dem Gerät ändert:

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
}

Im Notifications Sample wird gezeigt, wie Sie Benachrichtigungen mit stopNotifications() beenden und den hinzugefügten characteristicvaluechanged-Ereignis-Listener richtig entfernen.

Verbindung zu einem Bluetooth-Gerät trennen

Für eine bessere Nutzererfahrung sollten Sie auf Trennungsereignisse achten und den Nutzer auffordern, die Verbindung wiederherzustellen:

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.`);
}

Sie können auch device.gatt.disconnect() aufrufen, um die Verbindung Ihrer Web-App zum Bluetooth-Gerät zu trennen. Dadurch werden vorhandene gattserverdisconnected-Ereignis-Listener ausgelöst. Hinweis: Die Kommunikation mit Bluetooth-Geräten wird NICHT beendet, wenn eine andere App bereits mit dem Bluetooth-Gerät kommuniziert. Weitere Informationen finden Sie im Beispiel für das Trennen der Verbindung zum Gerät und im Beispiel für die automatische Wiederverbindung.

Bluetooth-Deskriptoren lesen und schreiben

Bluetooth GATT-Deskriptoren sind Attribute, die einen charakteristischen Wert beschreiben. Sie können sie ähnlich wie Bluetooth GATT-Merkmale lesen und in sie schreiben.

Sehen wir uns beispielsweise an, wie die Nutzerbeschreibung des Messintervalls des Gesundheitsthermometers des Geräts gelesen wird.

Im Beispiel unten ist health_thermometer der Gesundheitsthermometer-Dienst, measurement_interval das Merkmal „Messintervall“ und gatt.characteristic_user_description der Deskriptor „Benutzerbeschreibung des Merkmals“.

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); });

Nachdem wir die Nutzerbeschreibung des Messintervalls des Gesundheitsthermometers des Geräts gelesen haben, sehen wir uns an, wie wir sie aktualisieren und einen benutzerdefinierten Wert schreiben können.

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); });

Beispiele, Demos und Codelabs

Alle Web Bluetooth-Beispiele unten wurden erfolgreich getestet. Damit Sie diese Beispiele optimal nutzen können, empfehle ich Ihnen, die [BLE Peripheral Simulator Android App] zu installieren. Diese App simuliert ein BLE-Peripheriegerät mit einem Battery Service, einem Heart Rate Service oder einem Health Thermometer Service.

Anfänger

  • Geräteinformationen: Ruft grundlegende Geräteinformationen von einem BLE-Gerät ab.
  • Akkustand: Ruft Akkuinformationen von einem BLE-Gerät ab, das Akkuinformationen überträgt.
  • Energie zurücksetzen: Setzt den Energieverbrauch zurück, der von einem BLE-Gerät gemeldet wird, das die Herzfrequenz überträgt.
  • Characteristic Properties (Eigenschaften von Merkmalen): Hier werden alle Eigenschaften eines bestimmten Merkmals eines BLE-Geräts angezeigt.
  • Benachrichtigungen: Starten und beenden Sie charakteristische Benachrichtigungen von einem BLE-Gerät.
  • Gerät trennen: Sie werden benachrichtigt, wenn die Verbindung zu einem BLE-Gerät getrennt wird, nachdem Sie es verbunden haben.
  • Get Characteristics (Merkmale abrufen): Ruft alle Merkmale eines beworbenen Dienstes von einem BLE-Gerät ab.
  • Get Descriptors (Deskriptoren abrufen): Ruft alle Deskriptoren der Merkmale eines beworbenen Dienstes von einem BLE-Gerät ab.
  • Herstellerdatenfilter: Ruft grundlegende Geräteinformationen von einem BLE-Gerät ab, das mit Herstellerdaten übereinstimmt.
  • Ausschlussfilter: Ruft grundlegende Geräteinformationen von einem BLE-Gerät mit grundlegenden Ausschlussfiltern ab.

Mehrere Vorgänge kombinieren

  • GAP-Merkmale: Ruft alle GAP-Merkmale eines BLE-Geräts ab.
  • Geräteinformationen: Ruft alle Geräteinformationen eines BLE-Geräts ab.
  • Link Loss: Legen Sie das Merkmal „Alert Level“ eines BLE-Geräts fest (readValue & writeValue).
  • Dienste und Merkmale ermitteln: Ermitteln Sie alle zugänglichen primären Dienste und ihre Merkmale von einem BLE-Gerät.
  • Automatische Wiederverbindung: Wiederverbindung mit einem getrennten BLE-Gerät über einen exponentiellen Backoff-Algorithmus.
  • Read Characteristic Value Changed (Merkmalwertänderung lesen): Akkustand lesen und über Änderungen von einem BLE-Gerät benachrichtigt werden.
  • Deskriptoren lesen: Alle Deskriptoren der Merkmale eines Dienstes von einem BLE-Gerät lesen.
  • Write Descriptor (Deskriptor schreiben): Schreiben Sie in den Deskriptor „Characteristic User Description“ (Charakteristische Nutzerbeschreibung) auf einem BLE-Gerät.

Sehen Sie sich auch unsere Web Bluetooth-Demos und die offiziellen Web Bluetooth-Codelabs an.

Bibliotheken

  • web-bluetooth-utils ist ein npm-Modul, das der API einige praktische Funktionen hinzufügt.
  • Ein Web Bluetooth API-Shim ist in noble verfügbar, dem beliebtesten zentralen BLE-Modul für Node.js. So können Sie noble mit webpack/browserify verwenden, ohne dass ein WebSocket-Server oder andere Plug-ins erforderlich sind.
  • angular-web-bluetooth ist ein Modul für Angular, das den gesamten Boilerplate-Code abstrahiert, der zum Konfigurieren der Web Bluetooth API erforderlich ist.

Tools

  • Erste Schritte mit Web Bluetooth ist eine einfache Web-App, mit der der gesamte JavaScript-Boilerplate-Code generiert wird, um mit einem Bluetooth-Gerät zu interagieren. Geben Sie einen Gerätenamen, einen Dienst oder ein Merkmal ein, definieren Sie die zugehörigen Eigenschaften und schon kann es losgehen.
  • Wenn Sie bereits Bluetooth-Entwickler sind, generiert das Web Bluetooth Developer Studio-Plug-in auch den Web Bluetooth-JavaScript-Code für Ihr Bluetooth-Gerät.

Tipps

In Chrome ist unter about://bluetooth-internals die Seite Bluetooth Internals verfügbar, auf der Sie alle Informationen zu Bluetooth-Geräten in der Nähe einsehen können: Status, Dienste, Merkmale und Deskriptoren.

Screenshot der internen Seite zum Debuggen von Bluetooth in Chrome
Interne Seite in Chrome zum Debuggen von Bluetooth-Geräten.

Außerdem empfehle ich dir, die offizielle Seite Web Bluetooth-Fehler melden zu lesen, da das Debuggen von Bluetooth manchmal schwierig sein kann.

Nächste Schritte

Sehen Sie zuerst im Implementierungsstatus für Browser und Plattformen nach, welche Teile der Web Bluetooth API derzeit implementiert werden.

Auch wenn es noch nicht vollständig ist, können Sie hier schon einmal einen Blick auf die Neuerungen werfen, die in naher Zukunft zu erwarten sind:

  • Suche nach BLE-Werbung in der Nähe erfolgt mit navigator.bluetooth.requestLEScan().
  • Mit einem neuen serviceadded-Ereignis werden neu erkannte Bluetooth-GATT-Dienste erfasst, mit einem serviceremoved-Ereignis werden entfernte Dienste erfasst. Ein neues servicechanged-Ereignis wird ausgelöst, wenn einem Bluetooth-GATT-Dienst eine Eigenschaft und/oder ein Deskriptor hinzugefügt oder daraus entfernt wird.

API-Support zeigen

Möchten Sie die Web Bluetooth API verwenden? Ihre öffentliche Unterstützung hilft dem Chrome-Team, Funktionen zu priorisieren, und zeigt anderen Browseranbietern, wie wichtig es ist, sie zu unterstützen.

Senden Sie einen Tweet an @ChromiumDev mit dem Hashtag #WebBluetooth und teilen Sie uns mit, wo und wie Sie die Funktion verwenden.

Ressourcen

Danksagungen

Vielen Dank an Kayce Basques für die Überprüfung.