Creazione di un'applicazione web Accesso ai dispositivi

1. Introduzione

Il programma Accesso ai dispositivi fornisce l'API Smart Device Management, un'API REST che consente agli sviluppatori di controllare i dispositivi Google Nest dalle loro applicazioni. Gli utenti devono dare il proprio consenso affinché terze parti possano accedere ai loro dispositivi Nest.

52f77aa38cda13a6.png

Per un'integrazione ottimale del programma Accesso ai dispositivi sono previsti tre passaggi fondamentali:

  1. Creazione di progetti: crea un progetto nella piattaforma Google Cloud e registrati come sviluppatore nella Console Accesso ai dispositivi.
  2. Collegamento dell'account: incoraggia gli utenti attraverso il flusso di collegamento dell'account e recupera un codice di accesso. Scambia il codice con un token di accesso.
  3. Controllo dispositivi: effettua richieste all'API Smart Device Management per controllare i dispositivi inviando comandi con il token di accesso.

In questo codelab, approfondiremo il funzionamento dell'accesso ai dispositivi creando un'applicazione web per la gestione dell'autenticazione ed effettuando chiamate all'API Smart Device Management. Esploreremo anche il deployment di un semplice server proxy utilizzando Node.js ed Express per instradare le richieste di accesso ai dispositivi.

Prima di iniziare, sarebbe utile dare un'occhiata alle tecnologie web comuni che utilizzeremo in questo codelab, come l'autenticazione con OAuth 2.0 o la creazione di un'app web con Node.js, anche se non sono prerequisiti.

Cosa ti servirà

  • Node.js 8 o versioni successive
  • Account Google con un Nest Thermostat collegato

Obiettivi didattici

  • Configurazione di un progetto Firebase che ospita pagine web statiche e Cloud Functions
  • Invio di richieste di accesso al dispositivo tramite un'applicazione web basata su browser
  • Creazione di un server proxy con Node.js ed Express per instradare le richieste

2. Creazione di progetti

Per configurare l'integrazione di Accesso ai dispositivi, gli sviluppatori devono creare un progetto Google Cloud Platform (Google Cloud). Un ID client e un client secret generati all'interno del progetto Google Cloud verranno utilizzati nell'ambito del flusso OAuth tra l'applicazione dello sviluppatore e Google Cloud. Gli sviluppatori devono inoltre visitare la Console Accesso ai dispositivi per creare un progetto per accedere all'API Smart Device Management.

Piattaforma Google Cloud

Vai alla piattaforma Google Cloud. Fai clic su Crea un nuovo progetto e specifica un nome per il progetto. Verrà visualizzato anche l'ID progetto [GCP-Project-Id] per Google Cloud. Registralo perché verrà utilizzato durante la configurazione di Firebase. (Ci riferiremo a questo ID come [GCP-Project-Id] in questo codelab).

585e926b21994ac9.png

Il primo passaggio consiste nell'abilitare la libreria API necessaria nel nostro progetto. Vai su API e Servizi > Library e cerca l'API Smart Device Management. Devi abilitare questa API per autorizzare il progetto a effettuare richieste alle chiamate API Device Access.

14e7eabc422c7fda.png

Prima di procedere con la creazione delle credenziali OAuth, dobbiamo configurare la schermata per il consenso OAuth per il nostro progetto. Vai su API e Servizi > Schermata consenso OAuth. In Tipo di utente, scegli esterno. Per completare la prima schermata, fornisci un nome, un indirizzo email per l'assistenza per la tua app e i dati di contatto dello sviluppatore. Quando ti viene chiesto di indicare Utenti di test, assicurati di fornire l'indirizzo email dei dispositivi collegati in questo passaggio.

Dopo aver configurato la schermata per il consenso OAuth, vai ad API e Servizi > Credenziali. Fai clic su +Crea credenziali e seleziona ID client OAuth. Per il tipo di applicazione, seleziona Applicazione web.

5de534212d44fce7.png

