欢迎使用 Google Home 开发者中心,您可以在这里学习有关如何开发智能家居 Action 的新平台。注意:你将继续在 Actions 控制台中构建操作。

将智能家居设备关联到 Google 助理

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

1. 准备工作

作为一名物联网 (IoT) 开发者,您可以构建智能家居 Action,让用户能够通过 Google Home 应用中的触控功能和 Google 助理的语音指令来控制设备。

79266e5f45e6ae20.gif

智能家居 Action 依靠 Home Graph 来提供住宅及其设备的环境数据,从而创建住宅逻辑图。借助这些环境数据,Google 助理能够根据用户在住宅中的位置更自然地理解用户请求。例如,Home Graph 可以存储客厅这个概念,而客厅中包含来自不同制造商的多种类型的设备(例如恒温器、灯、风扇和吸尘器)。

d009cef0f903d284.jpeg

前提条件

构建内容

在此 Codelab 中,您将发布一个用于管理虚拟智能洗衣机的云服务,然后构建一个智能家居 Action,并将其关联到 Google 助理。

学习内容

  • 如何部署智能家居云服务
  • 如何将您的服务关联到 Google 助理
  • 如何将设备状态更改发布到 Google

所需条件

2. 开始使用

启用活动控件

若要使用 Google 助理,你必须与 Google 分享某些活动数据。Google 助理需要使用这些数据才能正常运行;然而,分享数据的要求并非专门针对该 SDK。如果你还没有 Google 帐号,请创建一个 Google 帐号,以便分享这些数据。你可以使用任何 Google 帐号,不要求必须用你的开发者帐号。

打开要与 Google 助理搭配使用的 Google 帐号的活动控件页面

确保已启用以下切换开关:

  • 网络与应用活动记录 - 此外,请务必选中包括 Chrome 历史记录和使用 Google 服务的网站、应用和设备中的活动记录复选框。
  • 设备信息
  • 语音和音频活动记录

创建 Actions 项目

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

3D6b68ca79afd54c.png

选择智能家居应用

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

2fa4988f44f8914b.png

选择智能家居体验卡片,点击开始构建,然后系统会将您转到项目控制台。

安装 Firebase CLI

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

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

npm install -g firebase-tools

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

firebase --version

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

firebase login

3. 运行入门级应用

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

获取源代码

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

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

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

项目简介

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

  • public: 一种前端界面,可轻松地控制和监控智能洗衣机的状态。
  • functions: 一种已全面实现的云服务,可使用 Cloud Functions for Firebase 和 Firebase Realtime Database 来管理智能洗衣机。

关联到 Firebase

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

cd washer-start
firebase use <project-id>

配置 Firebase 项目

初始化 Firebase 项目。

firebase init

依次选择 DatabaseFunctionsHosting CLI 功能。

? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then
 Enter to confirm your choices. 
❯◉ Database: Configure Firebase Realtime Database and deploy rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◉ Functions: Configure and deploy Cloud Functions
 ◉ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules
 ◯ Emulators: Set up local emulators for Firebase features
 ◯ Remote Config: Get, deploy, and rollback configurations for Remote Config

这将初始化项目的必要 API 和功能。

在系统显示提示时,初始化 Realtime Database。你可以使用数据库实例的默认位置。

? It seems like you haven't initialized Realtime Database in your project yet. Do you want to set it up? 
Yes

? Please choose the location for your default Realtime Database instance: us-central1

由于你使用的是入门级项目代码,因此请为安全规则选择默认文件,并确保不覆盖现有的数据库规则文件。

? What file should be used for Realtime Database Security Rules? Database.rules.json

? File database.rules.json already exists. Do you want to overwrite it with the Realtime Database Security Rules for <project-ID>-default-rtdb from the Firebase Console? 
No

同样,配置 Functions 时,你应该使用默认文件,并确保不覆盖项目示例中的现有 index.jspackage.json 文件。

? What language would you like to use to write Cloud Functions? 
JavaScript

? File functions/package.json already exists. Overwrite? 
No

? File functions/index.js already exists. Overwrite? 
No

? Do you want to install dependencies with npm now? 
Yes

最后,配置 Hosting 设置以便使用项目代码中的 public 目录,并使用现有的 index.html 文件。

? What do you want to use as your public directory? 
public

? Configure as a single-page app (rewrite all urls to /index.html)? 
Yes

? File public/index.html already exists. Overwrite?
 No

部署到 Firebase

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

