Wdrażanie lokalnej aplikacji do realizacji zamówień

Aby umożliwić realizację lokalnej realizacji zamówień, musisz utworzyć aplikację do obsługi tych intencji inteligentnego domu:

  • IDENTIFY: wspiera wykrywanie urządzeń, które można lokalnie sterować. Moduł obsługi intencji wyodrębnia dane, które urządzenie zwraca podczas wykrywania, i wysyła je w odpowiedzi do Google.
  • EXECUTE: obsługuje wykonywanie poleceń.
  • QUERY: obsługuje wysyłanie zapytań o stan urządzenia.
  • REACHABLE_DEVICES (opcjonalnie) umożliwia wykrywanie urządzeń końcowych, które można lokalnie kontrolować, za urządzeniami centrali (lub mostkami).

Ta aplikacja działa na urządzeniach Google Home lub Google Nest użytkownika i łączy Twoje urządzenie z Asystentem. Aplikację możesz utworzyć za pomocą języka TypeScript (preferowane) lub JavaScriptu.

Zalecamy korzystanie z TypeScript, ponieważ możesz wykorzystać powiązania, aby statycznie mieć pewność, że dane zwracane przez aplikację są zgodne z typami, których oczekuje platforma.

Więcej informacji o tym interfejsie API znajdziesz w dokumentacji interfejsu Local Home SDK API.

Poniższe fragmenty kodu pokazują, jak zainicjować lokalną aplikację realizacji i dołączyć moduły obsługi.

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

Tworzenie projektu

Aby wdrożyć lokalną aplikację realizacji zamówień, musisz utworzyć pakiet JavaScript dla swojego kodu i wszystkich jego zależności.

Użyj inicjatora projektu w lokalnej aplikacji realizującej projekt, aby wczytać odpowiednią strukturę projektu z preferowaną konfiguracją pakietu SDK.

Szablony projektów

Aby wybrać konfigurację usługi tworzenia pakietów, uruchom polecenie npm init w sposób pokazany na tych przykładach:

Brak

TypeScript bez konfiguracji pakietu SDK:

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

Struktura projektu:

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

Zastąp project-directory nowym katalogiem, który będzie zawierał projekt lokalnej aplikacji do realizacji zamówień.

Pakiet internetowy

TypeScript z konfiguracją pakietu webpack:

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

Struktura projektu:

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

Zastąp project-directory nowym katalogiem, który będzie zawierał projekt lokalnej aplikacji do realizacji zamówień.

Podsumowanie

TypeScript z konfiguracją pakietu Rollup:

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

Struktura projektu:

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

Zastąp project-directory nowym katalogiem, który będzie zawierał projekt lokalnej aplikacji do realizacji zamówień.

Paczka

TypeScript z konfiguracją pakietu Parcel:

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

Struktura projektu:

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

Zastąp project-directory nowym katalogiem, który będzie zawierał projekt lokalnej aplikacji do realizacji zamówień.

Wykonywanie typowych zadań na poziomie projektu

Wygenerowany projekt obsługuje te skrypty npm:

Pakiet
cd project-directory/
npm run build

Ten skrypt kompiluje kod źródłowy TypeScript i łączy Twoją aplikację z zależnościami środowiska wykonawczego Chrome w podkatalogu dist/web i środowisku wykonawczym Node.js w podkatalogu dist/node.

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

Ten skrypt sprawdza składnię kodu TypeScript, kompilowa go bez generowania danych wyjściowych w podkatalogu dist/ i uruchamia automatyczne testy w test.ts.

Udostępniaj
cd project-directory/
npm run start

W trakcie programowania skrypt ten udostępnia pakiety aplikacji lokalnie w środowisku wykonawczym Chrome i Node.js.

Implementowanie modułu obsługi IDENTIFY

Moduł obsługi IDENTIFY uruchomi się po ponownym uruchomieniu urządzenia Google Home lub Google Nest i zobaczy niezweryfikowane urządzenia lokalne (w tym urządzenia końcowe podłączone do hubu). Platforma Local Home będzie skanować w poszukiwaniu urządzeń lokalnych przy użyciu podanych wcześniej informacji o konfiguracji skanowania, a potem z wynikami skanowania wywołać moduł obsługi IDENTIFY.

IdentifyRequest z platformy Local Home zawiera dane skanowania instancji LocalIdentifiedDevice. Zapełniona jest tylko 1 instancja device na podstawie konfiguracji skanowania, która wykryła urządzenie.

Jeśli wyniki skanowania są zgodne z Twoim urządzeniem, moduł obsługi IDENTIFY powinien zwrócić obiekt IdentifyResponsePayload, który zawiera obiekt device z metadanymi inteligentnego domu (takimi jak typy, cechy i stan raportu).

Google tworzy powiązanie urządzeń, jeśli verificationId z odpowiedzi IDENTIFY odpowiada jednej z wartości otherDeviceIds zwróconych przez odpowiedź SYNC.

Przykład

Poniższe fragmenty kodu pokazują, jak utworzyć moduły obsługi IDENTIFY odpowiednio na potrzeby samodzielnych integracji urządzeń i centrów.

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

