為雲端到雲端整合啟用本機執行作業

1. 事前準備

智慧型住宅整合功能可讓 Google 助理控制住家中已連結的裝置。如要建構雲端到雲端整合,您必須提供可處理智慧型住宅意圖的雲端 Webhook 端點。舉例來說,當使用者說出「Ok Google,開燈」,Google 助理就會將指令傳送至雲端執行服務,以便更新裝置狀態。

Local Home SDK 會新增本機路徑,直接將智慧型住宅意圖轉送至 Google Home 裝置,藉此提升可靠性,並縮短處理使用者指令的延遲時間,進而提升智慧型住宅整合程度。您可以使用這個 API 在 TypeScript 或 JavaScript 中編寫及部署區域服務應用程式,以便在任何 Google Home 智慧音箱或 Google Nest 智慧螢幕上識別裝置並執行指令。接著,應用程式會透過區域網路,使用現有的標準通訊協定來執行指令,直接與使用者的現有智慧型裝置通訊。

72ffb320986092c.png

必要條件

建構項目

在本程式碼研究室中,您將部署先前透過 Firebase 建構的智慧型家居整合功能,然後在開發人員控制台中套用掃描設定,並使用 TypeScript 建構本機應用程式,將以 Node.js 編寫的命令傳送至虛擬洗衣機裝置。

課程內容

  • 如何在開發人員控制台中啟用及設定本機服務。
  • 如何使用 Local Home SDK 編寫本機執行應用程式。
  • 如何對 Google Home 音箱或 Google Nest 智慧螢幕上載的本機執行服務應用程式進行偵錯。

軟硬體需求

2. 開始使用

啟用活動控制項

如要使用 Google 助理,您必須與 Google 共用特定活動資料。Google 助理需要這項資料才能正常運作,但分享資料的要求並非 SDK 專屬。如要分享這項資料,請先建立 Google 帳戶。您可以使用任何 Google 帳戶,不必是開發人員帳戶。

開啟要與 Google 助理搭配使用的 Google 帳戶的「活動控制項」頁面

確認已啟用下列切換鈕:

  • 網路和應用程式活動:此外,請務必勾選「包括 Chrome 歷史記錄,以及採用 Google 服務的網站、應用程式和裝置中的活動記錄」核取方塊。
  • 裝置資訊
  • 語音和音訊活動

建立雲端到雲端整合專案

  1. 前往開發人員控制台
  2. 按一下「建立專案」,輸入專案名稱,然後按一下「建立專案」

命名專案

選取雲端到雲端整合

在「開發人員控制台」的「專案首頁」中,選取「雲端到雲端」下方的「新增雲端到雲端整合」

新增雲端到雲端整合

安裝 Firebase CLI

Firebase 指令列介面 (CLI) 可讓您在本機上提供網頁應用程式,並將網頁應用程式部署至 Firebase 代管服務。

如要安裝 CLI,請在終端機中執行下列 npm 指令:

npm install -g firebase-tools

如要確認 CLI 安裝正確無誤,請執行以下命令:

firebase --version

請執行以下命令,使用 Google 帳戶授權 Firebase CLI:

firebase login

啟用 HomeGraph API

HomeGraph API 可讓您在使用者的 Home Graph 中儲存及查詢裝置和裝置狀態。如要使用這個 API,您必須先開啟 Google Cloud 控制台,並啟用 HomeGraph API

在 Google Cloud 控制台中,請務必選取與整合項目相符的專案 <project-id>. 接著,在 HomeGraph API 的「API 程式庫」畫面中,按一下「啟用」

5SVCzM8IZLi_9DV8M0nEklv16NXkpvM0bIzQK2hSyKyvnFHBxPOz90rbr72ayxzmxd5aNROOqC_Cp4outbdlwJdObDs0DIE_8vYzw6dovoVrP9IZWlWsZxDS7UHOi1jiRbDMG8MqUA

3. 執行範例應用程式

設定開發環境後,您可以部署範例專案,確認所有設定皆正確無誤。

取得原始碼

按一下下方連結,即可在開發機器上下載這個程式碼研究室的範例:

