为智能家居 Action 启用本地执行方式

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

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

72ffb320986092c.png

前提条件

构建内容

在此 Codelab 中,您将利用 Firebase 部署一个以前构建的智能家居集成,然后在 Actions 控制台中应用一个扫描配置,并使用 TypeScript 构建一个本地应用,用于将以 Node.js 编写的命令发送到虚拟洗衣机设备。

学习内容

  • 如何在 Actions 控制台中启用和配置本地执行方式。
  • 如何使用 Local Home SDK 编写本地执行方式应用。
  • 如何调试在 Google Home 音箱或 Google Nest 智能显示屏上加载的本地执行方式应用。

所需条件

启用活动控件

在您计划与 Google 助理搭配使用的 Google 帐号中启用以下活动控件

  • 网络与应用活动记录
  • 设备信息
  • 语音和音频活动记录

创建 Actions 项目

  1. 转到 Actions on Google 开发者控制台
  2. 点击 New Project,输入项目名称,然后点击 CREATE PROJECT

AWXw5E1m9zVgvVeyeL3uxwCX6DtWOCK6LRSLmOATFzjMbmE5cSWBdSVhJZDFpEFH2azZTK2eMs6OYYdMJYiGb5bKqFEzxaLyRUYuwVGBlSjXzTyy8Z9CvwpXvRwP7xdycklETzFc7Q

选择智能家居应用

在 Actions 控制台的“Overview”屏幕中,选择 Smart home

36RsBUWBgbgsa5xZ7MJVMm1sIg07nXbfjv0mWCxXViaC5SlbL2gMigw9hgXsZQhNMHLLFOfiKdZsSTNXONFB1i47gksw3SBNpkVYl492WeryOlgxKjpVrLAvg-5cZqu1DI-s5kxM3g

选择 Smart home 体验卡片,然后系统会将您转到项目控制台。

pzgHPsmc2LvLoeUvJfkjKQqD_BvO4v8JOPlcrxsmyptFkkjL4PP6LqrM9r5tNvEIfT9HmK-UKw3GWFPXTjqo4nUrhD2o5shUKHBE31OT8iIA69JZCev7_0_nh-lnL2oJHoxGfqqZ4w

安装 Firebase CLI

借助 Firebase 命令行界面 (CLI),您可以在本地提供 Web 应用,并将您的 Web 应用部署到 Firebase Hosting。

如需安装 CLI,请从终端运行以下 npm 命令:

npm install -g firebase-tools

如需验证 CLI 是否已正确安装,请运行以下命令:

firebase --version

运行以下命令,授权您的 Google 帐号使用 Firebase CLI:

firebase login

启用 HomeGraph API

借助 HomeGraph API,您可以在用户 Home Graph 中存储并查询设备及其状态。如需使用此 API,您必须先打开 Google Cloud Console,然后启用 HomeGraph API

在 Google Cloud Console 中,请务必选择与您的 Actions <project-id>. 相匹配的项目。然后,在 HomeGraph API 的 API 库屏幕中,点击启用

5SVCzM8IZLi_9DV8M0nEklv16NXkpvM0bIzQK2hSyKyvnFHBxPOz90rbr72ayxzmxd5aNROOqC_Cp4outbdlwJdObDs0DIE_8vYzw6dovoVrP9IZWlWsZxDS7UHOi1jiRbDMG8MqUA

开发环境设置完毕后,您可以部署入门级项目以验证所有设置是否已配置正确。

获取源代码

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

下载源代码

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

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

项目简介

入门级项目包含以下子目录:

  • public - 可控制和监控智能洗衣机的前端网络界面
  • functions - 为智能家居 Action 实现云执行方式的 Cloud Functions 函数
  • local - 框架本地执行方式应用项目,包含在 index.ts 中打桩的 intent 处理程序

提供的云执行方式在 index.js 中包括以下函数:

  • fakeauth - 用于帐号关联的授权端点
  • faketoken - 用于帐号关联的令牌端点
  • smarthome - 智能家居 intent 执行方式端点
  • reportstate - 在设备状态改变时调用 HomeGraph API
  • updateDevice - 虚拟设备用来触发报告状态的端点

