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

关于此 Codelab
schedule35 分钟
subject上次更新时间:2024年11月20日
account_circleGoogle 员工编写

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

79266e5f45e6ae20.gif

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

d009cef0f903d284.jpeg

前提条件

构建内容

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

学习内容

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

所需条件

2. 开始使用

启用活动控件

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

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

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

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

创建云到云集成项目

  1. 前往开发者控制台
  2. 点击 Create Project,输入项目名称,然后点击 Create Project

为项目命名

选择云到云集成

在 Play 管理中心的项目首页上,选择云到云下的添加云到云集成

添加云到云集成

安装 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/google-home/smarthome-washer.git

项目简介

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

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

创建 Firebase 项目

  1. 前往 Firebase
  2. 点击创建项目,然后输入项目名称。
  3. 选中同意复选框,然后点击继续。如果没有协议复选框,您可以跳过此步骤。
    创建 Firebase 项目
  4. 创建 Firebase 项目后,找到项目 ID。前往项目概览,然后依次点击“设置”图标 > 项目设置
    打开项目设置
  5. 您的项目会列在常规标签页下。
    常规项目设置

关联到 Firebase

前往 washer-start 目录,然后使用您的集成项目设置 Firebase CLI:

cd washer-start
firebase use <firebase-project-id>

配置 Firebase 项目

初始化 Firebase 项目。

firebase init

选择 CLI 功能 Realtime DatabaseFunctions 以及包含 Firebase 托管的 Hosting 功能。

? Which Firebase CLI features do you want to set up for this directory? Press Space to select features, then
 Enter to confirm your choices.
❯◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◯ Firestore: Configure security rules and indexes files for Firestore
 ◉ Functions: Configure a Cloud Functions directory and its files
 ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◯ Storage: Configure a security rules file for Cloud Storage
 ◯ Emulators: Set up local emulators for Firebase products
 ◯ Remote Config: Configure a template file for Remote Config
 ◯ Extensions: Set up an empty Extensions manifest

这将初始化项目的必要 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

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

? 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

如果您要重新初始化项目,则在系统询问您是要初始化还是覆盖代码库时,选择覆盖

? Would you like to initialize a new codebase, or overwrite an existing one?
Overwrite

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

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

? Do you want to use ESLint to catch probable bugs and enforce style?
No

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

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

如果您要重新初始化项目,则在系统询问您是否要初始化或覆盖 functions/.gitignore 时,选择 No

? File functions/.gitignore already exists. Overwrite?
No
? Do you want to install dependencies with npm now?
Yes

最后,配置 Hosting 设置以便使用项目代码中的 public 目录,并使用现有的 index.html 文件。当系统要求您使用 ESLint 时,选择

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

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

? Set up automatic builds and deploys with GitHub?
No

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

如果您不小心启用了 ESLint,可以通过以下两种方法将其停用:

  1. 使用 GUI 前往项目下的 ../functions 文件夹,选择隐藏文件 .eslintrc.js 并将其删除。请勿将其与同名 .eslintrc.json 混淆。
  2. 使用命令行:
    cd functions
    rm .eslintrc.js
    

washer-done/firebase.json 文件中,使用以下代码完成相应代码:

{
 
"database": {
   
"rules": "database.rules.json"
 
},
 
"hosting": {
   
"public": "public",
   
"ignore": [
     
"firebase.json",
     
"**/.*",
     
"**/node_modules/**"
   
],
   
"rewrites": [
     
{
       
"source": "**",
       
"destination": "/index.html"
     
}
   
]
 
},
   
"headers": [{
     
"source" : "**/*.@(js|html)",
     
"headers" : [ {
       
"key" : "Cache-Control",
       
"value" : "max-age=0"
     
} ]
   
}],
 
"functions": [
   
{
     
"source": "functions",
     
"codebase": "default",
     
"ignore": [
       
"node_modules",
       
".git",
       
"firebase-debug.log",
       
"firebase-debug.*.log",
       
"*.local"
     
]
   
}
 
]
}

部署到 Firebase

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

firebase deploy

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

...

✔ Deploy complete!

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

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

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

5845443e94705557.png

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

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

配置您的 Play 管理中心项目

Develop 标签页中,为您的互动添加显示名称。此名称会显示在 Google Home 应用中。

添加显示名称

应用品牌信息下,为应用图标上传一个大小为 144 x 144 像素且名为 .pngpng 文件。

添加应用图标

如需启用账号关联,请使用以下账号关联设置:

客户 ID

ABC123

客户端密钥

DEF456

授权网址

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

令牌网址

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

更新账号关联网址

Cloud fulfillment 网址 下,输入为智能家居 intent 提供执行方式的 Cloud Functions 函数的网址。

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

添加 Cloud Functions 函数网址

点击 Save 保存您的项目配置,然后点击 Next: Test 针对您的项目启用测试。

测试云到云集成

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

4. 创建洗衣机

集成配置完毕后,您就可以添加设备并发送数据了。您的云服务需要处理以下 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

为了测试云到云集成,您需要将项目与 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
= params.start
     
? {isRunning: true, isPaused: false}
     
: {isRunning: false, isPaused: false};
     
ref = firebaseRef.child(deviceId).child('StartStop');
     
break;
   
case 'action.devices.commands.PauseUnpause':
     
const data = await queryDevice(deviceId);
      state
= (data.isPaused === false && data.isRunning === false)
       
? {isRunning: false, isPaused: false}
       
: {isRunning: !params.pause, isPaused: params.pause};
     
ref = firebaseRef.child(deviceId).child('StartStop');
     
break;
 
}

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

6. 测试集成

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

部署到 Firebase

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

firebase deploy --only functions

测试洗衣机

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

“Ok Google,启动洗衣机。”

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

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

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

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

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

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

您可以在 Firebase 控制台函数部分中,查看函数下方显示的日志中显示的这些查询和命令。如需详细了解 Firebase 日志,请参阅写入和查看日志

您还可以在 Google Cloud 控制台中找到这些查询和命令,方法是依次前往 Logging > Logs Explorer。如需详细了解 Google Cloud 日志记录,请参阅使用 Cloud Logging 访问事件日志

7. 向 Google 报告更新

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

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

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

启用 HomeGraph API

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

在 Google Cloud 控制台中,请务必选择与您的集成 <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

测试实现效果

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

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

8. 恭喜

674c4f4392e98c1.png

恭喜!您已成功使用云到云集成将 Google 助理与设备云服务集成。

了解详情

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

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