Implémenter l'application de traitement en local

Pour prendre en charge le traitement en local, vous devez créer une application capable de gérer ces intents de maison connectée:

  • IDENTIFY: prend en charge la détection d'appareils connectés contrôlables localement. La le gestionnaire d'intents extrait les données renvoyées par votre appareil connecté lors de la détection et l'envoie en réponse à Google.
  • EXECUTE: accepte l'exécution de commandes.
  • QUERY: permet d'interroger l'état de l'appareil.
  • REACHABLE_DEVICES (facultatif) : accepte la détection de ressources contrôlables localement. périphériques derrière un périphérique hub (ou pont).

Cette application s'exécute sur les appareils Google Home ou Google Nest de l'utilisateur et connecte votre appareil connecté à l'Assistant Google. Vous pouvez créer l'application en utilisant TypeScript (recommandé) ou JavaScript.

TypeScript est recommandé, car vous pouvez exploiter liaisons pour vous assurer, de manière statique, que les données renvoyées par votre application correspondent aux types attendu par la plate-forme.

Pour en savoir plus sur l'API, consultez la Documentation de référence de l'API du SDK Local Home

Les extraits de code suivants montrent comment initialiser l'application de traitement en local et associer vos gestionnaires.

Autonome
import App = smarthome.App;
const localHomeApp: App = new App("1.0.0");
localHomeApp
  .onIdentify(identifyHandler)
  .onExecute(executeHandler)
  .listen()
  .then(() => {
    console.log("Ready");
  });
Pôle
import App = smarthome.App;
const localHomeApp: App = new App("1.0.0");
localHomeApp
  .onIdentify(identifyHandler)
  .onReachableDevices(reachableDevicesHandler)
  .onExecute(executeHandler)
  .listen()
  .then(() => {
    console.log("Ready");
  });

Créer un projet

Pour déployer votre application de traitement en local, vous devez créer un bundle JavaScript pour votre code et toutes ses dépendances.

Utilisez le projet de l'application de traitement en local initialiseur pour amorcer la structure de projet appropriée avec le bundler de votre choix configuration.

Modèles de projet

Pour sélectionner votre configuration bundler, exécutez la commande npm init comme indiqué dans les les exemples suivants:

Aucun

TypeScript sans configuration bundler:

npm init @google/local-home-app project-directory/ --bundler none

Structure du projet:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
└── serve.js

Remplacez project-directory par un nouveau répertoire qui contiendra le d'un projet d'application de traitement local.

Webpack

TypeScript avec Bundler webpack configuration:

npm init @google/local-home-app project-directory/ --bundler webpack

Structure du projet:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
├── webpack.config.web.js
├── webpack.config.node.js
└── serve.js

Remplacez project-directory par un nouveau répertoire qui contiendra le d'un projet d'application de traitement local.

Vue d'ensemble

TypeScript avec Rollup Configuration de bundler:

npm init @google/local-home-app project-directory/ --bundler rollup

Structure du projet:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
├── rollup.config.js
└── serve.js

Remplacez project-directory par un nouveau répertoire qui contiendra le d'un projet d'application de traitement local.

Colis

TypeScript avec Parcel Configuration de bundler:

npm init @google/local-home-app project-directory/ --bundler parcel

Structure du projet:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
└── serve.js

Remplacez project-directory par un nouveau répertoire qui contiendra le d'un projet d'application de traitement local.

Effectuer des tâches courantes au niveau du projet

Le projet généré accepte les commandes npm scripts:

Lot
cd project-directory/
npm run build

Ce script compile la source TypeScript et regroupe votre application avec ses dépendances pour l'environnement d'exécution Chrome dans le sous-répertoire dist/web et l'environnement d'exécution Node.js dans le sous-répertoire dist/node.

Valider
cd project-directory/
npm run lint
npm run compile
npm test

Ce script vérifie la syntaxe de votre code TypeScript, le compile sans générer de sortie dans le sous-répertoire dist/, et exécute des tests automatisés à partir de test.ts.

Livrer
cd project-directory/
npm run start

Pendant le développement, ce script diffuse localement vos app bundles pour les environnements d'exécution Chrome et Node.js.

Implémenter le gestionnaire IDENTIFY

Le gestionnaire IDENTIFY est déclenché lorsque l'appareil Google Home ou Google Nest redémarre et voit les appareils locaux non validés (y compris les appareils finaux connectés à un hub). La La plate-forme Local Home recherche les appareils locaux à l'aide des informations de configuration d'analyse que vous avez spécifié précédemment, puis appelez votre gestionnaire IDENTIFY avec les résultats de l'analyse.