Specifica un nome per il cliente e fai clic su CREA. Aggiungeremo un'origine JavaScript autorizzata e un URI di reindirizzamento autorizzato più avanti. Completando la procedura, verranno visualizzati gli elementi [Client-Id] e [Client-Secret] associati a questo client OAuth 2.0.

e6a670da18952f08.png

Console Accesso ai dispositivi

Vai alla Console Accesso ai dispositivi. Se non hai mai utilizzato la Console Accesso ai dispositivi, riceverai il contratto relativo ai Termini di servizio e una quota di registrazione di $5.

Crea un nuovo progetto e assegnagli un nome. Nella finestra successiva, fornisci l'[Client-Id] ricevuto da Google Cloud nel passaggio precedente.

f8a3f27354bc2625.png

Se abiliti gli eventi e completi i passaggi di creazione del progetto, verrà visualizzata la home page del progetto. Il tuo [Project-Id] verrà elencato sotto il nome che hai assegnato al tuo progetto.

db7ba33d8b707148.png

Prendi nota di [Project-Id] perché lo utilizzeremo per inviare richieste all'API Smart Device Management.

3. Configurazione di Firebase

Firebase offre agli sviluppatori un modo semplice e veloce per eseguire il deployment delle applicazioni web. Svilupperemo un'applicazione web lato client per l'integrazione del programma Accesso ai dispositivi mediante Firebase.

Crea un progetto Firebase

Vai alla Console Firebase. Fai clic su Aggiungi progetto, quindi seleziona il progetto che hai creato al passaggio Creazione del progetto. Verrà creato un progetto Firebase che verrà collegato al tuo progetto Google Cloud [GCP-Project-Id].

Una volta creato il progetto Firebase, viene visualizzata la seguente schermata:

dbb02bbacac093f5.png

Installa gli strumenti di Firebase

Firebase fornisce una serie di strumenti dell'interfaccia a riga di comando per creare ed eseguire il deployment della tua app. Per installare questi strumenti, apri una nuova finestra del terminale ed esegui questo comando. In questo modo, gli strumenti Firebase verranno installati a livello globale.

$ npm i -g firebase-tools

Per verificare che gli strumenti Firebase siano installati correttamente, controlla le informazioni sulla versione.

$ firebase --version

Puoi accedere agli strumenti dell'interfaccia a riga di comando di Firebase con il tuo Account Google utilizzando il comando login.

$ firebase login

Inizializza progetto di hosting

Quando sei in grado di accedere, il passaggio successivo è inizializzare un progetto di hosting per la tua applicazione web. Dal terminale, vai alla cartella in cui vuoi creare il progetto ed esegui questo comando:

$ firebase init hosting

Firebase ti porrà una serie di domande per iniziare a utilizzare un progetto di hosting:

  1. Seleziona un'opzione: Utilizza un progetto esistente
  2. Seleziona un progetto Firebase predefinito per questa directory: Scegli***[GCP-Project-Id]***
  3. Quale directory pubblica vuoi utilizzare? — Pubblico
  4. Configurare come app a pagina singola? — Sì
  5. Vuoi configurare build e deployment automatici con GitHub? - No

Una volta inizializzato il progetto, puoi eseguirne il deployment in Firebase con il seguente comando:

$ firebase deploy

Firebase eseguirà la scansione del progetto ed eseguirà il deployment dei file necessari nel cloud hosting.

fe15cf75e985e9a1.png

Quando apri l'URL di Hosting in un browser, dovresti vedere la pagina di cui hai appena eseguito il deployment:

e40871238c22ebe2.png

Ora che conosci le nozioni di base su come eseguire il deployment di una pagina web con Firebase, passiamo a eseguire il deployment del nostro esempio di Codelab.

4. Esempio di codelab

Puoi clonare il repository codelab ospitato su GitHub utilizzando il comando seguente:

$ git clone https://github.com/google/device-access-codelab-web-app.git

In questo repository forniamo esempi in due cartelle separate. La cartella codelab-start contiene i file necessari per aiutarti a iniziare dal punto attuale di questo codelab. La cartella codelab-done contiene una versione completa di questo codelab, con il client completamente funzionale e il server node.js.