...或者,您也可以從指令列複製 GitHub 存放區:

git clone https://github.com/google-home/smarthome-local.git

關於專案

範例專案包含下列子目錄:

  • public:用於控制及監控智慧洗衣機的前端網頁 UI
  • functions:為雲端對雲端整合實作雲端執行服務的 Cloud 函式
  • local:在 index.ts 中設定意圖處理常式,並建立本機訂單履行應用程式專案的架構

提供的雲端服務滿足方案包含 index.js 中的下列函式:

  • fakeauth:帳戶連結的授權端點
  • faketoken:帳戶連結的符記端點
  • smarthome:智慧型住宅意圖執行要求端點
  • reportstate:在裝置狀態變更時叫用 HomeGraph API
  • updateDevice:虛擬裝置用來觸發回報狀態的端點

連結至 Firebase

前往 app-start 目錄,然後設定 Firebase CLI 與雲端到雲端整合專案:

cd app-start
firebase use <project-id>

設定 Firebase 專案

初始化 Firebase 專案。

firebase init

選取 CLI 功能、即時資料庫Functions託管功能 (包含 Firebase 託管)。

? Which Firebase CLI features do you want to set up for this directory? Press Space to select features, then
 Enter to confirm your choices.
❯◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◯ Firestore: Configure security rules and indexes files for Firestore
 ◉ Functions: Configure a Cloud Functions directory and its files
 ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◯ Storage: Configure a security rules file for Cloud Storage
 ◯ Emulators: Set up local emulators for Firebase products
 ◯ Remote Config: Configure a template file for Remote Config
 ◯ Extensions: Set up an empty Extensions manifest

這會為專案初始化必要的 API 和功能。

系統提示時,請初始化即時資料庫。您可以使用資料庫執行個體的預設位置。

? It seems like you haven't initialized Realtime Database in your project yet. Do you want to set it up?
Yes

? Please choose the location for your default Realtime Database instance:
us-central1

由於您使用的是入門專案程式碼,請為安全性規則選擇預設檔案,並確保不會覆寫現有的資料庫規則檔案。

? File database.rules.json already exists. Do you want to overwrite it with the Realtime Database Security Rules for <project-ID>-default-rtdb from the Firebase Console?
No

如果您要重新初始化專案,在系統詢問您是否要初始化或覆寫程式碼集時,請選取「覆寫」

? Would you like to initialize a new codebase, or overwrite an existing one?
Overwrite

設定函式時,請使用預設檔案,並確保不會覆寫專案範例中現有的 index.jspackage.json 檔案。

? What language would you like to use to write Cloud Functions?
JavaScript

? Do you want to use ESLint to catch probable bugs and enforce style?
No

? File functions/package.json already exists. Overwrite?
No

? File functions/index.js already exists. Overwrite?
No

如果您要重新初始化專案,在系統詢問是否要初始化或覆寫 functions/.gitignore 時,請選取「否」

? File functions/.gitignore already exists. Overwrite?
No
? Do you want to install dependencies with npm now?
Yes

最後,請設定代管設定,以便使用專案程式碼中的 public 目錄,並使用現有的 index.html 檔案。系統詢問是否要使用 ESLint 時,請選取「否」

? What do you want to use as your public directory?
public

? Configure as a single-page app (rewrite all urls to /index.html)?
Yes

? Set up automatic builds and deploys with GitHub?
No

? File public/index.html already exists. Overwrite?
 No

如果不小心啟用 ESLint,可以透過兩種方法停用:

  1. 使用 GUI 前往專案底下的 ../functions 資料夾,選取隱藏的檔案 .eslintrc.js 並刪除。請勿將其與名稱相似的 .eslintrc.json 搞混。
  2. 使用指令列:
    cd functions
    rm .eslintrc.js
    

為確保您擁有正確且完整的 Firebase 設定,請將 firebase.json 檔案從 washer-done 目錄複製到 washer-start 目錄,並覆寫 washer-start 中的檔案。

washer-start 目錄中:

cp -vp ../washer-done/firebase.json .

部署至 Firebase

