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

如要支援區域執行,您必須建構應用程式來處理下列智慧住宅意圖:

  • 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 套件。

使用當地執行應用程式專案初始化工具,透過偏好的套件組合設定啟動適當的專案結構。

專案範本

如要選取 Bundler 設定,請執行 npm init 指令,如以下範例所示:

無

不含 Bundler 設定的 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

TypeScript 搭配 webpack 繫結器設定:

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 子目錄中的 Chrome 執行階段環境,以及 dist/node 子目錄中的 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 處理常式。

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

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

如果 IDENTIFY 回應中的 verificationId 與 SYNC 回應傳回的其中一個 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 處理常式。

Hub
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 意圖時相同。

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

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

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