Connecter des appareils connectés à l'Assistant Google

1. Avant de commencer

En tant que développeur de solutions IoT (Internet des objets), vous pouvez créer des intégrations cloud à cloud qui permettent aux utilisateurs de contrôler leurs appareils à l'aide de commandes tactiles dans l'application Google Home et de commandes vocales via l'Assistant.

79266e5f45e6ae20.gif

Les intégrations cloud à cloud s'appuient sur Home Graph pour fournir des données contextuelles sur la maison et ses appareils, créant ainsi une carte logique de la maison. Ce contexte permet à l'Assistant de mieux comprendre les demandes formulées par l'utilisateur en fonction de sa position dans la maison. Par exemple, Home Graph peut stocker le concept d'une salle de séjour contenant plusieurs types d'appareils de différents fabricants, comme un thermostat, une lampe, un ventilateur ou encore un aspirateur.

d009cef0f903d284.jpeg

Prérequis

Ce que vous allez faire

Dans cet atelier de programmation, vous allez publier un service cloud qui gère une machine à laver connectée virtuelle, puis créer une intégration cloud à cloud et la connecter à l'Assistant.

Points abordés

  • Déployer un service cloud pour la maison connectée
  • Connecter le service à l'Assistant
  • Publier les changements d'état de l'appareil sur Google

Prérequis

2. Premiers pas

Activer les commandes relatives à l'activité

Pour utiliser l'Assistant Google, vous devez partager certaines données d'activité avec Google. L'Assistant Google a besoin de ces données pour fonctionner correctement. Toutefois, l'obligation de partager les données n'est pas spécifique au SDK. Pour partager ces données, créez un compte Google si vous n'en possédez pas. Vous pouvez utiliser n'importe quel compte Google. Il ne doit pas nécessairement s'agir de votre compte de développeur.

Ouvrez la page "Commandes relatives à l'activité" du compte Google que vous souhaitez utiliser avec l'Assistant.

Vérifiez que les boutons suivants sont activés :

  • Activité sur le Web et les applications : veillez également à cocher la case Inclure l'historique Chrome et l'activité liée aux sites, aux applications et aux appareils qui utilisent les services Google.
  • Informations provenant des appareils
  • Activités vocales et audio

Créer un projet d'intégration cloud à cloud

  1. Accédez à la console pour les développeurs.
  2. Cliquez sur Créer un projet, saisissez un nom pour le projet, puis cliquez sur Créer un projet.

Nommer le projet

Sélectionner l'intégration cloud à cloud

Sur la page Project Home (Accueil du projet) de la console du développeur, sélectionnez Add cloud-to-cloud integration (Ajouter une intégration cloud à cloud) sous Cloud-to-cloud (Cloud à cloud).

Ajouter une intégration cloud à cloud

Installer la CLI Firebase

L'interface de ligne de commande (CLI) Firebase vous permet de diffuser vos applications Web en local et de les déployer sur Firebase Hosting.

Pour installer la CLI, exécutez la commande npm suivante à partir du terminal :

npm install -g firebase-tools

Pour vérifier que la CLI a bien été installée, exécutez la commande suivante :

firebase --version

Autorisez la CLI Firebase avec votre compte Google en exécutant la commande suivante:

firebase login

3. Exécuter l'application de démarrage

Maintenant que vous avez configuré votre environnement de développement, vous pouvez déployer le projet d'initiation pour vous assurer que la configuration est correcte.

Obtenir le code source

Cliquez sur le lien suivant pour télécharger l'exemple utilisé dans cet atelier de programmation sur votre ordinateur de développement :

Vous pouvez également cloner le dépôt GitHub à partir de la ligne de commande:

git clone https://github.com/google-home/smarthome-washer.git

À propos du projet

Le projet de démarrage contient les sous-répertoires suivants :

  • public: interface utilisateur permettant de contrôler et de surveiller facilement l'état du lave-linge connecté.
  • functions: service cloud entièrement implémenté qui gère le lave-linge connecté avec Cloud Functions for Firebase et Firebase Realtime Database.

Créer un projet Firebase

  1. Accédez à Firebase.
  2. Cliquez sur Créer un projet, puis saisissez le nom de votre projet.
  3. Cochez la case du contrat, puis cliquez sur Continuer. Si aucune case à cocher n'est disponible, vous pouvez ignorer cette étape.
    Créer un projet Firebase
  4. Une fois votre projet Firebase créé, recherchez son ID. Accédez à Vue d'ensemble du projet, puis cliquez sur l'icône des paramètres > Paramètres du projet.
    Ouvrir les paramètres du projet
  5. Votre projet est listé dans l'onglet Général.
    Paramètres généraux du projet