关联到 Firebase

转到 app-start 目录,然后使用您的 Actions 项目设置 Firebase CLI:

cd app-start
firebase use <project-id>

部署到 Firebase

转到 functions 文件夹,然后使用 npm. 安装所有必要的依赖项

cd functions
npm install

依赖性安装完毕且配置好项目后,您就可以首次运行此应用了。

firebase deploy

您应该会看到以下控制台输出:

...

✔ Deploy complete!

Project Console: https://console.firebase.google.cn/project/<project-id>/overview
Hosting URL: https://<project-id>.firebaseapp.com

此命令会部署一个 Web 应用以及几个 Cloud Functions for Firebase

在浏览器 (https://<project-id>.firebaseapp.com) 中打开托管网址以查看此 Web 应用。您会看到以下界面:

L60eA7MOnPmbBMl2XMipT9MdnP-RaVjyjf0Y93Y1b7mEyIsqZrrwczE7D3RQISRs-iusL1g4XbNmGhuA6-5sLcWefnczwNJEPfNLtwBsO4Tb9YvcAZBI6_rX19z8rxbik9Vq8F2fwg

此网络界面表示用于查看或修改设备状态的第三方平台。如需使用设备信息填充数据库,请点击 UPDATE。此页面不会显示任何更改,但洗衣机的当前状态会存储在数据库中。

现在,您可以通过 Actions 控制台将您部署的云服务关联到 Google 助理。

配置您的 Actions 控制台项目

Overview > Build your Action 下,选择 Add Action(s)。输入为智能家居 intent 提供执行方式的 Cloud Functions 函数的网址,然后点击 Save

https://us-central1-<project-id>.cloudfunctions.net/smarthome

Uso-o00XQXBHvOR9vQq9tmpYDYQJKsFEstsgRFnxPAJf7zJ2FxwhISiodo3dB1Tz49Okd6ivi66fjpo7rarS_GZelglGWCT1r9FzDGUl1r67ddIcIbQrxqN8jG9F9GAKOpk0Ckc-eA

Develop > Invocation 标签页中,在 Display name 中为您的 Action 添加显示名,然后点击 Save。此名称会显示在 Google Home 应用中。

gvC-TvmKDy-D-xjwkeCjNt__9ErA7DL8hZWa1oH1yPJ9SpYOepDYjxx6WnJ56IG-t37fJ65kmHISQdh72Ot2G-0tu6Flxf4gom5kvx_3hlvFeMqYuFgXr_85pfWWn7VLFHtS55p1zw

s4yc1kOW4XtKUQN1EYegiDLU5oTqmxQ2PNbeaujm26OQmYKKpjug7j5FYmutLSAZ1zBd-ZkcZlL7zyTZqw4bge3_oOeWvJTsqJ-A08vfZwImYQrKiquLskLuTpmMqXEZD1xchhCWGQ

如需启用帐号关联,请在左侧导航栏中依次选择 Develop > Account linking 选项。使用以下帐号关联设置:

客户端 ID

ABC123

客户端密钥

DEF456

授权网址

https://us-central1-.cloudfunctions.net/fakeauth

令牌网址

https://us-central1-.cloudfunctions.net/faketoken

rRyZTiBSTuPk3YtJtXjDK1pPftUxsaEhs9jzpvFtbHTD6bEwYxM8jV4MWxiljKA1bKVZrIRoO9O3jtBefLKf_OyMpukPjwIj8zGvyU3UwASzMrnRskl-hVAfAmQVi4sC_zAwgYwRXw

点击 Save 保存您的帐号关联配置,然后点击 Test 针对您的项目启用测试。

OgUvpQfXioygkRwPcaJpzjyNQDZy6enidUC8YMPaCOrZi0YeWCFsCJV9Gqg-_UfsqTnn4KEg--uE3Ymr0QuamDonF4RyYHtRKcULXABDuaEnj2hq8i20LYj1SrGP_1lQ_UsUB90pGw

系统会将您重定向到 Simulator。将鼠标指针悬停在“Testing on Device”(soCeBB1CkSIEqsBmDc8Cth6EjgcXUnrOHeOpLNlvMiiXM73Rmh8iBK1ZFLFd47kycYqIMq3Fm49ryAGUt79BXVPDyEB1IU3W0fgiL49iqTAVrpRszL10mmxzq_AQTJZVrXor-vne2w) 图标上,以便验证是否已针对您的项目启用测试。

2zbfeYpG-wEd2SFP07Wc4mJzHakLX7YvrNw3IV0_0Kd-TonfsKIvvjKWlwvrmTm5jLj3XPWqCtcDd5J2z6gwn9fnchpYVraw1j_mE4M0LVppAl5WY5cK7g0uZyhZ3VFFS25yPmyksg

为了测试您的智能家居 Action,您需要将项目与 Google 帐号相关联。这样一来,您就可以使用登录同一帐号的 Google 助理界面和 Google Home 应用来进行测试。

  1. 在手机上打开 Google 助理设置。请注意,您登录的帐号应该与控制台所用的帐号相同。
  2. 依次转到 Google 助理 > 设置 > 家居控制(位于“Google 助理”下方)。
  3. 点击右下角的加号 (+) 图标
  4. 您应该会看到带有 [test] 前缀及您设置的显示名的测试应用。
  5. 选择此项目。然后,Google 助理会通过您的服务进行身份验证并发送 SYNC 请求,以要求您的服务提供用户设备列表。

打开 Google Home 应用,然后验证您能否看到相应洗衣机设备。

XcWmBVamBZtPfOFqtsr5I38stPWTqDcMfQwbBjetBgxt0FCjEs285pa9K3QXSASptw0KYN2G8yfkT0-xg664V4PjqMreDDs-HPegHjOc4EVtReYPu-WKZyygq9Xmkf8X8z9177nBjQ

验证您是否可以在 Google Home 应用中通过语音指令控制洗衣机。您还应该会在云执行方式的前端网络界面中看到设备状态更改情况。

现在,可以开始为您的 Action 添加本地执行方式了。

为了支持本地执行方式,您需要将一个新的设备专用字段(名为 otherDeviceIds)添加到包含设备唯一本地标识符的云 SYNC 响应中。此字段还表示在本地控制该设备的功能。

otherDeviceIds 字段添加到 SYNC 响应中,如以下代码段所示:

functions/index.js

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '123',
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [ ... ],
        name: { ... },
        deviceInfo: { ... },
        willReportState: true,
        attributes: {
          pausable: true,
        },
        otherDeviceIds: [{
          deviceId: 'deviceid123',
        }],
      }],
    },
  };
});

