欢迎使用 Google Home 开发者中心,您可以在这里学习有关如何开发智能家居 Action 的新平台。注意:你将继续在 Actions 控制台中构建操作。

调试 Local Home

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

1. 准备工作

通过智能家居集成,Google 助理可以控制用户住宅中已连接的设备。如要构建智能家居 Action,你需要提供一个能够处理智能家居 intent 的云端网络钩子端点。例如,当用户说“Ok Google,打开灯”时,Google 助理会将这条指令发给您的云执行方式,以更新设备状态。

Local Home SDK 可以增强智能家居集成。它添加了一条本地路径,能直接将智能家居 intent 传递给 Google Home 设备,从而提高可靠性并缩短处理用户指令时的延迟时间。借助此 SDK,您可以用 TypeScript 或 JavaScript 编写和部署本地执行方式应用,以识别设备并在任意一个 Google Home 智能音箱或 Google Nest 智能显示屏上执行指令。然后,您的应用使用现有的标准协议,通过局域网直接与用户的现有智能设备进行通信,以便执行指令。

72ffb320986092c.png

调试智能家居 Action 是打造具有生产质量的 Action 的关键步骤,但如果没有具有参考价值且易于使用的问题排查和测试工具,颇具挑战性和耗时性。为方便调试智能家居 Action,我们提供了 Google Cloud Platform (GCP) 指标Logging智能家居测试套件,可帮助你识别和解决 Action 的问题。

前提条件

构建内容

在此 Codelab 中,您将为智能家居 Action 构建本地执行方式,并将其与 Google 助理相关联,然后通过测试套件对智能家居和 Google Cloud Platform (GCP) 指标和日志记录进行调试。

学习内容

  • 如何使用 GCP 指标和日志记录找出并解决生产环境中的问题。
  • 如何使用该测试套件来识别功能和 API 问题。
  • 如何在开发 Local Home 应用时使用 Chrome 开发者工具。

所需条件

2. 运行洗衣机应用

获取源代码

点击以下链接,将此 Codelab 的示例下载到您的开发机器上:

…或者,您也可以通过命令行克隆 GitHub 代码库:

$ git clone https://github.com/googlecodelabs/smarthome-debug-local.git

项目简介

起始应用包含与为智能家居 Action 启用本地执行方式 Codelab 类似的子目录和 Cloud Functions 函数。但这里不是app-start,而是app-faulty。我们先从一款能够正常运行的本地家居应用开始。

关联到 Firebase

我们将使用你在为智能家居 Action 启用本地执行方式 Codelab 中创建的同一项目,但我们将部署在此 Codelab 中下载的文件。

前往 app-faulty 目录,然后在为智能家居 Action 启用本地执行方式 Codelab 中创建的 Actions 项目来设置 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