Useremo i file dalla cartella codelab-start in questo codelab, ma se ti blocchi in qualsiasi momento, puoi fare riferimento anche alla versione completata dal codelab.

File di esempio codelab

La struttura dei file della cartella codelab-start è la seguente:

public
├───index.html
├───scripts.js
├───style.css
firebase.json

La cartella pubblica contiene pagine statiche della nostra applicazione. firebase.json è responsabile dell'instradamento delle richieste web alla nostra app. Nella versione codelab-done, vedrai anche una directory functions, contenente la logica per il nostro server proxy (express) di cui eseguire il deployment su Google Cloud Functions.

Esempio di deployment di codelab

Copia i file da codelab-start nella directory del progetto.

$ firebase deploy

Al termine del deployment di Firebase, dovresti essere in grado di vedere l'applicazione Codelab:

e84c1049eb4cca92.png

L'avvio del flusso di autorizzazione richiede le credenziali del partner, che tratteremo nella prossima sezione.

5. Gestione di OAuth

OAuth è lo standard web per la delega dell'accesso, comunemente utilizzata dagli utenti per concedere alle applicazioni di terze parti l'accesso ai dati del proprio account senza condividere le password. Utilizziamo OAuth 2.0 per consentire agli sviluppatori che accedono ai dispositivi degli utenti tramite il programma Accesso ai dispositivi.

7ee31f5d9c37f699.png

Specifica URI di reindirizzamento

Il primo passaggio del flusso OAuth prevede il trasferimento di una serie di parametri all'endpoint OAuth 2.0 di Google. Dopo aver ottenuto il consenso dell'utente, i server OAuth di Google emettono una richiesta con un codice di autorizzazione all'URI di reindirizzamento.

Aggiorna la costante SERVER_URI (riga 19) con il tuo URL di Hosting in scripts.js:

const SERVER_URI = "https://[GCP-Project-Id].web.app";

Se esegui di nuovo il deployment dell'app con questa modifica, l'URI di reindirizzamento utilizzato per il progetto verrà aggiornato.

$ firebase deploy

Abilita URI di reindirizzamento

Dopo aver aggiornato l'URI di reindirizzamento nel file di script, devi aggiungerlo all'elenco degli URI di reindirizzamento consentiti per l'ID client creato per il progetto. Vai alla pagina delle credenziali in Google Cloud Platform, dove sono elencate tutte le credenziali create per il tuo progetto:

1a07b624b5e548da.png

Nell'elenco ID client OAuth 2.0, seleziona l'ID client che hai creato nel passaggio Creazione progetto. Aggiungi l'URI di reindirizzamento dell'app all'elenco di URI di reindirizzamento autorizzati per il tuo progetto.

6d65b298e1f005e2.png

Prova ad accedere.

Vai all'URL di Hosting impostato con Firebase, inserisci le tue credenziali partner e fai clic sul pulsante ACCEDI. L'ID client e il client secret sono le credenziali che hai ottenuto da Google Cloud Platform, mentre l'ID progetto proviene dalla console Accesso ai dispositivi.

78b48906a2dd7c05.png

Il pulsante ACCEDI guiderà gli utenti attraverso il flusso OAuth per la tua azienda, a partire dalla schermata di accesso al loro Account Google. Dopo aver eseguito l'accesso, agli utenti verrà chiesto di fornire le autorizzazioni del tuo progetto per accedere ai loro dispositivi Nest.

e9b7887c4ca420.png

Poiché questa è un'app fittizia, Google emetterà un avviso prima di emettere un reindirizzamento.

b227d510cb1df073.png

Fai clic su "Avanzate", poi seleziona "Vai a web.app (non sicuro)". per completare il reindirizzamento alla tua app.

673a4fd217e24dad.png

Nella richiesta GET in arrivo verrà fornito un codice OAuth, che l'app scambierà poi con un token di accesso e un token di aggiornamento.

