歡迎使用 Google Home 開發人員中心,探索全新功能,瞭解如何開發智慧住宅動作。注意:請繼續在「動作」控制台中建立動作。

實作本地出貨應用程式

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

如要支援本機執行要求,您必須建構應用程式來處理這些智慧型住宅意圖:

  • 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 整合器設定的 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 處理常式。本機主畫面平台將使用您先前指定的掃描設定資訊掃描本機裝置,並呼叫 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。

本地首頁平台的 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 處理常式會處理使用者指令,並使用本機通訊協定,透過本機通訊協定存取智慧型裝置。

本機主畫面平台會將輸入酬載傳送至 EXECUTE 處理常式函式,就像您向雲端執行要求使用的EXECUTE意圖一樣。同樣地,EXECUTE 處理常式會傳回輸出資料,格式與處理 EXECUTE 意圖相同。如要簡化回應建立作業,您可以使用 Local Home SDK 提供的 Execute.Response.Builder 類別。

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

指定指令至裝置時,請使用 SYNC 回應中的裝置 ID (以及 customData 欄位的參數,包括裝置) 與裝置通訊。

範例

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

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

實作 QUERY 處理常式

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

本機首頁平台會將相同的要求酬載傳送至「QUERY」處理常式函式,就像對雲端執行要求一樣的 QUERY 意圖。同樣地,QUERY 處理常式會傳回與處理 QUERY 意圖相同的格式資料。

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

為控制中心後方的裝置,您可能需要在傳送至 Hub 的通訊協定專屬指令酬載中提供其他資訊,讓中心識別指令的目標裝置。在某些情況下,您可以直接從 device.id 值推論而得,但如果是這種情況,則應在 customData 欄位中加入這項額外資料。

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