Zidentyfikuj urządzenia za hubem

Jeśli Google zidentyfikuje urządzenie centralne, potraktuje je jako kanał łączący połączone z nim urządzenia końcowe i spróbuje zweryfikować te urządzenia.

Aby umożliwić Google potwierdzenie obecności urządzenia centralnego, postępuj zgodnie z tymi instrukcjami dotyczącymi modułu obsługi IDENTIFY:

  • Jeśli odpowiedź SYNC zawiera identyfikatory lokalnych urządzeń końcowych połączonych z hub, ustaw isProxy na true w IdentifyResponsePayload.
  • Jeśli odpowiedź SYNC nie zgłasza urządzenia centrali, ustaw isLocalOnly jako true w IdentifyResponsePayload.
  • Pole device.id zawiera lokalny identyfikator samego urządzenia centrali.

Wdróż moduł obsługi REACHABLE_DEVICE (tylko integracje centrali)

Google wysyła intencję REACHABLE_DEVICES w celu sprawdzenia, które urządzenia końcowe mogą być sterowane lokalnie. Ta intencja jest wywoływana za każdym razem, gdy Google uruchamia skanowanie wykrywania (mniej więcej raz na minutę), o ile wykryje, że centrum jest online.

Moduł obsługi REACHABLE_DEVICES jest wdrażany podobnie jak moduł obsługi IDENTIFY, z tą różnicą, że musi on zbierać dodatkowe identyfikatory urządzeń dostępne dla lokalnego serwera proxy (czyli urządzenia centrali). Pole device.verificationId zawiera lokalny identyfikator urządzenia końcowego połączonego z centrum.

ReachableDevicesRequest z platformy Local Home zawiera wystąpienie LocalIdentifiedDevice. Dzięki tej instancji możesz uzyskać identyfikator urządzenia proxy oraz dane z wyników skanowania.

Moduł obsługi REACHABLE_DEVICES powinien zwracać obiekt ReachableDevicesPayload zawierający obiekt devices zawierający tablicę wartości verificationId reprezentującą urządzenia końcowe sterowane przez centrum. Wartości verificationId muszą być zgodne z jedną z wartości otherDeviceIds w odpowiedzi SYNC.

Poniższy fragment pokazuje, jak możesz utworzyć moduł obsługi REACHABLE_DEVICES.

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

Implementowanie modułu obsługi EXECUTE

Moduł obsługi EXECUTE w aplikacji przetwarza polecenia użytkownika i używa pakietu SDK Local Home do uzyskiwania dostępu do urządzeń za pomocą istniejącego protokołu.

Platforma lokalna strona główna przekazuje ten sam ładunek wejściowy do funkcji obsługi EXECUTE co w przypadku intencji EXECUTE w ramach realizacji w chmurze. Podobnie moduł obsługi EXECUTE zwraca dane wyjściowe w tym samym formacie co podczas przetwarzania intencji EXECUTE. Aby uprościć tworzenie odpowiedzi, możesz użyć klasy Execute.Response.Builder udostępnianej przez pakiet SDK Local Home.

Aplikacja nie ma bezpośredniego dostępu do adresu IP urządzenia. Zamiast tego używaj interfejsu CommandRequest do tworzenia poleceń na podstawie jednego z tych protokołów: UDP, TCP lub HTTP. Następnie wywołaj funkcję deviceManager.send(), aby wysłać polecenia.

Podczas kierowania poleceń na urządzenia użyj identyfikatora urządzenia (i parametrów z pola customData, jeśli jest uwzględnione) z odpowiedzi SYNC, aby komunikować się z urządzeniem.

Przykład

Poniższy fragment kodu pokazuje, jak możesz utworzyć moduł obsługi EXECUTE.

Niezależne/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());
  };

Implementowanie modułu obsługi QUERY

Moduł obsługi QUERY w aplikacji przetwarza żądania użytkowników i używa pakietu SDK Local Home do raportowania stanu urządzeń.

Lokalna platforma główna przekazuje ten sam ładunek żądania do funkcji obsługi „QUERY” co dla intencji QUERY w przypadku realizacji w chmurze. Podobnie moduł obsługi QUERY zwraca dane w tym samym formacie co podczas przetwarzania intencji QUERY.

Wysyłanie poleceń do urządzeń za centralą

Aby sterować urządzeniami końcowymi za centrum, konieczne może być podanie dodatkowych informacji w ładunku poleceń protokołu wysyłanym do centrum. Dzięki temu centrum będzie mogło zidentyfikować urządzenie, którego dotyczy polecenie. W niektórych przypadkach można to określić bezpośrednio na podstawie wartości device.id, ale jeśli nie jest to konieczne, uwzględnij te dodatkowe dane w polu customData.

Jeśli Twoja aplikacja została utworzona za pomocą TypeScriptu, pamiętaj o skompilowaniu jej do języka JavaScript. Do pisania kodu możesz użyć wybranego systemu modułów. Upewnij się, że środowisko docelowe jest obsługiwane przez przeglądarkę Chrome.