6. Controllo dei dispositivi

L'app di esempio Accesso ai dispositivi utilizza le chiamate API REST Smart Device Management per controllare i dispositivi Google Nest. Queste chiamate implicano il passaggio del token di accesso nell'intestazione di una richiesta GET o POST, insieme a un payload necessario per determinati comandi.

Abbiamo scritto una funzione di richiesta di accesso generica per gestire queste chiamate. Tuttavia, sarà necessario fornire a questa funzione l'endpoint corretto e l'oggetto payload quando necessario.

function deviceAccessRequest(method, call, localpath, payload = null) {...}
  • : tipo di richiesta HTTP (GET o POST)
  • chiamata : una stringa che rappresenta la chiamata API, utilizzata per instradare le risposte (listDevices, thermostatMode, temperatureSetpoint)
  • localpath: l'endpoint a cui viene inviata la richiesta, contenente l'ID progetto e l'ID dispositivo (aggiunto dopo https://smartdevicemanagement.googleapis.com/v1)
  • payload (*): dati aggiuntivi richiesti per la chiamata API (ad esempio, un valore numerico che rappresenta la temperatura per un set-point)

Creeremo controlli UI di esempio (Elenco dispositivi, Modalità Imposta, Imposta temperatura) per controllare un termostato Nest:

86f8a193aa397421.png

Questi controlli UI chiameranno le funzioni corrispondenti (listDevices(), postThermostatMode(), postTemperatureSetpoint()) da scripts.js. Vengono lasciate vuote per consentirti di implementarle. L'obiettivo è scegliere il metodo/percorso corretto e passare il payload alla funzione deviceAccessRequest(...).

Elenco dispositivi

La chiamata Accesso ai dispositivi più semplice è listDevices. Utilizza una richiesta GET e non richiede payload. L'endpoint deve essere strutturato utilizzando projectId. Completa la funzione listDevices() nel seguente modo:

function listDevices() {
  var endpoint = "/enterprises/" + projectId + "/devices";
  deviceAccessRequest('GET', 'listDevices', endpoint);
}

Salva le modifiche ed esegui nuovamente il deployment del progetto Firebase con il seguente comando:

$ firebase deploy

Dopo il deployment della nuova versione dell'app, prova a ricaricare la pagina e fai clic su ELENCA DISPOSITIVI. Dovrebbe apparire nell'elenco sotto Controllo dispositivi, dove dovresti vedere l'ID del tuo termostato:

b64a198673ed289f.png

Se selezioni i dispositivi dall'elenco, il campo deviceId verrà aggiornato nel file scripts.js. Per i prossimi due controlli, dovremo specificare deviceId per il dispositivo specifico che vogliamo controllare.

Controllo del termostato

Il controllo di base di Nest Thermostat nell'API Smart Device Management offre due caratteristiche. ThermostatMode e TemperatureSetpoint. ThermostatMode imposta la modalità del termostato Nest su una delle quattro possibili modalità: {Spento, Heat, Cool, HeatCool}. Dobbiamo quindi fornire la modalità selezionata come parte del payload.

Sostituisci la funzione postThermostatMode() in scripts.js con quanto segue:

function postThermostatMode() {
  var endpoint = "/enterprises/" + projectId + "/devices/" + deviceId + ":executeCommand";
  var tempMode = id("tempMode").value;
  var payload = {
    "command": "sdm.devices.commands.ThermostatMode.SetMode",
    "params": {
      "mode": tempMode
    }
  };
  deviceAccessRequest('POST', 'thermostatMode', endpoint, payload);
}

La funzione successiva, postTemperatureSetpoint(), gestisce l'impostazione della temperatura (in Celsius) per Nest Thermostat. A seconda della modalità del termostato selezionata, è possibile impostare due parametri nel payload, heatCelsius e coolCelsius.

