调试 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 指标和 Logging 来识别和解决生产问题。
  • 如何使用测试套件来识别功能和 API 问题。
  • 在开发 Local Home 应用时如何使用 Chrome 开发者工具。

所需条件

2. 运行洗衣机应用

获取源代码

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

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

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

项目简介

起始应用包含与为智能家居 Action 启用本地执行方式 Codelab 类似的子目录和云函数。但这里使用的是app-faulty,而不是 app-start。我们从本地家居应用入手,该应用可正常运行,但效果不佳。

关联到 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,启动洗衣机。”

“Hey Google, force local.”

“Ok Google,停止洗衣机。”

当你尝试在“强制本地”后控制洗衣机时,Google 助理会回复“抱歉,目前似乎无法开启故障洗衣机”。

这意味着用户无法通过本地路径访问该设备。它可以在发出“Ok Google,强制使用本地”之前发挥作用,因为当设备无法通过本地路径访问时,我们会回退到使用云路径。不过,在“强制采用本地部署”后,回退到云路径的选项已停用。

为了找出问题所在,我们可以利用 Google Cloud Platform (GCP) 指标日志记录和 Chrome 开发者工具。

5. 调试 Local Home 应用

在下一部分,您将使用 Google 提供的工具查找通过本地路径无法访问设备的原因。你可以使用 Google Chrome 开发者工具连接到 Google Home 设备,查看控制台日志,并调试 Local Home 应用。你还可以将自定义日志发送到 Cloud Logging,以便你了解用户在本地 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.png

此输出意味着 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 Console 中,转到项目页面。
  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 设备,以便它可以加载更新后的本地住宅应用。请确保本地住宅应用版本为 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. 项目 ID 字段中,输入智能家居 Action 的项目 ID。然后点击下一步以继续。
  4. Test Settings(测试设置)步骤中,您应该会在 Device and Trais(设备与 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 设备,以便它可以加载更新后的本地住宅应用。请确保本地住宅应用版本为 1.0.3。

测试修正效果

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

b7fc8c5d3c727d8d.png

7. 恭喜

764dbc83b95782a.png

恭喜!您已成功了解如何通过测试套件对 Local Home 应用进行智能家居和 Cloud Logging 问题排查。

了解详情

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

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