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

如要支援本地執行要求,您必須建構應用程式來處理下列智慧型家居意圖:

  • 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 設定啟動適當的專案結構。

專案範本

如要選取 Bundler 設定,請執行 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 來源,並將應用程式與其依附元件,針對 Chrome 執行階段環境 (位於 dist/web 子目錄) 和 Node.js 執行階段環境 (位於 dist/node 子目錄) 進行封裝。

驗證
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 回應中的 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 (也就是 Hub) 裝置可存取的其他裝置 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 透過現有通訊協定存取智慧型裝置。

本機 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 瀏覽器支援目標。