将更新后的项目部署到 Firebase:

firebase deploy --only functions

部署完成后,转到网络界面,然后点击工具栏中的 Refresh ae8d3b25777a5e30.png 按钮。这会触发请求同步操作,以便 Google 助理接收更新后的 SYNC 响应数据。

bf4f6a866160a982.png

在本部分中,您会将本地执行方式所必需的配置选项添加到您的智能家居 Action。在开发过程中,您可以将本地执行方式应用发布到 Firebase Hosting,Google Home 设备可以从这里访问并下载该应用。

Actions 控制台中,依次选择 Develop > Actions,然后找到 Configure local home SDK 部分。在测试网址字段中输入以下网址,插入您的项目 ID,然后点击 Save

https://<project-id>.firebaseapp.com/local-home/index.html

7d59b31f8d2a988.png

接下来,需要定义 Google Home 设备应如何发现本地智能设备。Local Home 平台支持多种设备发现协议,包括 mDNS、UPnP 和 UDP 广播。您将使用 UDP 广播来发现智能洗衣机。

点击 Device scan configuration 下的 New scan config,添加新的扫描配置。选择 UDP 作为协议,并填写以下属性:

字段

说明

建议值

Broadcast address

UDP 广播地址

255.255.255.255

Broadcast port

Google Home 用于发送 UDP 广播的端口

