1. 准备工作
通过智能家居集成,Google 助理可以控制用户住宅中已连接的设备。如需构建云到云集成,您需要提供一个能够处理智能家居 intent 的云网络钩子端点。例如,当用户说“Ok Google,打开灯”时,Google 助理会将这条指令发给您的云执行方式,以更新设备状态。
Local Home SDK 可以增强智能家居集成。它添加了一条本地路径,能直接将智能家居 intent 传递给 Google Home 设备,从而提高可靠性并缩短处理用户指令时的延迟时间。借助此 SDK,您可以用 TypeScript 或 JavaScript 编写和部署本地执行方式应用,以识别设备并在任意一个 Google Home 智能音箱或 Google Nest 智能显示屏上执行指令。然后,您的应用使用现有的标准协议,通过局域网直接与用户的现有智能设备进行通信,以便执行指令。
调试云到云集成是构建具有生产环境质量的集成的关键步骤,但如果没有信息丰富且易于使用的问题排查和测试工具,这项工作会很有挑战且耗时。为了便于调试云到云集成,Google Cloud Platform (GCP) Metrics 和 Logging 以及智能家居测试套件可帮助您发现和解决集成问题。
前提条件
- 创建云到云集成开发者指南
- 运行为云到云集成启用本地执行方式 Codelab
构建内容
在此 Codelab 中,您将构建一个适用于云到云集成的本地执行方式并将其关联到 Google 助理,然后通过适用于智能家居和 Google Cloud Platform (GCP) 指标和日志记录的测试套件调试 Local Home 应用。
学习内容
- 如何使用 GCP Metrics 和 Logging 来识别和解决生产问题。
- 如何使用测试套件来发现功能和 API 问题。
- 如何在开发本地 Home 应用时使用 Chrome 开发者工具。
所需条件
- 最新版本的 Google Chrome
- 安装了 Google Home 应用的 iOS 或 Android 设备
- Google Home 智能音箱或 Google Nest 智能显示屏
- Node.js 10.16 或更高版本
- Google 账号
- Google Cloud 结算账号
2. 运行洗衣机应用
获取源代码
点击以下链接,将此 Codelab 的示例下载到您的开发机器上:
…或者,您也可以通过命令行克隆 GitHub 代码库:
$ git clone https://github.com/google-home/smarthome-debug-local.git
项目简介
此起始应用包含与为云到云集成启用本地执行方式 Codelab 中类似的子目录和 Cloud Functions 函数。但这里使用的是 app-faulty
,而不是 app-start
。我们将从一个功能还不太完善的本地住宅应用开始。
关联到 Firebase
我们将使用您在为云到云集成启用本地执行方式 Codelab 中创建的项目,但我们将部署在此 Codelab 中下载的文件。
前往 app-faulty
目录,然后使用您在为云到云集成启用本地执行方式 Codelab 中创建的集成项目设置 Firebase CLI:
$ cd app-faulty $ firebase use <project-id>
部署到 Firebase
前往 app-faulty/functions
文件夹,然后使用 npm
安装所有必要的依赖项:
$ cd functions $ npm install
注意:如果您看到以下消息,可以忽略并继续操作。此警告是由于一些较旧的依赖项而导致的,您可以点击此处了解详情。
found 5 high severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for details
转到 app-faulty/local/
目录并运行以下命令,以下载 TypeScript 编译器并编译应用:
$ cd ../local $ npm install $ npm run build
这会编译 index.ts
(TypeScript) 源代码,并将以下内容放入 app-faulty/public/local-home/
目录:
bundle.js
- 已编译的 JavaScript 输出,包含本地应用和依赖项。index.html
- 用于提供应用以在设备上进行测试的本地托管网页。
依赖性安装完毕且配置好项目后,您就可以首次运行此应用了。
$ firebase deploy
您应该会看到以下控制台输出:
... ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/<project-id>/overview Hosting URL: https://<projectcd -id>.web.app
此命令会部署一个 Web 应用以及几个 Cloud Functions for Firebase。
更新 HomeGraph
在浏览器 (https://<project-id>.web.app
) 中打开托管网址以查看此 Web 应用。在 Web 界面中,点击 Refresh 按钮,通过请求同步将故障洗衣机应用中的最新设备元数据更新到 HomeGraph:
打开 Google Home 应用,然后验证您能否看到名称已更改为“故障洗衣机”的洗衣机设备。请务必将设备分配到包含 Nest 设备的房间。
3. 启动智能洗衣机
如果您已运行为云到云集成启用本地执行方式 Codelab,则应该已启动虚拟智能洗衣机。如果已停止,请记得重启虚拟设备。
启动设备
转到 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
4. 测试本地 Home 应用
向 Google Home 设备下达语音指令,将命令发送到您的设备,例如:
“Hey Google,开启洗衣机。”
“Hey Google,启动洗衣机。”
“Hey Google,强制使用本地播放。”
“Hey Google,让洗衣机停止运行。”
您会发现,在“强制本地”后,当您尝试控制洗衣机时,Google 助理会回复“抱歉,Faulty Washer 目前似乎无法使用”。
这意味着无法通过本地路径访问设备。在发出“Hey Google,强制使用本地路径”之前,它可以正常运行,因为当无法通过本地路径访问设备时,我们会回退到使用云端路径。不过,在使用“强制本地”后,系统会停用回退到云端路径的选项。
为了找出问题所在,我们可以使用 Google Cloud Platform (GCP) Metrics 和 Logging 以及 Chrome 开发者工具。
5. 调试本地 Home 应用
在下一部分中,您将使用 Google 提供的工具找出无法通过本地路径访问设备的原因。您可以使用 Google Chrome 开发者工具连接到 Google Home 设备、查看控制台日志,并调试 Local Home 应用。您还可以将自定义日志发送到 Cloud Logging,以便了解用户在 Local Home 应用中发现的主要错误。
关联 Chrome 开发者工具
如需将调试程序关联到您的本地执行方式应用,请按照以下步骤操作:
- 确保您已将 Google Home 设备与有权访问 Developers Console 项目的用户相关联。
- 重新启动 Google Home 设备,使其能够获取您的 HTML 的网址,以及您在开发者控制台中添加的扫描配置。
- 在开发机器上启动 Chrome。
- 打开新的 Chrome 标签页,然后在地址字段中输入
chrome://inspect
以启动检查器。
您应该会在页面上看到设备列表,并且您的应用网址应该会出现在您的 Google Home 设备名称下方。
启动检查器
点击应用网址下方的 Inspect,以启动 Chrome 开发者工具。选择 Console 标签页,并验证您是否可以看到您的 TypeScript 应用输出的 IDENTIFY
intent 内容。
此输出表示 IDENTIFY 处理脚本已成功触发,但 IdentifyResponse
中返回的 verificationId
与 HomeGraph 中的任何设备都不匹配。我们来添加一些自定义日志,以便了解原因。
添加自定义日志
虽然 Local Home SDK 会输出 DEVICE_VERIFICATION_FAILED
错误,但这对查找根本原因没有多大帮助。我们来添加一些自定义日志,以确保正确读取和处理扫描数据。请注意,如果我们因错误而拒绝 promise,系统实际上也会将错误消息发送到 Cloud Logging。
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.
// Is there something wrong here?
const localDeviceId = Buffer.from(scanData.data);
console.log(`IDENTIFY handler: received local device id
${localDeviceId}`);
// Add custom logs
if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
const err = new IntentFlow.HandlerError(request.requestId,
'invalid_device', 'Invalid device id from scan data ' +
localDeviceId);
return Promise.reject(err);
}
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);
}
此外,请更改本地 Home 应用版本,以便我们确定是否使用的是正确的版本。
local/index.ts
const localHomeSdk = new App('1.0.1');
添加自定义日志后,您需要重新编译应用并重新部署到 Firebase。
$ cd ../app-faulty/local $ npm run build $ firebase deploy --only hosting
现在,请重新启动 Google Home 设备,以便其加载更新后的本地 Home 应用。您可以查看 Chrome 开发者工具中的控制台日志,了解 Google Home 设备是否使用了预期版本。
访问 Cloud Logging
我们来看看如何使用 Cloud Logging 查找错误。如需访问你的项目的 Cloud Logging,请按以下步骤操作:
- 在 Cloud Platform 控制台中,前往项目页面。
- 选择你的智能家居项目。
- 在运维下,选择 Logging > 日志浏览器。
对于您的集成项目的用户,对日志记录数据的访问权限是通过 Identity and Access Management (IAM) 进行管理的。如需详细了解日志记录数据的角色和权限,请参阅 Cloud Logging 访问权限控制。
使用高级过滤条件
我们知道 IDENTIFY
intent 中会发生错误,因为本地路径无法正常运行,因为本地设备未能识别。不过,我们想确切了解问题所在,因此先滤除 IDENTIFY
处理程序中发生的错误。
点击显示查询切换开关,它应会变成查询构建器框。在查询构建器框中输入 jsonPayload.intent="IDENTIFY"
,然后点击运行查询按钮。
因此,您会收到 IDENTIFY
处理程序中抛出的所有错误日志。接下来,展开上一个错误。您会在 IDENTIFY
处理程序中找到您在拒绝 promise 时刚刚设置的 errorCode
和 debugString
。
从 debugString
中,我们可以得知本地设备 ID 的格式不符合预期。Local Home 应用预计会以 deviceid
开头、后跟 3 位数字的字符串形式获取本地设备 ID,但此处的本地设备 ID 是十六进制字符串。
修正错误
返回我们从扫描数据中解析本地设备 ID 的源代码,我们注意到,在将字符串转换为字节时,我们并未提供编码。扫描数据以十六进制字符串的形式接收,因此在调用 Buffer.from()
时,请将 hex
作为字符编码传递。
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');
console.log(`IDENTIFY handler: received local device id
${localDeviceId}`);
if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
const err = new IntentFlow.HandlerError(request.requestId,
'invalid_device', 'Invalid device id from scan data ' +
localDeviceId);
return Promise.reject(err);
}
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);
}
此外,请更改本地 Home 应用版本,以便我们确定是否使用的是正确的版本。
local/index.ts
const localHomeSdk = new App('1.0.2');
修复错误后,请编译应用并重新部署到 Firebase。在 app-faulty/local
中,运行以下命令:
$ npm run build $ firebase deploy --only hosting
测试您的修复程序
部署完成后,请重新启动 Google Home 设备,以便其加载更新后的本地 Home 应用。确保本地 Home 应用的版本为 1.0.2,这次,您应该不会在 Chrome 开发者工具控制台中看到任何错误。
现在,您可以尝试再次向设备发送命令。
“Hey Google,强制使用本地播放。”
“Hey Google,让洗衣机停止运行。”
“Hey Google,开启洗衣机。”
…
“Hey Google,强制设为默认。”
6. 运行智能家居测试套件
使用 Google Home 应用中的触控功能或通过语音指令验证设备后,您可以使用自动化的智能家居测试套件,根据与您的集成关联的设备类型和特征来验证用例。测试套件会运行一系列测试来检测集成中的问题,并针对失败的测试用例显示信息丰富的消息,以便您在深入研究事件日志之前加快调试速度。
运行智能家居测试套件
请按照以下说明通过 Test Suite 测试云到云集成:
- 在网络浏览器中,打开智能家居测试套件。
- 使用右上角的按钮登录 Google。这样一来,测试套件便可直接将命令发送给 Google 助理。
- 在 Project ID 字段中,输入云到云集成的项目 ID。然后,点击下一步以继续。
- 在测试设置步骤中,您应该会在设备和特征部分看到故障洗衣机。
- 停用 Test Request Sync 选项,因为示例洗衣机应用没有用于添加 / 移除 / 重命名洗衣机的界面。在正式版系统中,每当用户添加 / 移除 / 重命名设备时,您都必须触发请求同步。
- 请让 Local Home SDK 选项保持启用状态,因为我们将同时测试本地和云路径。
- 点击下一步:测试环境以开始运行测试。
测试完成后,您会发现本地路径中的暂停/继续测试失败,而云端路径中的暂停/继续测试通过。
分析错误消息
仔细查看失败测试用例中的错误消息。这些信息会告知您该测试的预期状态和实际状态。在本例中,“暂停洗衣机”的预期状态为 isPaused: true
,但实际状态为 isPaused: false
。同样,对于“暂停洗衣机”,预期状态为 isPaused: true
,但实际状态为 isPaused: false
。
根据错误消息,我们在本地路径中似乎反向设置了 isPaused
状态。
找出并修正错误
我们来查找 Local Home 应用向设备发送执行命令的源代码。getDataCommand()
是 executeHandler()
调用的函数,用于在发送到设备的执行命令中设置 payload
。
local/index.ts
getDataForCommand(command: string, params: IWasherParams): unknown {
switch (command) {
case 'action.devices.commands.OnOff':
return {
on: params.on ? true : false
};
case 'action.devices.commands.StartStop':
return {
isRunning: params.start ? true : false
};
case 'action.devices.commands.PauseUnpause':
return {
// Is there something wrong here?
isPaused: params.pause ? false : true
};
default:
console.error('Unknown command', command);
return {};
}
}
我们确实是在相反状态下设置 isPause
,当 params.pause
为 true
时,它应设置为 true
,否则应设置为 false
。接下来,我们来解决这个问题。
local/index.ts
getDataForCommand(command: string, params: IWasherParams): unknown {
switch (command) {
case 'action.devices.commands.OnOff':
return {
on: params.on ? true : false
};
case 'action.devices.commands.StartStop':
return {
isRunning: params.start ? true : false
};
case 'action.devices.commands.PauseUnpause':
return {
isPaused: params.pause ? true : false
};
default:
console.error('Unknown command', command);
return {};
}
}
更改本地 Home 应用版本,以便我们确定是否使用的是正确的版本。
local/index.ts
const localHomeSdk = new App('1.0.3');
请务必重新编译应用并重新部署到 Firebase。在 app-faulty/local
中,运行以下命令:
$ npm run build $ firebase deploy --only hosting
现在,请重新启动 Google Home 设备,以便其加载更新后的本地住宅应用。请确保本地住宅应用的版本为 1.0.3。
测试您的修复程序
现在,使用相同的配置重新运行智能家居测试套件,您会发现所有测试用例均已通过。
7. 恭喜
恭喜!您已成功了解如何通过智能家居和 Cloud Logging 的测试套件排查 Local Home 应用的问题。
了解详情
您还可以尝试下面这些操作:
- 向您的设备添加更多支持的特征,并使用测试套件进行测试。
- 在每个 intent 处理脚本中添加更多自定义日志,并在 Cloud Logging 中查看这些日志。
- 创建信息中心、设置提醒,并以编程方式访问指标数据,以获取有关集成的实用使用情况指标。
您还可以详细了解如何测试和提交集成以供审核,包括用于向用户发布集成的认证流程。