La IdentifyRequest de la plate-forme Local Home contient les données de recherche LocalIdentifiedDevice Compute Engine. Une seule instance device est renseignée, en fonction de la configuration d'analyse ayant découvert l'appareil.

Si les résultats de l'analyse correspondent à votre appareil, votre gestionnaire IDENTIFY doit renvoyer une IdentifyResponsePayload qui inclut un objet device avec Métadonnées de maison connectée (telles que les types, les caractéristiques et l'état du rapport)

Google établit une association d'appareils si l'élément verificationId de la réponse IDENTIFY correspond à l'un des Valeurs otherDeviceIds renvoyées par la réponse SYNC.

Exemple

Les extraits de code suivants montrent comment créer des gestionnaires IDENTIFY pour des intégrations d'appareils autonomes et de hub, respectivement.

Autonome
const identifyHandler = (request: IntentFlow.IdentifyRequest):
  IntentFlow.IdentifyResponse => {

    // Obtain scan data from protocol defined in your scan config
    const device = request.inputs[0].payload.device;
    if (device.udpScanData === undefined) {
      throw Error("Missing discovery response");
    }
    const scanData = device.udpScanData.data;

    // Decode scan data to obtain metadata about local device
    const verificationId = "local-device-id";

    // Return a response
    const response: IntentFlow.IdentifyResponse = {
      intent: Intents.IDENTIFY,
      requestId: request.requestId,
      payload: {
        device: {
          id: device.id || "",
          verificationId, // Must match otherDeviceIds in SYNC response
        },
      },
    };
    return response;
  };
Pôle
const identifyHandler = (request: IntentFlow.IdentifyRequest):
  IntentFlow.IdentifyResponse => {

    // Obtain scan data from protocol defined in your scan config
    const device = request.inputs[0].payload.device;
    if (device.udpScanData === undefined) {
      throw Error("Missing discovery response");
    }
    const scanData = device.udpScanData.data;

    // Decode scan data to obtain metadata about local device
    const proxyDeviceId = "local-hub-id";

    // Return a response
    const response: IntentFlow.IdentifyResponse = {
      intent: Intents.IDENTIFY,
      requestId: request.requestId,
      payload: {
        device: {
          id: proxyDeviceId,
          isProxy: true,     // Device can control other local devices
          isLocalOnly: true, // Device not present in `SYNC` response
        },
      },
    };
    return response;
  };

Identifier les appareils derrière un hub

Si Google identifie un hub, il traitera le hub comme le canal. aux appareils finaux connectés au hub et tenter de les valider.

Pour autoriser Google à confirmer la présence d'un hub, procédez comme suit : instructions pour votre gestionnaire IDENTIFY:

  • Si votre réponse SYNC indique les ID des appareils de terminaison locaux connectés au hub, définissez isProxy sur true dans IdentifyResponsePayload
  • Si votre réponse SYNC ne signale pas votre hub, définissez isLocalOnly en tant que true dans IdentifyResponsePayload
  • Le champ device.id contient l'ID d'appareil local de l'appareil hub lui-même.

Implémenter le gestionnaire REACHABLE_DEVICE (intégrations hub uniquement)

L'intent REACHABLE_DEVICES est envoyé par Google pour confirmer les appareils finaux peuvent être contrôlées localement. Cet intent est déclenché chaque fois que Google exécute de découverte (environ une fois toutes les minutes), à condition que le hub soit détecté être en ligne.

