Para admitir la entrega local, debes crear una app que controle estos intents de casa inteligente:
IDENTIFY
: Admite el descubrimiento de dispositivos inteligentes que se pueden controlar de forma local. El controlador de intents extrae los datos que devuelve tu dispositivo inteligente durante la detección y los envía en una respuesta a Google.EXECUTE
: Admite la ejecución de comandos.QUERY
: Admite consultas sobre el estado del dispositivo.REACHABLE_DEVICES
: (Opcional) Admite el descubrimiento de dispositivos finales controlables de forma local detrás de un dispositivo concentrador (o puente).
Esta app se ejecuta en los dispositivos Google Home o Google Nest del usuario y conecta tu dispositivo inteligente al Asistente. Puedes crear la app con TypeScript (opción preferida) o JavaScript.
Se recomienda TypeScript porque puedes aprovechar las vinculaciones para garantizar de forma estática que los datos que devuelve tu app coincidan con los tipos que espera la plataforma.
Para obtener más detalles sobre la API, consulta la referencia de la API del SDK de Local Home.
En los siguientes fragmentos, se muestra cómo puedes inicializar la app de entrega local y adjuntar tus controladores.
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"); });
Crea tu proyecto
Para implementar tu app de procesamiento local, debes compilar un paquete de JavaScript para tu código y todas sus dependencias.
Usa el inicializador de proyectos de la app de entrega local para iniciar la estructura de proyecto adecuada con la configuración de agrupador que prefieras.
Plantillas de proyectos
Para seleccionar la configuración del empaquetador, ejecuta el comando npm init
como se muestra en los siguientes ejemplos:
TypeScript sin configuración de bundler:
npm init @google/local-home-app project-directory/ --bundler none
Estructura del proyecto:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json └── serve.js
Reemplaza project-directory por un directorio nuevo que contendrá el proyecto de la app de cumplimiento local.
Configuración de TypeScript con el agrupador webpack:
npm init @google/local-home-app project-directory/ --bundler webpack
Estructura del proyecto:
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
Reemplaza project-directory por un directorio nuevo que contendrá el proyecto de la app de cumplimiento local.
Configuración del bundler Rollup con TypeScript:
npm init @google/local-home-app project-directory/ --bundler rollup
Estructura del proyecto:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json ├── rollup.config.js └── serve.js
Reemplaza project-directory por un directorio nuevo que contendrá el proyecto de la app de cumplimiento local.
Configuración del bundler Parcel con TypeScript:
npm init @google/local-home-app project-directory/ --bundler parcel
Estructura del proyecto:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json └── serve.js
Reemplaza project-directory por un directorio nuevo que contendrá el proyecto de la app de cumplimiento local.
Realiza tareas comunes a nivel del proyecto
El proyecto generado admite los siguientes scripts de npm:
cd project-directory/ npm run build
Esta secuencia de comandos compila la fuente de TypeScript y agrupa tu app con sus dependencias para el entorno de ejecución de Chrome en el subdirectorio dist/web
y el entorno de ejecución de Node.js en el subdirectorio dist/node
.
cd project-directory/ npm run lint npm run compile npm test
Esta secuencia de comandos verifica la sintaxis de tu código TypeScript, lo compila sin generar ningún resultado en el subdirectorio dist/
y ejecuta pruebas automatizadas desde test.ts
.
cd project-directory/ npm run start
Durante el desarrollo, esta secuencia de comandos entrega los paquetes de tu app para los entornos de ejecución de Chrome y Node.js de forma local.
Implementa el controlador IDENTIFY
El controlador IDENTIFY
se activará cuando se reinicie el dispositivo Google Home o Google Nest y vea dispositivos locales no verificados (incluidos los dispositivos finales conectados a una unidad central). La plataforma de Local Home analizará los dispositivos locales con la información de configuración de análisis que especificaste antes y llamará a tu controlador IDENTIFY
con los resultados del análisis.
El objeto IdentifyRequest
de la plataforma de Local Home contiene los datos de análisis de una instancia de LocalIdentifiedDevice
. Solo se propaga una instancia de device
, según la configuración de análisis que descubrió el dispositivo.
Si los resultados del análisis coinciden con tu dispositivo, el controlador IDENTIFY
debe devolver un objeto IdentifyResponsePayload
que incluya un objeto device
con metadatos de la casa inteligente (como los tipos, los rasgos y el estado del informe).
Google establece una asociación de dispositivo si el verificationId
de la respuesta IDENTIFY
coincide con uno de los valores otherDeviceIds
que muestra la respuesta SYNC
.
Ejemplo
En los siguientes fragmentos, se muestra cómo podrías crear controladores de IDENTIFY
para integraciones de dispositivos independientes y de concentradores, respectivamente.
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; };
Cómo identificar dispositivos detrás de un concentrador
Si Google identifica un dispositivo concentrador, lo tratará como el conducto hacia los dispositivos finales conectados al concentrador y tratará de verificar esos dispositivos finales.
Para permitir que Google confirme que hay un dispositivo concentrador presente, sigue estas instrucciones para tu controlador de IDENTIFY
:
- Si la respuesta de
SYNC
informa los IDs de los dispositivos finales locales conectados al hub, estableceisProxy
comotrue
enIdentifyResponsePayload
. - Si la respuesta de
SYNC
no informa tu dispositivo concentrador, estableceisLocalOnly
comotrue
enIdentifyResponsePayload
. - El campo
device.id
contiene el ID del dispositivo local para el dispositivo concentrador.
Implementa el controlador REACHABLE_DEVICES (solo para integraciones de concentradores)
Google envía la intención REACHABLE_DEVICES
para confirmar qué dispositivos finales se pueden controlar de forma local. Esta intención se activa cada vez que Google ejecuta un análisis de detección (aproximadamente una vez por minuto), siempre y cuando se detecte que la central está en línea.
Implementas el controlador REACHABLE_DEVICES
de manera similar al controlador IDENTIFY
, excepto que tu controlador debe recopilar IDs de dispositivos adicionales a los que se puede acceder con el dispositivo proxy local (es decir, la unidad central). El campo device.verificationId
contiene el ID del dispositivo local para un dispositivo final conectado al concentrador.
El objeto ReachableDevicesRequest
de la plataforma de Local Home contiene una instancia de LocalIdentifiedDevice
.
A través de esta instancia, puedes obtener el ID del dispositivo proxy y los datos de los resultados del análisis.
Tu controlador REACHABLE_DEVICES
debe devolver un objeto ReachableDevicesPayload
que incluya un objeto devices
que contenga un array de valores verificationId
que representen los dispositivos finales que controla la unidad central. Los valores de verificationId
deben coincidir con uno de los otherDeviceIds
de la respuesta SYNC
.
En el siguiente fragmento, se muestra cómo podrías crear tu controlador 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; };
Implementa el controlador EXECUTE
Tu controlador de EXECUTE
en la app procesa los comandos del usuario y usa el SDK de Local Home para acceder a tus dispositivos inteligentes a través de un protocolo existente.
La plataforma de Local Home pasa la misma carga útil de entrada a la función de controlador EXECUTE
que para el intent EXECUTE
a tu entrega en la nube. Del mismo modo, tu controlador de EXECUTE
devuelve datos de salida en el mismo formato que el procesamiento de la intención EXECUTE
.
Para simplificar la creación de respuestas, puedes usar la clase Execute.Response.Builder
que proporciona el SDK de Local Home.
Tu app no tiene acceso directo a la dirección IP del dispositivo. En su lugar, usa la interfaz CommandRequest
para crear comandos basados en uno de estos protocolos: UDP, TCP o HTTP. Luego, llama a la función deviceManager.send()
para enviar los comandos.
Cuando envíes comandos a dispositivos, usa el ID del dispositivo (y los parámetros del campo customData
, si se incluye) de la respuesta de SYNC
para comunicarte con el dispositivo.
Ejemplo
En el siguiente fragmento de código, se muestra cómo podrías crear tu controlador 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()); };
Implementa el controlador de QUERY
Tu controlador QUERY
en la app procesa las solicitudes del usuario y usa el SDK de Local Home para informar el estado de tus dispositivos inteligentes.
La plataforma de Local Home pasa la misma carga útil de la solicitud a la función del controlador "QUERY" que para el intent QUERY
a tu entrega en la nube. Del mismo modo, tu controlador de QUERY
devuelve datos en el mismo formato que el procesamiento de la intención QUERY
.
Envía comandos a dispositivos que se encuentran detrás de un concentrador
Para controlar los dispositivos finales detrás de un concentrador, es posible que debas proporcionar información adicional en la carga útil del comando específico del protocolo que se envía al concentrador para que este identifique a qué dispositivo se dirige el comando. En algunos casos, esto se puede inferir directamente del valor de device.id
, pero, cuando no es así, debes incluir estos datos adicionales como parte del campo customData
.
Si creaste tu app con TypeScript, recuerda compilarla en JavaScript. Puedes usar el sistema de módulos que prefieras para escribir tu código. Asegúrate de que tu objetivo sea compatible con el navegador Chrome.