3311

Listen port

Google Home 用于监听响应的端口

3312

Discovery packet

UDP 广播数据载荷

48656c6c6f4c6f63616c486f6d6553444b

4777bf63c53b6858.png

最后,点击窗口顶部的 Save 以发布所作更改。

您将借助 Local Home SDK typings 软件包,使用 TypeScript 开发本地执行方式应用。查看入门级项目中提供的框架:

local/index.ts

/// <reference types="@google/local-home-sdk" />

import App = smarthome.App;
import Constants = smarthome.Constants;
import DataFlow = smarthome.DataFlow;
import Execute = smarthome.Execute;
import Intents = smarthome.Intents;
import IntentFlow = smarthome.IntentFlow;

...

class LocalExecutionApp {

  constructor(private readonly app: App) { }

  identifyHandler(request: IntentFlow.IdentifyRequest):
      Promise<IntentFlow.IdentifyResponse> {
    // TODO: Implement device identification
  }

  executeHandler(request: IntentFlow.ExecuteRequest):
      Promise<IntentFlow.ExecuteResponse> {
    // TODO: Implement local fulfillment
  }

  ...
}

const localHomeSdk = new App('1.0.0');
const localApp = new LocalExecutionApp(localHomeSdk);
localHomeSdk
  .onIdentify(localApp.identifyHandler.bind(localApp))
  .onExecute(localApp.executeHandler.bind(localApp))
  .listen()
  .then(() => console.log('Ready'))
  .catch((e: Error) => console.error(e));

本地执行方式的核心组件是 smarthome.App 类。入门级项目附加针对 IDENTIFYEXECUTE 两个 intent 的处理程序,然后调用 listen() 方法来通知 Local Home SDK 此应用已准备就绪。

添加 IDENTIFY 处理程序

当 Google Home 设备根据 Actions 控制台提供的扫描配置发现本地网络中有未经验证的设备时,Local Home SDK 就会触发 IDENTIFY 处理程序。

与此同时,当 Google 发现匹配的设备时,此平台会使用生成的扫描数据调用 identifyHandler。在您的应用中,使用 UDP 广播执行扫描,而提供给 IDENTIFY 处理程序的扫描数据包括本地设备发送的响应载荷。

处理程序会返回一个包含本地设备唯一标识符的 IdentifyResponse 实例。将以下代码添加到您的 identifyHandler 方法中,以处理来自本地设备的 UDP 响应并确定合适的本地设备 ID:

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');

  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);
}

请注意,verificationId 字段必须与您的 SYNC 响应中的某个 otherDeviceIds值相匹配,这会在用户的 Home Graph 中将相应设备标记为可用于本地执行方式。Google 找到匹配项之后,相应设备就会被视为已通过验证,并可随时用于本地执行方式。

添加 EXECUTE 处理程序

当支持本地执行方式的设备收到命令时,Local Home SDK 会触发您的 EXECUTE 处理程序。本地 intent 的内容等同于发送到您的云执行方式的 EXECUTE intent,因此本地处理 intent 的逻辑与您在云中的处理方式相似。

该应用可使用 TCP/UDP 套接字或 HTTP(S) 请求与本地设备通信。在本 Codelab 中,HTTP 充当用于控制虚拟设备的协议。端口号在 index.ts 中被定义为 SERVER_PORT 变量。

将以下代码添加到您的 executeHandler 方法,以处理传入的命令,并通过 HTTP 将命令发送到本地设备:

local/index.ts

