Wdrażanie lokalnej aplikacji do realizacji zamówień

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

  • IDENTIFY: obsługa wykrywania inteligentnych urządzeń, którymi można sterować lokalnie. Obsługa intencji wyodrębnia dane, które Twoje urządzenie inteligentne zwraca podczas wykrywania, i wysyła je w odpowiedzi do Google.
  • EXECUTE: obsługa wykonywania poleceń.
  • QUERY: obsługa zapytań o stan urządzenia.
  • REACHABLE_DEVICES: (opcjonalnie) obsługa wykrywania urządzeń końcowych z możliwością sterowania lokalnie za pomocą centrali (lub mostu).

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ą TypeScript (preferowana opcja) lub JavaScriptu.

Zalecamy TypeScript, ponieważ możesz korzystać z wiązań, aby zapewnić statycznie, że dane zwracane przez aplikację są zgodne z typami wymaganymi przez platformę.

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

Poniższe fragmenty kodu pokazują, jak zainicjować aplikację do obsługi lokalnie dostępnej usługi i dołączyć swoje moduły obsługi.

Samodzielny
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 obsługi lokalnego zamówienia, musisz utworzyć pakiet JavaScriptu dla kodu i wszystkich jego zależności.

Użyj inicjatora projektu aplikacji do obsługi lokalnej, aby uruchomić odpowiednią strukturę projektu z preferowaną konfiguracją pakietu.

Szablony projektów

Aby wybrać konfigurację pakietu, uruchom polecenie npm init w ten sposób:

Brak

TypeScript bez konfiguracji pakietu:

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 obsługi lokalnej.

Webpack

TypeScript z konfiguracją z użyciem 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 aplikacji do obsługi lokalnej.

Podsumowanie

TypeScript z konfiguracją 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 obsługi lokalnej.

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 aplikacji do obsługi lokalnej.

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 tworzy pakiet aplikacji z jej zależnościami dla środowiska wykonawczego Chrome w podkatalogu dist/web i środowiska wykonawczego Node.js w podkatalogu dist/web.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 testy automatyczne z katalogu test.ts.

Usługa
cd project-directory/
npm run start

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

Zaimplementuj moduł obsługi IDENTIFY

Obsługa IDENTIFY zostanie uruchomiona, 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 centralą). Platforma Local Home przeskanuje lokalne urządzenia, korzystając z podanych wcześniej informacji o konfiguracji skanowania, i wywołuje moduł obsługi IDENTIFY z wynikami skanowania.

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

Jeśli wyniki skanowania pasują do Twojego urządzenia, element obsługi IDENTIFY powinien zwrócić obiekt IdentifyResponsePayload, który zawiera obiekt device z metadanymi inteligentnego domu (np. typami, cechami i stanem raportu).

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

Przykład

Poniższe fragmenty kodu pokazują, jak utworzyć IDENTIFY dla samodzielnego urządzenia i integracji z hubem.

Samodzielny
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ń podłączonych do koncentratora

Jeśli Google wykryje urządzenie koncentratora, będzie je traktować jako przewód do urządzeń końcowych z połączonymi urządzeniami końcowymi i będzie próbować je zweryfikować.

Aby umożliwić Google potwierdzenie obecności urządzenia sterującego, wykonaj te instrukcje dotyczące IDENTIFY:

  • 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 hub, ustaw isLocalOnly jako trueIdentifyResponsePayload.
  • Pole device.id zawiera lokalny identyfikator urządzenia dla samego urządzenia koncentratora.

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

Intencją REACHABLE_DEVICES jest przesłanie przez Google potwierdzenia, które urządzenia końcowe mogą być kontrolowane lokalnie. Ta intencja jest wywoływana za każdym razem, gdy Google przeprowadza skanowanie w celu wykrywania (około raz na minutę), o ile hub jest online.

Obsługę REACHABLE_DEVICES wdrażasz podobnie jak obsługę IDENTIFY, z tym że Twój handler musi zbierać dodatkowe identyfikatory urządzeń dostępne dla lokalnego serwera proxy (czyli urządzenia koncentratora). Pole device.verificationId zawiera identyfikator lokalnego urządzenia końcowego, które jest połączone z hubem.

Element ReachableDevicesRequest z platformy Local Home zawiera instancję elementu LocalIdentifiedDevice. Dzięki temu możesz uzyskać identyfikator urządzenia proxy oraz dane z wyników skanowania.

Twój handler REACHABLE_DEVICES powinien zwracać obiekt ReachableDevicesPayload, który zawiera obiekt devices zawierający tablicę wartości verificationId reprezentujących urządzenia końcowe kontrolowane przez hub. Wartości verificationId muszą być zgodne z wartością otherDeviceIds w odpowiedzi SYNC.

W tym fragmencie kodu pokazujemy, jak utworzyć element 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;
  };

Implementacja modułu obsługi EXECUTTE

Obsługa EXECUTE w aplikacji przetwarza polecenia użytkownika i korzysta z pakietu SDK Local Home, aby uzyskać dostęp do Twoich urządzeń inteligentnych za pomocą istniejącego protokołu.

Platforma Local Home przekazuje funkcji obsługi EXECUTE te same dane wejściowe, co w przypadku intencji EXECUTE do przetwarzania w chmurze. Podobnie, EXECUTE zwraca dane wyjściowe w tym samym formacie co po przetworzeniu inencji 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 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.

Aby kierować polecenia na urządzenia, użyj identyfikatora urządzenia (oraz parametrów z pola customData, jeśli jest dostępne) z odpowiedzi SYNC do komunikacji z urządzeniem.

Przykład

Poniższy fragment kodu pokazuje, jak utworzyć element 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());
  };

Zaimplementuj moduł obsługi zapytania.

Handler QUERY w aplikacji przetwarza żądania użytkownika i korzysta z pakietu Local Home SDK, aby zgłaszać stan urządzeń inteligentnych.

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

Wysyłanie poleceń do urządzeń podłączonych do koncentratora

Aby sterować urządzeniami końcowymi za pomocą koncentratora, konieczne może być podanie dodatkowych informacji w danych ładunku poleceń związanych z danym protokołem, które są wysyłane do koncentratora, aby ten mógł zidentyfikować urządzenie docelowe. W niektórych przypadkach można to określić bezpośrednio na podstawie wartości device.id, ale jeśli nie jest to możliwe, należy podać te dodatkowe dane w polu customData.

Jeśli aplikacja została utworzona za pomocą TypeScript, pamiętaj, aby skompilować ją na JavaScript. Do pisania kodu możesz użyć dowolnego systemu modułów. Upewnij się, że przeglądarka Chrome obsługuje docelowy system operacyjny.