cd functions
npm install

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

firebase deploy

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

...

✔ Deploy complete!

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

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

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

5845443e94705557

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

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

配置您的 Actions 控制台项目

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

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

9d7b223427f587ca.png

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

774d0c40c351c7da.png

a8c4673eb11d76ee.png

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

客户端 ID

ABC123

客户端密钥

DEF456

授权网址

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

令牌网址

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

9730d20b90bcc038.png

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

ee0547f05b5efd98.png

系统会将您重定向到 Simulator。如果您没有看到“测试已启用”,请点击重置测试以验证测试已启用。

d0495810dbadf059.png

现在,您可以开始实现必要的网络钩子,以便将设备状态关联到 Google 助理。

4. 创建洗衣机

Action 配置完毕后,您就可以添加设备并发送数据了。您的云服务需要处理以下 intent:

  • 当 Google 助理想要了解用户关联了哪些设备时,就会发生 SYNC intent。当用户关联帐号时,系统会向您的服务发送此 intent。您应该在响应时提供所有用户设备及其功能的 JSON 载荷。
  • 当 Google 助理想要了解设备的当前状态时,就会发生 QUERY intent。您应该在响应时提供 JSON 载荷,其中包含所请求的每台设备的状态。
  • 当 Google 助理想要代表用户控制设备时,就会发生 EXECUTE intent。您应该在响应时提供 JSON 载荷,其中包含所请求的每台设备的执行状态。
  • 当用户将帐号与 Google 助理解除关联时,就会发生 DISCONNECT intent。您应该停止向 Google 助理发送此用户设备的相关事件。

您将在后面几个部分中更新之前部署的用于处理这些 intent 的函数。

更新 SYNC 响应

打开 functions/index.js,其中包含用于响应来自 Google 助理的请求的代码。

您需要通过返回设备元数据和功能来处理 SYNC intent。更新 onSync 数组中的 JSON,以便包含洗衣机的设备信息和推荐特征

index.js

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
        ],
        name: {
          defaultNames: ['My Washer'],
          name: 'Washer',
          nicknames: ['Washer'],
        },
        deviceInfo: {
          manufacturer: 'Acme Co',
          model: 'acme-washer',
          hwVersion: '1.0',
          swVersion: '1.0.1',
        },
        willReportState: true,
        attributes: {
          pausable: true,
        },
      }],
    },
  };
});

部署到 Firebase

使用 Firebase CLI 部署更新后的云执行方式:

firebase deploy --only functions

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

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

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

ae252220753726f6.png

5. 处理命令和查询

您的云服务可以向 Google 正确报告洗衣机设备后,您需要添加用于请求设备状态和发送命令的功能。

处理 QUERY intent

QUERY intent 包含一组设备。对于每种设备,您应该在响应时提供其当前状态。

functions/index.js 中,修改 QUERY 处理程序,以处理 intent 请求中包含的目标设备列表。

index.js

app.onQuery(async (body) => {
  const {requestId} = body;
  const payload = {
    devices: {},
  };
  const queryPromises = [];
  const intent = body.inputs[0];
  for (const device of intent.payload.devices) {
    const deviceId = device.id;
    queryPromises.push(queryDevice(deviceId)
        .then((data) => {
        // Add response to device payload
          payload.devices[deviceId] = data;
        }
        ));
  }
  // Wait for all promises to resolve
  await Promise.all(queryPromises);
  return {
    requestId: requestId,
    payload: payload,
  };
});

针对请求中包含的每台设备,返回 Realtime Database 中存储的当前状态。更新 queryFirebasequeryDevice 函数,以返回洗衣机的状态数据。

index.js

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
  };
};

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{
      currentCycle: 'rinse',
      nextCycle: 'spin',
      lang: 'en',
    }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
  };
};

处理 EXECUTE intent

EXECUTE intent 可处理用于更新设备状态的命令。响应会返回每个命令的状态(例如 SUCCESSERRORPENDING)以及新的设备状态。

functions/index.js 中,修改 EXECUTE 处理程序,以处理需要更新的特征列表以及每个命令的目标设备集:

index.js

