作为一名物联网 (IoT) 开发者,您可以构建智能家居 Action,让用户能够通过 Google Home 应用中的触控功能和 Google 助理的语音指令来控制设备。
智能家居 Action 依靠 Home Graph 来提供住宅及其设备的环境数据,从而创建住宅逻辑图。借助这些环境数据,Google 助理能够根据用户在住宅中的位置更自然地理解用户请求。例如,Home Graph 可以存储客厅这个概念,而客厅中包含来自不同制造商的多种类型的设备(例如恒温器、灯、风扇和吸尘器)。
前提条件
- 创建智能家居 Action 开发者指南
构建内容
在此 Codelab 中,您将发布一个用于管理虚拟智能洗衣机的云服务,然后构建一个智能家居 Action,并将其关联到 Google 助理。
学习内容
- 如何部署智能家居云服务
- 如何将您的服务关联到 Google 助理
- 如何将设备状态更改发布到 Google
所需条件
- 网络浏览器,例如 Google Chrome
- 安装了 Google Home 应用的 iOS 或 Android 设备
- Node.js 版本 10.16 或更高版本
- Google Cloud 结算帐号
启用活动控件
在您计划与 Google 助理搭配使用的 Google 帐号中启用以下活动控件:
- 网络与应用活动记录
- 设备信息
- 语音和音频活动记录
创建 Actions 项目
- 转到 Actions on Google 开发者控制台。
- 点击 New Project,输入项目名称,然后点击 CREATE PROJECT。
选择智能家居应用
在 Actions 控制台的“Overview”屏幕中,选择 Smart home。
选择 Smart home 体验卡片,然后系统会将您转到项目控制台。
安装 Firebase CLI
借助 Firebase 命令行界面 (CLI),您可以在本地提供 Web 应用,并将您的 Web 应用部署到 Firebase Hosting。
如需安装 CLI,请从终端运行以下 npm 命令:
npm install -g firebase-tools
如需验证 CLI 是否已正确安装,请运行以下命令:
firebase --version
运行以下命令,授权您的 Google 帐号使用 Firebase CLI:
firebase login
开发环境设置完毕后,您可以部署入门级项目以验证所有设置是否已配置正确。
获取源代码
点击以下链接,将此 Codelab 的示例下载到您的开发机器上:
...或者,您也可以通过命令行克隆 GitHub 代码库:
git clone https://github.com/googlecodelabs/smarthome-washer.git
项目简介
入门级项目包含以下子目录:
public:
一种前端界面,可轻松地控制和监控智能洗衣机的状态。functions:
一种已全面实现的云服务,可使用 Cloud Functions for Firebase 和 Firebase Realtime Database 来管理智能洗衣机。
关联到 Firebase
转到 washer-start
目录,然后使用您的 Actions 项目设置 Firebase CLI:
cd washer-start firebase use <project-id>
部署到 Firebase
转到 functions
文件夹,然后使用 npm.
安装所有必要的依赖项
cd functions npm install
依赖性安装完毕且配置好项目后,您就可以首次运行此应用了。
firebase deploy
您应该会看到以下控制台输出:
... ✔ Deploy complete! Project Console: https://console.firebase.google.cn/project/<project-id>/overview Hosting URL: https://<project-id>.firebaseapp.com
此命令会部署一个 Web 应用以及几个 Cloud Functions for Firebase。
在浏览器 (https://<project-id>.firebaseapp.com
) 中打开托管网址以查看此 Web 应用。您会看到以下界面:
此网络界面表示用于查看或修改设备状态的第三方平台。如需使用设备信息填充数据库,请点击 UPDATE。此页面不会显示任何更改,但洗衣机的当前状态会存储在数据库中。
现在,您可以通过 Actions 控制台将您部署的云服务关联到 Google 助理。
配置您的 Actions 控制台项目
在 Overview > Build your Action 下,选择 Add Action(s)。输入为智能家居 intent 提供执行方式的 Cloud Functions 函数的网址,然后点击 Save。
https://us-central1-<project-id>.cloudfunctions.net/smarthome
在 Develop > Invocation 标签页中,在 Display name 中为您的 Action 添加显示名,然后点击 Save。此名称会显示在 Google Home 应用中。
如需启用帐号关联,请在左侧导航栏中依次选择 Develop > Account linking 选项。使用以下帐号关联设置:
客户端 ID |
|
客户端密钥 |
|
授权网址 |
|
令牌网址 |
|
点击 Save 保存您的帐号关联配置,然后点击 Test 针对您的项目启用测试。
系统会将您重定向到 Simulator。将鼠标指针悬停在“Testing on Device”() 图标上,以便验证是否已针对您的项目启用测试。
现在,您可以开始实现必要的网络钩子,以便将设备状态关联到 Google 助理。
Action 配置完毕后,您就可以添加设备并发送数据了。您的云服务需要处理以下 intent:
- 当 Google 助理想要了解用户关联了哪些设备时,就会发生
SYNC
intent。当用户关联帐号时,系统会向您的服务发送此 intent。您应该在响应时提供所有用户设备及其功能的 JSON 载荷。 - 当 Google 助理想要了解设备的当前状态时,就会发生
QUERY
intent。您应该在响应时提供 JSON 载荷,其中包含所请求的每台设备的状态。 - 当 Google 助理想要代表用户控制设备时,就会发生
EXECUTE
intent。您应该在响应时提供 JSON 载荷,其中包含所请求的每台设备的执行状态。 - 当用户将帐号与 Google 助理解除关联时,就会发生
DISCONNECT
intent。您应该停止向 Google 助理发送此用户设备的相关事件。
您将在后面几个部分中更新之前部署的用于处理这些 intent 的函数。
更新 SYNC 响应
打开 functions/index.js
,其中包含用于响应来自 Google 助理的请求的代码。
您需要通过返回设备元数据和功能来处理 SYNC
intent。更新 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 助理
为了测试您的智能家居 Action,您需要将项目与 Google 帐号相关联。这样一来,您就可以使用登录同一帐号的 Google 助理界面和 Google Home 应用来进行测试。
- 在手机上打开 Google 助理设置。请注意,您登录的帐号应该与控制台所用的帐号相同。
- 依次转到 Google 助理 > 设置 > 家居控制(位于“Google 助理”下方)。
- 点击右下角的加号 (+) 图标。
- 您应该会看到带有 [test] 前缀及您设置的显示名的测试应用。
- 选择此项目。然后,Google 助理会通过您的服务进行身份验证并发送
SYNC
请求,以要求您的服务提供用户设备列表。
打开 Google Home 应用,然后验证您能否看到相应洗衣机设备。
您的云服务可以向 Google 正确报告洗衣机设备后,您需要添加用于请求设备状态和发送命令的功能。
处理 QUERY intent
QUERY
intent 包含一组设备。对于每种设备,您应该在响应时提供其当前状态。
在 functions/index.js
中,修改 QUERY
处理程序,以处理 intent 请求中包含的目标设备列表。
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,
};
});
针对请求中包含的每台设备,返回 Realtime Database 中存储的当前状态。更新 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 intent
EXECUTE
intent 可处理用于更新设备状态的命令。响应会返回每个命令的状态(例如 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],
},
};
});
针对每个命令和目标设备,更新 Realtime Database 中与所请求的特征相对应的值。修改 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 = {isRunning: params.start};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
case 'action.devices.commands.PauseUnpause':
state = {isPaused: params.pause};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
}
return ref.update(state)
.then(() => state);
};
三个 intent 全部实现后,您可以测试 Action 是否能够控制洗衣机。
部署到 Firebase
使用 Firebase CLI 部署更新后的云执行方式:
firebase deploy --only functions
测试洗衣机
现在,如果您通过手机尝试下达以下任意一条语音指令,就会看到值发生变化:
“Ok Google,启动洗衣机。”
“Ok Google,让洗衣机暂停运行。”
“Ok Google,让洗衣机停止运行。”
您还可以通过提问来查询洗衣机的当前状态。
“Ok Google,我的洗衣机启动了吗?”
“Ok Google,我的洗衣机在运行吗?”
“Ok Google,我的洗衣机洗到哪一步了?”
您还可以在 Firebase 控制台日志中查看这些查询和命令,具体方法是在导航菜单中依次点击开发 > 函数 > 日志。
您已将云服务与智能家居 intent 完全集成,让用户能够控制和查询设备的当前状态。不过,此实现仍缺少让您的服务能够主动向 Google 助理发送事件信息(例如,设备使用状态或状态的更改)的方法。
借助请求同步功能,您可以在用户添加或移除设备后,或在用户设备功能更改后触发新的同步请求。借助报告状态功能,在用户手动更改设备状态(例如,打开灯的开关)或使用其他服务更改状态后,您的云服务可以主动向 Home Graph 发送设备状态。
在此部分中,您将添加代码以从前端 Web 应用调用这些方法。
启用 HomeGraph API
借助 HomeGraph API,您可以在用户 Home Graph 中存储并查询设备及其状态。如需使用此 API,您必须先打开 Google Cloud Console,然后启用 HomeGraph API。
在 Google Cloud Console 中,请务必选择与您的 Actions <project-id>.
相匹配的项目。然后,在 HomeGraph API 的 API 库屏幕中,点击启用。
启用报告状态
对 Realtime Database 的写入操作会触发入门级项目中的 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);
});
启用请求同步
刷新前端网络界面的图标会触发入门级项目中的 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
测试实现效果
点击网络界面中的刷新 按钮,并验证能否在 Firebase 控制台日志中看到同步请求。
接下来,调整前端网络界面中的洗衣机设备属性,然后点击更新。验证能否在 Firebase 控制台日志中看到向 Google 报告的状态更改。
恭喜!您已成功使用智能家居 Action 将 Google 助理与设备云服务集成。
了解详情
您可以实现以下想法以进行更深入的研究:
您还可以详细了解如何测试和提交 Action 以供审核,包括用于向用户发布 Action 的认证流程。