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

增强智能家居 Action 并使其安全无虞

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

1. 准备工作

智能家居 Action 使用设备类型来让 Google 助理了解对某个设备应该使用哪种语法。设备特征定义各个设备类型的功能。设备会继承添加到 Action 的每个设备特征的状态。

dc8dce0dea87cd5c.png

您可以将任何支持的特征关联到您选择的设备类型,以便自定义用户设备的功能。如果您想在 Action 中实现目前设备架构中并未提供的自定义特征,则 ModesToggle 特征允许使用您定义的自定义名称来控制特定设置。

除了按类型和特征提供的基本控制功能以外,Smart Home API 还提供其他用于完善用户体验的功能。当 intent 未成功实现时,错误响应可提供详细的用户反馈。第二层用户身份验证会扩展这些响应,并提升您选择的设备特征的安全性。通过发送特定错误响应验证 Google 助理发出的阻止后,您的智能家居 Action 可能需要其他授权才能完成相应命令。

前提条件

构建内容

在本 Codelab 中,您将利用 Firebase 部署一个预构建的智能家居集成,然后学习如何向智能家居洗衣机添加针对负荷容量和涡轮模式的非标准特征。您还将实现错误和异常报告,并了解如何使用第二层用户身份验证强制启用口头确认功能来开启洗衣机。

学习内容

  • 如何向 Action 添加“Modes”和“Toggles”特征
  • 如何报告错误和异常
  • 如何应用第二层用户身份验证

所需条件

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

启用 HomeGraph API

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

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

ee198858a6eac112.png

3. 运行入门级应用

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

获取源代码

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

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

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

解压下载的 ZIP 文件。

项目简介

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

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

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

  • fakeauth:用于帐号关联的授权端点
  • faketoken:用于帐号关联的令牌端点
  • smarthome:智能家居 intent 执行方式端点
  • reportstate:在设备状态发生变化时调用 Home Graph API
  • requestsync:无需用户重新关联帐号即可启用用户设备更新

关联到 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

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

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

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

ae252220753726f6.png

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

现在,您已经部署了一个基础洗衣机,接下来,您可以自定义设备上的可用模式。

4. 添加模式

action.devices.traits.Modes 特征可让设备针对一种模式提供任意数量的设置(一次只能设定一项设置)。您可以向洗衣机添加模式,以便定义洗衣负荷容量:小、中或大。

更新 SYNC 响应

您需要向 functions/index.js 中的 SYNC 响应添加新特征的相关信息。这些数据会显示在 traits 数组和 attributes 对象中,如以下代码段所示。

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    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',
          // Add Modes trait          
          'action.devices.traits.Modes',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          //Add availableModes
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en',
            }],
            settings: [{
              setting_name: 'small',
              setting_values: [{
                setting_synonym: ['small'],
                lang: 'en',
              }]
            }, {
              setting_name: 'medium',
              setting_values: [{
                setting_synonym: ['medium'],
                lang: 'en',
              }]
            }, {
              setting_name: 'large',
              setting_values: [{
                setting_synonym: ['large'],
                lang: 'en',
              }]
            }],
            ordered: true,
          }],
        },
      }],
    },
  };
});

添加新的 EXECUTE intent 命令

EXECUTE intent 中添加 action.devices.commands.SetModes 命令,如以下代码段所示。

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution; 
  let state, 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;
    // Add SetModes command
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
}

更新 QUERY 响应

接下来,更新 QUERY 响应,以便报告洗衣机的当前状态。

将更新后的更改添加到 queryFirebasequeryDevice 函数以获取存储在 Realtime Database 中的状态。

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,
    // Add Modes snapshot
    load: snapshotVal.Modes.load,
  };
}

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    // Add currentModeSettings
    currentModeSettings: {
      load: data.load,
    },
  };
};

更新报告状态

最后,更新 reportstate 函数,以便将洗衣机的当前负荷设置报告给 Home Graph。

index.js

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

部署到 Firebase

运行以下命令以部署更新后的 Action:

firebase deploy --only functions

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

