Wdrażanie lokalnej aplikacji do realizacji zamówień

Aby obsługiwać realizację zamówień lokalnych, musisz utworzyć aplikację, która będzie obsługiwać te intencje dotyczące inteligentnego domu:

  • IDENTIFY: obsługuje wykrywanie inteligentnych urządzeń, którymi można sterować lokalnie. Obsługa intencji wyodrębnia dane, które inteligentne urządzenie zwraca podczas wykrywania, i wysyła je w odpowiedzi do Google.
  • EXECUTE: obsługuje wykonywanie poleceń.
  • QUERY: umożliwia wysyłanie zapytań o stan urządzenia.
  • REACHABLE_DEVICES: (Opcjonalnie) obsługuje wykrywanie urządzeń końcowych, którymi można sterować lokalnie, znajdujących się za centralą (lub mostkiem).

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

Zalecamy używanie TypeScriptu, ponieważ możesz korzystać z powiązań, aby statycznie sprawdzać, czy dane zwracane przez aplikację są zgodne z typami oczekiwanymi przez platformę.

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

Poniższe fragmenty kodu pokazują, jak zainicjować aplikację do realizacji zamówień lokalnych i dołączyć do niej moduły obsługi.

Samodzielne
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ć aplikację do realizacji zamówień lokalnych, musisz utworzyć pakiet JavaScript dla swojego kodu i wszystkich jego zależności.

Użyj aplikacji do realizacji zamówień w sklepie stacjonarnym project initializer, aby zainicjować odpowiednią strukturę projektu z preferowaną konfiguracją narzędzia do łączenia plików.

Szablony projektów

Aby wybrać konfigurację narzędzia do łączenia, uruchom polecenie npm init, jak pokazano w tych przykładach:

Brak

TypeScript bez konfiguracji narzędzia do łączenia:

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 aplikacji do realizacji zamówień lokalnych.

Webpack

TypeScript z konfiguracją narzędzia do łączenia 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 aplikacji do realizacji zamówień lokalnych.

Podsumowanie

TypeScript z konfiguracją narzędzia do łączenia plików 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 aplikacji do realizacji zamówień lokalnych.

Parcel

TypeScript z konfiguracją narzędzia do łączenia plików 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 aplikacji do realizacji zamówień lokalnych.

Wykonywanie typowych czynności na poziomie projektu

Wygenerowany projekt obsługuje te skrypty npm:

Pakiet
cd project-directory/
npm run build

Ten skrypt kompiluje źródło TypeScript i pakuje aplikację wraz z zależnościami dla środowiska wykonawczego Chrome w podkatalogu dist/web oraz dla środowiska wykonawczego Node.js w podkatalogu dist/node.

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

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

Wyświetlanie
cd project-directory/
npm run start

Podczas programowania ten skrypt lokalnie udostępnia pakiety aplikacji dla środowisk wykonawczych Chrome i Node.js.

Zaimplementuj moduł obsługi IDENTIFY

Procedura obsługi IDENTIFY zostanie uruchomiona, gdy urządzenie Google Home lub Google Nest zostanie ponownie uruchomione i wykryje niezweryfikowane urządzenia lokalne (w tym urządzenia końcowe połączone z hubem). Platforma Local Home przeskanuje urządzenia lokalne, korzystając z informacji o konfiguracji skanowania podanych wcześniej, i wywoła Twój moduł obsługi IDENTIFY z wynikami skanowania.

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

Jeśli wyniki skanowania pasują do urządzenia, 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ądzenia, jeśli wartość verificationId z odpowiedzi IDENTIFY jest zgodna z jedną z wartości otherDeviceIds zwróconych w odpowiedzi SYNC.

Przykład

Poniższe fragmenty kodu pokazują, jak utworzyć moduły obsługi IDENTIFY w przypadku integracji z samodzielnym urządzeniem i hubem.

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

Identyfikowanie urządzeń za koncentratorem

Jeśli Google zidentyfikuje urządzenie będące hubem, będzie traktować je jako kanał komunikacji z podłączonymi do niego urządzeniami końcowymi i spróbuje je zweryfikować.

