Controllare una candela PLAYBULB con Web Bluetooth

1. Di cosa si tratta?

IMG_19700101_023537~2~2.jpg

In questo codelab illuminante, imparerai a controllare una candela LED senza fiamma PLAYBULB utilizzando solo JavaScript grazie all'API Web Bluetooth. Durante il percorso, utilizzerai anche le funzionalità di JavaScript ES2015, come Classi, Funzioni freccia, Mappa e Promise.

Cosa imparerai a fare

  • Come interagire con un dispositivo Bluetooth nelle vicinanze in JavaScript
  • Come utilizzare le classi ES2015, le funzioni di freccia, la mappa e le promesse

Che cosa ti serve

2. Riproduci per primo

Prima di iniziare questo codelab, ti consigliamo di dare un'occhiata alla versione finale dell'app che stai per creare all'indirizzo https://googlecodelabs.github.io/candle-bluetooth e di provare il dispositivo Bluetooth PLAYBULB Candle che hai a disposizione.

Puoi anche guardarmi mentre cambio colore su https://www.youtube.com/watch?v=fBCPA9gIxlU

3. Configurazione

Scarica il codice campione

Puoi ottenere il codice campione per questo codice scaricando il file ZIP qui:

o clonando questo repository Git:

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

Se hai scaricato l'origine come file ZIP, la decompressione dovrebbe darti una cartella principale candle-bluetooth-master.

Installa e verifica il server web

Anche se puoi utilizzare il tuo server web, questo codelab è progettato per funzionare bene con il server web Chrome. Se non hai ancora installato l'app, puoi farlo dal Chrome Web Store.

Dopo aver installato l'app Web Server for Chrome, fai clic sulla scorciatoia App nella barra dei preferiti:

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

Nella finestra che si apre, fai clic sull'icona Web Server:

9f3c21b2cf6cbfb5.png

Successivamente, vedrai questa finestra di dialogo, che ti consente di configurare il tuo server web locale:

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

Fai clic sul pulsante Scegli cartella e seleziona la directory principale del repository clonato (o non archiviato). In questo modo potrai pubblicare il tuo lavoro in corso tramite l'URL evidenziato nella finestra di dialogo del server web (nella sezione URL server web).

Nella sezione Opzioni, seleziona la casella accanto a "Mostra automaticamente index.html", come mostrato di seguito:

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

Ora visita il tuo sito nel browser web (facendo clic sull'URL del server web evidenziato) e dovresti vedere una pagina simile alla seguente:

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

Se vuoi vedere l'aspetto di questa app sul tuo smartphone Android, devi attivare il debug remoto su Android e configurare il port forwarding (il numero di porta predefinito è 8887). Dopodiché, puoi semplicemente aprire una nuova scheda di Chrome all'indirizzo http://localhost:8887 sul tuo smartphone Android.

A seguire

Al momento questa app web non fa molto. Iniziamo ad aggiungere il supporto Bluetooth.

4. Scopri la candela

Inizieremo scrivendo una libreria che utilizza una classe JavaScript ES2015 per il dispositivo Bluetooth PLAYBULB Candle.

Mantieni la calma. La sintassi della classe non introduce un nuovo modello di ereditarietà orientato agli oggetti in JavaScript. Fornisce semplicemente una sintassi molto più chiara per creare oggetti e gestire l'ereditarietà, come puoi leggere di seguito.

Innanzitutto, definiamo una classe PlaybulbCandle in playbulbCandle.js e creiamo un'istanza playbulbCandle che sarà disponibile nel file app.js in un secondo momento.

playbulbCandle.js

(function() {
  'use strict';

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

  window.playbulbCandle = new PlaybulbCandle();

})();

Per richiedere l'accesso a un dispositivo Bluetooth nelle vicinanze, dobbiamo chiamare navigator.bluetooth.requestDevice. Poiché il dispositivo PLAYBULB Candle trasmette continuamente (se non è già accoppiato) un UUID del servizio GATT Bluetooth costante noto nella sua forma breve come 0xFF02, possiamo semplicemente definire una costante e aggiungerla al parametro dei servizi dei filtri in un nuovo metodo connect pubblico della classe PlaybulbCandle.

Inoltre, terremo traccia internamente dell'oggetto BluetoothDevice in modo da potervi accedere in un secondo momento, se necessario. Poiché navigator.bluetooth.requestDevice restituisce una promessa JavaScript ES2015, lo faremo nel metodo 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();

})();