Se connecter à Firebase

Accédez au répertoire washer-start, puis configurez la CLI Firebase avec votre projet d'intégration:

cd washer-start
firebase use <firebase-project-id>

Configurer un projet Firebase

Initialisez un projet Firebase.

firebase init

Sélectionnez les fonctionnalités de la CLI, Realtime Database, Functions et la fonctionnalité Hosting qui inclut Firebase Hosting.

? Which Firebase CLI features do you want to set up for this directory? Press Space to select features, then
 Enter to confirm your choices.
❯◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◯ Firestore: Configure security rules and indexes files for Firestore
 ◉ Functions: Configure a Cloud Functions directory and its files
 ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◯ Storage: Configure a security rules file for Cloud Storage
 ◯ Emulators: Set up local emulators for Firebase products
 ◯ Remote Config: Configure a template file for Remote Config
 ◯ Extensions: Set up an empty Extensions manifest

Cette opération initialise les API et les fonctionnalités nécessaires pour votre projet.

Lorsque vous y êtes invité, initialisez Realtime Database. Vous pouvez utiliser l'emplacement par défaut pour l'instance de base de données.

? It seems like you haven't initialized Realtime Database in your project yet. Do you want to set it up?
Yes

? Please choose the location for your default Realtime Database instance:
us-central1

Comme vous utilisez le code du projet d'initiation, choisissez le fichier par défaut pour les règles de sécurité, en veillant à ne pas écraser le fichier des règles de base de données existantes.

? File database.rules.json already exists. Do you want to overwrite it with the Realtime Database Security Rules for <project-ID>-default-rtdb from the Firebase Console?
No

Si vous réinitialisez votre projet, sélectionnez Remplacer lorsque vous êtes invité à initialiser ou à remplacer un codebase.

? Would you like to initialize a new codebase, or overwrite an existing one?
Overwrite

Lorsque vous configurez vos fonctions, vous devez utiliser les fichiers par défaut et veiller à ne pas écraser les fichiers index.js et package.json existants dans l'exemple de projet.

? What language would you like to use to write Cloud Functions?
JavaScript

? Do you want to use ESLint to catch probable bugs and enforce style?
No

? File functions/package.json already exists. Overwrite?
No

? File functions/index.js already exists. Overwrite?
No

Si vous réinitialisez votre projet, sélectionnez Non lorsque vous êtes invité à initialiser ou à écraser functions/.gitignore.

? File functions/.gitignore already exists. Overwrite?
No
? Do you want to install dependencies with npm now?
Yes

Enfin, configurez votre configuration Hosting pour utiliser le répertoire public dans le code du projet, et le fichier index.html existant. Sélectionnez Non lorsque vous êtes invité à utiliser ESLint.

? What do you want to use as your public directory?
public

? Configure as a single-page app (rewrite all urls to /index.html)?
Yes

? Set up automatic builds and deploys with GitHub?
No

? File public/index.html already exists. Overwrite?
 No

Si ESLint a été activé par erreur, vous pouvez le désactiver de deux manières:

  1. Dans l'IUG, accédez au dossier ../functions sous le projet, sélectionnez le fichier masqué .eslintrc.js, puis supprimez-le. Ne le confondez pas avec .eslintrc.json, dont le nom est très similaire.
  2. À l'aide de la ligne de commande:
    cd functions
    rm .eslintrc.js
    

Dans le fichier washer-done/firebase.json, complétez le code avec:

{
  "database": {
    "rules": "database.rules.json"
  },
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
    "headers": [{
      "source" : "**/*.@(js|html)",
      "headers" : [ {
        "key" : "Cache-Control",
        "value" : "max-age=0"
      } ]
    }],
  "functions": [
    {
      "source": "functions",
      "codebase": "default",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log",
        "*.local"
      ]
    }
  ]
}

Déployer sur Firebase

Maintenant que vous avez installé les dépendances et configuré votre projet, vous êtes prêt à exécuter l'application.

firebase deploy

La console doit afficher le résultat suivant :

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<firebase-project-id>/overview
Hosting URL: https://<firebase-project-id>.web.app

Cette commande déploie une application Web, ainsi que plusieurs fonctions Cloud Functions for Firebase.

