1. 事前準備
智慧住宅整合功能可讓 Google 助理控制使用者住家中的連結裝置。如要建構雲端對雲端整合,您必須提供可處理智慧住宅意圖的雲端 Webhook 端點。舉例來說,當使用者說出「Ok Google,開燈」時,Google 助理會將指令傳送至雲端服務,更新裝置狀態。
Local Home SDK 會新增本機路徑,將智慧型住宅意圖直接轉送至 Google Home 裝置,藉此提升智慧型住宅整合程度,並提高處理使用者指令的可靠性及縮短延遲時間。您可以使用 TypeScript 或 JavaScript 編寫及部署本機履行應用程式,在任何 Google Home 智慧音箱或 Google Nest 智慧螢幕上識別裝置並執行指令。然後,應用程式會使用現有的標準通訊協定,透過區域網路直接與使用者的現有智慧型裝置通訊,以執行指令。
必要條件
建構項目
在本程式碼研究室中,您將部署先前使用 Firebase 建構的智慧住宅整合功能,然後在開發人員控制台中套用掃描設定,並使用 TypeScript 建構本機應用程式,將以 Node.js 編寫的指令傳送至虛擬洗衣機裝置。
課程內容
- 如何在管理中心啟用及設定店內取貨功能。
- 如何使用 Local Home SDK 編寫本機執行應用程式。
- 如何對 Google Home 音箱或 Google Nest 智慧螢幕上載入的本機履行應用程式進行偵錯。
軟硬體需求
- 最新版 Google Chrome
- 已安裝 Google Home 應用程式的 iOS 或 Android 裝置
- Google Home 智慧音箱或 Google Nest 智慧螢幕
- Node.js 10.16 以上版本
- Google 帳戶
- Google Cloud 帳單帳戶
2. 開始使用
啟用活動控制項
如要使用 Google 助理,必須與 Google 分享特定活動資料。Google 助理需要這項資料才能正常運作,但分享資料的要求並非 SDK 專屬。如要分享這項資料,請建立 Google 帳戶 (如果還沒有的話)。您可以使用任何 Google 帳戶,不一定要是開發人員帳戶。
開啟要與 Google 助理搭配使用的 Google 帳戶的活動控制項頁面。
確認已啟用下列切換鈕:
- 網路和應用程式活動 - 此外,請務必勾選「包括 Chrome 歷史記錄,以及採用 Google 服務的網站、應用程式和裝置上的活動記錄」核取方塊。
- 裝置資訊
- 語音和音訊活動
建立雲端對雲端整合專案
- 前往開發人員控制台。
- 按一下「建立專案」,輸入專案名稱,然後按一下「建立專案」。
選取「雲端對雲端整合」
在開發人員控制台的「專案首頁」中,選取「雲端對雲端」下方的「新增雲端對雲端整合」。
安裝 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 程式庫」畫面中,按一下「啟用」。
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 APIupdateDevice
:虛擬裝置用來觸發「回報狀態」的端點
連結至 Firebase
前往 app-start
目錄,然後使用 Cloud-to-Cloud 整合專案設定 Firebase CLI:
cd app-start firebase use <project-id>
設定 Firebase 專案
初始化 Firebase 專案。
firebase init
選取 CLI 功能、「Realtime Database」和「Functions」功能。
? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed) >( ) Data Connect: Set up a Firebase Data Connect service ( ) Firestore: Configure security rules and indexes files for Firestore ( ) Genkit: Setup a new Genkit project with Firebase (*) Functions: Configure a Cloud Functions directory and its files ( ) App Hosting: Configure an apphosting.yaml file for App Hosting ( ) Hosting: Configure files for Firebase Hosting and (optionally) 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 (*) Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance ( ) Data Connect: Set up a Firebase Data Connect service ( ) Firestore: Configure security rules and indexes files for Firestore
這會為專案初始化必要的 API 和功能。
系統提示時,請初始化 Realtime Database。您可以沿用資料庫執行個體的預設位置。
? 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.js 和 package.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
如果不小心啟用了 ESLint,可以透過以下兩種方法停用:
- 使用 GUI 前往專案底下的
../functions
資料夾,選取隱藏檔案.eslintrc.js
並刪除。請勿與名稱相似的.eslintrc.json
混淆。 - 使用指令列:
cd functions rm .eslintrc.js
為確保 Firebase 設定正確無誤,請將 washer-done
目錄中的 firebase.json
檔案複製到 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
),即可查看網頁應用程式。您會看到下列介面:
這個網頁版 UI 代表第三方平台,可查看或修改裝置狀態。如要開始在資料庫中填入裝置資訊,請按一下「更新」。頁面上不會顯示任何變更,但洗衣機的目前狀態會儲存在資料庫中。
現在請使用開發人員控制台,將部署的雲端服務連結至 Google 助理。
設定開發人員控制台專案
在「開發」分頁中,為互動新增「顯示名稱」。這個名稱會顯示在 Google Home 應用程式中。
在「應用程式品牌宣傳」下方,上傳大小為 144 x 144 像素且名為
的應用程式圖示 png
檔案。
如要啟用帳戶連結,請使用下列帳戶連結設定:
用戶端 ID |
|
用戶端密碼 |
|
驗證網址 |
|
權杖網址 |
|
在「Cloud fulfillment URL」(雲端服務網址) 下方,輸入雲端函式的網址,該函式會提供智慧住宅意圖的服務。
https://us-central1-<project-id>.cloudfunctions.net/smarthome
按一下「儲存」儲存專案設定,然後按一下「下一步:測試」,在專案中啟用測試。
現在可以開始實作必要的 Webhook,將裝置狀態與 Google 助理連結。
連結至 Google 助理
如要測試雲端對雲端整合,您必須將專案連結至 Google 帳戶。這樣一來,只要登入相同帳戶,就能透過 Google 助理介面和 Google Home 應用程式進行測試。
- 在手機上開啟 Google 助理設定。請注意,您登入的帳戶應與控制台中的帳戶相同。
- 依序前往「Google 助理」>「設定」>「居家控制」 (位於「Google 助理」下方)。
- 按一下右上方的搜尋圖示。
- 使用 [test] 前置字元搜尋測試應用程式,找出特定測試應用程式。
- 選取該項目。Google 助理接著會向服務進行驗證,並傳送
SYNC
要求,請服務提供使用者的裝置清單。
開啟 Google Home 應用程式,確認是否能看到洗衣機裝置。
確認你可以在 Google Home 應用程式中使用語音指令控制洗衣機,且雲端服務前端網頁 UI 應會顯示裝置狀態變更。
現在可以開始在整合作業中新增店內取貨功能。
4. 更新雲端履行狀態
如要支援本機出貨,您需要在雲端 SYNC
回應中新增名為 otherDeviceIds
的裝置專屬欄位,其中包含裝置的專屬本機 ID。這個欄位也會指出是否能在本機控制該裝置。
在 SYNC
回應中新增 otherDeviceIds
欄位,如以下程式碼片段所示:
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
部署完成後,請前往網頁使用者介面,然後點選工具列中的「重新整理」 按鈕。這會觸發「要求同步」作業,讓 Google 助理接收更新後的
SYNC
回應資料。
5. 設定店內取貨功能
在本節中,您將為雲端整合加入本機出貨的必要設定選項。開發期間,您會將本機服務應用程式發布至 Firebase Hosting,Google Home 裝置可存取並下載該應用程式。
在 Google Home 開發人員控制台中,前往畫面左側的「專案」>「雲端對雲端」,然後選取整合的「編輯」。在「設定與配置」頁面中,捲動至「店內取貨」,然後開啟這項設定。在每個測試網址欄位中輸入下列網址,插入專案 ID,然後按一下「儲存」:
https://<project-id>.web.app/local-home/index.html
接下來,我們需要定義 Google Home 裝置應如何探索本機智慧型裝置。Local Home 平台支援多種裝置探索通訊協定,包括 mDNS、UPnP 和 UDP 廣播。您將使用 UDP 廣播來探索智慧洗衣機。
按一下「裝置探索」下方的「+ 新增掃描設定」,即可新增掃描設定。選取 UDP 做為通訊協定,然後填寫下列屬性:
欄位 | 說明 | 建議值 |
探索地址 | UDP 探索位址 |
|
廣播通訊埠 | Google Home 傳送 UDP 廣播的通訊埠 |
|
監聽通訊埠 | Google Home 監聽回應的通訊埠 |
|
探索封包 | UDP 廣播資料酬載 |
|
最後,按一下視窗底部的「儲存」,即可發布變更。
6. 實作店面取貨功能
您將使用 Local Home SDK 型別套件,以 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
類別。範例專案會附加 IDENTIFY
和 EXECUTE
意圖的處理常式,然後呼叫 listen()
方法,通知 Local Home SDK 應用程式已準備就緒。
新增 IDENTIFY 處理常式
當 Google 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
值相符,這會將裝置標示為可在使用者住家圖表中進行店面取貨。Google 找到相符裝置後,該裝置就會視為已驗證,可進行店面取貨。
新增 EXECUTE 處理常式
當支援本機執行的裝置收到指令時,Local Home SDK 會觸發 EXECUTE
處理常式。本機意圖的內容等同於傳送至雲端服務的 EXECUTE
意圖,因此本機處理意圖的邏輯與在雲端處理意圖的方式類似。
應用程式可以使用 TCP/UDP Socket 或 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 Hosting,以便從 Google Home 裝置存取這些檔案。
firebase deploy --only hosting
7. 啟動智慧型洗衣機
現在可以測試本機執行要求應用程式與智慧洗衣機之間的通訊!本程式碼研究室的入門專案包含以 Node.js 編寫的虛擬智慧洗衣機,可模擬使用者在本機控制的智慧洗衣機。
設定裝置
您必須設定虛擬裝置,使用您在開發人員控制台中套用至裝置探索掃描設定的相同 UDP 參數。此外,您還需要告知虛擬裝置要回報哪個本機裝置 ID,以及裝置狀態變更時,雲端對雲端整合要用於「回報狀態」事件的專案 ID。
參數 | 建議值 |
deviceId |
|
discoveryPortOut |
|
discoveryPacket |
|
projectId | 雲端對雲端整合的專案 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 開發人員工具
如要將偵錯工具連線至本機履行應用程式,請按照下列步驟操作:
- 確認你已將 Google Home 裝置連結至具備 開發人員控制台專案存取權的使用者。
- 重新啟動 Google Home 裝置,這樣裝置就能取得 HTML 的網址,以及您在開發人員控制台中輸入的掃描設定。
- 在開發機器上啟動 Chrome。
- 開啟新的 Chrome 分頁,然後在網址欄位中輸入
chrome://inspect
,啟動檢查器。
網頁上應會顯示裝置清單,而應用程式網址則會顯示在 Google Home 裝置名稱下方。
啟動檢查器
按一下應用程式網址下方的「檢查」,啟動 Chrome 開發人員工具。選取「控制台」分頁,確認您可以看到 TypeScript 應用程式列印的 IDENTIFY
意圖內容。
這項輸出內容表示本機執行要求應用程式已成功探索及識別虛擬裝置。
測試本機履行
使用 Google Home 應用程式中的觸控設定,或透過語音指令控制 Google Home 裝置,例如:
「Ok Google,開啟洗衣機。」
「Ok Google,啟動洗衣機。」
「Ok Google,停止洗衣機。」
這應該會觸發平台將 EXECUTE
意圖傳送至 TypeScript 應用程式。
確認每次發出指令後,都能看到智慧洗衣機的狀態變化。
... ***** The washer is RUNNING ***** ... ***** The washer is STOPPED *****
9. 恭喜
恭喜!您已使用 Local Home SDK,將本機執行功能整合至雲端對雲端整合服務。
瞭解詳情
你也可以嘗試下列做法:
- 變更掃描設定,讓掃描作業正常運作。例如,嘗試使用其他 UDP 連接埠或探索封包。
- 修改虛擬智慧型裝置的程式碼集,在嵌入式裝置 (例如 Raspberry Pi) 上執行,並使用 LED 或螢幕顯示目前狀態。