Come funzionalità di sicurezza, il rilevamento di dispositivi Bluetooth nelle vicinanze con navigator.bluetooth.requestDevice deve essere chiamato tramite un'attivazione dall'utente, ad esempio un tocco o un clic del mouse. Per questo motivo, chiameremo il metodo connect quando l'utente fa clic sul pulsante "Connetti" nel file 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);
  });
});

Esegui l'app

A questo punto, visita il tuo sito nel browser web (facendo clic sull'URL del server web evidenziato nell'app del server web) o aggiorna semplicemente la pagina esistente. Fai clic sul pulsante verde "Connetti", scegli il dispositivo nel selettore e apri la tua console DevTools preferita con la scorciatoia da tastiera Ctrl + Maiusc + J e osserva l'oggetto BluetoothDevice registrato.

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

Potresti ricevere un errore se il Bluetooth è disattivato e/o il dispositivo Bluetooth PLAYBULB Candle è spento. In questo caso, attivalo e riprova.

Bonus obbligatorio

Non so tu, ma io vedo già troppi function() {} in questo codice. Passiamo invece alle () => {} funzioni a freccia di JavaScript ES2015. Sono dei veri e propri salvavita: tutta la bellezza delle funzioni anonime, senza la tristezza del binding.

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

A seguire

- OK… can I actually talk to this candle or what?

- Certo… vai al passaggio successivo

Domande frequenti

5. Leggi qualcosa

Cosa devi fare ora che hai un BluetoothDevice restituito dalla promise di navigator.bluetooth.requestDevice? Connettiamoci al server GATT remoto Bluetooth che contiene le definizioni delle caratteristiche e del servizio Bluetooth chiamando 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();
      });
    }
  }

Leggi il nome del dispositivo

Qui siamo connessi al server GATT del dispositivo Bluetooth PLAYBULB Candle. Ora vogliamo ottenere il servizio GATT principale (pubblicizzato in precedenza come 0xFF02) e leggere la caratteristica del nome del dispositivo (0xFFFF) che appartiene a questo servizio. Per farlo, basta aggiungere un nuovo metodo getDeviceName alla classe PlaybulbCandle e utilizzare device.gatt.getPrimaryService e service.getCharacteristic. Il metodo characteristic.readValue restituirà un DataView che decodificheremo semplicemente con 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);
      });
    }

Aggiungiamo questo elemento a app.js chiamando playbulbCandle.getDeviceName una volta stabilita la connessione e visualizziamo il nome del dispositivo.

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

A questo punto, visita il tuo sito nel browser web (facendo clic sull'URL del server web evidenziato nell'app del server web) o aggiorna semplicemente la pagina esistente. Assicurati che PLAYBULB Candle sia acceso, quindi fai clic sul pulsante "Connetti" nella pagina. Dovresti vedere il nome del dispositivo sotto il selettore colori.

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

Leggi il livello batteria

Nel dispositivo Bluetooth PLAYBULB Candle è disponibile anche una caratteristica Bluetooth standard del livello batteria che contiene il livello batteria del dispositivo. Ciò significa che possiamo utilizzare nomi standard come battery_service per l'UUID del servizio GATT Bluetooth e battery_level per l'UUID della caratteristica GATT Bluetooth.

Aggiungiamo un nuovo metodo getBatteryLevel alla classe PlaybulbCandle e leggiamo il livello batteria in percentuale.

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

Dobbiamo anche aggiornare l'oggetto JavaScript options per includere il servizio batteria nella chiave optionalServices, poiché non è pubblicizzato dal dispositivo Bluetooth PLAYBULB Candle, ma è comunque obbligatorio per accedervi.

playbulbCandle.js

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

Come prima, colleghiamo questo elemento a app.js chiamando playbulbCandle.getBatteryLevel una volta ottenuto il nome del dispositivo e visualizziamo il livello batteria.

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 + '%';
}

A questo punto, visita il tuo sito nel browser web (facendo clic sull'URL del server web evidenziato nell'app del server web) o aggiorna semplicemente la pagina esistente. Fai clic sul pulsante "Connetti" nella pagina. Dovresti vedere sia il nome del dispositivo sia il livello batteria.

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

A seguire

- Come faccio a cambiare il colore di questa lampadina? Ecco perché sono qui!

- Ci sei quasi, te lo prometto…

Domande frequenti

6. Cambia il colore

Per cambiare il colore, basta scrivere un insieme specifico di comandi in una caratteristica Bluetooth (0xFFFC) nel servizio GATT principale pubblicizzato come 0xFF02. Ad esempio, per impostare la candela PLAYBULB sul rosso, devi scrivere un array di numeri interi senza segno a 8 bit pari a [0x00, 255, 0, 0], dove 0x00 è la saturazione del bianco e 255, 0, 0 sono, rispettivamente, i valori di rosso, verde e blu .