您已安裝依附元件並設定專案,現在可以首次執行應用程式了。

firebase deploy

您應該會看到下列控制台輸出內容:

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<project-id>/overview
Hosting URL: https://<project-id>.web.app

這個指令會部署網頁應用程式,以及幾個 Cloud Functions for Firebase

在瀏覽器 (https://<project-id>.web.app) 中開啟代管網址,即可查看網路應用程式。您會看到下列介面:

L60eA7MOnPmbBMl2XMipT9MdnP-RaVjyjf0Y93Y1b7mEyIsqZrrwczE7D3RQISRs-iusL1g4XbNmGhuA6-5sLcWefnczwNJEPfNLtwBsO4Tb9YvcAZBI6_rX19z8rxbik9Vq8F2fwg

這個網頁版 UI 代表第三方平台,可用於查看或修改裝置狀態。如要開始在資料庫中填入裝置資訊,請按一下「更新」。雖然頁面上不會顯示任何變更,但洗衣機的目前狀態會儲存在資料庫中。

接下來,您可以使用開發人員控制台,將已部署的雲端服務連結至 Google 助理。

設定開發人員控制台專案

在「開發」分頁中,為互動新增「顯示名稱」。這個名稱會顯示在 Google Home 應用程式中。

新增顯示名稱

在「應用程式品牌」下方,上傳應用程式圖示的 png 檔案,大小為 144 x 144 像素,並命名為 .png

新增應用程式圖示

如要啟用帳戶連結功能,請使用下列帳戶連結設定:

用戶端 ID

ABC123

用戶端密碼

DEF456

驗證網址

https://us-central1-
.cloudfunctions.net/fakeauth

符記網址

https://us-central1-
.cloudfunctions.net/faketoken

更新帳戶連結網址

在「雲端執行網址」下方,輸入提供智慧家庭意圖執行功能的雲端函式網址。

https://us-central1--cloudfunctions.net/smarthome

新增 Cloud 函式網址

按一下「儲存」儲存專案設定,然後點選「下一步:測試」,啟用專案測試功能。

測試雲端對雲端整合

您現在可以開始實作用於將裝置狀態連結至 Google 助理的 webhook。

如要測試雲端到雲端整合功能,您必須將專案連結至 Google 帳戶。這樣一來,您就能透過已登入相同帳戶的 Google 助理介面和 Google Home 應用程式進行測試。

  1. 在手機上開啟 Google 助理設定頁面,請注意,您必須使用與控制台相同的帳戶登入。
  2. 依序前往「Google 助理」>「設定」>「居家控制」 (位於「Google 助理」下方)。
  3. 按一下右上方的「搜尋」圖示
  4. 使用 [test] 前置字串搜尋測試應用程式,找出特定測試應用程式。
  5. 選取該項目。接著,Google 助理會向您的服務進行驗證,並傳送 SYNC 要求,要求您的服務為使用者提供裝置清單。

開啟 Google Home 應用程式,確認你能看到洗衣機裝置。

XcWmBVamBZtPfOFqtsr5I38stPWTqDcMfQwbBjetBgxt0FCjEs285pa9K3QXSASptw0KYN2G8yfkT0-xg664V4PjqMreDDs-HPegHjOc4EVtReYPu-WKZyygq9Xmkf8X8z9177nBjQ

確認你可以透過 Google Home 應用程式語音指令控制洗衣機。你應該也會在雲端執行服務的前端網頁 UI 中看到裝置狀態變更。

您現在可以開始在整合中加入當地訂單履行服務。

4. 更新雲端執行服務

如要支援本地執行,您必須在雲端 SYNC 回應中新增名為 otherDeviceIds 的裝置專屬欄位,其中包含裝置的專屬本地 ID。這個欄位也會指出是否可在本機控制該裝置。

otherDeviceIds 欄位新增至 SYNC 回應,如以下程式碼片段所示:

functions/index.js

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '123',
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [ ... ],
        name: { ... },
        deviceInfo: { ... },
        willReportState: true,
        attributes: {
          pausable: true,
        },
        otherDeviceIds: [{
          deviceId: 'deviceid123',
        }],
      }],
    },
  };
});

