1. 事前準備
物聯網 (IoT) 開發人員可以建構雲端到雲端整合,讓使用者透過 Google Home 應用程式中的觸控控制選項,以及 Google 助理的語音指令控制裝置。
雲端對雲端整合功能會使用住家圖表提供住家和裝置的相關資料,建立住家的邏輯地圖。有了這個情境資訊,Google 助理就能更自然地瞭解使用者在住家中的位置,並根據這個位置處理使用者的請求。舉例來說,Home Graph 可以儲存客廳的概念,其中包含不同製造商的多種裝置,例如溫度控制器、燈具、風扇和吸塵器。
必要條件
- 建立雲端到雲端整合 開發人員指南
建構項目
在本程式碼研究室中,您將發布可管理虛擬智慧型洗衣機的雲端服務,然後建構雲端到雲端整合,並將其連結至 Google 助理。
課程內容
- 如何部署智慧型住宅雲端服務
- 如何將服務連結至 Google 助理
- 如何將裝置狀態變更發布給 Google
軟硬體需求
- 網路瀏覽器,例如 Google Chrome
- 已安裝 Google Home 應用程式的 iOS 或 Android 裝置
- Node.js 10.16 以上版本
- 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
3. 執行範例應用程式
設定開發環境後,您可以部署範例專案,確認所有設定皆正確無誤。
取得原始碼
按一下下方連結,即可在開發機器上下載這個程式碼研究室的範例:
您也可以從指令列複製 GitHub 存放區:
git clone https://github.com/google-home/smarthome-washer.git
關於專案
這個範例專案包含下列子目錄:
public:
前端 UI,可輕鬆控制及監控智慧型洗衣機的狀態。functions:
透過 Cloud Functions for Firebase 和 Firebase 即時資料庫,管理智慧洗衣機的完整雲端服務。
建立 Firebase 專案
- 前往 Firebase。
- 按一下「建立專案」,然後輸入專案名稱。
- 勾選同意方塊,然後按一下「繼續」。如果沒有同意聲明核取方塊,您可以略過這個步驟。
- 建立 Firebase 專案後,請找出專案 ID。前往「Project Overview」(專案總覽),然後依序按一下設定圖示 >「Project Settings」(專案設定)。
- 專案會列在「General」分頁下方。
連結至 Firebase
前往 washer-start
目錄,然後設定 Firebase CLI 與整合專案:
cd washer-start firebase use <firebase-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.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
最後,請設定代管設定,以便使用專案程式碼中的 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,可以透過兩種方法停用:
- 使用 GUI 前往專案底下的
../functions
資料夾,選取隱藏的檔案.eslintrc.js
並刪除。請勿將其與名稱相似的.eslintrc.json
搞混。 - 使用指令列:
cd functions rm .eslintrc.js
在 washer-done/firebase.json
檔案中,使用以下程式碼完成程式碼:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
"headers": [{
"source" : "**/*.@(js|html)",
"headers" : [ {
"key" : "Cache-Control",
"value" : "max-age=0"
} ]
}],
"functions": [
{
"source": "functions",
"codebase": "default",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log",
"*.local"
]
}
]
}
部署至 Firebase
您已安裝依附元件並設定專案,現在可以首次執行應用程式了。
firebase deploy
您應該會看到下列控制台輸出內容:
... ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/<firebase-project-id>/overview Hosting URL: https://<firebase-project-id>.web.app
這個指令會部署網頁應用程式,以及幾個 Cloud Functions for Firebase。
在瀏覽器 (https://
) 中開啟主機網址,即可查看網路應用程式。您會看到下列介面:
這個網頁版 UI 代表第三方平台,可用於查看或修改裝置狀態。如要開始在資料庫中填入裝置資訊,請按一下「更新」。您不會在頁面上看到任何變更,但洗衣機的目前狀態會儲存在資料庫中。
現在,請使用 Google Home 開發人員控制台,將已部署的雲端服務連結至 Google 助理。
設定開發人員控制台專案
在「開發」分頁中,為互動新增「顯示名稱」。這個名稱會顯示在 Google Home 應用程式中。
在「應用程式品牌」下方,上傳應用程式圖示的 png
檔案,大小為 144 x 144 像素,並命名為
。
如要啟用帳戶連結功能,請使用下列帳戶連結設定:
用戶端 ID |
|
用戶端密碼 |
|
驗證網址 |
|
符記網址 |
|
在「雲端執行網址」下方,輸入提供智慧家庭意圖執行功能的雲端函式網址。
https://us-central1-
按一下「儲存」儲存專案設定,然後點選「下一步:測試」,啟用專案測試功能。
您現在可以開始實作用於將裝置狀態連結至 Google 助理的 webhook。
4. 建立洗滌器
整合設定完成後,您可以新增裝置並傳送資料。您的雲端服務需要處理下列意圖:
- 當 Google 助理想知道使用者已連結哪些裝置時,就會產生
SYNC
意圖。使用者連結帳戶時,這會傳送至您的服務。您應以 JSON 酬載回應所有使用者的裝置和功能。 - 當 Google 助理想瞭解裝置目前的狀態時,就會產生
QUERY
意圖。您應該回傳 JSON 酬載,其中包含每個要求裝置的狀態。 - 當 Google 助理要代替使用者控制裝置時,就會發生
EXECUTE
意圖。您應以 JSON 酬載回應,其中包含每個要求裝置的執行狀態。 - 當使用者取消連結帳戶與 Google 助理時,就會觸發
DISCONNECT
意圖。您應停止將此使用者裝置的事件傳送至 Google 助理。
您將在後續章節中更新先前部署的函式,以便處理這些意圖。
更新 SYNC 回應
開啟 functions/index.js
,其中包含用來回應 Google 助理要求的程式碼。
您必須傳回裝置中繼資料和功能,才能處理 SYNC
意圖。更新 onSync
陣列中的 JSON,納入洗衣機的裝置資訊和建議特徵。
index.js
app.onSync((body) => {
return {
requestId: body.requestId,
payload: {
agentUserId: USER_ID,
devices: [{
id: 'washer',
type: 'action.devices.types.WASHER',
traits: [
'action.devices.traits.OnOff',
'action.devices.traits.StartStop',
'action.devices.traits.RunCycle',
],
name: {
defaultNames: ['My Washer'],
name: 'Washer',
nicknames: ['Washer'],
},
deviceInfo: {
manufacturer: 'Acme Co',
model: 'acme-washer',
hwVersion: '1.0',
swVersion: '1.0.1',
},
willReportState: true,
attributes: {
pausable: true,
},
}],
},
};
});
部署至 Firebase
使用 Firebase CLI 部署更新版雲端執行要求:
firebase deploy --only functions
連結至 Google 助理
如要測試雲端到雲端整合功能,您必須將專案連結至 Google 帳戶。這樣一來,您就能透過已登入相同帳戶的 Google 助理介面和 Google Home 應用程式進行測試。
- 在手機上開啟 Google 助理設定頁面,請注意,您必須使用與控制台相同的帳戶登入。
- 依序前往「Google 助理」>「設定」>「居家控制」 (位於「Google 助理」下方)。
- 按一下右上方的「搜尋」圖示。
- 使用 [test] 前置字串搜尋測試應用程式,找出特定測試應用程式。
- 選取該項目。接著,Google 助理會向您的服務進行驗證,並傳送
SYNC
要求,要求您的服務為使用者提供裝置清單。
開啟 Google Home 應用程式,確認你能看到洗衣機裝置。
5. 處理指令和查詢
雲端服務現在已正確將洗衣機裝置回報給 Google,您需要新增要求裝置狀態和傳送指令的功能。
處理 QUERY 意圖
QUERY
意圖包含一組裝置。請針對每部裝置回覆其目前的狀態。
在 functions/index.js
中編輯 QUERY
處理常式,以便處理意圖要求中包含的目標裝置清單。
index.js
app.onQuery(async (body) => {
const {requestId} = body;
const payload = {
devices: {},
};
const queryPromises = [];
const intent = body.inputs[0];
for (const device of intent.payload.devices) {
const deviceId = device.id;
queryPromises.push(queryDevice(deviceId)
.then((data) => {
// Add response to device payload
payload.devices[deviceId] = data;
}
));
}
// Wait for all promises to resolve
await Promise.all(queryPromises);
return {
requestId: requestId,
payload: payload,
};
});
針對要求中包含的每部裝置,傳回儲存在即時資料庫中的目前狀態。更新 queryFirebase
和 queryDevice
函式,以便傳回洗衣機的狀態資料。
index.js
const queryFirebase = async (deviceId) => {
const snapshot = await firebaseRef.child(deviceId).once('value');
const snapshotVal = snapshot.val();
return {
on: snapshotVal.OnOff.on,
isPaused: snapshotVal.StartStop.isPaused,
isRunning: snapshotVal.StartStop.isRunning,
};
};
const queryDevice = async (deviceId) => {
const data = await queryFirebase(deviceId);
return {
on: data.on,
isPaused: data.isPaused,
isRunning: data.isRunning,
currentRunCycle: [{
currentCycle: 'rinse',
nextCycle: 'spin',
lang: 'en',
}],
currentTotalRemainingTime: 1212,
currentCycleRemainingTime: 301,
};
};
處理 EXECUTE 意圖
EXECUTE
意圖會處理用於更新裝置狀態的指令。回應會傳回每個指令的狀態 (例如 SUCCESS
、ERROR
或 PENDING
) 和新的裝置狀態。
在 functions/index.js
中編輯 EXECUTE
處理程序,處理需要更新的特徵清單,以及每個指令的目標裝置組合:
index.js
app.onExecute(async (body) => {
const {requestId} = body;
// Execution results are grouped by status
const result = {
ids: [],
status: 'SUCCESS',
states: {
online: true,
},
};
const executePromises = [];
const intent = body.inputs[0];
for (const command of intent.payload.commands) {
for (const device of command.devices) {
for (const execution of command.execution) {
executePromises.push(
updateDevice(execution, device.id)
.then((data) => {
result.ids.push(device.id);
Object.assign(result.states, data);
})
.catch(() => functions.logger.error('EXECUTE', device.id)));
}
}
}
await Promise.all(executePromises);
return {
requestId: requestId,
payload: {
commands: [result],
},
};
});
針對每個指令和目標裝置,更新即時資料庫中與要求特徵對應的值。修改 updateDevice
函式,以更新適當的 Firebase 參照,並傳回更新後的裝置狀態。
index.js
const updateDevice = async (execution, deviceId) => {
const {params, command} = execution;
let state; let ref;
switch (command) {
case 'action.devices.commands.OnOff':
state = {on: params.on};
ref = firebaseRef.child(deviceId).child('OnOff');
break;
case 'action.devices.commands.StartStop':
state = params.start
? {isRunning: true, isPaused: false}
: {isRunning: false, isPaused: false};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
case 'action.devices.commands.PauseUnpause':
const data = await queryDevice(deviceId);
state = (data.isPaused === false && data.isRunning === false)
? {isRunning: false, isPaused: false}
: {isRunning: !params.pause, isPaused: params.pause};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
}
return ref.update(state)
.then(() => state);
};
6. 測試整合
實作所有三個意圖後,您可以測試整合功能是否可控制洗衣機。
部署至 Firebase
使用 Firebase CLI 部署更新版雲端執行要求:
firebase deploy --only functions
測試洗衣機
現在,只要透過手機試試下列任一語音指令,即可看到值的變化:
「Ok Google,打開洗衣機。」
「Ok Google,暫停洗衣機。」
「Ok Google,停止洗衣機運作。」
你也可以透過提問,查看洗衣機的目前狀態。
「Ok Google,我的洗衣機是否開啟?」
「Ok Google,洗衣機是否運作中?」
「Ok Google,我的洗衣機正在執行哪個週期?」
您可以在 Firebase 控制台的「Functions」部分,在函式下方顯示的記錄檔中查看這些查詢和指令。如要進一步瞭解 Firebase 記錄,請參閱「寫入及查看記錄」。
您也可以在 Google Cloud 控制台中找到這些查詢和指令,方法是前往「Logging」(記錄) >「Logs Explorer」(記錄檔探索工具)。如要進一步瞭解 Google Cloud 記錄,請參閱「透過 Cloud Logging 存取事件記錄」一文。
7. 向 Google 回報更新內容
您已將雲端服務與智慧型住宅意圖完全整合,讓使用者能夠控制及查詢裝置目前的狀態。不過,實作方式仍缺少一種方法,無法讓服務主動將事件資訊 (例如裝置狀態或狀態變更) 傳送至 Google 助理。
使用「Request Sync」時,如果使用者新增或移除裝置,或是裝置功能有所變更,您就能觸發新的同步處理要求。使用「回報狀態」時,當使用者實際變更裝置狀態 (例如開啟燈具開關),或使用其他服務變更狀態時,雲端服務就能主動將裝置狀態傳送至 Home Graph。
在本節中,您將新增程式碼,從前端網頁應用程式呼叫這些方法。
啟用 HomeGraph API
HomeGraph API 可讓您在使用者的 Home Graph 中儲存及查詢裝置和裝置狀態。如要使用這個 API,您必須先開啟 Google Cloud 控制台,然後啟用 HomeGraph API。
在 Google Cloud 控制台中,請務必選取符合整合項目的專案 <project-id>.
接著,在 HomeGraph API 的「API 程式庫」畫面中,按一下「啟用」。
啟用回報狀態
寫入即時資料庫會觸發入門專案中的 reportstate
函式。更新 functions/index.js
中的 reportstate
函式,擷取寫入資料庫的資料,並透過「回報狀態」發布至 Home Graph。
index.js
exports.reportstate = functions.database.ref('{deviceId}').onWrite(
async (change, context) => {
functions.logger.info('Firebase write event triggered Report State');
const snapshot = change.after.val();
const requestBody = {
requestId: 'ff36a3cc', /* Any unique ID */
agentUserId: USER_ID,
payload: {
devices: {
states: {
/* Report the current state of our washer */
[context.params.deviceId]: {
on: snapshot.OnOff.on,
isPaused: snapshot.StartStop.isPaused,
isRunning: snapshot.StartStop.isRunning,
},
},
},
},
};
const res = await homegraph.devices.reportStateAndNotification({
requestBody,
});
functions.logger.info('Report state response:', res.status, res.data);
});
啟用要求同步功能
重新整理前端網頁 UI 中的圖示,會觸發 Starter 專案中的 requestsync
函式。在 functions/index.js
中實作 requestsync
函式,以便呼叫 HomeGraph API。
index.js
exports.requestsync = functions.https.onRequest(async (request, response) => {
response.set('Access-Control-Allow-Origin', '*');
functions.logger.info(`Request SYNC for user ${USER_ID}`);
try {
const res = await homegraph.devices.requestSync({
requestBody: {
agentUserId: USER_ID,
},
});
functions.logger.info('Request sync response:', res.status, res.data);
response.json(res.data);
} catch (err) {
functions.logger.error(err);
response.status(500).send(`Error requesting sync: ${err}`);
}
});
部署至 Firebase
使用 Firebase CLI 部署更新後的程式碼:
firebase deploy --only functions
測試實作成果
按一下網頁 UI 中的「Refresh」 按鈕,確認 Firebase 控制台記錄中顯示同步處理要求。
接著,請在前端網頁 UI 中調整洗衣機裝置的屬性,然後按一下「Update」。確認您可以在 Firebase 控制台記錄中,看到向 Google 回報的狀態變更。
8. 恭喜
恭喜!你已成功使用雲端到雲端整合功能,將 Google 助理與裝置雲端服務整合。
瞭解詳情
以下是一些可實作並深入瞭解的構想:
您也可以進一步瞭解如何測試及提交整合項目供審查,包括向使用者發布整合項目的認證程序。