Wenn du die lokale Ausführung unterstützen möchtest, musst du eine App erstellen, die diese Smart-Home-Intents verarbeiten kann:
IDENTIFY: Unterstützt die Erkennung von lokal steuerbaren Smart-Home-Geräten. Der Intent-Handler extrahiert Daten, die dein Smart-Home-Gerät während der Erkennung zurückgibt, und sendet diese in einer Antwort an Google.EXECUTE: Unterstützt die Ausführung von Befehlen.QUERY: Unterstützt das Abfragen des Gerätestatus.REACHABLE_DEVICES: (Optional) Unterstützt die Erkennung von lokal steuerbaren Endgeräten hinter einem Hub- (oder Bridge-)Gerät.
Diese App wird auf den Google Home- oder Google Nest-Geräten des Nutzers ausgeführt und verbindet dein Smart-Home-Gerät mit Assistant. Sie können die App mit TypeScript (bevorzugt) oder JavaScript erstellen.
TypeScript wird empfohlen, da Sie Bindungen verwenden können, um statisch sicherzustellen, dass die von Ihrer Anwendung zurückgegebenen Daten mit den von der Plattform erwarteten Typen übereinstimmen.
Weitere Informationen zur API findest du in der Local Home SDK API-Referenz.
Die folgenden Snippets zeigen, wie du die lokale Ausführungs-App initialisieren und deine Handler anhängen kannst.
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"); });
Projekt erstellen
Wenn du deine App für die lokale Ausführung bereitstellen möchtest, musst du ein JavaScript-Bundle für deinen Code und alle zugehörigen Abhängigkeiten erstellen.
Verwende den Projektinitialisierer der App für die lokale Ausführung, um die entsprechende Projektstruktur mit deiner bevorzugten Bundler-Konfiguration zu bootstrappen.
Projektvorlagen
Führen Sie den Befehl npm init aus, um die Bundler-Konfiguration auszuwählen, wie in den folgenden Beispielen gezeigt:
TypeScript ohne Bundler-Konfiguration:
npm init @google/local-home-app project-directory/ --bundler none
Projektstruktur:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json └── serve.js
Ersetzen Sie project-directory durch ein neues Verzeichnis, das das lokale Auftragsausführungsprojekt enthalten soll.
TypeScript mit webpack-Bundler-Konfiguration:
npm init @google/local-home-app project-directory/ --bundler webpack
Projektstruktur:
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
Ersetzen Sie project-directory durch ein neues Verzeichnis, das das lokale Auftragsausführungsprojekt enthalten soll.
TypeScript mit Rollup-Bundler-Konfiguration:
npm init @google/local-home-app project-directory/ --bundler rollup
Projektstruktur:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json ├── rollup.config.js └── serve.js
Ersetzen Sie project-directory durch ein neues Verzeichnis, das das lokale Auftragsausführungsprojekt enthalten soll.
TypeScript mit Parcel-Bundler-Konfiguration:
npm init @google/local-home-app project-directory/ --bundler parcel
Projektstruktur:
project-directory/ ├── node_modules/ ├── package.json ├── .gitignore ├── index.ts ├── test.ts ├── tsconfig.json ├── tslint.json └── serve.js
Ersetzen Sie project-directory durch ein neues Verzeichnis, das das lokale Auftragsausführungsprojekt enthalten soll.
Häufige Aufgaben auf Projektebene ausführen
Das generierte Projekt unterstützt die folgenden npm-Skripts:
cd project-directory/ npm run build
Dieses Script kompiliert TypeScript-Quellcode und bündelt Ihre Anwendung mit ihren Abhängigkeiten für die Chrome-Laufzeitumgebung im Unterverzeichnis dist/web und die Node.js-Laufzeitumgebung im Unterverzeichnis dist/node.
cd project-directory/ npm run lint npm run compile npm test
Dieses Script überprüft die Syntax Ihres TypeScript-Codes, kompiliert ihn, ohne eine Ausgabe im Unterverzeichnis dist/ zu erzeugen, und führt automatisierte Tests aus test.ts aus.
cd project-directory/ npm run start
Während der Entwicklung stellt dieses Script Ihre App-Bundles für die Chrome- und Node.js-Laufzeitumgebungen lokal bereit.
IDENTIFY-Handler implementieren
Der Handler IDENTIFY wird ausgelöst, wenn das Google Home- oder Google Nest-Gerät neu gestartet wird und nicht verifizierte lokale Geräte erkennt (einschließlich Endgeräte, die mit einem Hub verbunden sind). Die Local Home-Plattform sucht anhand der zuvor angegebenen Informationen zur Scan-Konfiguration nach lokalen Geräten und ruft den IDENTIFY-Handler mit den Scanergebnissen auf.
Der IdentifyRequest von der Local Home-Plattform enthält die Scandaten einer LocalIdentifiedDevice-Instanz. Es wird nur eine device-Instanz ausgefüllt, basierend auf der Scankonfiguration, die das Gerät erkannt hat.
Wenn die Scanergebnisse mit deinem Gerät übereinstimmen, sollte dein IDENTIFY-Handler ein IdentifyResponsePayload-Objekt zurückgeben, das ein device-Objekt mit Smart-Home-Metadaten (z. B. Typen, Eigenschaften und Berichtsstatus) enthält.
Google stellt eine Geräteverknüpfung her, wenn die verificationId aus der IDENTIFY-Antwort mit einem der otherDeviceIds-Werte übereinstimmt, die von der SYNC-Antwort zurückgegeben werden.
Beispiel
Die folgenden Snippets zeigen, wie Sie IDENTIFY-Handler für eigenständige Geräte- und Hub-Integrationen erstellen können.
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; };
Geräte hinter einem Hub identifizieren
Wenn Google ein Hub-Gerät identifiziert, wird der Hub als Verbindung zu den verbundenen Endgeräten des Hubs behandelt und es wird versucht, diese Endgeräte zu verifizieren.
Damit Google bestätigen kann, dass ein Hub-Gerät vorhanden ist, folge dieser Anleitung für deinen IDENTIFY-Handler:
- Wenn in deiner
SYNC-Antwort die IDs lokaler Endgeräte gemeldet werden, die mit dem Hub verbunden sind, legeisProxyalstruein derIdentifyResponsePayloadfest. - Wenn dein Hub-Gerät in der
SYNC-Antwort nicht gemeldet wird, legeisLocalOnlyalstruein derIdentifyResponsePayloadfest. - Das Feld
device.identhält die lokale Geräte-ID für das Hub-Gerät selbst.
REACHABLE_DEVICES-Handler implementieren (nur Hub-Integrationen)
Der Intent REACHABLE_DEVICES wird von Google gesendet, um zu bestätigen, welche Endgeräte lokal gesteuert werden können. Dieser Intent wird jedes Mal ausgelöst, wenn Google einen Erkennungsscan ausführt (etwa einmal pro Minute), solange der Hub als online erkannt wird.
Der REACHABLE_DEVICES-Handler wird ähnlich wie der IDENTIFY-Handler implementiert. Der Handler muss jedoch zusätzliche Geräte-IDs erfassen, die über das lokale Proxy-Gerät (also den Hub) erreichbar sind. Das Feld device.verificationId enthält die lokale Geräte-ID für ein Endgerät, das mit dem Hub verbunden ist.
Der ReachableDevicesRequest der Local Home-Plattform enthält eine Instanz von LocalIdentifiedDevice.
Über diese Instanz können Sie die Proxy-Geräte-ID sowie Daten aus den Scanergebnissen abrufen.
Der REACHABLE_DEVICES-Handler sollte ein ReachableDevicesPayload-Objekt zurückgeben, das ein devices-Objekt mit einem Array von verificationId-Werten enthält, die die vom Hub gesteuerten Endgeräte darstellen. Die verificationId-Werte müssen mit einem der otherDeviceIds aus der SYNC-Antwort übereinstimmen.
Das folgende Snippet zeigt, wie Sie den REACHABLE_DEVICES-Handler erstellen können.
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-Handler implementieren
Der EXECUTE-Handler in der App verarbeitet Nutzerbefehle und verwendet das Local Home SDK, um über ein vorhandenes Protokoll auf deine Smart-Home-Geräte zuzugreifen.
Die Local Home-Plattform übergibt dieselbe Eingabenutzlast an die EXECUTE-Handler-Funktion wie für den Intent EXECUTE an die Cloud-Auftragsausführung. Ebenso gibt der EXECUTE-Handler Ausgabedaten im selben Format wie bei der Verarbeitung des EXECUTE-Intents zurück.
Um die Erstellung der Antwort zu vereinfachen, können Sie die Klasse Execute.Response.Builder verwenden, die vom Local Home SDK bereitgestellt wird.
Ihre App hat keinen direkten Zugriff auf die IP-Adresse des Geräts. Verwenden Sie stattdessen die Schnittstelle CommandRequest, um Befehle basierend auf einem der folgenden Protokolle zu erstellen: UDP, TCP oder HTTP. Rufen Sie dann die Funktion deviceManager.send() auf, um die Befehle zu senden.
Wenn Sie Befehle an Geräte senden, verwenden Sie die Geräte-ID (und Parameter aus dem Feld customData, falls enthalten) aus der SYNC-Antwort, um mit dem Gerät zu kommunizieren.
Beispiel
Das folgende Code-Snippet zeigt, wie Sie den EXECUTE-Handler erstellen können.
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-Handler implementieren
Der QUERY-Handler in der App verarbeitet Nutzeranfragen und verwendet das Local Home SDK, um den Status deiner Smart-Home-Geräte zu melden.
Die Local Home-Plattform übergibt dieselbe Anfragenutzlast an die Handler-Funktion „QUERY“ wie für den Intent QUERY an deine Cloud-Auftragsausführung. Ebenso gibt der QUERY-Handler Daten im selben Format zurück wie bei der Verarbeitung des QUERY-Intents.
Befehle an Geräte hinter einem Hub senden
Wenn du Endgeräte hinter einem Hub steuern möchtest, musst du möglicherweise zusätzliche Informationen in der protokollspezifischen Befehlsnutzlast angeben, die an den Hub gesendet wird, damit der Hub erkennen kann, für welches Gerät der Befehl bestimmt ist. In einigen Fällen kann dies direkt aus dem Wert device.id abgeleitet werden. Wenn dies nicht der Fall ist, sollten Sie diese zusätzlichen Daten in das Feld customData aufnehmen.
Wenn Sie Ihre App mit TypeScript erstellt haben, müssen Sie sie in JavaScript kompilieren. Sie können das Modulsystem Ihrer Wahl verwenden, um Ihren Code zu schreiben. Achten Sie darauf, dass Ihr Ziel vom Chrome-Browser unterstützt wird.