將更新後的專案部署至 Firebase:

firebase deploy --only functions

部署完成後,前往網頁 UI,然後按一下工具列中的「Refresh」 ae8d3b25777a5e30.png 按鈕。這會觸發「Request Sync」作業,讓 Google 助理接收更新的 SYNC 回應資料。

bf4f6a866160a982.png

5. 設定本機執行要求

在本節中,您將為雲端到雲端整合作業新增必要的本機訂單履行設定選項。開發期間,您會將本機執行服務應用程式發布至 Firebase 代管服務,讓 Google Home 裝置存取及下載該應用程式。

Google Home 開發人員控制台中,前往畫面左側的「Project」>「Cloud-to-cloud」,然後選取要整合的「Edit」。在「設定和配置」頁面中,捲動至「本機訂單履行」,然後將設定切換為開啟。在每個測試網址欄位中輸入下列網址、插入專案 ID,然後按一下「儲存」

https://<project-id>.web.app/local-home/index.html

local-fulfillment.png

接下來,我們需要定義 Google Home 裝置如何探索本機智慧型裝置。Local Home 平台支援多種裝置探索通訊協定,包括 mDNS、UPnP 和 UDP 廣播。您將使用 UDP 廣播來探索智慧洗衣機。

按一下「Device discovery」下方的「+ Add scan configuration」,新增掃描設定。選取 UDP 做為通訊協定,然後填寫下列屬性:

欄位

說明

建議值

探索地址

UDP 探索位址

255.255.255.255

廣播埠

Google Home 傳送 UDP 廣播的通訊埠

3311

監聽通訊埠

Google Home 用來監聽回應的通訊埠

3312

探索封包

UDP 廣播資料酬載

48656c6c6f4c6f63616c486f6d6553444b

device-discovery.png

最後,按一下視窗底部的「儲存」,即可發布變更。

6. 實作本機執行要求

您將使用 Local Home SDK typings 套件,以 TypeScript 開發本機服務應用程式。請查看範例專案中提供的骨架:

local/index.ts

/// <reference types="@google/local-home-sdk" />

import App = smarthome.App;
import Constants = smarthome.Constants;
import DataFlow = smarthome.DataFlow;
import Execute = smarthome.Execute;
import Intents = smarthome.Intents;
import IntentFlow = smarthome.IntentFlow;

...

class LocalExecutionApp {

  constructor(private readonly app: App) { }

  identifyHandler(request: IntentFlow.IdentifyRequest):
      Promise<IntentFlow.IdentifyResponse> {
    // TODO: Implement device identification
  }

  executeHandler(request: IntentFlow.ExecuteRequest):
      Promise<IntentFlow.ExecuteResponse> {
    // TODO: Implement local fulfillment
  }

  ...
}

const localHomeSdk = new App('1.0.0');
const localApp = new LocalExecutionApp(localHomeSdk);
localHomeSdk
  .onIdentify(localApp.identifyHandler.bind(localApp))
  .onExecute(localApp.executeHandler.bind(localApp))
  .listen()
  .then(() => console.log('Ready'))
  .catch((e: Error) => console.error(e));

本機執行的核心元件是 smarthome.App 類別。範例專案會為 IDENTIFYEXECUTE 意圖附加處理程序,然後呼叫 listen() 方法,通知 Local Home SDK 應用程式已就緒。

新增 IDENTIFY 處理常式

當 Google Home 裝置根據開發人員工作室提供的掃描設定,在區域網路上發現未經驗證的裝置時,Local Home SDK 會觸發 IDENTIFY 處理常式。

同時,當 Google 發現相符的裝置時,平台會使用掃描結果資料叫用 identifyHandler。在應用程式中,掃描作業會使用 UDP 廣播執行,而提供給 IDENTIFY 處理常式的掃描資料會包含本機裝置傳送的回應酬載。

這個處理程序會傳回 IdentifyResponse 例項,其中包含本機裝置的專屬 ID。將下列程式碼新增至 identifyHandler 方法,以便處理來自本機裝置的 UDP 回應,並判斷適當的本機裝置 ID:

