事件是异步的,由 Google Cloud Pub/Sub 管理,每个 Project对应一个主题。事件会提供所有设备和结构的更新,只要用户未撤消访问令牌且事件消息未过期,就一定会收到事件。
允许显示活动
事件是 SDM API 的一项可选功能。请参阅 启用事件 ,了解如何为 Project启用事件。
Google Cloud Pub/Sub
如需详细了解 Pub/Sub 的运作方式,请参阅 Google Cloud Pub/Sub 文档。具体而言:
- 通过操作指南了解 Pub/Sub 的基础知识。
- 了解身份验证的工作原理。
- 选择提供的客户端库,或自行编写并使用 REST/HTTP 或 gRPC API 接口。
事件订阅
在 2025 年 1 月之前,如果为您的 Project启用了事件,您会收到一个特定于该 Project ID 的 Google 托管主题,格式如下:
projects/sdm-prod/topics/enterprise-project-id
现在,所有项目都必须在其自己的 Google Cloud 项目中自行托管其 Pub/Sub 主题(自 2025 年 1 月起,新项目必须这样做,所有现有项目必须在 2026 年 11 月 30 日之前这样做),格式如下:
projects/gcp-project-name/subscriptions/topic-id
如果您的 project 已在您自己的 Google Cloud 项目中使用自托管主题,则无需执行任何操作。迁移 Pub/Sub 主题配置不会使现有用户访问令牌或 OAuth 权限失效。自托管主题和订阅需遵循标准 Google Cloud Pub/Sub 价格(包括每月 10 GB 的免费层级)。如需了解详情,请参阅创建主题。
如需接收事件,请根据您的应用场景创建对该主题的拉取或推送订阅。支持对 SDM 主题进行多次订阅。如需了解详情,请参阅管理订阅。
发起事件
创建 Pub/Sub 订阅后,如需首次发起事件,请进行
devices.list API 调用作为一次性触发器。此调用之后,系统会发布所有结构和设备的事件。
如需查看示例,请参阅《快速入门指南》中的授权页面。
活动顺序
Pub/Sub 不保证事件的有序传送,事件的接收顺序可能与事件的实际发生顺序不一致。使用 timestamp 字段可帮助协调事件顺序。事件也可能单独到达或合并到单个事件消息中。
如需了解详情,请参阅消息排序。
用户 ID
如果您的实现基于用户(而非结构或设备),请使用事件载荷中的 userID 字段来关联资源和事件。此字段是代表特定用户的经过混淆处理的 ID。
userID 也会在每个 API 调用的 HTTP 响应标头中提供。
关系事件
关系事件表示资源的关联更新。例如,向结构中添加设备或从结构中删除设备时。
关系事件分为三种类型:
- CREATED
- 已删除
- 已更新
关系事件的载荷如下所示:
载荷
{
"eventId" : "1a2e13bc-c23c-4bc4-8366-6b5669607f0e",
"timestamp" : "2019-01-01T00:00:01Z",
"relationUpdate" : {
"type" : "CREATED",
"subject" : "enterprises/project-id/structures/structure-id",
"object" : "enterprises/project-id/devices/device-id"
},
"userId": "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi"
}在关系事件中,object 是触发事件的资源,subject 是 object 现在与之具有关系的资源。在上面的示例中, user 已向 developer授予对此特定设备的访问权限,并且 user的授权设备现在与其授权结构相关联,这会触发事件。
subject 只能是房间或结构体。如果 a developer 无权查看 user的结构,则 subject 始终为空。
字段
| 字段 | 说明 | 数据类型 |
|---|---|---|
eventId |
事件的唯一标识符。 | string示例:"a121b435-e3bb-4ba1-9dea-945686b7dabf" |
timestamp |
事件发生的时间。 | string示例:"2019-01-01T00:00:01Z" |
relationUpdate |
详细说明关系更新信息的对象。 | object |
userId |
代表用户的唯一、经过混淆处理的标识符。 | string示例:"AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi" |
如需详细了解不同类型的事件及其运作方式,请参阅事件。
示例
每种类型的关系事件的事件载荷各不相同:
创建时间
已创建结构
"relationUpdate" : {
"type" : "CREATED",
"subject" : "",
"object" : "enterprises/project-id/structures/structure-id"
}已创建设备
"relationUpdate" : {
"type" : "CREATED",
"subject" : "enterprises/project-id/structures/structure-id",
"object" : "enterprises/project-id/devices/device-id"
}已创建设备
"relationUpdate" : {
"type" : "CREATED",
"subject" : "enterprises/project-id/structures/structure-id/rooms/room-id",
"object" : "enterprises/project-id/devices/device-id"
}已更新
设备移动完毕
"relationUpdate" : {
"type" : "UPDATED",
"subject" : "enterprises/project-id/structures/structure-id/rooms/room-id",
"object" : "enterprises/project-id/devices/device-id"
}已删除
结构已删除
"relationUpdate" : {
"type" : "DELETED",
"subject" : "",
"object" : "enterprises/project-id/structures/structure-id"
}设备已删除
"relationUpdate" : {
"type" : "DELETED",
"subject" : "enterprises/project-id/structures/structure-id",
"object" : "enterprises/project-id/devices/device-id"
}设备已删除
"relationUpdate" : {
"type" : "DELETED",
"subject" : "enterprises/project-id/structures/structure-id/rooms/room-id",
"object" : "enterprises/project-id/devices/device-id"
}在以下情况下,系统不会发送关系事件:
- 聊天室已删除
资源事件
资源事件表示特定于资源的更新。这可能是为了响应特征字段值的变化,例如更改温控器的模式。 它还可以表示不更改特征字段的设备操作,例如按下设备按钮。
为响应 trait 字段值的更改而生成的事件包含一个 traits 对象,类似于设备 GET 调用:
载荷
{
"eventId" : "caec1000-1cab-43d3-946f-bc3c5f8615c9",
"timestamp" : "2019-01-01T00:00:01Z",
"resourceUpdate" : {
"name" : "enterprises/project-id/devices/device-id",
"traits" : {
"sdm.devices.traits.ThermostatMode" : {
"mode" : "COOL"
}
}
},
"userId": "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi",
"resourceGroup" : [
"enterprises/project-id/devices/device-id"
]
}请参阅各个特征文档,了解任何特征字段更改资源事件的载荷格式。
为响应不更改 trait 字段的设备操作而生成的事件也具有包含 resourceUpdate 对象的载荷,但具有 events 对象而不是 traits 对象:
载荷
{
"eventId" : "0111d3f2-5e7d-4ecf-88ed-8f3b0324f75c",
"timestamp" : "2019-01-01T00:00:01Z",
"resourceUpdate" : {
"name" : "enterprises/project-id/devices/device-id",
"events" : {
"sdm.devices.events.CameraMotion.Motion" : {
"eventSessionId" : "CjY5Y3VKaTZwR3o4Y19YbTVfMF...",
"eventId" : "jOvQqNOSL76837k_JxTR4BJ6nw...",
}
}
}
"userId" : "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi",
"eventThreadId" : "d67cd3f7-86a7-425e-8bb3-462f92ec9f59",
"eventThreadState" : "STARTED",
"resourceGroup" : [
"enterprises/project-id/devices/device-id"
]
}这些类型的资源事件在特定 trait 中定义。例如,运动事件在 CameraMotion 特征中定义。请参阅每个 trait 的文档,了解这些类型的资源事件的载荷格式。
字段
| 字段 | 说明 | 数据类型 |
|---|---|---|
eventId |
事件的唯一标识符。 | string示例:"0111d3f2-5e7d-4ecf-88ed-8f3b0324f75c" |
timestamp |
事件发生的时间。 | string示例:"2019-01-01T00:00:01Z" |
resourceUpdate |
详细说明资源更新信息的对象。 | object |
userId |
代表用户的唯一、经过混淆处理的标识符。 | string示例:"AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi" |
eventThreadId |
事件线程的唯一标识符。 | string示例:"d67cd3f7-86a7-425e-8bb3-462f92ec9f59" |
eventThreadState |
事件线程的状态。 | string值:"STARTED"、"UPDATED"、"ENDED" |
resourceGroup |
一个对象,用于指示可能与此事件有类似更新的资源。事件本身的资源(来自 resourceUpdate 对象)将始终存在于此对象中。 |
object |
如需详细了解不同类型的事件及其运作方式,请参阅事件。
可更新的通知
基于资源事件的通知可以在应用(例如 Android 或 iOS 应用)中实现。为了减少发送的通知数量,可以实现一项名为可更新通知的功能,该功能会根据同一事件线程中的后续事件,使用新信息更新现有通知。部分事件支持可更新的通知,并在文档中标记为可更新 eventThreadId 的额外字段。使用此字段可将各个事件关联在一起,以便更新已向用户显示的现有通知。
事件会话与事件会话不同。事件线程用于标识同一线程中先前事件的更新状态。事件会话用于标识彼此相关的单独事件,并且给定事件会话可以有多个事件线程。
出于通知目的,不同类型的事件会分组到不同的会话中。
此会话分组和时间逻辑由 Google 处理,随时可能发生变化。A developer 应根据 SDM API 提供的事件线程和会话更新通知。
线程状态
支持可更新通知的事件还具有一个 eventThreadState 字段,用于指示该时间点的事件线程状态。此字段具有以下值:
- STARTED - 事件线程中的第一个事件。
- UPDATED - 正在进行的事件线程中的事件。在单个线程中,可以有零个或多个具有此状态的事件。
- ENDED - 事件线程中的最后一个事件,可能是最后一个 UPDATED 事件的重复事件,具体取决于线程类型。
此字段可用于跟踪事件线程的进度以及事件线程结束的时间。
事件过滤
在某些情况下,设备检测到的事件可能会被过滤掉,而不会发布到 SDM Pub/Sub 主题。这种行为称为事件过滤。事件过滤的目的是避免在短时间内发布过多类似的事件消息。
例如,系统可能会针对初始移动事件向 SDM 主题发布消息。在此之后,系统会过滤掉其他 Motion 消息,直到设定的时间段过去。该时间段过后,系统可能会再次发布该事件类型的事件消息。
在 Google Home 应用 (GHA) 中,被过滤掉的事件仍会显示在 user的事件记录中。不过,此类事件不会生成应用通知(即使已启用该通知类型)。
每种类型的事件都有自己的事件过滤逻辑,该逻辑由 Google 定义,并且随时可能发生变化。此事件过滤逻辑独立于事件线程和会话逻辑。
服务账号
建议使用服务账号来管理 SDM API 订阅和事件消息。服务账号由应用或虚拟机(而非个人)使用,并具有自己的唯一账号密钥。
Pub/Sub API 的服务账号授权使用二足式 OAuth (2LO)。
在 2LO 授权流程中:
- developer 使用服务密钥请求访问令牌。
- developer 在调用 API 时使用访问令牌。
如需详细了解 Google 2LO 以及如何进行设置,请参阅针对服务器对服务器应用使用 OAuth 2.0。
授权
应授权服务账号与 Pub/Sub API 搭配使用:
- 在 Google Cloud 中启用 Cloud Pub/Sub API。
- 按照创建服务账号中的说明创建服务账号和服务账号密钥。我们建议仅向其授予 Pub/Sub Subscriber 角色。请务必将服务账号密钥下载到将使用 Pub/Sub API 的机器。
- 按照上一步页面中的说明,向应用代码提供身份验证凭证(服务账号密钥),或者如果您想快速测试 API 访问权限,请使用
oauth2l手动获取访问令牌。 - 将服务账号凭证或访问令牌与 Pub/Sub
project.subscriptionsAPI 搭配使用,以拉取和确认消息。
oauth2l
Google oauth2l 是一个用 Go 编写的 OAuth 命令行工具。您可以使用 Go 为 Mac 或 Linux 安装该工具。
- 如果您的系统上没有 Go,请先下载并安装。
- 安装 Go 后,安装
oauth2l并将其位置添加到PATH环境变量中:go install github.com/google/oauth2l@latestexport PATH=$PATH:~/go/bin - 使用
oauth2l获取 API 的访问令牌,并使用适当的 OAuth 范围: 例如,如果您的服务密钥位于oauth2l fetch --credentials path-to-service-key.json --scope https://www.googleapis.com/auth/pubsub https://www.googleapis.com/auth/cloud-platform~/myServiceKey-eb0a5f900ee3.json:oauth2l fetch --credentials ~/myServiceKey-eb0a5f900ee3.json --scope https://www.googleapis.com/auth/pubsub https://www.googleapis.com/auth/cloud-platformya29.c.Elo4BmHXK5...
如需了解更多使用信息,请参阅 oauth2l 自述文件。
Google API 客户端库
有多个客户端库可用于利用 OAuth 2.0 的 Google API。如需详细了解您选择的语言,请参阅 Google API 客户端库。
将这些库与 Pub/Sub API搭配使用时,请使用以下范围字符串:
https://www.googleapis.com/auth/pubsub https://www.googleapis.com/auth/cloud-platform
错误
系统可能会返回与本指南相关的以下错误代码:
| 错误消息 | RPC | 问题排查 |
|---|---|---|
| 摄像头图片已无法下载。 | DEADLINE_EXCEEDED |
活动图片会在活动发布 30 秒后失效。请务必在到期前下载图片。 |
| 事件 ID 不属于该摄像头。 | FAILED_PRECONDITION |
使用相机事件返回的正确 eventID。 |
如需查看 API 错误代码的完整列表,请参阅 API 错误代码参考文档。