Contrôler une bougie PLAYBULB via Bluetooth Web

1. De quoi s'agit-il ?

IMG_19700101_023537~2~2.jpg

Dans cet atelier de programmation, vous allez apprendre à contrôler une bougie LED sans flamme PLAYBULB avec JavaScript uniquement grâce à l'API Web Bluetooth. Vous allez également vous familiariser avec les fonctionnalités JavaScript ES2015 telles que Classes, Arrow functions, Map et Promises.

Points abordés

  • Interagir avec un appareil Bluetooth à proximité en JavaScript
  • Utiliser les classes ES2015, les fonctions fléchées, les maps et les promesses

Prérequis

2. Lire en premier

Vous pouvez consulter la version finale de l'application que vous allez créer sur https://googlecodelabs.github.io/candle-bluetooth et jouer avec l'appareil Bluetooth PLAYBULB Candle dont vous disposez avant de vous lancer dans cet atelier de programmation.

Vous pouvez également me voir changer de couleur sur https://www.youtube.com/watch?v=fBCPA9gIxlU.

3. Configuration

Télécharger l'exemple de code

Vous pouvez obtenir l'exemple de code pour ce code en téléchargeant le fichier ZIP ici :

ou en clonant ce dépôt Git :

git clone https://github.com/googlecodelabs/candle-bluetooth.git

Si vous avez téléchargé la source au format ZIP, la décompression devrait vous donner un dossier racine candle-bluetooth-master.

Installer et valider le serveur Web

Bien que vous soyez libre d'utiliser votre propre serveur Web, cet atelier de programmation est conçu pour fonctionner correctement avec le serveur Web Chrome. Si l'application n'est pas encore installée, vous pouvez le faire depuis le Chrome Web Store.

Après avoir installé l'application de serveur Web pour Chrome, cliquez sur le raccourci "Applications" dans la barre des favoris :

Screen Shot 2016-11-16 at 4.10.42 PM.png

Dans la fenêtre qui s'affiche, cliquez sur l'icône du serveur Web :

9f3c21b2cf6cbfb5.png

La boîte de dialogue suivante s'affiche. Elle vous permet de configurer votre serveur Web local :

Screen Shot 2016-11-16 at 3.40.47 PM.png

Cliquez sur le bouton choose folder (choisir un dossier), puis sélectionnez la racine du dépôt cloné (ou décompressé). Cela vous permet de diffuser votre travail en cours via l'URL indiquée dans la boîte de dialogue du serveur Web (dans la section Web Server URL(s)).

