實作本機執行要求應用程式

如要支援店面取貨,您必須建構應用程式來處理下列智慧住宅意圖:

  • IDENTIFY:支援探索可在本機控制的智慧型裝置。意圖處理常式會擷取智慧型裝置在探索期間傳回的資料,並在回應中傳送給 Google。
  • EXECUTE:支援執行指令。
  • QUERY:支援查詢裝置狀態。
  • REACHABLE_DEVICES:(選用) 支援探索中樞裝置 (或橋接器) 後方可在本機控制的終端裝置。

這項應用程式會在使用者 Google Home 或 Google Nest 裝置上執行,並將智慧型裝置連結至 Google 助理。您可以使用 TypeScript (建議) 或 JavaScript 建立應用程式。

建議使用 TypeScript,因為您可以運用繫結,靜態確保應用程式傳回的資料符合平台預期的類型。

如要進一步瞭解 API,請參閱「Local Home SDK API 參考資料」。

下列程式碼片段顯示如何初始化本機執行要求應用程式,以及附加處理常式。

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

建立專案

如要部署本機履行應用程式,您需要為程式碼和所有依附元件建構 JavaScript 套件。

使用本機出貨應用程式專案初始化程式,透過偏好的打包工具設定,啟動適當的專案結構。

專案範本

如要選取套件組合工具設定,請執行 npm init 指令,如下列範例所示:

TypeScript (不含套件組合器設定):

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

專案結構:

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

project-directory 替換為新的目錄,其中會包含本機履行應用程式專案。

Webpack

使用 webpack 封裝工具設定的 TypeScript:

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

專案結構:

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

project-directory 替換為新的目錄,其中會包含本機履行應用程式專案。

匯總

使用 Rollup 的 TypeScript 套件組合工具設定:

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

專案結構:

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

project-directory 替換為新的目錄,其中會包含本機履行應用程式專案。

Parcel

使用 Parcel 的 TypeScript 套件組合器設定:

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

專案結構:

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

project-directory 替換為新的目錄,其中會包含本機履行應用程式專案。

執行常見的專案層級工作

產生的專案支援下列 npm 指令碼

套裝組合
cd project-directory/
npm run build

這個指令碼會編譯 TypeScript 來源,並將應用程式及其依附元件分別封裝至 dist/web 子目錄和 dist/node 子目錄,供 Chrome 執行階段環境和 Node.js 執行階段環境使用。

驗證
cd project-directory/
npm run lint
npm run compile
npm test

這個指令碼會驗證 TypeScript 程式碼的語法,編譯程式碼 (不會在 dist/ 子目錄中產生任何輸出內容),並從 test.ts 執行自動化測試。

提供
cd project-directory/
npm run start

在開發期間,這個指令碼會在本機提供 Chrome 和 Node.js 執行階段環境的應用程式套件。

實作 IDENTIFY 處理常式

當 Google Home 或 Google Nest 裝置重新啟動,並發現未經驗證的本機裝置 (包括連線至中樞的終端裝置) 時,系統就會觸發 IDENTIFY 處理常式。Local Home 平台會使用您先前指定的掃描設定資訊掃描本機裝置,並透過掃描結果呼叫 IDENTIFY 處理常式。

本機住家平台中的 IdentifyRequest 包含 LocalIdentifiedDevice 執行個體的掃描資料。系統只會根據發現裝置的掃描設定,填入一個 device 執行個體。

如果掃描結果與裝置相符,IDENTIFY 處理常式應傳回 IdentifyResponsePayload 物件,其中包含 device 物件和智慧住宅中繼資料 (例如類型、特徵和回報狀態)。

如果 IDENTIFY 回應中的 verificationIdSYNC 回應傳回的其中一個 otherDeviceIds 值相符,Google 就會建立裝置關聯。

範例

下列程式碼片段分別說明如何為獨立裝置和中樞整合建立 IDENTIFY 處理常式。

獨立式
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;
  };
中樞
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;
  };

