Wdrażanie lokalnej aplikacji do realizacji zamówień

Aby obsługiwać realizację lokalną, musisz utworzyć aplikację, która będzie obsługiwać te intencje inteligentnego domu:

  • IDENTIFY: umożliwia wykrywanie inteligentnych urządzeń, którymi można sterować lokalnie. Moduł obsługi intencji wyodrębnia dane zwracane przez urządzenie podczas wykrywania i wysyła je w odpowiedzi do Google.
  • EXECUTE: obsługuje wykonywanie poleceń.
  • QUERY: obsługuje sprawdzanie stanu urządzenia.
  • REACHABLE_DEVICES: (opcjonalnie) obsługuje wykrywanie urządzeń końcowych sterowanych lokalnie, które znajdują się za urządzeniem centralnym (lub mostkiem).

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

Zalecamy TypeScript, ponieważ możesz wykorzystać powiązania, aby statycznie zapewnić, że 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 lokalnej i dołączyć 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 lokalnej, musisz utworzyć pakiet JavaScript dla kodu i wszystkich jego zależności.

Użyj inicjatora projektu aplikacji do realizacji lokalnej, aby utworzyć odpowiednią strukturę projektu z preferowaną konfiguracją bundlera.

Szablony projektów

Aby wybrać konfigurację bundlera, uruchom polecenie npm init, jak pokazano w tych przykładach:

Brak

TypeScript bez konfiguracji bundlera:

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

Webpack

Konfiguracja TypeScript z pakietem 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 lokalnej.

Podsumowanie

TypeScript z konfiguracją bundlera 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 lokalnej.

Parcel

Konfiguracja bundlera TypeScript z 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 lokalnej.

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 aplikację z jej zależnościami dla środowiska wykonawczego Chrome w podkatalogu dist/web i ś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 żadnych danych wyjściowych w podkatalogu dist/ i uruchamia automatyczne testy z test.ts.

Wyświetlaj
cd project-directory/
npm run start

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

Implementowanie modułu obsługi IDENTIFY

Moduł obsługi IDENTIFY zostanie aktywowany, gdy urządzenie Google Home lub Google Nest uruchomi się ponownie i wykryje niezweryfikowane urządzenia lokalne (w tym urządzenia końcowe połączone z koncentratorem). Platforma Local Home wyszuka urządzenia lokalne, korzystając z podanych wcześniej informacji o konfiguracji skanowania, i wywoła moduł obsługi IDENTIFY z wynikami skanowania.

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

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

Przykład

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

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 centralne, potraktuje je jako kanał do połączonych z nim urządzeń końcowych i spróbuje je zweryfikować.

Aby umożliwić Google potwierdzenie obecności urządzenia centralnego, wykonaj te instrukcje dotyczące modułu obsługi IDENTIFY:

  • Jeśli odpowiedź SYNC zawiera identyfikatory lokalnych urządzeń końcowych połączonych z koncentratorem, ustaw isProxy na true w IdentifyResponsePayload.
  • Jeśli odpowiedź SYNC nie zawiera informacji o urządzeniu hub, ustaw isLocalOnly jako true w IdentifyResponsePayload.
  • Pole device.id zawiera identyfikator urządzenia lokalnego dla samego urządzenia centralnego.

Implementowanie modułu obsługi REACHABLE_DEVICES (tylko integracje z koncentratorem)

Google wysyła intencję REACHABLE_DEVICES, aby potwierdzić, którymi urządzeniami końcowymi można sterować lokalnie. Ta intencja jest wywoływana za każdym razem, gdy Google przeprowadza skanowanie wykrywania (mniej więcej raz na minutę), o ile koncentrator jest wykrywany jako online.

Moduł obsługi REACHABLE_DEVICES implementuje się podobnie jak moduł obsługi IDENTIFY, z tą różnicą, że moduł obsługi musi zebrać dodatkowe identyfikatory urządzeń, do których lokalny serwer proxy (czyli centrum) może dotrzeć. Pole device.verificationId zawiera lokalny identyfikator urządzenia końcowego, które jest połączone z koncentratorem.

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

Twój moduł obsługi REACHABLE_DEVICES powinien zwracać obiekt ReachableDevicesPayload, który zawiera obiekt devices z tablicą wartości verificationId reprezentujących urządzenia końcowe, którymi steruje hub. Wartości verificationId muszą być zgodne z jedną z wartości otherDeviceIds z odpowiedzi SYNC.

Poniższy fragment kodu pokazuje, jak 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 Local Home SDK, aby uzyskiwać dostęp do inteligentnych urządzeń za pomocą istniejącego protokołu.

Platforma Local Home przekazuje do funkcji obsługi EXECUTE te same dane wejściowe co w przypadku zamiaru EXECUTE do realizacji w chmurze. Podobnie moduł 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 SDK Local Home.

Aplikacja nie ma bezpośredniego dostępu do adresu IP urządzenia. Zamiast tego użyj interfejsu CommandRequest, aby utworzyć 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 (oraz 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.

Samodzielne/centrum
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 zapytania QUERY

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 modułu obsługi „QUERY” te same dane żądania co w przypadku zamiaru QUERY do realizacji w chmurze. Podobnie moduł obsługi QUERY zwraca dane w tym samym formacie co w przypadku przetwarzania intencji QUERY.

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 związanego z protokołem wysyłanego do koncentratora, aby koncentrator mógł zidentyfikować, do którego urządzenia jest skierowane polecenie. W niektórych przypadkach można to wywnioskować bezpośrednio z wartości device.id, ale jeśli nie jest to możliwe, należy dołączyć te dodatkowe dane w polu customData.

Jeśli aplikacja została utworzona za pomocą TypeScript, pamiętaj o jej skompilowaniu do JavaScriptu. Do pisania kodu możesz użyć wybranego systemu modułów. Upewnij się, że cel jest obsługiwany przez przeglądarkę Chrome.