在浏览器中打开 Hosting 网址 (https://<project-id>.web.app) 以查看 Web 应用。在网页界面上,点击刷新 ae8d3b25777a5e30.png 按钮,通过请求同步功能更新 HomeGraph,使其包含来自洗衣机应用的最新设备元数据:

fa3c47f293cfe0b7.png

请打开 Google Home 应用,验证你能否看到名为“Faulty Washer”的新洗衣机设备。请务必将设备分配给包含 Nest 设备的房间。

2a082ee11d47ad1a.png

3. 启动智能洗衣机

如果你已经运行为智能家居 Action 启用本地执行方式 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. 测试 Local Home 应用

通过语音指令向 Google Home 设备发送命令,例如:

“Ok Google,打开洗衣机。”

“Ok Google,启动洗衣机。”

“Ok Google,强制本地。”

“Ok Google,停止洗衣机。”

当你尝试在“强制本地”后控制洗衣机时,Google 助理会说“抱歉,现在好像没法使用洗衣机”。

这意味着,用户无法通过本地路径访问该设备。它在发出“Hey Google, force local”之前有效,因为当设备无法通过本地路径访问时,我们会回退到使用 Cloud 路径。但是,在“强制本地”之后,回退到云路径的选项已停用。

如要找出问题所在,不妨使用我们拥有的工具:Google Cloud Platform (GCP) 指标以及 Logging 和 Chrome 开发者工具。

5. 调试 Local Home 应用

在下文中,您将使用 Google 提供的工具,找出设备无法通过本地路径访问的原因。你可以使用 Google Chrome 开发者工具连接到 Google Home 设备,查看控制台日志,以及调试 Local Home 应用。你还可以将自定义日志发送到 Cloud Logging,以便了解用户在 Local Home 应用中遇到的最常见错误。

关联 Chrome 开发者工具

如需将调试程序关联到您的本地执行方式应用,请按照以下步骤操作:

  1. 请确保你的 Google Home 设备已与有权访问 Actions 控制台项目的用户相关联。
  2. 重新启动 Google Home 设备,使其能够获取您的 HTML 的网址,以及您在 Actions 控制台中添加的扫描配置。
  3. 在开发机器上启动 Chrome。
  4. 打开新的 Chrome 标签页,然后在地址字段中输入 chrome://inspect 以启动检查器。

您应该会在页面上看到设备列表,并且您的应用网址应该会出现在您的 Google Home 设备名称下方。

567f97789a7d8846.png

启动检查器

点击应用网址下方的 Inspect,以启动 Chrome 开发者工具。选择 Console 标签页,并验证您是否可以看到您的 TypeScript 应用输出的 IDENTIFY intent 内容。

774c460c59f9f84a

此输出意味着 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 设备,以便它加载更新后的本地家居应用。你可以查看 Chrome 开发者工具中的控制台日志,了解 Google Home 设备是否使用了预期的版本。

ecc56508ebcf9ab.png

访问 Cloud Logging

我们来看看如何使用 Cloud Logging 查找错误。如需访问你的项目的 Cloud Logging,请按以下步骤操作:

  1. 在 Cloud Platform 控制台中,转到项目页面。
  2. 选择你的智能家居项目。
  3. 运维下,选择 Logging > 日志浏览器

通过 Identity and Access Management (IAM) 管理你的 Actions 项目用户对日志记录数据的访问权限。如需详细了解日志记录数据的角色和权限,请参阅 Cloud Logging 访问权限控制

使用高级过滤器

我们知道,IDENTIFY intent 中会出现错误,因为本地设备无法识别,因为本地路径无效。不过,由于我们希望确切了解问题所在,因此首先要滤除 IDENTIFY 处理程序中发生的错误。

展开查询预览框,它应该会变成一个查询构建器框。在查询构建器框中输入 jsonPayload.intent="IDENTIFY",然后点击运行查询按钮。

4c0b9d2828ee2447.png

因此,您会获得 IDENTIFY 处理程序中抛出的所有错误日志。接下来,展开最后一个错误。您可以在 IDENTIFY 处理程序中找到刚刚拒绝 promise 时设置的 errorCodedebugString

71f2f156c6887496.png

debugString 中,我们可以看到本地设备 ID 的格式不符合预期。Local Home 应用希望获取字符串形式的本地设备 ID(以 deviceid 开头,后跟 3 位数字),但此处的本地设备 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 设备,以便它加载更新后的 Local Home 应用。请确保 Local Home 应用版本为 1.0.2,这次 Chrome 开发者工具中应该不会显示错误。

c8456f7b5f77f894.png

现在,您可以再次尝试向设备发送命令。

“Ok Google,强制本地。”

“Ok Google,停止洗衣机。”

“Ok Google,打开洗衣机。”

……

“Ok Google,强制默认。”

6. 运行智能家居测试套件

使用 Google Home 应用中的触控功能或语音指令验证你的设备后,你可以根据与 Action 关联的设备类型和特征,使用自动化的智能家居测试套件进行验证。测试套件会运行一系列测试来检测 Action 中的问题,并显示失败测试用例的信息性消息,在调试事件日志之前加快调试速度。

运行智能家居测试套件

请按照以下说明测试智能家居 Action by Test Suite:

  1. 在网络浏览器中,打开智能家居测试套件
  2. 使用右上角的按钮登录 Google。这样,测试套件便可以直接将命令发送给 Google 助理。
  3. Project ID 字段中,输入智能家居 Action 的项目 ID。然后,点击下一步以继续操作。
  4. 测试设置步骤中,您应该会在设备和 Trais 部分看到故障洗衣机。
  5. 停用测试请求同步选项,因为示例洗衣机应用没有添加/移除/重命名洗衣机的界面。在生产系统中,每当用户添加/移除/重命名设备时,都必须触发请求同步
  6. 启用 Local Home SDK 选项,因为我们要同时测试本地路径和云路径。
  7. 点击下一步以开始运行测试。

67433d9190fa770e.png

完成测试后,您会发现本地路径中的暂停/恢复测试失败,而云路径中的暂停/恢复测试通过了。

d1ebd5cfae2a2a47.png

分析错误消息

仔细查看失败测试用例中的错误消息。他们会告诉您该测试的预期状态是什么,以及实际状态是什么。在本例中,对于“Pause the Washer”(暂停洗衣机),预期状态为 isPaused: true,但在实际状态下,我们使用的是 isPaused: false。同样,对于“Pause the Washer”(暂停洗衣机),预期状态为 isPaused: true,但在实际状态下,我们获取的是 isPaused: false

6bfd3acef9c16b84.png

根据错误消息,我们似乎在本地路径中设置了反向 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.pausetrue 时,应将其设置为 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 设备,以便它加载更新后的 Local Home 应用。请确保 Local Home 应用版本为 1.0.3。

测试修正效果

现在,使用相同的配置重新运行智能家居测试套件,您会发现所有测试用例都已通过。

b7fc8c5d3c727d8d.png

7. 恭喜

764dbc83b95782a.png

恭喜!你已成功学会如何通过测试套件对智能家居和 Cloud Logging 进行问题排查。

了解详情

您还可以尝试下面这些操作:

  • 向您的设备添加更多支持的特征,然后使用测试套件进行测试。
  • 在每个 intent 处理程序中添加更多自定义日志,并在 Cloud Logging 中查看这些日志。
  • 你可以创建信息中心、设置提醒以及以编程方式访问指标数据,以便获取有关 Action 的实用使用情况指标。

您还可以详细了解如何测试和提交 Action 以供审核,包括向用户发布 Action 的认证流程。