调试 Local Home

1. 准备工作

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

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

72ffb320986092c.png

调试云到云集成是构建具有生产环境质量的集成的关键步骤,但如果没有信息丰富且易于使用的问题排查和测试工具,这项工作会很有挑战且耗时。为了便于调试云到云集成,Google Cloud Platform (GCP) MetricsLogging 以及智能家居测试套件可帮助您发现和解决集成问题。

前提条件

构建内容

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

学习内容

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

所需条件

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 界面中,点击 Refreshae8d3b25777a5e30.png 按钮,通过请求同步将故障洗衣机应用中的最新设备元数据更新到 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. 测试本地 Home 应用

向 Google Home 设备下达语音指令,将命令发送到您的设备,例如:

“Hey Google,开启洗衣机。”

“Hey Google,启动洗衣机。”

“Hey Google,强制使用本地播放。”

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

您会发现,在“强制本地”后,当您尝试控制洗衣机时,Google 助理会回复“抱歉,Faulty Washer 目前似乎无法使用”。

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

为了找出问题所在,我们可以使用 Google Cloud Platform (GCP) MetricsLogging 以及 Chrome 开发者工具。

5. 调试本地 Home 应用

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

关联 Chrome 开发者工具

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

  1. 确保您已将 Google Home 设备与有权访问 Developers Console 项目的用户相关联。
  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 中的任何设备都不匹配。我们来添加一些自定义日志,以便了解原因。

添加自定义日志

虽然 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 设备是否使用了预期版本。

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 处理程序中找到您在拒绝 promise 时刚刚设置的 errorCodedebugString

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

运行智能家居测试套件

请按照以下说明通过 Test Suite 测试云到云集成:

  1. 在网络浏览器中,打开智能家居测试套件
  2. 使用右上角的按钮登录 Google。这样一来,测试套件便可直接将命令发送给 Google 助理。
  3. Project ID 字段中,输入云到云集成的项目 ID。然后,点击下一步以继续。
  4. 测试设置步骤中,您应该会在设备和特征部分看到故障洗衣机。
  5. 停用 Test Request Sync 选项,因为示例洗衣机应用没有用于添加 / 移除 / 重命名洗衣机的界面。在正式版系统中,每当用户添加 / 移除 / 重命名设备时,您都必须触发请求同步
  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 设备,以便其加载更新后的本地住宅应用。请确保本地住宅应用的版本为 1.0.3。

测试您的修复程序

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

b7fc8c5d3c727d8d.png

7. 恭喜

764dbc83b95782a.png

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

了解详情

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

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