local/index .ts

identifyHandler(request: IntentFlow.IdentifyRequest):
    Promise<IntentFlow.IdentifyResponse> {
  console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));

  const scanData = request.inputs[0].payload.device.udpScanData;
  if (!scanData) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_request', 'Invalid scan data');
    return Promise.reject(err);
  }

  // In this codelab, the scan data contains only local device ID.
  const localDeviceId = Buffer.from(scanData.data, 'hex');

  const response: IntentFlow.IdentifyResponse = {
    intent: Intents.IDENTIFY,
    requestId: request.requestId,
    payload: {
      device: {
        id: 'washer',
        verificationId: localDeviceId.toString(),
      }
    }
  };
  console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));

  return Promise.resolve(response);
}

請注意,verificationId 欄位必須與 SYNC 回應中的 otherDeviceIds 值相符,這會在使用者的 Home Graph 中標示裝置可用於本地執行。Google 找到相符項目後,就會將該裝置視為已驗證,並可進行店面提貨。

新增 EXECUTE 處理常式

當支援本機執行的裝置收到指令時,Local Home SDK 會觸發 EXECUTE 處理常式。本機意圖的內容等同於傳送至雲端執行服務的 EXECUTE 意圖,因此在本機處理意圖的邏輯與在雲端處理時的處理方式相似。

應用程式可以使用 TCP/UDP 套接字或 HTTP(S) 要求,與本機裝置通訊。在本程式碼研究室中,HTTP 是用來控制虛擬裝置的通訊協定。在 index.ts 中,通訊埠號碼會定義為 SERVER_PORT 變數。

將下列程式碼新增至 executeHandler 方法,以便處理傳入的指令,並透過 HTTP 將指令傳送至本機裝置:

local/index.ts

executeHandler(request: IntentFlow.ExecuteRequest):
    Promise<IntentFlow.ExecuteResponse> {
  console.log("EXECUTE intent: " + JSON.stringify(request, null, 2));

  const command = request.inputs[0].payload.commands[0];
  const execution = command.execution[0];
  const response = new Execute.Response.Builder()
    .setRequestId(request.requestId);

  const promises: Array<Promise<void>> = command.devices.map((device) => {
    console.log("Handling EXECUTE intent for device: " + JSON.stringify(device));

    // Convert execution params to a string for the local device
    const params = execution.params as IWasherParams;
    const payload = this.getDataForCommand(execution.command, params);

    // Create a command to send over the local network
    const radioCommand = new DataFlow.HttpRequestData();
    radioCommand.requestId = request.requestId;
    radioCommand.deviceId = device.id;
    radioCommand.data = JSON.stringify(payload);
    radioCommand.dataType = 'application/json';
    radioCommand.port = SERVER_PORT;
    radioCommand.method = Constants.HttpOperation.POST;
    radioCommand.isSecure = false;

    console.log("Sending request to the smart home device:", payload);

    return this.app.getDeviceManager()
      .send(radioCommand)
      .then(() => {
        const state = {online: true};
        response.setSuccessState(device.id, Object.assign(state, params));
        console.log(`Command successfully sent to ${device.id}`);
      })
      .catch((e: IntentFlow.HandlerError) => {
        e.errorCode = e.errorCode || 'invalid_request';
        response.setErrorState(device.id, e.errorCode);
        console.error('An error occurred sending the command', e.errorCode);
      });
  });

  return Promise.all(promises)
    .then(() => {
      return response.build();
    })
    .catch((e) => {
      const err = new IntentFlow.HandlerError(request.requestId,
          'invalid_request', e.message);
      return Promise.reject(err);
    });
}

編譯 TypeScript 應用程式

前往 local/ 目錄,然後執行下列指令,下載 TypeScript 編譯器並編譯應用程式:

cd local
npm install
npm run build

這會編譯 index.ts (TypeScript) 來源,並將下列內容放入 public/local-home/ 目錄:

  • bundle.js:包含本機應用程式和依附元件的編譯 JavaScript 輸出內容。
  • index.html:本機代管頁面,用於提供應用程式,以便在裝置上進行測試。