Ouvrez l'URL d'hébergement dans votre navigateur (https://.web.app) pour afficher l'application Web. L'interface suivante s'affiche :

5845443e94705557.png

Cette interface utilisateur Web représente une plate-forme tierce permettant d'afficher ou de modifier l'état des appareils. Pour commencer à renseigner des informations provenant des appareils dans votre base de données, cliquez sur UPDATE (Mettre à jour). Aucune modification n'est affichée sur la page, mais l'état actuel de votre lave-linge est stocké dans la base de données.

Il est maintenant temps d'associer le service cloud que vous avez déployé à l'Assistant Google à l'aide de la console de développement Google Home.

Configurer votre projet dans la console développeur

Dans l'onglet Develop (Développer), ajoutez un Display Name (Nom à afficher) à votre interaction. Ce nom sera affiché dans l'application Google Home.

Ajouter un nom à afficher

Sous Identité visuelle de l'application, importez un fichier png pour l'icône de l'application, de taille 144 x 144 px et nommé .png.

Ajouter une icône d&#39;application

Pour activer l'association de comptes, utilisez les paramètres suivants:

ID client

ABC123

Client secret (Code secret du client)

DEF456

Authorization URL (URL de l'autorisation)

https://us-central1-
.cloudfunctions.net/fakeauth

Token URL (URL du jeton)

https://us-central1-
.cloudfunctions.net/faketoken

Modifier les URL d&#39;association de comptes

Sous URL de traitement cloud, saisissez l'URL de votre fonction cloud qui fournit le traitement nécessaire pour les intents de la maison connectée.

https://us-central1--cloudfunctions.net/smarthome

Ajouter l&#39;URL de la fonction Cloud

Cliquez sur Save (Enregistrer) pour enregistrer la configuration de votre projet, puis sur Next: Test (Suivant : Test) pour activer les tests sur votre projet.

Tester votre intégration cloud à cloud

Vous pouvez maintenant commencer à implémenter les webhooks nécessaires pour associer l'état de l'appareil à l'Assistant.

4. Créer un lave-linge

Maintenant que vous avez configuré votre intégration, vous pouvez ajouter des appareils et envoyer des données. Votre service cloud doit gérer les intents suivants :

  • Un intent SYNC se produit lorsque l'Assistant souhaite connaître les appareils que l'utilisateur a connectés. Cet intent est envoyé à votre service lorsque l'utilisateur associe un compte. En réponse, vous devez fournir une charge utile JSON avec tous les appareils de l'utilisateur, ainsi que leurs fonctionnalités.
  • Un intent QUERY se produit lorsque l'Assistant souhaite connaître l'état ou le statut actuel d'un appareil. En réponse, vous devez fournir une charge utile JSON avec l'état de chaque appareil demandé.
  • Un intent EXECUTE se produit lorsque l'Assistant souhaite contrôler un appareil pour le compte d'un utilisateur. En réponse, vous devez fournir une charge utile JSON avec le statut d'exécution de chaque appareil demandé.
  • Un intent DISCONNECT se produit lorsque l'utilisateur dissocie son compte de l'Assistant. Vous devez cesser d'envoyer des événements concernant les appareils de cet utilisateur à l'Assistant.

Vous allez mettre à jour les fonctions que vous avez déployées précédemment pour gérer ces intents dans les sections suivantes.

Mettre à jour la réponse SYNC

Ouvrez le fichier functions/index.js qui contient le code permettant de répondre aux requêtes de l'Assistant.

Vous devrez gérer un intent SYNC en renvoyant les métadonnées et les fonctionnalités de l'appareil. Mettez à jour le fichier JSON dans le tableau onSync afin d'inclure les informations provenant des appareils ainsi que les caractéristiques recommandées pour un lave-linge.

index.js

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
        ],
        name: {
          defaultNames: ['My Washer'],
          name: 'Washer',
          nicknames: ['Washer'],
        },
        deviceInfo: {
          manufacturer: 'Acme Co',
          model: 'acme-washer',
          hwVersion: '1.0',
          swVersion: '1.0.1',
        },
        willReportState: true,
        attributes: {
          pausable: true,
        },
      }],
    },
  };
});

Déployer le code sur Firebase

Déployez le traitement cloud mis à jour à l'aide de la CLI Firebase :

firebase deploy --only functions

Pour tester votre intégration cloud à cloud, vous devez associer votre projet à un compte Google. Vous pourrez effectuer des tests via les surfaces de l'Assistant Google et l'application Google Home connectées au même compte.

  1. Sur votre téléphone, ouvrez les paramètres de l'Assistant Google. Notez que votre appareil doit être connecté avec le même compte que celui utilisé dans la console.
  2. Accédez à Assistant Google > Paramètres > Contrôle de la maison (sous "Assistant").
  3. Cliquez sur l'icône de recherche en haut à droite.
  4. Recherchez votre application de test à l'aide du préfixe [test] pour la trouver.
  5. Sélectionnez-la. L'Assistant Google s'authentifie alors auprès de votre service et envoie une requête SYNC pour lui demander de fournir une liste d'appareils pour l'utilisateur.