bf4f6a866160a982.png

现在,您可以发出一个命令来设置洗衣机的模式,例如:

“Ok Google, set the washer load to large.”(Ok Google,把洗衣机负荷容量设为大。)

此外,您还可以提出有关洗衣机的问题,例如:

“Ok Google, what is the washer load?”(Ok Google,洗衣机的负荷容量是多少?)

5. 添加切换开关

action.devices.traits.Toggles 特征表示设备的具有 true 或 false 状态的已命名方面,例如洗衣机是否处于涡轮模式。

更新 SYNC 响应

SYNC 响应中,您需要添加有关新设备特征的信息。这些信息将显示在 traits 数组和 attributes 对象中,如以下代码段所示。

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    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',
          'action.devices.traits.Modes',
          // Add Toggles trait
          'action.devices.traits.Toggles',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en'
            }],
            settings: [{ ... }],
            ordered: true,
          }],
          //Add availableToggles
          availableToggles: [{
            name: 'Turbo',
            name_values: [{
              name_synonym: ['turbo'],
              lang: 'en',
            }],
          }],
        },
      }],
    },
  };
});

添加新的 EXECUTE intent 命令

EXECUTE intent 中添加 action.devices.commands.SetToggles 命令,如以下代码段所示。

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, 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;
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
    // Add SetToggles command
    case 'action.devices.commands.SetToggles':
      state = {Turbo: params.updateToggleSettings.Turbo};
      ref = firebaseRef.child(deviceId).child('Toggles');
      break;
  }

更新 QUERY 响应

最后,您需要更新 QUERY 响应以报告洗衣机的涡轮模式。将更新后的更改添加到 queryFirebasequeryDevice 函数以获取存储在 Realtime Database 中的切换状态。

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,
    load: snapshotVal.Modes.load,
    // Add Toggles snapshot
    Turbo: snapshotVal.Toggles.Turbo,
  };
}

const queryDevice = async (deviceId) => {
  const data = queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    currentModeSettings: {
      load: data.load,
    },
    // Add currentToggleSettings
    currentToggleSettings: {
      Turbo: data.Turbo,
    },
  };
};

更新报告状态

最后,更新 reportstate 函数,以便向洗衣机报告洗衣机是否已设为涡轮模式。

index.js

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

部署到 Firebase

运行以下命令以部署更新后的函数:

firebase deploy --only functions

部署完成后,点击网络界面中的 Refresh ae8d3b25777a5e30.png 按钮以触发请求同步。

现在,您可以说出指令,以便将洗衣机设为涡轮模式:

“Ok Google, turn on turbo for the washer.”(Ok Google,开启洗衣机的涡轮模式。)

您还可以提出以下问题,以便检查洗衣机是否已处于涡轮模式:

“Ok Google, is my washer in turbo mode?”(Ok Google,我的洗衣机开启涡轮模式了吗?)

6. 报告错误和异常

借助智能家居 Action 中的错误处理功能,您可以在有问题导致 EXECUTEQUERY 响应失败时向用户报告相关信息。这些通知可在用户与您的智能设备和 Action 互动时为用户提供更积极的用户体验。

每当 EXECUTEQUERY 请求失败时,您的 Action 都应返回错误代码。例如,如果您想在用户试图在机盖处于打开状态的情况下启动洗衣机时抛出错误,那么您的 EXECUTE 响应将如以下代码段所示:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "456"
        ],
        "status": "ERROR",
        "errorCode": "deviceLidOpen"
      }
    ]
  }
}

现在,如果用户要求启动洗衣机,Google 助理会说出以下内容作为响应:

“The lid is open on the washer. Please close it and try again.”(洗衣机的机盖打开了。请关闭机盖,然后重试。)

异常与错误类似,但异常指示的是某个提醒与某个命令相关联的情况,这种情况未必会阻止顺利执行。异常可使用 StatusReport 特征提供相关信息,例如电池电量或近期状态更改。不会阻止执行的异常代码会随 SUCCESS 状态一起返回,而会阻止执行的异常代码会随 EXCEPTIONS 状态一起返回。