Utilizzeremo characteristic.writeValue per scrivere alcuni dati nella caratteristica Bluetooth nel nuovo metodo pubblico setColor della classe PlaybulbCandle. Inoltre, restituiremo i valori effettivi di rosso, verde e blu quando la promise viene soddisfatta, in modo da poterli utilizzare in seguito in 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]);
    }

Aggiorniamo la funzione changeColor in app.js per chiamare playbulbCandle.setColor quando il pulsante di opzione "Nessun effetto" è selezionato. Le variabili di colore globali r, g, b sono già impostate quando l'utente fa clic sul canvas del selettore colori.

app.js

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

A questo punto, visita il tuo sito nel browser web (facendo clic sull'URL del server web evidenziato nell'app del server web) o aggiorna semplicemente la pagina esistente. Fai clic sul pulsante "Connetti" nella pagina e sul selettore colori per cambiare il colore della candela PLAYBULB tutte le volte che vuoi.

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

Altri effetti candela

Se hai già acceso una candela, sai che la luce non è statica. Per nostra fortuna, esiste un'altra caratteristica Bluetooth (0xFFFB) nel servizio GATT principale pubblicizzato come 0xFF02 che consente all'utente di impostare alcuni effetti della candela.

Ad esempio, per impostare un "effetto candela" puoi scrivere [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]. Puoi anche impostare l'"effetto lampeggiante" con [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00].

Aggiungiamo i metodi setCandleEffectColor e setFlashingColor alla 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]);
    }

Aggiorniamo la funzione changeColor in app.js per chiamare playbulbCandle.setCandleEffectColor quando il pulsante di opzione "Candle Effect" è selezionato e playbulbCandle.setFlashingColor quando il pulsante di opzione "Flashing" è selezionato. Questa volta utilizzeremo switch, se per te va bene.

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

A questo punto, visita il tuo sito nel browser web (facendo clic sull'URL del server web evidenziato nell'app del server web) o aggiorna semplicemente la pagina esistente. Fai clic sul pulsante "Connetti" nella pagina e gioca con gli effetti Candela e Lampeggiante.

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

A seguire

- Tutto qui? 3 effetti candela scadenti? Is this why I'm here?

- Ce ne sono altri, ma questa volta dovrai trovarli autonomamente.

7. Supera le aspettative

Ed eccoci qui. Potresti pensare che sia quasi finita, ma l'app non è ancora terminata. Vediamo se hai capito davvero cosa hai copiato e incollato durante questo codelab. Ecco cosa devi fare ora per far brillare questa app.

Aggiungi effetti mancanti

Ecco i dati relativi agli effetti mancanti:

  • Pulse: [0x00, r, g, b, 0x01, 0x00, 0x09, 0x00] (potresti voler modificare i valori di r, g, b)
  • Arcobaleno: [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00] (le persone con epilessia potrebbero voler evitare questa opzione)
  • Rainbow Fade: [0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]

In pratica, questo significa aggiungere nuovi metodi setPulseColor, setRainbow e setRainbowFade alla classe PlaybulbCandle e chiamarli in changeColor.

Correggi "Nessun effetto"

Come avrai notato, l'opzione"Nessun effetto" non reimposta alcun effetto in corso, si tratta di un problema minore, ma comunque presente. Risolviamo il problema. Nel metodo setColor, devi prima verificare se un effetto è in corso tramite una nuova variabile di classe _isEffectSet e, se true, disattivare l'effetto prima di impostare un nuovo colore con questi dati: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00].

Scrivi il nome del dispositivo

Questa è facile. La scrittura di un nome personalizzato del dispositivo è semplice quanto la scrittura della caratteristica del nome del dispositivo Bluetooth precedente. Ti consiglio di utilizzare il metodo encode di TextEncoder per ottenere un Uint8Array contenente il nome del dispositivo.

Poi, aggiungerei un "input" eventListener a document.querySelector('#deviceName') e chiamerei playbulbCandle.setDeviceName per semplificare.

Io ho chiamato la mia PLAY💡 CANDLE!

8. È tutto.

Che cosa hai imparato

  • Come interagire con un dispositivo Bluetooth nelle vicinanze in JavaScript
  • Come utilizzare le classi ES2015, le funzioni di freccia, la mappa e le promesse

Passaggi successivi