为了支持本地执行方式,你需要构建一个应用来处理以下智能家居 intent:
IDENTIFY
:支持发现可在本地控制的智能设备。intent 处理程序会提取你的智能设备在发现过程中返回的数据,并在响应 Google 时发送该数据。EXECUTE
:支持执行命令。QUERY
:支持查询设备状态。REACHABLE_DEVICES
:(可选)支持发现 hub(或网桥)设备所连接的本地可控终端设备。
此应用在用户的 Google Home 或 Google Nest 设备上运行,并将你的智能设备连接到 Google 助理。你可以使用 TypeScript(首选)或 JavaScript 创建应用。
建议您使用 TypeScript,因为您可以利用绑定以静态方式确保应用返回的数据与平台预期的类型相匹配。
如需详细了解该 API,请参阅 Local Home SDK API 参考文档。
以下代码段展示了如何初始化本地执行方式应用并附加处理程序。
import App = smarthome.App; const localHomeApp: App = new App("1.0.0"); localHomeApp .onIdentify(identifyHandler) .onExecute(executeHandler) .listen() .then(() => { console.log("Ready"); });
import App = smarthome.App; const localHomeApp: App = new App("1.0.0"); localHomeApp .onIdentify(identifyHandler) .onReachableDevices(reachableDevicesHandler) .onExecute(executeHandler) .listen() .then(() => { console.log("Ready"); });
创建项目
为了部署本地执行方式应用,你需要为代码及其所有依赖项构建 JavaScript 软件包。
使用本地执行方式应用项目初始化程序,使用你的首选捆绑器配置引导相应的项目结构。
项目模板
如需选择捆绑器配置,请运行 npm init
命令,如以下示例所示:
无捆绑器配置的 TypeScript:
npm init @google/local-home-app project-directory/ --bundler none
项目结构:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json └── serve.js
将 project-directory 替换为将包含本地执行方式应用项目的新目录。
使用 webpack 捆绑器配置的 TypeScript:
npm init @google/local-home-app project-directory/ --bundler webpack
项目结构:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json ├── webpack.config.web.js ├── webpack.config.node.js └── serve.js
将 project-directory 替换为将包含本地执行方式应用项目的新目录。
使用 Rollup 捆绑器配置的 TypeScript:
npm init @google/local-home-app project-directory/ --bundler rollup
项目结构:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json ├── rollup.config.js └── serve.js
将 project-directory 替换为将包含本地执行方式应用项目的新目录。
包含 Parcel 捆绑器配置的 TypeScript:
npm init @google/local-home-app project-directory/ --bundler parcel
项目结构:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json └── serve.js
将 project-directory 替换为将包含本地执行方式应用项目的新目录。
执行常见的项目级任务
生成的项目支持以下 npm 脚本:
cd project-directory/ npm run build
此脚本会编译 TypeScript 源代码,并在 dist/web
子目录中将你的应用与其 Chrome 运行时环境的依赖项捆绑,以及在 dist/node
子目录中将你的应用与 Node.js 运行时环境的依赖项捆绑。
cd project-directory/ npm run lint npm run compile npm test
此脚本会验证 TypeScript 代码的语法、对其进行编译(而不会在 dist/
子目录中产生任何输出),以及从 test.ts
运行自动测试。
cd project-directory/ npm run start
在开发期间,该脚本会在本地为 Chrome 运行时环境和 Node.js 运行时环境提供 app bundle。
实现 IDENTIFY 处理程序
当 Google Home 或 Google Nest 设备重新启动并发现未经验证的本地设备(包括连接到 hub 的终端设备)时,就会触发 IDENTIFY
处理程序。Local Home 平台将使用你之前指定的扫描配置信息扫描本地设备,并对扫描结果调用 IDENTIFY
处理程序。
Local Home 平台中的 IdentifyRequest
包含 LocalIdentifiedDevice
实例的扫描数据。根据发现了设备的扫描配置,系统仅填充一个 device
实例。
如果扫描结果与您的设备匹配,您的 IDENTIFY
处理程序应返回一个 IdentifyResponsePayload
对象,其中包括带有智能家居元数据(例如类型、特征和报告状态)的 device
对象。
如果 IDENTIFY
响应中的 verificationId
与 SYNC
响应返回的某个 otherDeviceIds
值匹配,Google 就会建立设备关联。
示例
以下代码段展示了如何分别为独立设备和 hub 集成创建 IDENTIFY
处理程序。
const identifyHandler = (request: IntentFlow.IdentifyRequest): IntentFlow.IdentifyResponse => { // Obtain scan data from protocol defined in your scan config const device = request.inputs[0].payload.device; if (device.udpScanData === undefined) { throw Error("Missing discovery response"); } const scanData = device.udpScanData.data; // Decode scan data to obtain metadata about local device const verificationId = "local-device-id"; // Return a response const response: IntentFlow.IdentifyResponse = { intent: Intents.IDENTIFY, requestId: request.requestId, payload: { device: { id: device.id || "", verificationId, // Must match otherDeviceIds in SYNC response }, }, }; return response; };
const identifyHandler = (request: IntentFlow.IdentifyRequest): IntentFlow.IdentifyResponse => { // Obtain scan data from protocol defined in your scan config const device = request.inputs[0].payload.device; if (device.udpScanData === undefined) { throw Error("Missing discovery response"); } const scanData = device.udpScanData.data; // Decode scan data to obtain metadata about local device const proxyDeviceId = "local-hub-id"; // Return a response const response: IntentFlow.IdentifyResponse = { intent: Intents.IDENTIFY, requestId: request.requestId, payload: { device: { id: proxyDeviceId, isProxy: true, // Device can control other local devices isLocalOnly: true, // Device not present in `SYNC` response }, }, }; return response; };
识别连接到 hub 的设备
如果 Google 发现一个 hub 设备,则会将该 hub 视为连接到 hub 的终端设备的管道,并尝试验证这些终端设备。
如需让 Google 确认某个 hub 设备的存在,请按照以下 IDENTIFY
处理程序相关说明进行操作:
- 如果你的
SYNC
响应报告已连接到 hub 的本地终端设备 ID,请在IdentifyResponsePayload
中将isProxy
设置为true
。 - 如果您的
SYNC
响应未报告您的 hub 设备,请在IdentifyResponsePayload
中将isLocalOnly
设置为true
。 device.id
字段包含 hub 设备本身的本地设备 ID。
实现 REACHABLE_DEVICES 处理程序(仅限 hub 集成)
REACHABLE_DEVICES
intent 由 Google 发送,用于确认哪些终端设备可在本地控制。每当 Google 运行发现扫描(大约每分钟一次)时,只要检测到 hub 处于在线状态,就会触发此 intent。
REACHABLE_DEVICES
处理程序与 IDENTIFY
处理程序的实现方式类似,区别在于前者需要收集本地代理(即 hub)设备可以访问的附加设备 ID。device.verificationId
字段包含连接到 hub 的终端设备的本地设备 ID。
Local Home 平台中的 ReachableDevicesRequest
包含一个 LocalIdentifiedDevice
实例。通过此实例,你可以获取代理设备 ID 以及扫描结果中的数据。
你的 REACHABLE_DEVICES
处理程序应返回一个 ReachableDevicesPayload
对象,该对象包含一个 devices
对象(其中包含代表 hub 控制的终端设备的一组 verificationId
值)。verificationId
值必须与 SYNC
响应中的某个 otherDeviceIds
匹配。
以下代码段展示了如何创建 REACHABLE_DEVICES
处理程序。
const reachableDevicesHandler = (request: IntentFlow.ReachableDevicesRequest): IntentFlow.ReachableDevicesResponse => { // Reference to the local proxy device const proxyDeviceId = request.inputs[0].payload.device.id; // Gather additional device ids reachable by local proxy device // ... const reachableDevices = [ // Each verificationId must match one of the otherDeviceIds // in the SYNC response { verificationId: "local-device-id-1" }, { verificationId: "local-device-id-2" }, ]; // Return a response const response: IntentFlow.ReachableDevicesResponse = { intent: Intents.REACHABLE_DEVICES, requestId: request.requestId, payload: { devices: reachableDevices, }, }; return response; };
实现 EXECUTE 处理程序
应用中的 EXECUTE
处理程序会处理用户命令,并使用 Local Home SDK 通过现有协议访问你的智能设备。
Local Home 平台将同一输入载荷传递给 EXECUTE
处理程序函数,与将 EXECUTE
intent 传递到你的云执行方式的操作相同。同样,EXECUTE
处理程序返回的输出数据格式与处理 EXECUTE
intent 时相同。为了简化响应创建过程,您可以使用 Local Home SDK 提供的 Execute.Response.Builder
类。
你的应用没有直接访问设备 IP 地址的权限。请改用 CommandRequest
接口,根据以下协议之一创建命令:UDP、TCP 或 HTTP。然后,调用 deviceManager.send()
函数来发送创建的命令。
将命令定位到设备时,请使用 SYNC
响应中的设备 ID(以及 customData
字段中的参数,如果包含)与设备进行通信。
示例
以下代码段展示了如何创建 EXECUTE
处理程序。
const executeHandler = (request: IntentFlow.ExecuteRequest): Promise<IntentFlow.ExecuteResponse> => { // Extract command(s) and device target(s) from request const command = request.inputs[0].payload.commands[0]; const execution = command.execution[0]; const response = new Execute.Response.Builder() .setRequestId(request.requestId); const result = command.devices.map((device) => { // Target id of the device provided in the SYNC response const deviceId = device.id; // Metadata for the device provided in the SYNC response // Use customData to provide additional required execution parameters const customData: any = device.customData; // Convert execution command into payload for local device let devicePayload: string; // ... // Construct a local device command over TCP const deviceCommand = new DataFlow.TcpRequestData(); deviceCommand.requestId = request.requestId; deviceCommand.deviceId = deviceId; deviceCommand.data = devicePayload; deviceCommand.port = customData.port; deviceCommand.operation = Constants.TcpOperation.WRITE; // Send command to the local device return localHomeApp.getDeviceManager() .send(deviceCommand) .then((result) => { response.setSuccessState(result.deviceId, state); }) .catch((err: IntentFlow.HandlerError) => { err.errorCode = err.errorCode || IntentFlow.ErrorCode.INVALID_REQUEST; response.setErrorState(device.id, err.errorCode); }); }); // Respond once all commands complete return Promise.all(result) .then(() => response.build()); };
实现 QUERY 处理程序
应用中的 QUERY
处理程序会处理用户请求,并使用 Local Home SDK 报告你的智能设备的状态。
Local Home 平台将同一请求载荷传递给“QUERY”处理程序函数,与将 QUERY
intent 传递给你的云执行方式的操作相同。同样,QUERY
处理程序返回的数据格式与处理 QUERY
intent 时相同。
向连接到 hub 的设备发送命令
如需控制连接到 hub 的终端设备,你可能需要在发送到该 hub 的协议专用命令载荷中提供额外信息,以便该 hub 确定该命令针对的是哪个设备。在某些情况下,这可以根据 device.id
值直接推断出来,但如果不是这种情况,则应将该额外数据作为 customData
字段的一部分包含在内。
如果你使用 TypeScript 创建了应用,请记得将应用编译为 JavaScript。你可以使用自己选择的模块系统来编写代码。 确保 Chrome 浏览器支持你的目标。