Wdrażanie lokalnej aplikacji do realizacji zamówień

Aby obsługiwać realizację lokalną, musisz utworzyć aplikację do obsługi tych intencji inteligentnego domu:

  • IDENTIFY: obsługuje wykrywanie urządzeń, którymi można sterować lokalnie. 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) obsługuje wykrywanie kontrolowanych lokalnie urządzeń końcowych za urządzeniem centralnym (lub mostkiem).

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ć przy użyciu języka TypeScript (preferowany sposób) lub JavaScriptu.

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

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

Poniższe fragmenty kodu pokazują, jak zainicjować lokalną aplikację do realizacji zamówień 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ę do realizacji zamówień, musisz utworzyć pakiet JavaScript dla swojego kodu i wszystkich jego zależności.

Użyj inicjatora projektu w lokalnej aplikacji do realizacji zamówień, aby uruchomić odpowiednią strukturę projektu z preferowaną konfiguracją pakietu.

Szablony projektu

Aby wybrać konfigurację usługi tworzenia pakietów, uruchom polecenie npm init w sposób pokazany poniżej:

Brak

TypeScript bez konfiguracji programu pakowania:

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ń.

Parcel

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 źródło 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 weryfikuje składnię kodu TypeScript, kompiluje go bez generowania żadnych 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 środowiskach wykonawczych 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 połączone z koncentratorem). Platforma Local Home będzie szukać urządzeń lokalnych przy użyciu podanych wcześniej informacji o konfiguracji skanowania, a następnie wywoła moduł obsługi IDENTIFY z wynikami skanowania.

IdentifyRequest z platformy Lokalna strona główna 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 (np. typy, cechy i stan raportu).

Google ustanawia powiązanie urządzenia, 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 możesz utworzyć moduły obsługi IDENTIFY odpowiednio na potrzeby integracji samodzielnych urządzeń i centrum.

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

Identyfikowanie urządzeń za koncentratorem

Jeśli Google zidentyfikuje urządzenie centralne, potraktuje je jako kanał do podłączonych do niego urządzeń końcowych 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 centrum, ustaw isProxy jako true w IdentifyResponsePayload.
  • Jeśli odpowiedź SYNC nie zgłasza urządzenia centralnego, ustaw isLocalOnly jako true w IdentifyResponsePayload.
  • Pole device.id zawiera lokalny identyfikator samego urządzenia centralnego.

Wdróż moduł obsługi REACHABLE_DEVICES (tylko integracje z centrum)

Google wysyła intencję REACHABLE_DEVICES do sprawdzania, które urządzenia końcowe mogą być sterowane lokalnie. Ta intencja jest wywoływana za każdym razem, gdy Google przeprowadza skanowanie wykrywania (mniej więcej raz na minutę), o ile urządzenie działa online.

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

ReachableDevicesRequest z platformy Lokalna strona główna zawiera instancję LocalIdentifiedDevice. Dzięki tej instancji możesz uzyskać identyfikator urządzenia proxy, a także dane z wyników skanowania.

Moduł obsługi REACHABLE_DEVICES powinien zwracać obiekt ReachableDevicesPayload zawierający obiekt devices zawierający tablicę verificationId wartości reprezentujących urządzenia końcowe kontrolowane przez centrum. Wartości verificationId muszą pasować do jednej z wartości otherDeviceIds w odpowiedzi SYNC.

Poniższy fragment kodu 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 wykonywania poleceń 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ą dotychczasowego 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 dostępnej w pakiecie Local Home SDK.

Aplikacja nie ma bezpośredniego dostępu do adresu IP urządzenia. Zamiast tego użyj interfejsu CommandRequest do tworzenia poleceń opartych 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 możesz utworzyć moduł obsługi EXECUTE.

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

Implementacja modułu obsługi zapytania QUERY

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

Platforma lokalna strona główna przekazuje ten sam ładunek żądania do funkcji obsługi „QUERY” jak w przypadku intencji QUERY w 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 koncentratorem

W przypadku sterowania urządzeniami końcowymi za koncentratorem konieczne może być podanie dodatkowych informacji w ładunku poleceń protokołu wysyłanym do centrum, aby centrum mogło rozpoznać, do jakiego urządzenia jest przeznaczone to polecenie. W niektórych przypadkach można to wywnioskować bezpośrednio na podstawie wartości device.id, ale gdy tak nie jest, należy uwzględnić te dodatkowe dane w polu customData.

Jeśli Twoja aplikacja została utworzona za pomocą TypeScriptu, pamiętaj o jej skompilowaniu pod kątem JavaScriptu. Do pisania kodu możesz użyć wybranego systemu modułów. Upewnij się, że wartość docelowa jest obsługiwana przez przeglądarkę Chrome.