Sous "Options", cochez la case Automatically show index.html (Afficher automatiquement l'index.html), comme indiqué ci-dessous :

Screen Shot 2016-11-16 at 3.40.56 PM.png

Accédez maintenant à votre site dans votre navigateur Web (en cliquant sur l'URL du serveur Web en surbrillance). Vous devriez voir une page qui ressemble à ceci :

Screen Shot 2016-11-16 at 3.20.22 PM.png

Si vous souhaitez voir à quoi ressemble cette application sur votre téléphone Android, vous devez activer le débogage à distance sur Android et configurer le transfert de port (le numéro de port est 8887 par défaut). Ensuite, vous pouvez simplement ouvrir un nouvel onglet Chrome sur http://localhost:8887 sur votre téléphone Android.

Étape suivante

Pour le moment, cette application Web ne fait pas grand-chose. Commençons par ajouter la compatibilité Bluetooth.

4. Découvrir la bougie

Nous allons commencer par écrire une bibliothèque qui utilise une classe JavaScript ES2015 pour l'appareil Bluetooth PLAYBULB Candle.

Gardez votre calme. La syntaxe de classe n'introduit pas de nouveau modèle d'héritage orienté objet dans JavaScript. Elle fournit simplement une syntaxe beaucoup plus claire pour créer des objets et gérer l'héritage, comme vous pouvez le lire ci-dessous.

Commençons par définir une classe PlaybulbCandle dans playbulbCandle.js et créons une instance playbulbCandle qui sera disponible dans le fichier app.js ultérieurement.

playbulbCandle.js

(function() {
  'use strict';

  class PlaybulbCandle {
    constructor() {
      this.device = null;
    }
  }

  window.playbulbCandle = new PlaybulbCandle();

})();

Pour demander l'accès à un appareil Bluetooth à proximité, nous devons appeler navigator.bluetooth.requestDevice. Étant donné que l'appareil PLAYBULB Candle diffuse en continu (s'il n'est pas déjà associé) un UUID de service GATT Bluetooth constant connu sous sa forme abrégée 0xFF02, nous pouvons simplement définir une constante et l'ajouter au paramètre de services de filtres dans une nouvelle méthode publique connect de la classe PlaybulbCandle.

Nous effectuerons également un suivi interne de l'objet BluetoothDevice afin de pouvoir y accéder ultérieurement si nécessaire. Étant donné que navigator.bluetooth.requestDevice renvoie une promesse JavaScript ES2015, nous allons le faire dans la méthode then.

playbulbCandle.js

(function() {
  'use strict';

  const CANDLE_SERVICE_UUID = 0xFF02;

  class PlaybulbCandle {
    constructor() {
      this.device = null;
    }
    connect() {
      let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
      return navigator.bluetooth.requestDevice(options)
      .then(function(device) {
        this.device = device;
      }.bind(this)); 
    }
  }

  window.playbulbCandle = new PlaybulbCandle();

})();

Par mesure de sécurité, la découverte des appareils Bluetooth à proximité avec navigator.bluetooth.requestDevice doit être appelée par le biais d'un geste de l'utilisateur, comme un appui ou un clic de souris. C'est pourquoi nous appellerons la méthode connect lorsque l'utilisateur cliquera sur le bouton "Connect" (Connecter) dans le fichier app.js :

app.js

document.querySelector('#connect').addEventListener('click', function(event) {
  document.querySelector('#state').classList.add('connecting');
  playbulbCandle.connect()
  .then(function() {
    console.log(playbulbCandle.device);
    document.querySelector('#state').classList.remove('connecting');
    document.querySelector('#state').classList.add('connected');
  })
  .catch(function(error) {
    console.error('Argh!', error);
  });
});

Exécuter l'application

À ce stade, accédez à votre site dans votre navigateur Web (en cliquant sur l'URL du serveur Web mise en évidence dans l'application de serveur Web) ou actualisez simplement la page existante. Cliquez sur le bouton vert "Connect" (Connecter), sélectionnez l'appareil dans le sélecteur et ouvrez votre console d'outils de développement préférée à l'aide du raccourci clavier Ctrl+Maj+J. Vous remarquerez que l'objet BluetoothDevice est consigné.

Screen Shot 2016-11-16 at 3.27.12 PM.png

Une erreur peut s'afficher si le Bluetooth est désactivé et/ou si l'appareil Bluetooth PLAYBULB Candle est éteint. Dans ce cas, allumez-le et réessayez.

Bonus obligatoire

Je ne sais pas ce que vous en pensez, mais je vois déjà trop de function() {} dans ce code. Passons plutôt aux fonctions fléchées JavaScript ES2015 () => {}. Elles sont absolument indispensables : toute la beauté des fonctions anonymes, sans la tristesse de la liaison.

playbulbCandle.js

(function() {
  'use strict';

  const CANDLE_SERVICE_UUID = 0xFF02;

  class PlaybulbCandle {
    constructor() {
      this.device = null;
    }
    connect() {
      let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
      return navigator.bluetooth.requestDevice(options)
      .then(device => {
        this.device = device;
      }); 
    }
  }

  window.playbulbCandle = new PlaybulbCandle();

})();

app.js

document.querySelector('#connect').addEventListener('click', event => {
  playbulbCandle.connect()
  .then(() => {
    console.log(playbulbCandle.device);
    document.querySelector('#state').classList.remove('connecting');
    document.querySelector('#state').classList.add('connected');
  })
  .catch(error => {
    console.error('Argh!', error);
  });
});

Étape suivante

- OK… mais est-ce que je peux vraiment parler à cette bougie ?

- Oui, passez à l'étape suivante

Questions fréquentes

5. Lis quelque chose

Que faire maintenant que vous avez reçu un BluetoothDevice de la promesse de navigator.bluetooth.requestDevice ? Connectons-nous au serveur GATT de la télécommande Bluetooth qui contient les définitions du service et des caractéristiques Bluetooth en appelant device.gatt.connect() :

playbulbCandle.js

  class PlaybulbCandle {
    constructor() {
      this.device = null;
    }
    connect() {
      let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
      return navigator.bluetooth.requestDevice(options)
      .then(device => {
        this.device = device;
        return device.gatt.connect();
      });
    }
  }

Lire le nom de l'appareil

Nous sommes ici connectés au serveur GATT de l'appareil Bluetooth PLAYBULB Candle. Nous souhaitons maintenant obtenir le service GATT principal (annoncé précédemment comme 0xFF02) et lire la caractéristique du nom de l'appareil (0xFFFF) qui appartient à ce service. Pour ce faire, il suffit d'ajouter une méthode getDeviceName à la classe PlaybulbCandle et d'utiliser device.gatt.getPrimaryService et service.getCharacteristic. La méthode characteristic.readValue renvoie en fait un DataView que nous décodons simplement avec TextDecoder.

playbulbCandle.js

  const CANDLE_DEVICE_NAME_UUID = 0xFFFF;

  ...

    getDeviceName() {
      return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
      .then(service => service.getCharacteristic(CANDLE_DEVICE_NAME_UUID))
      .then(characteristic => characteristic.readValue())
      .then(data => {
        let decoder = new TextDecoder('utf-8');
        return decoder.decode(data);
      });
    }

Ajoutons cela à app.js en appelant playbulbCandle.getDeviceName une fois que nous sommes connectés et affichons le nom de l'appareil.

app.js

document.querySelector('#connect').addEventListener('click', event => {
  playbulbCandle.connect()
  .then(() => {
    console.log(playbulbCandle.device);
    document.querySelector('#state').classList.remove('connecting');
    document.querySelector('#state').classList.add('connected');
    return playbulbCandle.getDeviceName().then(handleDeviceName);
  })
  .catch(error => {
    console.error('Argh!', error);
  });
});

function handleDeviceName(deviceName) {
  document.querySelector('#deviceName').value = deviceName;
}

À ce stade, accédez à votre site dans votre navigateur Web (en cliquant sur l'URL du serveur Web mise en évidence dans l'application de serveur Web) ou actualisez simplement la page existante. Assurez-vous que la PLAYBULB Candle est allumée, puis cliquez sur le bouton "Connect" (Connecter) sur la page. Le nom de l'appareil devrait s'afficher sous le sélecteur de couleur.

Screen Shot 2016-11-16 at 3.29.21 PM.png

Lire le niveau de la batterie

Une caractéristique Bluetooth standard du niveau de batterie est également disponible dans l'appareil Bluetooth PLAYBULB Candle et indique le niveau de batterie de l'appareil. Cela signifie que nous pouvons utiliser des noms standards tels que battery_service pour l'UUID du service GATT Bluetooth et battery_level pour l'UUID de la caractéristique GATT Bluetooth.

Ajoutons une méthode getBatteryLevel à la classe PlaybulbCandle et lisons le niveau de la batterie en pourcentage.

playbulbCandle.js

    getBatteryLevel() {
      return this.device.gatt.getPrimaryService('battery_service')
      .then(service => service.getCharacteristic('battery_level'))
      .then(characteristic => characteristic.readValue())
      .then(data => data.getUint8(0));
    }

Nous devons également mettre à jour l'objet JavaScript options pour inclure le service de batterie à la clé optionalServices, car il n'est pas annoncé par l'appareil Bluetooth PLAYBULB Candle, mais reste obligatoire pour y accéder.

playbulbCandle.js

      let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
                     optionalServices: ['battery_service']};
      return navigator.bluetooth.requestDevice(options)

Comme précédemment, branchons cela sur app.js en appelant playbulbCandle.getBatteryLevel une fois que nous avons le nom de l'appareil et affichons le niveau de la batterie.

app.js

document.querySelector('#connect').addEventListener('click', event => {
  playbulbCandle.connect()
  .then(() => {
    console.log(playbulbCandle.device);
    document.querySelector('#state').classList.remove('connecting');
    document.querySelector('#state').classList.add('connected');
    return playbulbCandle.getDeviceName().then(handleDeviceName)
    .then(() => playbulbCandle.getBatteryLevel().then(handleBatteryLevel));
  })
  .catch(error => {
    console.error('Argh!', error);
  });
});

function handleDeviceName(deviceName) {
  document.querySelector('#deviceName').value = deviceName;
}

function handleBatteryLevel(batteryLevel) {
  document.querySelector('#batteryLevel').textContent = batteryLevel + '%';
}

À ce stade, accédez à votre site dans votre navigateur Web (en cliquant sur l'URL du serveur Web mise en évidence dans l'application de serveur Web) ou actualisez simplement la page existante. Cliquez sur le bouton "Connecter" sur la page. Le nom de l'appareil et le niveau de la batterie devraient s'afficher.

Screen Shot 2016-11-16 at 3.29.21 PM.png

Étape suivante

Comment changer la couleur de cette ampoule ? C'est pourquoi je suis là !

- Vous y êtes presque, je vous le promets…

Questions fréquentes

6. Modifier la couleur

Pour changer la couleur, il suffit d'écrire un ensemble spécifique de commandes dans une caractéristique Bluetooth (0xFFFC) du service GATT principal annoncé comme 0xFF02. Par exemple, pour que votre PLAYBULB Candle devienne rouge, vous devez écrire un tableau d'entiers non signés de 8 bits égal à [0x00, 255, 0, 0], où 0x00 correspond à la saturation blanche et 255, 0, 0 aux valeurs rouge, verte et bleue, respectivement .

Nous allons utiliser characteristic.writeValue pour écrire des données dans la caractéristique Bluetooth de la nouvelle méthode publique setColor de la classe PlaybulbCandle. Nous renverrons également les valeurs réelles de rouge, de vert et de bleu lorsque la promesse sera tenue, afin que nous puissions les utiliser ultérieurement dans app.js :

playbulbCandle.js

  const CANDLE_COLOR_UUID = 0xFFFC;

  ...

    setColor(r, g, b) {
      let data = new Uint8Array([0x00, r, g, b]);
      return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
      .then(service => service.getCharacteristic(CANDLE_COLOR_UUID))
      .then(characteristic => characteristic.writeValue(data))
      .then(() => [r,g,b]);
    }

Mettons à jour la fonction changeColor dans app.js pour appeler playbulbCandle.setColor lorsque la case d'option "Aucun effet" est cochée. Les variables de couleur r, g, b globales sont déjà définies lorsque l'utilisateur clique sur le canevas du sélecteur de couleur.

app.js

function changeColor() {
  var effect = document.querySelector('[name="effectSwitch"]:checked').id;
  if (effect === 'noEffect') {
    playbulbCandle.setColor(r, g, b).then(onColorChanged);
  }
}

À ce stade, accédez à votre site dans votre navigateur Web (en cliquant sur l'URL du serveur Web mise en évidence dans l'application de serveur Web) ou actualisez simplement la page existante. Cliquez sur le bouton "Connect" (Connecter) sur la page, puis sur le sélecteur de couleurs pour modifier la couleur de votre PLAYBULB Candle autant de fois que vous le souhaitez.

Screen Shot 2016-11-16 at 3.31.37 PM.png

Plus d'effets de bougie

Si vous avez déjà allumé une bougie, vous savez que la lumière n'est pas statique. Heureusement pour nous, il existe une autre caractéristique Bluetooth (0xFFFB) dans le service GATT principal annoncé sous le nom 0xFF02 qui permet à l'utilisateur de définir des effets de bougie.

Pour définir un "effet bougie", par exemple, vous pouvez écrire [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]. Vous pouvez également définir l'effet de clignotement avec [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00].

Ajoutons les méthodes setCandleEffectColor et setFlashingColor à la classe PlaybulbCandle.

playbulbCandle.js

  const CANDLE_EFFECT_UUID = 0xFFFB;

  ...

    setCandleEffectColor(r, g, b) {
      let data = new Uint8Array([0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]);
      return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
      .then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
      .then(characteristic => characteristic.writeValue(data))
      .then(() => [r,g,b]);
    }
    setFlashingColor(r, g, b) {
      let data = new Uint8Array([0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00]);
      return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
      .then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
      .then(characteristic => characteristic.writeValue(data))
      .then(() => [r,g,b]);
    }

Mettons à jour la fonction changeColor dans app.js pour appeler playbulbCandle.setCandleEffectColor lorsque la case d'option "Effet bougie" est cochée et playbulbCandle.setFlashingColor lorsque la case d'option "Clignotant" est cochée. Cette fois, nous allons utiliser switch, si cela vous convient.

app.js

function changeColor() {
  var effect = document.querySelector('[name="effectSwitch"]:checked').id;
  switch(effect) {
    case 'noEffect':
      playbulbCandle.setColor(r, g, b).then(onColorChanged);
      break;
    case 'candleEffect':
      playbulbCandle.setCandleEffectColor(r, g, b).then(onColorChanged);
      break;
    case 'flashing':
      playbulbCandle.setFlashingColor(r, g, b).then(onColorChanged);
      break;
  }
}

À ce stade, accédez à votre site dans votre navigateur Web (en cliquant sur l'URL du serveur Web mise en évidence dans l'application de serveur Web) ou actualisez simplement la page existante. Cliquez sur le bouton "Connect" (Connecter) sur la page et testez les effets de bougie et de clignotement.

Screen Shot 2016-11-16 at 3.33.23 PM.png

Étape suivante

- C'est tout ? 3 effets de bougie médiocres ? Est-ce pour cela que je suis ici ?

- Il y en a d'autres, mais vous devrez vous débrouiller seul cette fois-ci.

7. Allez plus loin

Et voilà ! Vous pensez peut-être que c'est presque la fin, mais l'application n'est pas encore terminée. Voyons si vous avez réellement compris ce que vous avez copié et collé pendant cet atelier de programmation. Voici ce que vous devez faire vous-même pour que cette application brille.

Ajouter des effets manquants

Voici les données pour les effets manquants :

  • Impulsion : [0x00, r, g, b, 0x01, 0x00, 0x09, 0x00] (vous pouvez ajuster les valeurs r, g, b)
  • Arc-en-ciel : [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00] (les personnes épileptiques peuvent vouloir éviter celui-ci)
  • Fondu arc-en-ciel : [0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]

Cela signifie essentiellement ajouter de nouvelles méthodes setPulseColor, setRainbow et setRainbowFade à la classe PlaybulbCandle et les appeler dans changeColor.

Résoudre le problème "Aucun effet"

Comme vous l'avez peut-être remarqué, l'option "Aucun effet" ne réinitialise aucun effet en cours. C'est un détail, mais il est bon de le savoir. Résolvons à présent ce problème. Dans la méthode setColor, vous devez d'abord vérifier si un effet est en cours à l'aide d'une nouvelle variable de classe _isEffectSet. Si true, désactivez l'effet avant de définir une nouvelle couleur avec ces données : [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00].

Saisissez le nom de l'appareil

C'est facile ! Pour écrire un nom d'appareil personnalisé, il vous suffit d'écrire dans la caractéristique du nom de l'appareil Bluetooth précédent. Je vous recommande d'utiliser la méthode TextEncoder encode pour obtenir un Uint8Array contenant le nom de l'appareil.

Ensuite, j'ajouterais une eventListener "input" à document.querySelector('#deviceName') et j'appellerais playbulbCandle.setDeviceName pour faire simple.

Personnellement, je l'ai appelée PLAY💡 CANDLE!

8. Et voilà !

Connaissances acquises

  • Interagir avec un appareil Bluetooth à proximité en JavaScript
  • Utiliser les classes ES2015, les fonctions fléchées, les maps et les promesses

Étapes suivantes