function postTemperatureSetpoint() {
  var endpoint = "/enterprises/" + projectId + "/devices/" + deviceId + ":executeCommand";
  var heatCelsius = parseFloat(id("heatCelsius").value);
  var coolCelsius = parseFloat(id("coolCelsius").value);

  var payload = {
    "command": "",
    "params": {}
  };
  
  if ("HEAT" === id("tempMode").value) {
    payload.command = "sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat";
    payload.params["heatCelsius"] = heatCelsius;
  }
  else if ("COOL" === id("tempMode").value) {
    payload.command = "sdm.devices.commands.ThermostatTemperatureSetpoint.SetCool";
    payload.params["coolCelsius"] = coolCelsius;
  }
  else if ("HEATCOOL" === id("tempMode").value) {
    payload.command = "sdm.devices.commands.ThermostatTemperatureSetpoint.SetRange";
    payload.params["heatCelsius"] = heatCelsius;
    payload.params["coolCelsius"] = coolCelsius;
  } else {
    console.log("Off and Eco mode don't allow this function");
    return;
  }
  deviceAccessRequest('POST', 'temperatureSetpoint', endpoint, payload);
}

7. Server Node.js (facoltativo)

Complimenti! Hai creato un'applicazione web lato client che può effettuare richieste API Smart Device Management da un browser. Per coloro che desiderano utilizzare il lato server, è preferibile iniziare con un server proxy in grado di reindirizzare le richieste dal browser.

Per questo server proxy utilizzeremo Firebase Cloud Functions, Node.js ed Express.

Inizializza Cloud Functions

Apri una nuova finestra del terminale, vai alla directory del progetto ed esegui questo comando:

$ firebase init functions

Firebase ti porrà una serie di domande per inizializzare Cloud Functions:

  1. Quale linguaggio vorresti usare per scrivere funzioni Cloud Functions? - JavaScript
  2. Vuoi utilizzare ESLint per individuare probabili bug e applicare lo stile? - No
  3. Vuoi installare le dipendenze con npm ora? — Sì

Questa operazione inizializza una cartella functions nel progetto e installa le dipendenze necessarie. Vedrai che la cartella del progetto contiene una directory Functions, con un file index.js per definire le nostre funzioni Cloud Functions, package.json per definire le impostazioni e una directory node_modules per contenere le dipendenze.

Utilizzeremo due librerie npm per sviluppare la funzionalità lato server: express e xmlhttprequest. Dovrai aggiungere le seguenti voci all'elenco delle dipendenze nel file package.json:

"xmlhttprequest": "^1.8.0",
"express": "^4.17.0"

Quindi, eseguire l'installazione di npm dalla directory Functions dovrebbe installare le dipendenze per il progetto:

$ npm install

Se npm riscontra un problema con il download dei pacchetti, puoi provare a salvare xmlhttprequest ed esprimerti in modo esplicito con il seguente comando:

$ npm install express xmlhttprequest --save

Esegui l'upgrade al piano Blaze

L'utilizzo del comando firebase deploy richiede l'upgrade al piano Blaze, che richiede l'aggiunta di un metodo di pagamento al tuo account. Vai a Panoramica del progetto > Utilizzo e fatturazione e assicurati di selezionare il piano Blaze per il progetto.

c6a5e5a21397bef6.png

Crea un Express Server

Un server Express segue un semplice framework per rispondere alle richieste GET e POST in arrivo. Abbiamo creato un servlet che rimane in ascolto delle richieste POST, le trasmette a un URL di destinazione specificato nel payload e risponde con la risposta ricevuta dal trasferimento.

Modifica il file index.js nella directory Functions in modo che abbia il seguente aspetto:

const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
const functions = require('firebase-functions');
const express = require('express');
const http = require('http');

const app = express();
app.use(express.json());


//***** Device Access - Proxy Server *****//

