ローカル フルフィルメントをサポートするには、次のスマートホーム インテントを処理するアプリを作成する必要があります。
IDENTIFY: ローカルで制御可能なスマート デバイスの検出をサポートします。インテント ハンドラは、スマート デバイスが検出の際に返したデータを抽出し、レスポンスで Google に送信します。EXECUTE: コマンドの実行をサポートします。QUERY: デバイスの状態のクエリをサポートします。REACHABLE_DEVICES: (省略可)ハブ(またはブリッジ)デバイスにローカル接続され、ローカルで制御可能なエンドデバイスの検出をサポートします。
このアプリは、ユーザーの Google Home または Google Nest デバイスで動作し、スマート デバイスをアシスタントに接続します。アプリは 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 を、ローカル フルフィルメント アプリ プロジェクトを格納する新しいディレクトリに置き換えます。
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 scripts をサポートしています。
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 のランタイム環境にローカルでアプリ バンドルを提供します。
IDENTIFY ハンドラを実装する
IDENTIFY ハンドラは、Google Home または Google Nest デバイスが再起動して、未確認のローカル デバイス(ハブに接続されたエンドデバイスを含む)を検出したときにトリガーされます。ローカルホーム プラットフォームは、以前に指定されたスキャン設定情報を使用してローカル デバイスをスキャンし、スキャン結果を使用して IDENTIFY ハンドラを呼び出します。
ローカルホーム プラットフォームからの
IdentifyRequest
には、
LocalIdentifiedDevice
インスタンスのスキャンデータが含まれています。デバイスを検出したスキャン設定に基づいて作成される device インスタンスは 1 つのみです。
スキャン結果がデバイスと一致すると、IDENTIFY ハンドラは
IdentifyResponsePayload
オブジェクトを返します。このオブジェクトには、device オブジェクトと
スマートホーム メタデータ(タイプ、トレイト、レポート ステータスなど)が含まれます。
IDENTIFY レスポンスからの verificationId が SYNC レスポンスによって返された otherDeviceIds の値のいずれかと一致すると、Google でデバイスの関連付けが確立されます。
例
次のスニペットで、スタンドアロン デバイスとハブ統合用の 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; };
ハブに接続されたデバイスを識別する
ハブデバイスを確認すると、そのハブに接続されているエンドデバイスへのコンジットとして処理してから、それらのエンドデバイスの確認を試みます。
ハブデバイスがあることを Google が確認できるようにするには、IDENTIFY ハンドラに対し次の手順を行います。
SYNCレスポンスで、 ハブに接続されたローカルのエンドデバイスの ID が報告される場合は、isProxyをtrueに設定します。IdentifyResponsePayloadSYNCレスポンスがハブデバイスを報告しない場合は、isLocalOnlyをtrueに設定します。IdentifyResponsePayloaddevice.idフィールドには、ハブデバイス自体のローカル デバイス ID が含まれます。
REACHABLE_DEVICES ハンドラを実装する(ハブ統合のみ)
ローカルで制御できるエンドデバイスを確認するため、REACHABLE_DEVICES インテントが Google から送信されます。Google が検出スキャンを実行するたびに(毎分約 1 回)、ハブがオンラインであることが検出されると、このインテントがトリガーされます。
IDENTIFY ハンドラと同様に REACHABLE_DEVICES ハンドラを実装します。ただし、このハンドラでは、ローカル プロキシ(つまりハブ自体)のデバイスがアクセスできる追加のデバイス ID を収集する必要があります。device.verificationId フィールドには、ハブに接続されているエンドデバイスのローカル デバイス ID が含まれます。
ローカルホーム プラットフォームの
ReachableDevicesRequest
には、
LocalIdentifiedDeviceのインスタンスが含まれます。
このインスタンスを介し、スキャン結果のデータとともに、プロキシ デバイス ID を取得できます。
REACHABLE_DEVICES ハンドラは
ReachableDevicesPayload
オブジェクトを返します。このオブジェクトには、ハブが制御しているエンドデバイスを表す
verificationId 値の配列を含む devices オブジェクトが含まれます。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 を使用して既存のプロトコルを介してスマート デバイスにアクセスします。
ローカルホーム プラットフォームは、クラウド フルフィルメントへの EXECUTE インテントと同じ入力ペイロードを EXECUTE ハンドラ関数に渡します。同様に、EXECUTE ハンドラは、EXECUTE インテントの処理と同じ形式で出力データを返します。
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 を使用してスマート デバイスの状態を報告します。
ローカルホーム プラットフォームは、クラウド フルフィルメントへの QUERY インテントと同じリクエスト ペイロードを QUERY ハンドラ関数に渡します。同様に、QUERY ハンドラは、QUERY インテントの処理と同じ形式でデータを返します。
ハブに接続されたデバイスにコマンドを送信する
ハブに接続されたエンドデバイスを制御するには、ハブがコマンド対象のデバイスを識別できるよう、ハブに送信されるプロトコル固有のコマンドのペイロードに、追加情報を提供する必要があります。これは device.id 値から直接推定できる場合もありますが、推定できない場合は、この追加データを customData フィールドの一部として含める必要があります。
TypeScript を使用してアプリを作成した場合は、必ずアプリを JavaScript にコンパイルしてください。任意のモジュール システムを使用してコードを作成できます。 ターゲットが Chrome ブラウザでサポートされていることをご確認ください。