executeHandler(request: IntentFlow.ExecuteRequest):
    Promise<IntentFlow.ExecuteResponse> {
  console.log("EXECUTE intent: " + JSON.stringify(request, null, 2));

  const command = request.inputs[0].payload.commands[0];
  const execution = command.execution[0];
  const response = new Execute.Response.Builder()
    .setRequestId(request.requestId);

  const promises: Array<Promise<void>> = command.devices.map((device) => {
    console.log("Handling EXECUTE intent for device: " + JSON.stringify(device));

    // Convert execution params to a string for the local device
    const params = execution.params as IWasherParams;
    const payload = this.getDataForCommand(execution.command, params);

    // Create a command to send over the local network
    const radioCommand = new DataFlow.HttpRequestData();
    radioCommand.requestId = request.requestId;
    radioCommand.deviceId = device.id;
    radioCommand.data = JSON.stringify(payload);
    radioCommand.dataType = 'application/json';
    radioCommand.port = SERVER_PORT;
    radioCommand.method = Constants.HttpOperation.POST;
    radioCommand.isSecure = false;

    console.log("Sending request to the smart home device:", payload);

    return this.app.getDeviceManager()
      .send(radioCommand)
      .then(() => {
        const state = {online: true};
        response.setSuccessState(device.id, Object.assign(state, params));
        console.log(`Command successfully sent to ${device.id}`);
      })
      .catch((e: IntentFlow.HandlerError) => {
        e.errorCode = e.errorCode || 'invalid_request';
        response.setErrorState(device.id, e.errorCode);
        console.error('An error occurred sending the command', e.errorCode);
      });
  });

  return Promise.all(promises)
    .then(() => {
      return response.build();
    })
    .catch((e) => {
      const err = new IntentFlow.HandlerError(request.requestId,
          'invalid_request', e.message);
      return Promise.reject(err);
    });
}

编译 TypeScript 应用

转到 local/ 目录并运行以下命令,以下载 TypeScript 编译器并编译应用:

cd local
npm install
npm run build

这会编译 index.ts (TypeScript) 源代码,并将以下内容放入 public/local-home/ 目录:

  • bundle.js - 已编译的 JavaScript 输出,包含本地应用和依赖项。
  • index.html - 用于提供应用以在设备上进行测试的本地托管网页。

部署测试项目

将更新后的项目文件部署到 Firebase Hosting,以便您可以从 Google Home 设备访问这些文件。

firebase deploy --only hosting

现在,您可以测试本地执行方式应用与智能洗衣机之间的通信了!该 Codelab 入门级项目包括一个使用 Node.js 编写的虚拟智能洗衣机,它用于模拟一个用户可在本地控制的智能洗衣机。

配置设备

您需要配置该虚拟设备,以使用您在 Actions 控制台中为设备发现扫描配置应用的相同 UDP 参数。此外,您还需要告知虚拟设备要报告的本地设备 ID,以及当设备状态更改时用于报告状态事件的 Actions 项目 ID。

参数

建议值

deviceId

deviceid123

discoveryPortOut

3311

discoveryPacket

HelloLocalHomeSDK

projectId

您的 Actions 项目 ID

启动设备

转到 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

在下一部分中,您将验证 Google Home 设备是否可以通过本地网络正确扫描、识别虚拟智能洗衣机以及向其发出命令。使用 Google Chrome 开发者工具,您可以连接到 Google Home 设备、查看控制台日志,并调试 TypeScript 应用。

关联 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 内容。

6b67ded470a4c8be.png

此输出意味着您的本地执行方式应用已成功发现和标识虚拟设备。

测试本地执行方式

使用 Google Home 应用的触控功能向您的设备发送指令,或向 Google Home 设备下达语音指令,例如:

“Ok Google,启动洗衣机。”

“Ok Google,运行洗衣机。”

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

这会触发平台将 EXECUTE intent 传递给您的 TypeScript 应用的操作。

bc030517dacc3ac9.png

验证您能否看到每个指令引起的本地智能洗衣机状态更改。

...
***** The washer is RUNNING *****
...
***** The washer is STOPPED *****

764dbc83b95782a.png

恭喜!您使用 Local Home SDK 将本地执行方式集成到了智能家居 Action 中。

了解详情

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

  • 更改扫描配置并让其正常运行。例如,尝试使用其他的 UDP 端口或发现数据包。
  • 修改虚拟智能设备代码库,以在嵌入式设备(例如 Raspberry Pi)上运行,并使用 LED 或显示屏呈现当前状态。