// Serving Get Requests (Not used) 
app.get('*', (request, response) => {
  response.status(200).send("Hello World!");
});
// Serving Post Requests
app.post('*', (request, response) => {
  
  setTimeout(() => {
    // Read the destination address from payload:
    var destination = request.body.address;
    
    // Create a new proxy post request:
    var xhr = new XMLHttpRequest();
    xhr.open('POST', destination);
    
    // Add original headers to proxy request:
    for (var key in request.headers) {
            var value = request.headers[key];
      xhr.setRequestHeader(key, value);
    }
    
    // Add command/parameters to proxy request:
    var newBody = {};
    newBody.command = request.body.command;
    newBody.params = request.body.params;
    
    // Respond to original request with the response coming
    // back from proxy request (to Device Access Endpoint)
    xhr.onload = function () {
      response.status(200).send(xhr.responseText);
    };
    
    // Send the proxy request!
    xhr.send(JSON.stringify(newBody));
  }, 1000);
});

// Export our app to firebase functions:
exports.app = functions.https.onRequest(app);

Per indirizzare le richieste al nostro server, dobbiamo modificare le riscritture da firebase.json come segue:

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [{
        "source": "/proxy**",
        "function": "app"
      },{
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

In questo modo, gli URL che iniziano con /proxy verranno indirizzati al nostro server Express e il resto continuerà a indirizzare al nostro index.html.

Chiamate API proxy

Ora che il server è pronto, definiamo un URI del proxy in scripts.js per consentire al browser di inviare richieste a questo indirizzo:

const PROXY_URI = SERVER_URI + "/proxy";

Quindi, aggiungi una funzione proxyRequest (scripts.js), che ha la stessa firma della funzione deviceAccessRequest(...), per le chiamate indirette di Accesso ai dispositivi.

function proxyRequest(method, call, localpath, payload = null) {
    var xhr = new XMLHttpRequest();
    
    // We are doing our post request to our proxy server:
    xhr.open(method, PROXY_URI);
    xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
    xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
    xhr.onload = function () {
      // Response is passed to deviceAccessResponse function:
      deviceAccessResponse(call, xhr.response);
    };
    
    // We are passing the device access endpoint in address field of the payload:
    payload.address = "https://smartdevicemanagement.googleapis.com/v1" + localpath;
    if ('POST' === method && payload)
        xhr.send(JSON.stringify(payload));
    else
        xhr.send();
}

L'ultimo passaggio prevede la sostituzione delle chiamate deviceAccessRequest(...) con la funzione proxyRequest(...), nelle funzioni postThermostatMode() e postTemperatureSetpoint() all'interno di scripts.js.

Esegui firebase deploy per aggiornare l'app.

$ firebase deploy

Con questo, ora hai un server proxy Node.js in esecuzione che utilizza Express su Cloud Functions.

Fornisci le autorizzazioni della funzione Cloud Functions

L'ultimo passaggio consiste nel rivedere le autorizzazioni di accesso per le tue funzioni Cloud Functions e assicurarti che l'applicazione lato client sia in grado di chiamarle.

Da Google Cloud Platform, vai alla scheda Cloud Functions dal menu, quindi seleziona la funzione Cloud Functions:

461e9bae74227fc1.png

Fai clic su Autorizzazioni, quindi su Aggiungi membro. Scrivi allUsers nel campo del nuovo membro e seleziona Cloud Functions > Invoker di Cloud Functions. Facendo clic su Salva viene visualizzato un messaggio di avviso:

3adb01644217578c.png

Se selezioni Consenti accesso pubblico, l'applicazione lato client potrà utilizzare la funzione Cloud Functions.

Congratulazioni, hai completato tutti i passaggi. Ora puoi accedere alla tua app web e attivare i controlli dei dispositivi indirizzati tramite il server proxy.

Passaggi successivi

Stai cercando modi per ampliare le tue competenze relative al programma Accesso ai dispositivi? Consulta la documentazione sui trait per scoprire di più sul controllo di altri dispositivi Nest e sulla procedura di certificazione per conoscere la procedura per lanciare il tuo prodotto in tutto il mondo.

Approfondisci le tue competenze con l'app di esempio per l'applicazione web Accesso ai dispositivi, in cui potrai sviluppare la tua esperienza codelab e implementare un'applicazione web funzionante per controllare le videocamere, i campanelli e i termostati Nest.