app.onExecute(async (body) => {
  const {requestId} = body;
  // Execution results are grouped by status
  const result = {
    ids: [],
    status: 'SUCCESS',
    states: {
      online: true,
    },
  };

  const executePromises = [];
  const intent = body.inputs[0];
  for (const command of intent.payload.commands) {
    for (const device of command.devices) {
      for (const execution of command.execution) {
        executePromises.push(
            updateDevice(execution, device.id)
                .then((data) => {
                  result.ids.push(device.id);
                  Object.assign(result.states, data);
                })
                .catch(() => functions.logger.error('EXECUTE', device.id)));
      }
    }
  }

  await Promise.all(executePromises);
  return {
    requestId: requestId,
    payload: {
      commands: [result],
    },
  };
});

针对每个命令和目标设备,更新 Realtime Database 中与所请求的特征相对应的值。修改 updateDevice 函数,以更新相应的 Firebase 引用并返回更新后的设备状态。

index.js

const updateDevice = async (execution, deviceId) => {
  const {params, command} = execution;
  let state; let ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
  }

  return ref.update(state)
      .then(() => state);
};

6. 测试您的 Action

三个 intent 全部实现后,您可以测试 Action 是否能够控制洗衣机。

部署到 Firebase

使用 Firebase CLI 部署更新后的云执行方式:

firebase deploy --only functions

测试洗衣机

现在,如果您通过手机尝试下达以下任意一条语音指令,就会看到值发生变化:

“Ok Google,启动洗衣机。”

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

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

您还可以通过提问来查询洗衣机的当前状态。

“Ok Google,我的洗衣机启动了吗?”

“Ok Google,我的洗衣机在运行吗?”

“Ok Google,我的洗衣机洗到哪一步了?”

您还可以在 Firebase 控制台日志中查看这些查询和命令,具体方法是在导航菜单中依次点击开发 > 函数 > 日志

ce02fb66fe2037dc.png

7. 向 Google 报告更新

您已将云服务与智能家居 intent 完全集成,让用户能够控制和查询设备的当前状态。不过,此实现仍缺少让您的服务能够主动向 Google 助理发送事件信息(例如,设备使用状态或状态的更改)的方法。

借助请求同步功能,您可以在用户添加或移除设备后,或在用户设备功能更改后触发新的同步请求。借助报告状态功能,在用户手动更改设备状态(例如,打开灯的开关)或使用其他服务更改状态后,您的云服务可以主动向 Home Graph 发送设备状态。

在此部分中,您将添加代码以从前端 Web 应用调用这些方法。

启用 HomeGraph API

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

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

ee198858a6eac112.png

启用报告状态

对 Realtime Database 的写入操作会触发入门级项目中的 reportstate 函数。更新 functions/index.js 中的 reportstate 函数,以捕获向数据库写入的数据,并通过状态报告将其发布到 Home Graph。

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      functions.logger.info('Firebase write event triggered Report State');
      const snapshot = change.after.val();

      const requestBody = {
        requestId: 'ff36a3cc', /* Any unique ID */
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              /* Report the current state of our washer */
              [context.params.deviceId]: {
                on: snapshot.OnOff.on,
                isPaused: snapshot.StartStop.isPaused,
                isRunning: snapshot.StartStop.isRunning,
              },
            },
          },
        },
      };

      const res = await homegraph.devices.reportStateAndNotification({
        requestBody,
      });
      functions.logger.info('Report state response:', res.status, res.data);
    });

启用请求同步

刷新前端网络界面的图标会触发入门级项目中的 requestsync 函数。在 functions/index.js 中实现 requestsync 函数,以调用 HomeGraph API。

index.js

exports.requestsync = functions.https.onRequest(async (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');
  functions.logger.info(`Request SYNC for user ${USER_ID}`);
  try {
    const res = await homegraph.devices.requestSync({
      requestBody: {
        agentUserId: USER_ID,
      },
    });
    functions.logger.info('Request sync response:', res.status, res.data);
    response.json(res.data);
  } catch (err) {
    functions.logger.error(err);
    response.status(500).send(`Error requesting sync: ${err}`);
  }
});

部署到 Firebase

使用 Firebase CLI 部署更新后的代码:

firebase deploy --only functions

测试实现效果

点击网络界面中的刷新 ae8d3b25777a5e30.png 按钮,并验证能否在 Firebase 控制台日志中看到同步请求。

5241d663238a8d04.png

接下来,调整前端网络界面中的洗衣机设备属性,然后点击更新。验证能否在 Firebase 控制台日志中看到向 Google 报告的状态更改。

8. 恭喜

674c4f4392e98c1.png

恭喜!您已成功使用智能家居 Action 将 Google 助理与设备云服务集成。

了解详情

您可以实现以下想法以进行更深入的研究:

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