调试 Local Home

1. 准备工作

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

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

72ffb320986092c.png

调试云到云集成是构建生产质量集成的重要一步,但如果没有信息丰富且易于使用的问题排查和测试工具,这项工作会非常困难且耗时。为了方便调试云到云集成,Google Cloud Platform (GCP) 提供了指标日志记录智能家居测试套件,可帮助您识别和解决集成问题。

前提条件

构建内容

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

学习内容

  • 如何使用 GCP 指标和日志记录来识别和解决生产问题。
  • 如何使用测试套件来识别功能和 API 问题。
  • 如何在开发本地 Home 应用时使用 Chrome 开发者工具。

所需条件

2. 运行洗衣机应用

获取源代码

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

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

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

项目简介

起始应用包含与 为云到云集成启用本地执行 Codelab 类似的子目录和 Cloud Functions 函数。但这里不是 app-start,而是 app-faulty。我们将从一个可以正常运行但效果不太理想的本地住宅应用开始。

连接到 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 界面上,点击 Refreshae8d3b25777a5e30.png 按钮,以使用 Request Sync 通过有故障的洗衣机应用更新 HomeGraph 中的最新设备元数据。

fa3c47f293cfe0b7.png

打开 Google Home 应用,然后验证您能否看到相应洗衣机设备,其新名称为“故障洗衣机”。请务必将设备分配到有 Nest 设备的房间。

2a082ee11d47ad1a.png

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. 测试 Local Home 应用

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

“Hey Google,启动洗衣机。”

“Hey Google,启动洗衣机。”

“Hey Google,强制本地。”

“Hey Google,让洗衣机停止运行。”

您会发现,在“强制本地”后,当您尝试控制洗衣机时,Google 助理会回应“抱歉,‘故障洗衣机’目前不可用”。

这意味着无法通过本地路径访问该设备。在发出“Hey Google, force local”之前,它之所以能正常运行,是因为当设备无法通过本地路径访问时,我们会回退到使用云路径。不过,在“强制本地”后,回退到云路径的选项会被停用。

为了找出问题所在,我们来使用现有的工具:Google Cloud Platform (GCP) 指标日志记录以及 Chrome 开发者工具。

5. 调试 Local Home 应用

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

关联 Chrome 开发者工具

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

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

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

567f97789a7d8846.png

启动检查器

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

774c460c59f9f84a.png

此输出表示 IDENTIFY 处理程序已成功触发,但 IdentifyResponse 中返回的 verificationId 与 HomeGraph 中的任何设备都不匹配。我们来添加一些自定义日志,以找出原因。

添加自定义日志

虽然本地 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) 进行管理的。如需详细了解日志记录数据的角色和权限,请参阅 Cloud Logging 访问权限控制

使用高级过滤条件

我们知道 IDENTIFY intent 中出现了错误,因为本地路径无法正常运行,本地设备无法被识别。不过,我们想确切了解问题是什么,因此先过滤掉 IDENTIFY 处理程序中发生的错误。

点击显示查询切换开关,该开关应会变成查询构建器框。在查询构建器框中输入 jsonPayload.intent="IDENTIFY",然后点击运行查询按钮。

4c0b9d2828ee2447.png

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

71f2f156c6887496.png

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 开发者工具控制台中看到任何错误。

c8456f7b5f77f894.png

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

“Hey Google,强制本地化。”

“Hey Google,让洗衣机停止运行。”

“Hey Google,启动洗衣机。”

“Hey Google,强制默认。”

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

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

运行智能家居测试套件

按照以下说明使用测试套件测试云到云集成:

  1. 在网络浏览器中,打开智能家居测试套件
  2. 使用右上角的按钮登录 Google。这样一来,测试套件便可直接向 Google 助理发送命令。
  3. 项目 ID 字段中,输入云到云集成的项目 ID。然后,点击下一步继续。
  4. 测试设置步骤中,您应该会在设备和轨迹部分中看到 Faulty Washer。
  5. 停用测试请求同步选项,因为示例洗衣机应用没有用于添加 / 移除 / 重命名洗衣机的界面。在正式版系统中,每当用户添加 / 移除 / 重命名设备时,您都必须触发请求同步
  6. 由于我们要同时测试本地路径和云路径,因此请保持 Local Home SDK 选项处于启用状态。
  7. 点击下一步:测试环境,开始运行测试。

67433d9190fa770e.png

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

d1ebd5cfae2a2a47.png

分析错误消息

仔细查看失败的测试用例中的错误消息。它们会告知您相应测试的预期状态和实际状态。在本例中,对于“暂停洗衣机”,预期状态为 isPaused: true,但实际状态为 isPaused: false。同样,对于“暂停洗衣机”,预期状态为 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 设备,以便加载更新后的本地 Home 应用。确保本地 Home 应用版本为 1.0.3。

测试修复效果

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

b7fc8c5d3c727d8d.png

7. 恭喜

764dbc83b95782a.png

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

了解详情

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

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