Aby umożliwić Google potwierdzenie obecności urządzenia hub, wykonaj te instrukcje dla IDENTIFYobsługi:

  • Jeśli odpowiedź SYNC zawiera identyfikatory lokalnych urządzeń końcowych połączonych z hubem, ustaw isProxy jako trueIdentifyResponsePayload.
  • Jeśli odpowiedź SYNC nie zawiera informacji o urządzeniu koncentratora, ustaw isLocalOnly jako trueIdentifyResponsePayload.
  • Pole device.id zawiera lokalny identyfikator urządzenia dla samego huba.

Zaimplementuj moduł obsługi REACHABLE_DEVICES (tylko integracje z hubem)

Intencja REACHABLE_DEVICES jest wysyłana przez Google w celu potwierdzenia, które urządzenia końcowe mogą być sterowane lokalnie. Ten zamiar jest wywoływany za każdym razem, gdy Google przeprowadza skanowanie wykrywające (mniej więcej raz na minutę), o ile wykryje, że hub jest online.

Implementacja modułu obsługi REACHABLE_DEVICES jest podobna do modułu obsługi IDENTIFY, z tym że moduł obsługi musi zbierać dodatkowe identyfikatory urządzeń dostępnych za pomocą lokalnego serwera proxy (czyli huba). Pole device.verificationId zawiera lokalny identyfikator urządzenia końcowego połączonego z hubem.

ReachableDevicesRequest z platformy Local Home zawiera instancję LocalIdentifiedDevice. Za pomocą tej instancji możesz uzyskać identyfikator urządzenia proxy, a także dane z wyników skanowania.

Obsługa REACHABLE_DEVICES powinna zwracać obiekt ReachableDevicesPayload, który zawiera obiekt devices z tablicą wartości verificationId reprezentujących urządzenia końcowe sterowane przez koncentrator. Wartości verificationId muszą być zgodne z jedną z wartości otherDeviceIds z odpowiedzi SYNC.

Poniższy fragment kodu pokazuje, jak utworzyć REACHABLE_DEVICEShandler.

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

Twój moduł obsługi EXECUTE w aplikacji przetwarza polecenia użytkownika i używa pakietu Local Home SDK, aby uzyskać dostęp do inteligentnych urządzeń za pomocą istniejącego protokołu.

Platforma Local Home przekazuje ten sam ładunek wejściowy do funkcji EXECUTEhandler co w przypadku intencji EXECUTE do realizacji w chmurze. Podobnie funkcja obsługi EXECUTE zwraca dane wyjściowe w tym samym formacie co w przypadku przetwarzania intencji EXECUTE. Aby uprościć tworzenie odpowiedzi, możesz użyć klasy Execute.Response.Builder, którą udostępnia pakiet Local Home SDK.

Aplikacja nie ma bezpośredniego dostępu do adresu IP urządzenia. Zamiast tego użyj interfejsu CommandRequest, aby tworzyć polecenia oparte na jednym 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żywaj identyfikatora urządzenia (i parametrów z pola customData, jeśli są uwzględnione) z odpowiedzi SYNC, aby komunikować się z urządzeniem.

Przykład

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

Samodzielny/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

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

Platforma Local Home przekazuje do funkcji obsługi „QUERY” ten sam ładunek żądania co w przypadku intencji QUERY do realizacji w chmurze. Podobnie QUERYfunkcja obsługi zwraca daneQUERY w tym samym formacie co w przypadku przetwarzania QUERYintencjiQUERY.

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

Aby sterować urządzeniami końcowymi za koncentratorem, może być konieczne podanie dodatkowych informacji w ładunku polecenia specyficznego dla protokołu wysyłanego do koncentratora. Dzięki temu koncentrator będzie mógł określić, do którego urządzenia jest kierowane polecenie. W niektórych przypadkach można to wywnioskować bezpośrednio z wartości device.id, ale jeśli tak nie jest, należy uwzględnić te dodatkowe dane w polu customData.

Jeśli aplikacja została utworzona przy użyciu TypeScriptu, pamiętaj o skompilowaniu jej do JavaScriptu. Do pisania kodu możesz używać wybranego systemu modułów. Sprawdź, czy miejsce docelowe jest obsługiwane przez przeglądarkę Chrome.