Ouvrez l'application Google Home et vérifiez que votre lave-linge est affiché.

ae252220753726f6.png

5. Gérer les commandes et les requêtes

Maintenant que votre service cloud signale correctement le lave-linge à Google, vous devez lui permettre de demander l'état de l'appareil et d'envoyer des commandes.

Gérer l'intent QUERY

Un intent QUERY inclut un ensemble d'appareils. Pour chaque appareil, vous devez renvoyer l'état actuel.

Dans functions/index.js, modifiez le gestionnaire QUERY de manière à traiter la liste des appareils cibles figurant dans la requête d'intent.

index.js

app.onQuery(async (body) => {
  const {requestId} = body;
  const payload = {
    devices: {},
  };
  const queryPromises = [];
  const intent = body.inputs[0];
  for (const device of intent.payload.devices) {
    const deviceId = device.id;
    queryPromises.push(queryDevice(deviceId)
        .then((data) => {
        // Add response to device payload
          payload.devices[deviceId] = data;
        }
        ));
  }
  // Wait for all promises to resolve
  await Promise.all(queryPromises);
  return {
    requestId: requestId,
    payload: payload,
  };
});

Pour chaque appareil figurant dans la requête, renvoyez l'état actuel stocké dans Realtime Database. Mettez à jour les fonctions queryFirebase et queryDevice pour qu'elles renvoient les données d'état du lave-linge.

index.js

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
  };
};

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{
      currentCycle: 'rinse',
      nextCycle: 'spin',
      lang: 'en',
    }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
  };
};

Gérer l'intent EXECUTE

L'intent EXECUTE gère les commandes de mise à jour de l'état de l'appareil. La réponse renvoie le statut de chaque commande (SUCCESS, ERROR ou PENDING, par exemple) ainsi que le nouvel état de l'appareil.

Dans functions/index.js, modifiez le gestionnaire EXECUTE pour traiter la liste des caractéristiques qui doivent être mises à jour et l'ensemble des appareils cibles pour chaque commande :

index.js

app.onExecute(async (body) => {
  const {requestId} = body;
  // Execution results are grouped by status
  const result = {
    ids: [],
    status: 'SUCCESS',
    states: {
      online: true,
    },
  };

  const executePromises = [];
  const intent = body.inputs[0];
  for (const command of intent.payload.commands) {
    for (const device of command.devices) {
      for (const execution of command.execution) {
        executePromises.push(
            updateDevice(execution, device.id)
                .then((data) => {
                  result.ids.push(device.id);
                  Object.assign(result.states, data);
                })
                .catch(() => functions.logger.error('EXECUTE', device.id)));
      }
    }
  }

  await Promise.all(executePromises);
  return {
    requestId: requestId,
    payload: {
      commands: [result],
    },
  };
});

Pour chaque commande et appareil cible, mettez à jour les valeurs de Realtime Database qui correspondent à la caractéristique demandée. Modifiez la fonction updateDevice de manière à mettre à jour la référence Firebase appropriée et à renvoyer l'état de l'appareil mis à jour.

index.js

const updateDevice = async (execution, deviceId) => {
  const {params, command} = execution;
  let state; let ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = params.start
      ? {isRunning: true, isPaused: false}
      : {isRunning: false, isPaused: false};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      const data = await queryDevice(deviceId);
      state = (data.isPaused === false && data.isRunning === false)
        ? {isRunning: false, isPaused: false}
        : {isRunning: !params.pause, isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
  }

  return ref.update(state)
      .then(() => state);
};

6. Tester votre intégration

Après avoir implémenté les trois intents, vous pouvez vérifier que votre intégration contrôle bien le lave-linge.

Déployer sur Firebase

Déployez le traitement cloud mis à jour à l'aide de la CLI Firebase :

firebase deploy --only functions

Tester le lave-linge

Comme vous pouvez maintenant le constater, la valeur change lorsque vous énoncez l'une des commandes vocales suivantes à l'aide de votre téléphone :

"Hey Google, allume mon lave-linge."

"Hey Google, mets mon lave-linge en pause."

"Hey Google, arrête mon lave-linge."

Vous pouvez également poser des questions pour connaître l'état actuel de votre lave-linge.

"Hey Google, est-ce que mon lave-linge est allumé ?"

"Hey Google, est-ce que mon lave-linge est en marche ?"

"Hey Google, quel est le cycle défini sur mon lave-linge ?"

