1. 事前準備
智慧型住宅整合功能可讓 Google 助理控制使用者家中的智慧聯網裝置。如要建構智慧型住宅動作,您必須提供能處理智慧型住宅意圖的 Cloud Webhook 端點。比方說,當使用者說出「Ok Google,開燈」時,Google 助理會將指令傳送至雲端執行要求,藉此更新裝置的狀態。
Local Home SDK 會新增本機路徑,將智慧型住宅意圖直接轉送至 Google Home 裝置,藉此強化智慧型住宅的整合作業,進而提升可靠性並減少處理使用者指令的延遲時間。可讓你使用 TypeScript 或 JavaScript 編寫及部署本機執行要求應用程式,藉此識別裝置,並在任何 Google Home 智慧音箱或 Google Nest 智慧螢幕上執行指令。接著,您的應用程式會使用現有的標準通訊協定執行指令,藉此透過區域網路直接與使用者現有的智慧型裝置通訊。
必要條件
建構項目
在本程式碼研究室中,您將部署先前與 Firebase 建構的智慧型住宅整合,然後在動作控制台中套用掃描設定,並使用 TypeScript 建構本機應用程式,將以 Node.js 編寫的指令傳送至虛擬洗衣裝置。
課程內容
- 如何在 Actions 主控台啟用及設定本機執行要求。
- 如何使用 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 服務的網站、應用程式和裝置中的活動記錄」核取方塊。
- 裝置資訊
- 語音和音訊活動
建立動作專案
- 前往 Actions on Google Developer Console。
- 按一下「新增專案」並輸入專案名稱,然後按一下「建立專案」。
選取智慧型住宅應用程式
在動作控制台的總覽畫面上,選取「智慧型住宅」。
如果選擇「智慧型住宅」體驗資訊卡,系統會將您導向專案控制台。
安裝 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
:為智慧型住宅動作實作雲端執行要求的雲端函式local
:Skeleton 本機執行要求應用程式專案,其中含有虛設意圖處理常式在index.ts
中
提供的 Cloud 執行要求在 index.js
中包含下列函式:
fakeauth
:帳戶連結的授權端點faketoken
:帳戶連結的權杖端點smarthome
:智慧型住宅意圖執行要求端點reportstate
:在裝置狀態變更時叫用 HomeGraph APIupdateDevice
:虛擬裝置用來觸發報告狀態的端點
連結至 Firebase
前往 app-start
目錄,然後搭配 Actions 專案設定 Firebase CLI:
cd app-start firebase use <project-id>
設定 Firebase 專案
初始化 Firebase 專案。
firebase init
選取 CLI 功能、即時資料庫、函式,以及含有 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
如要重新初始化專案,請在系統詢問是否要初始化或覆寫程式碼集時,選取「Overwrite」。
? 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
如要重新初始化專案,請在系統詢問是否要初始化或覆寫 function/.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,您可以透過兩種方法停用 ESLint:
- 使用 GUI 前往專案下方的
../functions
資料夾,選取隱藏的檔案.eslintrc.js
,然後刪除該檔案。請勿將其誤認為.eslintrc.json
名稱類似。 - 使用指令列:
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
) 查看網路應用程式。您會看到以下介面:
此網頁版 UI 代表可查看或修改裝置狀態的第三方平台。如要開始在資料庫中填入裝置資訊,請按一下「更新」。您不會在頁面上看到任何變更,但洗衣機目前的狀態將儲存在資料庫中。
現在可以使用 Actions 主控台將部署的雲端服務連結至 Google 助理了。
設定 Actions 主控台專案
在「Overview」>「Build your Action」下方,選取「Add Action(s)」。輸入可為智慧型住宅意圖提供執行要求的 Cloud 函式網址,然後按一下「Save」。
https://us-central1-<project-id>.cloudfunctions.net/smarthome
在「開發」>「叫用」分頁中,為動作新增「顯示名稱」,然後按一下「儲存」。這個名稱會顯示在 Google Home 應用程式中。
如要啟用帳戶連結功能,請在左側導覽面板中依序選取「開發」>「帳戶連結」選項。使用下列帳戶連結設定:
用戶端 ID |
|
用戶端密碼 |
|
驗證網址 |
|
權杖網址 |
|
按一下「儲存」儲存帳戶連結設定,然後點選「測試」為專案啟用測試功能。
系統會將您重新導向至模擬工具。將滑鼠遊標移到「測試裝置」() 圖示上,確認專案已啟用測試功能。
連結至 Google 助理
如要測試智慧型住宅動作,你必須將專案連結至 Google 帳戶。如此一來,即可透過已登入相同帳戶的 Google 助理介面和 Google Home 應用程式進行測試。
- 在手機上開啟 Google 助理設定。請注意,您登入的帳戶必須與控制台相同。
- 依序前往「Google 助理」>「設定」>「居家控制系統」 (位於「Google 助理」下方)。
- 按一下右上方的「搜尋」圖示。
- 使用 [test] 前置字串搜尋測試版應用程式,找出特定的測試應用程式。
- 選取該項目。接著,Google 助理會向您的服務進行驗證並傳送
SYNC
要求,要求您的服務為使用者提供裝置清單。
開啟 Google Home 應用程式,確認可看到你的洗衣裝置。
請確認可以在 Google Home 應用程式中使用語音指令控制洗衣機。您也應該會在雲端執行要求的前端網頁 UI 中看到裝置狀態變更。
現在可以開始將本機執行要求新增至動作。
4. 更新 Cloud 執行要求
如要支援本機執行要求,您必須在 Cloud 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」按鈕 。這會觸發要求同步處理作業,讓 Google 助理接收更新後的 SYNC
回應資料。
5. 設定本機執行要求
在本節中,您將為智慧型住宅動作新增「本機執行要求」的必要設定選項。在開發期間,您會將本機執行要求應用程式發布至 Firebase 託管,以便 Google Home 裝置存取及下載。
在 Actions 控制台,依序選取「開發」 >「動作」,然後找出「設定本機 Home SDK」部分。在測試網址欄位中輸入下列網址,插入您的專案 ID,然後按一下「儲存」:
https://<project-id>.web.app/local-home/index.html
接下來,我們需要定義 Google Home 裝置應如何探索本機智慧型裝置。本地首頁平台支援多種用來探索裝置的通訊協定,包括 mDNS、UPnP 和 UDP 廣播。屆時您將會使用 UDP 廣播來探索智慧型洗衣機。
按一下「Device Scanner configuration」(裝置掃描設定) 下方的「NewScan config」(新增掃描設定),即可新增掃描設定。選取 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 裝置根據 Actions 主控台提供的掃描設定,在區域網路上偵測到未經驗證的裝置時,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 編寫的 v 實驗性智慧型洗衣機,可模擬使用者可在本機控制的智慧洗衣機。
設定裝置
您必須設定虛擬裝置,以便使用您在動作控制台中為掃描裝置套用的 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 裝置連結至具備 Actions 控制台專案存取權的使用者。
- 重新啟動 Google Home 裝置,取得 HTML 網址和您在「動作」控制台中儲存的掃描設定。
- 在開發電腦上啟動 Chrome。
- 開啟新的 Chrome 分頁,並在網址欄位中輸入
chrome://inspect
,即可啟動檢查器。
頁面上會出現裝置清單,您的應用程式網址應該會顯示在 Google Home 裝置的名稱下方。
啟動檢查器
按一下應用程式網址下方的「檢查」,啟動 Chrome 開發人員工具。選取「Console」分頁標籤,並確認您可以看到 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 或螢幕以視覺化方式呈現目前的狀態。