调试 Local Home

1. 准备工作

通过智能家居集成,Google 助理可以控制用户住宅中已连接的设备。如需构建智能家居 Action,您需要提供一个能够处理智能家居 intent 的 Cloud webhook 端点。例如,当用户说“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) 指标日志记录智能家居测试套件来帮助您识别和解决 Action 存在的问题。

前提条件

构建内容

在此 Codelab 中,您将为智能家居 Action 构建本地执行方式并将其关联到 Google 助理,然后通过智能家居、Google Cloud Platform (GCP) 指标和 Logging 的测试套件调试 Local Home 应用。

学习内容

  • 如何使用 GCP 指标和 Logging 找出和解决生产问题。
  • 如何使用测试套件识别功能和 API 问题。
  • 如何在开发 Local Home 应用时使用 Chrome 开发者工具。

所需条件

2. 运行洗衣机应用

获取源代码

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

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

$ git clone https://github.com/google-home/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 按钮,通过 Request Sync 使用发生故障的洗衣机应用的最新设备元数据更新 HomeGraph:

fa3c47f293cfe0b7.png

请打开 Google Home 应用,然后验证您能否看到名为“Faulty Washer”(故障洗衣机)的洗衣机设备。请务必将该设备分配给有 Nest 设备的房间。

2a082ee11d47ad1a

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 设备下达语音指令,向您的设备发送指令,例如:

“Hey Google,打开洗衣机。”

“Hey Google,启动洗衣机。”

“Hey Google, force local.”

“Hey Google,让洗衣机停掉。”

当您尝试在“强制本地”之后尝试控制洗衣机时,Google 助理会响应“抱歉,似乎有故障洗衣机现在无法使用”。

这意味着无法通过本地路径访问设备。它在发出“Hey Google, force local”之前就已经正常运行,因为当无法通过本地路径访问设备时,我们将回退到使用云路径。然而,“强制本地”后,回退到云端路径的选项将被停用。

要找出问题所在,不妨利用我们提供的工具:Google Cloud Platform (GCP) 指标日志记录以及 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

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

71f2f156c6887496

debugString 中可以看出,本地设备 ID 的格式不符合预期。Local Home 应用应以字符串形式获取本地设备 ID,该 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

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

“Hey Google,强制本地。”

“Hey Google,让洗衣机停掉。”

“Hey Google,打开洗衣机。”

...

“Hey Google,强制使用默认设置。”

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

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

运行智能家居测试套件

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

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

67433d9190fa770e

完成测试后,您会注意到,本地路径中的暂停/恢复测试失败,而云端路径中的暂停/恢复测试已通过。

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

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

了解详情

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

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