識別集線器後方的裝置

如果 Google 偵測到中樞裝置,就會將中樞裝置視為連線至中樞裝置的終端裝置的管道,並嘗試驗證這些終端裝置。

如要讓 Google 確認中樞裝置是否存在,請按照IDENTIFY處理常式適用的操作說明進行:

  • 如果 SYNC 回應會回報連線至中樞的本機終端裝置 ID,請在 IdentifyResponsePayload 中將 isProxy 設為 true
  • 如果 SYNC 回應未回報中樞裝置,請在 IdentifyResponsePayload 中將 isLocalOnly 設為 true
  • device.id 欄位包含中樞裝置本身的本機裝置 ID。

實作 REACHABLE_DEVICES 處理常式 (僅限中樞裝置整合)

Google 會傳送 REACHABLE_DEVICES 意圖,確認哪些終端裝置可在本機控制。只要系統偵測到中樞裝置處於連線狀態,Google 每次執行探索掃描 (大約每分鐘一次) 時,就會觸發這項意圖。

實作 REACHABLE_DEVICES 處理常式的方式與 IDENTIFY 處理常式類似,但處理常式必須收集可透過本機 Proxy (即中樞裝置) 存取的其他裝置 ID。device.verificationId 欄位包含連線至中樞裝置的終端裝置本機 ID。

來自 Local Home 平台的 ReachableDevicesRequest 包含 LocalIdentifiedDevice 的例項。透過這個執行個體,您可以取得 Proxy 裝置 ID,以及掃描結果中的資料。

REACHABLE_DEVICES 處理常式應傳回 ReachableDevicesPayload 物件,其中包含 devices 物件,該物件包含代表中樞控制終端裝置的 verificationId 值陣列。verificationId 值必須與 SYNC 回應中的其中一個 otherDeviceIds 相符。

下列程式碼片段說明如何建立 REACHABLE_DEVICES 處理常式。

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

實作 EXECUTE 處理常式

應用程式中的 EXECUTE 處理常式會處理使用者指令,並透過現有通訊協定使用 Local Home SDK 存取智慧型裝置。

Local Home 平台會將相同的輸入酬載傳遞至 EXECUTE 處理常式函式,就像將 EXECUTE 意圖傳遞至雲端服務一樣。同樣地,EXECUTE 處理常式會以與處理 EXECUTE 意圖時相同的格式,傳回輸出資料。為簡化回應建立作業,您可以使用 Local Home SDK 提供的 Execute.Response.Builder 類別。

您的應用程式無法直接存取裝置的 IP 位址。請改用 CommandRequest 介面,根據下列其中一種通訊協定建立指令:UDP、TCP 或 HTTP。然後呼叫 deviceManager.send() 函式來傳送指令。

將指令傳送至裝置時,請使用 SYNC 回應中的裝置 ID (以及 customData 欄位中的參數,如有) 與裝置通訊。

範例

下列程式碼片段說明如何建立 EXECUTE 處理常式。

獨立/中樞
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());
  };

實作 QUERY 處理常式

應用程式中的 QUERY 處理常式會處理使用者要求,並使用 Local Home SDK 回報智慧型裝置的狀態。

Local Home 平台會將與 QUERY「QUERY」意圖相同的要求酬載,傳遞至雲端服務的「QUERY」處理常式函式。同樣地,QUERY 處理常式會以與處理 QUERY 意圖時相同的格式傳回資料。

將指令傳送至中樞裝置後方的裝置

如要控制中樞控制器後方的終端裝置,您可能需要在傳送至中樞控制器的通訊協定專屬指令酬載中提供額外資訊,中樞控制器才能識別指令的目標裝置。在某些情況下,這可直接從 device.id 值推斷,但如果不是這樣,您應將這項額外資料納入 customData 欄位。

如果您使用 TypeScript 建立應用程式,請記得將應用程式編譯為 JavaScript。您可以選擇使用模組系統來編寫程式碼。確認 Chrome 瀏覽器支援目標裝置。