以下代码段中提供了包含异常的示例响应:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [{
      "ids": ["123"],
      "status": "SUCCESS",
      "states": {
        "online": true,
        "isPaused": false,
        "isRunning": false,
        "exceptionCode": "runCycleFinished"
      }
    }]
  }
}

Google 助理会说出以下内容作为响应:

“The washer has finished running.”(洗衣机已结束运行。)

如需为您的洗衣机添加错误报告,请打开 functions/index.js,然后添加错误类别定义,如以下代码段所示:

index.js

app.onQuery(async (body) => {...});

// Add SmartHome error handling
class SmartHomeError extends Error {
  constructor(errorCode, message) {
    super(message);
    this.name = this.constructor.name;
    this.errorCode = errorCode;
  }
}

更新执行响应以返回错误代码和错误状态:

index.js

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( ... )
          //Add error response handling
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
            }
          })
      );
    }
  }
}

现在,Google 助理可以告知用户您报告的任何错误代码了。在下一部分中,您会看到具体示例。

7. 添加次要用户验证信息

如果您的设备有任何需要保护的安全模式,或仅限特定授权用户群组使用(例如软件更新或锁定停用),您应在 Action 中实现第二层用户身份验证

您可以针对所有设备类型和特征实现第二层用户身份验证,从而自定义是否每次都要进行安全验证,或者是否需要满足特定条件。

支持的验证类型有以下三种

  • No challenge - 不使用身份验证的请求和响应(这是默认行为)
  • ackNeeded - 需要明确确认的次要用户验证(是或否)
  • pinNeeded - 第二层用户身份验证,要求提供个人识别码 (PIN)

在此 Codelab 中,向用于开启洗衣机的命令添加一个 ackNeeded 质询,以及在次要验证质询失败时返回错误的功能。

打开 functions/index.js,然后添加错误类别定义以返回错误代码和验证类型,如以下代码段所示:

index.js

class SmartHomeError extends Error { ... }

// Add secondary user verification error handling
class ChallengeNeededError extends SmartHomeError {
  /**
   * Create a new ChallengeNeededError
   * @param {string} suvType secondary user verification challenge type
   */
  constructor(suvType) {
    super('challengeNeeded', suvType);
    this.suvType = suvType;
  }
}

此外,您还需要更新相应的执行响应以返回 challengeNeeded 错误,具体如下所示:

index.js

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( ... )
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
              //Add error response handling
              if(error instanceof ChallengeNeededError) {
                result.challengeNeeded = {
                  type: error.suvType
                };
              }
            }
          })
      );
    }
  }
}

最后,修改 updateDevice 以要求通过显式确认来开启或关闭洗衣机。

index.js

const updateDevice = async (execution,deviceId) => {
  const {challenge,params,command} = execution; //Add secondary user challenge
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      //Add secondary user verification challenge
      if (!challenge || !challenge.ack) {
        throw new ChallengeNeededError('ackNeeded');
      }
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    ...
  }

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

部署到 Firebase

运行以下命令以部署更新后的函数:

firebase deploy --only functions

部署更新后的代码后,当您要求 Google 助理开启或关闭洗衣机时,您必须使用语音确认相应 Action,具体如下所示:

:“Ok Google, turn on the washer.”(Ok Google,开启洗衣机。)

Google 助理:“Are you sure you want to turn on the washer?”(确定要开启洗衣机吗?)

:“Yes.”(是的。)

您还可以打开 Firebase 日志,查看次要用户验证流程的每个步骤的详细响应。

289dbe48f4bb8106.png

8. 恭喜

674c4f4392e98c1.png

恭喜!你通过 ModesToggles 特征扩展了智能家居 Action 的功能,并通过第二层用户身份验证保障了它们的执行。

了解详情

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

  • 向您的设备添加本地执行功能。
  • 使用不同的次要用户验证验证类型来修改设备状态。
  • 更新 RunCycle 特征 QUERY 响应以进行动态更新。
  • 探索本 GitHub 示例