L'implémentation du gestionnaire REACHABLE_DEVICES s'effectue de la même manière que pour IDENTIFY. , sauf qu'il doit collecter des ID d'appareils supplémentaires. accessible par le périphérique proxy local (c'est-à-dire le hub). La Le champ device.verificationId contient l'ID d'appareil local d'un appareil final connecté au hub.

La ReachableDevicesRequest de la plate-forme Local Home contient une instance LocalIdentifiedDevice Via cette instance, vous pouvez obtenir l'ID de l'appareil proxy ainsi que les données de les résultats de l'analyse.

Votre gestionnaire REACHABLE_DEVICES doit renvoyer une ReachableDevicesPayload qui inclut un objet devices contenant un tableau de Valeurs verificationId représentant les appareils finaux contrôlés par le hub. La Les valeurs verificationId doivent correspondre à l'un des otherDeviceIds du SYNC.

L'extrait de code suivant montre comment créer votre REACHABLE_DEVICES .

Pôle
const reachableDevicesHandler = (request: IntentFlow.ReachableDevicesRequest):
  IntentFlow.ReachableDevicesResponse => {

    // Reference to the local proxy device
    const proxyDeviceId = request.inputs[0].payload.device.id;

    // Gather additional device ids reachable by local proxy device
    // ...

    const reachableDevices = [
      // Each verificationId must match one of the otherDeviceIds
      // in the SYNC response
      { verificationId: "local-device-id-1" },
      { verificationId: "local-device-id-2" },
    ];

    // Return a response
    const response: IntentFlow.ReachableDevicesResponse = {
      intent: Intents.REACHABLE_DEVICES,
      requestId: request.requestId,
      payload: {
        devices: reachableDevices,
      },
    };
    return response;
  };

Implémenter le gestionnaire EXECUTE

Le gestionnaire EXECUTE de l'application traite les commandes utilisateur et utilise le SDK Local Home pour accéder à vos appareils connectés via un protocole existant.

La plate-forme Local Home transmet la même charge utile d'entrée au gestionnaire EXECUTE. comme pour EXECUTE. dans votre traitement cloud. De même, votre gestionnaire EXECUTE renvoie des données de sortie au même format que celui obtenu lors du traitement de l'intent EXECUTE. Pour simplifier la création de réponses, vous pouvez utiliser la méthode Execute.Response.Builder fournie par le SDK Local Home.

Votre application ne dispose pas d'un accès direct à l'adresse IP de l'appareil. À la place, utilisez la CommandRequest pour créer des commandes basées sur l'un de ces protocoles: UDP, TCP ou HTTP. Ensuite, appelez la méthode deviceManager.send() pour envoyer les commandes.

Lorsque vous ciblez des appareils avec des commandes, utilisez l'ID de l'appareil (et les paramètres du champ le champ customData, s'il est inclus) de la réponse SYNC pour communiquer avec l'appareil.

Exemple

L'extrait de code suivant montre comment créer votre gestionnaire EXECUTE.

Autonome/Hub
const executeHandler = (request: IntentFlow.ExecuteRequest):
  Promise<IntentFlow.ExecuteResponse> => {

    // Extract command(s) and device target(s) from request
    const command = request.inputs[0].payload.commands[0];
    const execution = command.execution[0];

    const response = new Execute.Response.Builder()
      .setRequestId(request.requestId);

    const result = command.devices.map((device) => {
      // Target id of the device provided in the SYNC response
      const deviceId = device.id;
      // Metadata for the device provided in the SYNC response
      // Use customData to provide additional required execution parameters
      const customData: any = device.customData;

      // Convert execution command into payload for local device
      let devicePayload: string;
      // ...

      // Construct a local device command over TCP
      const deviceCommand = new DataFlow.TcpRequestData();
      deviceCommand.requestId = request.requestId;
      deviceCommand.deviceId = deviceId;
      deviceCommand.data = devicePayload;
      deviceCommand.port = customData.port;
      deviceCommand.operation = Constants.TcpOperation.WRITE;

      // Send command to the local device
      return localHomeApp.getDeviceManager()
        .send(deviceCommand)
        .then((result) => {
          response.setSuccessState(result.deviceId, state);
        })
        .catch((err: IntentFlow.HandlerError) => {
          err.errorCode = err.errorCode || IntentFlow.ErrorCode.INVALID_REQUEST;
          response.setErrorState(device.id, err.errorCode);
        });
    });

    // Respond once all commands complete
    return Promise.all(result)
      .then(() => response.build());
  };

Implémenter le gestionnaire QUERY

Le gestionnaire QUERY de l'application traite les requêtes des utilisateurs et utilise le SDK Local Home pour signaler l'état de vos appareils connectés.

La plate-forme Local Home transmet la même charge utile de requête à la requête "QUERY". gestionnaire comme pour QUERY dans votre traitement cloud. De même, votre gestionnaire QUERY renvoie des données dans le même format que celui utilisé pour le traitement de l'intent QUERY.

Envoyer des commandes à des appareils derrière un hub

Pour contrôler les appareils finaux derrière un hub, vous devrez peut-être fournir des informations supplémentaires dans la charge utile de commande spécifique au protocole envoyée au hub afin que le hub pour identifier l’appareil auquel la commande est destinée. Dans certains cas, cela peut être directement à partir de la valeur device.id, mais lorsque ce n'est pas le cas, vous devez inclure ces données supplémentaires dans le champ customData.

Si vous avez créé votre application à l'aide de TypeScript, n'oubliez pas de la compiler pour JavaScript. Vous pouvez utiliser le système de modules de votre choix pour écrire votre code. Assurez-vous que votre cible est compatible avec le navigateur Chrome.