部署測試專案

將更新後的專案檔案部署至 Firebase 託管,以便透過 Google Home 裝置存取這些檔案。

firebase deploy --only hosting

7. 啟動智慧型洗衣機

接下來,您可以測試本機執行要求應用程式和智慧洗衣機之間的通訊。程式碼研究室的入門專案包含以 Node.js 編寫的虛擬智慧型洗衣機,模擬使用者可在本機控制的智慧型洗衣機。

設定裝置

您必須設定虛擬裝置,讓裝置探索功能在開發人員控制台中使用與掃描設定相同的 UDP 參數。此外,您需要告訴虛擬裝置要回報哪個本機裝置 ID,以及在裝置狀態變更時,要使用哪個雲端到雲端整合的專案 ID 來回報「Report State」事件。

參數

建議值

deviceId

deviceid123

discoveryPortOut

3311

discoveryPacket

HelloLocalHomeSDK

projectId

Cloud-to-cloud 整合作業的專案 ID

啟動裝置

前往 virtual-device/ 目錄並執行裝置指令碼,將設定參數做為引數傳遞:

cd virtual-device
npm install
npm start -- \
  --deviceId=deviceid123 --projectId=<project-id> \
  --discoveryPortOut=3311 --discoveryPacket=HelloLocalHomeSDK

確認裝置指令碼是否以預期的參數執行:

(...): UDP Server listening on 3311
(...): Device listening on port 3388
(...): Report State successful

8. 對 TypeScript 應用程式進行偵錯

在下一個部分,您將驗證 Google Home 裝置是否能透過區域網路,正確掃描、辨識並傳送指令給虛擬智慧洗衣機。您可以使用 Google Chrome 開發人員工具連線至 Google Home 裝置、查看主控台記錄檔,以及對 TypeScript 應用程式偵錯。

連結 Chrome 開發人員工具

如要將偵錯工具連結至本機執行要求應用程式,請按照下列步驟操作:

  1. 請確認你已將 Google Home 裝置連結至具備 開發人員控制台專案存取權的使用者。
  2. 重新啟動 Google Home 裝置,讓裝置取得 HTML 的網址,以及你在開發人員控制台中放置的掃描設定。
  3. 在開發機器上啟動 Chrome。
  4. 開啟新的 Chrome 分頁,然後在網址欄中輸入 chrome://inspect,即可啟動檢查器。

頁面上會顯示裝置清單,應用程式網址則會顯示在 Google Home 裝置名稱下方。

567f97789a7d8846.png

啟動檢查器

按一下應用程式網址下方的「Inspect」,即可啟動 Chrome 開發人員工具。選取「控制台」分頁,確認您可以看到 TypeScript 應用程式列印的 IDENTIFY 意圖內容。

6b67ded470a4c8be.png

這項輸出內容表示您的本機執行要求應用程式已成功偵測及識別虛擬裝置。

測試本地訂單履行

你可以使用 Google Home 應用程式的觸控設定,或是對 Google Home 裝置下達語音指令,向裝置發出指令,例如:

「Ok Google,打開洗衣機。」

「Ok Google,啟動洗衣機。」

「Ok Google,停止洗衣機運作。」

這應該會觸發平台將 EXECUTE 意圖傳送至 TypeScript 應用程式。

bc030517dacc3ac9.png

確認您可以透過每個指令查看當地智慧洗衣機狀態的變化。

...
***** The washer is RUNNING *****
...
***** The washer is STOPPED *****

9. 恭喜

764dbc83b95782a.png

恭喜!您使用 Local Home SDK 將本機執行服務整合至雲端到雲端整合。

瞭解詳情

以下提供其他可嘗試的做法:

  • 變更掃描設定並讓其運作。例如,請嘗試使用其他 UDP 通訊埠或探索封包。
  • 修改虛擬智慧型裝置程式碼庫,以便在嵌入式裝置 (例如 Raspberry Pi) 上執行,並使用 LED 或螢幕來呈現目前狀態。