Vous pouvez afficher ces requêtes et commandes dans les journaux qui s'affichent sous votre fonction dans la section Functions (Fonctions) de la console Firebase. Pour en savoir plus sur les journaux Firebase, consultez Écrire et afficher les journaux.

Vous pouvez également trouver ces requêtes et commandes dans la console Google Cloud en accédant à Logging > Logs Explorer (Journalisation > Explorateur de journaux). Pour en savoir plus sur la journalisation Google Cloud, consultez Accéder aux journaux des événements avec Cloud Logging.

7. Signaler les mises à jour à Google

Votre service cloud est maintenant complètement intégré aux intents de maison connectée, ce qui permet aux utilisateurs de contrôler leurs appareils et d'en connaître l'état actuel. Toutefois, cette implémentation ne permet toujours pas au service d'envoyer, de manière proactive, des informations sur les événements (tels que des changements d'état ou de présence sur l'appareil) à l'Assistant.

La fonction Request Sync vous permet de déclencher une nouvelle demande de synchronisation lorsque des utilisateurs ajoutent ou suppriment des appareils, ou en cas de modification des fonctionnalités de leurs appareils. La fonction Report State permet à votre service cloud d'envoyer, de manière proactive, l'état d'un appareil à Home Graph lorsque des utilisateurs le modifient physiquement (en activant un interrupteur, par exemple) ou à l'aide d'un autre service.

Dans cette section, vous allez ajouter du code afin d'appeler ces méthodes à partir de l'interface de l'application Web.

Activer l'API HomeGraph

L'API HomeGraph permet de stocker les données des appareils et leur état, et de les interroger, depuis le Home Graph d'un utilisateur. Pour utiliser cette API, vous devez d'abord ouvrir la console Google Cloud et activer l'API HomeGraph.

Dans la console Google Cloud, veillez à sélectionner le projet correspondant à votre intégration <project-id>. Ensuite, sur l'écran "Bibliothèque d'API" de l'API HomeGraph, cliquez sur Activer.

ee198858a6eac112.png

Activer Report State

Lorsque des données sont écrites dans Realtime Database, la fonction reportstate est déclenchée dans le projet de démarrage. Mettez à jour la fonction reportstate dans functions/index.js pour capturer les données écrites dans la base de données et les publier sur Home Graph par le biais de la fonction Report State.

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      functions.logger.info('Firebase write event triggered Report State');
      const snapshot = change.after.val();

      const requestBody = {
        requestId: 'ff36a3cc', /* Any unique ID */
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              /* Report the current state of our washer */
              [context.params.deviceId]: {
                on: snapshot.OnOff.on,
                isPaused: snapshot.StartStop.isPaused,
                isRunning: snapshot.StartStop.isRunning,
              },
            },
          },
        },
      };

      const res = await homegraph.devices.reportStateAndNotification({
        requestBody,
      });
      functions.logger.info('Report state response:', res.status, res.data);
    });

Activer Request Sync

La fonction requestsync est déclenchée dans le projet de démarrage lors de l'actualisation de l'icône dans l'interface utilisateur Web. Implémentez la fonction requestsync dans functions/index.js pour appeler l'API HomeGraph.

index.js

exports.requestsync = functions.https.onRequest(async (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');
  functions.logger.info(`Request SYNC for user ${USER_ID}`);
  try {
    const res = await homegraph.devices.requestSync({
      requestBody: {
        agentUserId: USER_ID,
      },
    });
    functions.logger.info('Request sync response:', res.status, res.data);
    response.json(res.data);
  } catch (err) {
    functions.logger.error(err);
    response.status(500).send(`Error requesting sync: ${err}`);
  }
});

Déployer le code sur Firebase

Déployez le code mis à jour à l'aide de la CLI Firebase :

firebase deploy --only functions

Tester l'implémentation

Cliquez sur le bouton Actualiser ae8d3b25777a5e30.png dans l'interface utilisateur Web et vérifiez qu'une demande de synchronisation est répertoriée dans le journal de la console Firebase.

Modifiez ensuite les attributs du lave-linge dans l'interface utilisateur Web, puis cliquez sur Mettre à jour. Vérifiez que le changement d'état signalé à Google figure bien dans les journaux de votre console Firebase.

8. Félicitations

674c4f4392e98c1.png

Félicitations ! Vous avez intégré l'Assistant à un service cloud d'appareil à l'aide d'intégrations cloud à cloud.

En savoir plus

Voici quelques idées que vous pouvez mettre en pratique pour approfondir le sujet :

Vous pouvez également en savoir plus sur le test et l'envoi d'une intégration pour examen, y compris le processus de certification permettant de publier votre intégration auprès des utilisateurs.