スマートホーム アクションにローカル フルフィルメントを追加する

このガイドでは、既存のスマートホーム アクションのローカル フルフィルメントの開発とテストについて説明します。

始める前に

1. デバイスの検出をサポートする

Google で、クラウド フルフィルメントからの SYNC レスポンスで返されたデバイスとローカルで制御可能なデバイスが一致すると、ローカル フルフィルメント パスが確立されます。

Google がローカル ネットワーク上でデバイスを検出してローカル フルフィルメント パスを確立できるようにするには、Actions Console で検出情報を追加する必要があります。また、クラウド フルフィルメントからの SYNC レスポンスを更新して、ローカルで制御可能なデバイスに関する情報を Google に伝えることも必要です。

スキャン設定情報をセットアップする

検出情報を指定するには、次の手順に従ってください。

  1. Actions Console でスマートホーム プロジェクトを開きます。
  2. 左側のナビゲーションで、[Actions](アクション)をクリックします。
  3. [Configure local home SDK (optional)](Local Home SDK の設定(省略可)) > [Add device scan configuration](デバイス スキャン設定の追加)で、[New scan config](新しいスキャン設定)をクリックします。
  4. プルダウンからスキャン一致プロトコル タイプを選択し、Google がスキャンする値を入力します。

次の表に、Google でデバイスのスキャンに使用するプロトコルに基づいて、追加できる属性を示します。

mDNS
属性 説明 値の例
サービス名 必須。デバイスで公開されるサービス名(service.domain 形式)。 _http._tcp.local
名前

必須。一意のサービス インスタンス用のフィルタ(instance.service.domain 形式)。

プラットフォームでこの値が正規表現として処理され、一致するデバイスが返されます。
my-device-[0-9]{4}\._http\._tcp\.local
UPnP
属性 説明 値の例
サービスの種類 必須。UPnP サービスの完全修飾 ID(domain:service:type:version 形式)。 schemas-upnp-org:service:SwitchPower:1
OUI

省略可。組織に固有の識別子

デバイスのメーカーを識別する 24 ビットの値。 通常、デバイスの MAC アドレスの最初の 3 オクテットです。
1A:2B:3C
UDP
属性 説明 値の例
ブロードキャスト アドレス 必須。UDP ブロードキャストの宛先 IP アドレス。 255.255.255.255
ブロードキャスト ポート 必須。UDP ブロードキャストの宛先ポート。 5555
リッスンポート 必須。UDP 検出応答のリッスンポート。 5556
検出パケット

必須。UDP ブロードキャストで送信するペイロード。

バイトの 16 進数でエンコードされた文字列の形式。
48454C4C4F

クラウド フルフィルメントで SYNC レスポンスを更新する

SYNC インテントは、ユーザーが操作するデバイスとその機能をアシスタントに報告します。

ローカル フルフィルメントをサポートするため、ローカルホーム プラットフォームは、スマートホーム アクションのクラウド フルフィルメントからの SYNC レスポンスをチェックし、otherDeviceIds フィールド内のデバイス ID と IDENTIFY ハンドラによって返された検証 ID を照合します。otherDeviceIds フィールドのないデバイス エントリは、ローカル フルフィルメントから除外されます。

SYNC レスポンスの otherDeviceIds フィールドに、ローカルで制御可能なスマートホーム デバイスのデバイス ID を設定する必要があります。このフィールドはレスポンスの device レベルにあります。Google では、指定された ID を持つすべてのデバイスでローカル フルフィルメント パスを確立できます。

customData フィールドを使用して、Google がスタンドアロン デバイスに接続する、またはハブ経由でエンドデバイスをターゲットとする場合に必要となる追加データ(ポート番号やその他のプロトコル固有の情報など)を指定します。

次のスニペットに、SYNC ハンドラの作成方法を示します。

スタンドアロン / ハブ
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "agentUserId": "1836.15267389",
    "devices": [{
      "id": "123",
      "type": "action.devices.types.OUTLET",
      "traits": [
        "action.devices.traits.OnOff"
      ],
      "name": {
        "name": "Night light"
      },
      "willReportState": false,
      "otherDeviceIds": [{
        "deviceId": "local-device-id"
      }],
      "customData": {
        "port": 5555,
        "authToken": "..."
      }
    }]
  }
}

2. ローカル フルフィルメント アプリを実装する

ローカル フルフィルメントをサポートするには、次のスマートホーム インテントを処理するアプリを作成する必要があります。

  • IDENTIFY: ローカルで制御可能なスマート デバイスの検出をサポートします。インテント ハンドラは、スマート デバイスが検出の際に返したデータを抽出し、レスポンスで Google に送信します。
  • EXECUTE: コマンドの実行をサポートします。
  • 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

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

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

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 スクリプトをサポートしています。

bundle
cd project-directory/
npm run build

このスクリプトは、TypeScript ソースをコンパイルし、dist/web サブディレクトリ内の Chrome ランタイム環境の依存関係と、dist/node サブディレクトリ内の Node.js ランタイム環境の依存関係にアプリをバンドルします。

verify
cd project-directory/
npm run lint
npm run compile
npm test

このスクリプトは、TypeScript コードの構文を確認し、dist/ サブディレクトリに出力を生成することなくこのコードをコンパイルし、test.ts から自動テストを実行します。

serve
cd project-directory/
npm run start

開発時には、このスクリプトが Chrome と Node.js のランタイム環境にローカルでアプリ バンドルを提供します。

IDENTIFY ハンドラを実装する

IDENTIFY ハンドラは、Google Home または Google Nest デバイスが再起動して、未確認のローカル デバイス(ハブに接続されたエンドデバイスを含む)を確認したときにトリガーされます。ローカルホーム プラットフォームは、以前に指定されたスキャン設定情報を使用してローカル デバイスをスキャンし、スキャン結果を使用して IDENTIFY ハンドラを呼び出します。

ローカルホーム プラットフォームからの IdentifyRequest には、LocalIdentifiedDevice インスタンスのスキャンデータが含まれています。デバイスを検出したスキャン設定に基づいて作成される device インスタンスは 1 つのみです。

スキャン結果がデバイスと一致すると、IDENTIFY ハンドラは IdentifyResponsePayload オブジェクトを返します。このオブジェクトには、スマートホーム メタデータ(タイプ、トレイト、レポート ステータスなど)と device オブジェクトが含まれます。

IDENTIFY レスポンスからの verificationIdSYNC レスポンスによって返された 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 でハブデバイスが識別されると、ハブに接続されたエンドデバイスへのコンジットとしてハブが処理され、それらのエンドデバイスの確認を試みます。

ハブデバイスがあることを Google が確認できるようにするには、IDENTIFY ハンドラに対し次の手順を行います。

  • SYNC レスポンスで、ハブに接続されたローカルのエンドデバイスの ID が報告される場合は、IdentifyResponsePayloadisProxytrue に設定します。
  • SYNC レスポンスでハブデバイスが報告されない場合は、IdentifyResponsePayloadisLocalOnlytrue に設定します。
  • device.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());
  };

ハブに接続されたデバイスにコマンドを送信する

ハブに接続されたエンドデバイスを制御するには、ハブがコマンド対象のデバイスを識別できるよう、ハブに送信されるプロトコル固有のコマンドのペイロードに、追加情報を提供する必要があります。これは device.id 値から直接推定できる場合もありますが、推定できない場合は、この追加データを customData フィールドの一部として含める必要があります。

TypeScript を使用してアプリを作成した場合は、必ずアプリを JavaScript にコンパイルしてください。任意のモジュール システムを使用してコードを作成できます。 ターゲットが Chrome ブラウザでサポートされていることをご確認ください。

3.アプリをテストしてデバッグする

前述の手順を使用してローカル フルフィルメント アプリをビルドしてから、次の手順を使用して独自のホスティング環境でスマートホーム統合をテストすることをおすすめします。

  1. 独自のホスティング環境で、ローカル フルフィルメント アプリを実行する HTML ページを用意します。次のスニペットは、ローカル フルフィルメント アプリを実行する静的 HTML ファイルの例です。

    <html>
      <head>
        <!-- Local Home SDK -->
        <script src="//www.gstatic.com/eureka/smarthome/smarthome_sdk.js"></script>
        <!-- Local app under development -->
        <script src="local_execution.js"></script>
      </head>
    
    </html>
    
    
  2. デバイス制御をテストします

  3. Chrome からデバッグします。ブレークポイントとログを使用して統合のトラブルシューティングを行います。

  4. TypeScript コードを修正してコンパイルしてから、これらの手順を繰り返します。

このビルド&テストのプロセスを繰り返すことで、アクションの変更をすばやく確認し、コードの問題を簡単に見つけてデバッグすることができます。

デバイス制御をテストする

Actions Console で、ウェブアプリの URL を指定する必要があります。この URL で、ローカル フルフィルメント中に Google Home または Google Nest デバイスに読み込まれる HTML を提供します。

ローカル フルフィルメントを使用してデバイス制御をテストする手順は次のとおりです。

Chrome

  1. Actions Console でスマートホーム プロジェクトを開きます。
  2. 左側のナビゲーションで、[Actions](アクション)をクリックします。
  3. [Configure local home SDK (optional)] > [Enter your testing URL for Chrome](Chrome 用のテスト URL を入力)で、ローカル フルフィルメント アプリを実行する HTML を提供する開発用サーバーの URL を指定します。
  4. [Save](保存)をクリックします。Google がコンソールの変更を反映するまでに最大 30 分ほどかかることがあります。
  5. テスト用の Google Home または Google Nest デバイスを再起動します。
  6. スマート デバイスにコマンドを発行します。たとえば、デバイスが OnOff トレイトを実装している場合は、「OK Google, ライトをつけて」と話しかけます。

Node.js

  1. Actions Console でスマートホーム プロジェクトを開きます。
  2. 左側のナビゲーションで、[Actions](アクション)をクリックします。
  3. [Configure local home SDK (optional)] > [Enter your testing URL for Node](Node 用のテスト URL を入力)で、ローカル フルフィルメント アプリを実行する JavaScript を提供する開発用サーバーの URL を指定します。
  4. [Save](保存)をクリックします。Google がコンソールの変更を反映するまでに最大 30 分ほどかかることがあります。
  5. テスト用の Google Home または Google Nest デバイスを再起動します。
  6. スマート デバイスにコマンドを発行します。たとえば、デバイスが OnOff トレイトを実装している場合は、「OK Google, ライトをつけて」と話しかけます。

Chrome からデバッグする

ローカル フルフィルメント アプリをデバッグするには Chrome DevTools を使用します。デバッグの前に、環境が正しく設定されていることを確認してください。

  • コンソール内の開発用 URL が Google Home デバイスまたは Google Nest デバイスからアクセスできる URL に設定されている(ローカルエリア ネットワーク上かインターネット経由のどちらか)。
  • マシンが、テストしている Google Home または Google Nest デバイスと同じローカルエリア ネットワークに接続されている。
  • ネットワークがデバイス間のパケットをブロックしていない。
  • Actions Console 上と Google Home デバイスまたは Google Nest デバイス上で同じ Google アカウントを使用してログインしている。
  • クラウド フルフィルメントで SYNC レスポンスが更新されているotherDeviceIds フィールドで少なくとも 1 つの有効な値が返されます。
  • Actions Console で正しいスキャン設定情報が入力されている。

ローカル フルフィルメント アプリを Chrome DevTools デバッガに接続する手順は次のとおりです。

Chrome

  1. ローカルの開発用マシンに、Google Chrome ブラウザをインストールして起動します。
  2. Chrome ブラウザのアドレス欄に、「chrome://inspect#devices」と入力して Chrome インスペクタを起動します。ページにデバイスのリストが表示され、テスト対象の Google Home デバイスまたは Google Nest デバイスの名前の下に HTML ファイルが表示されます。
  3. HTML の下にある青色の検査リンクをクリックして Chrome DevTools を起動します。Console タブに切り替えます。 ローカルホーム プラットフォームが、アプリのバージョンと Local Home SDK バージョンをコンソールログに出力します。ログが表示された場合は、アプリが正常に読み込まれて接続できる状態になっています。ログが表示されない場合は、Google Home または Google Nest デバイスを再起動してください。
  4. 図 1. chrome://inspect のローカル フルフィルメント アプリ。

Node.js

  1. ローカルの開発用マシンに、Google Chrome ブラウザをインストールして起動します。
  2. テストデバイスのローカル IP アドレスを確認します。
  3. Chrome ブラウザのアドレス欄に、「chrome://inspect#devices」と入力して Chrome インスペクタを起動します。
  4. [Configure](設定)を選択して [Target discovery settings](ターゲット検出の設定)を開きます。
  5. 図 2. chrome://inspect でのターゲット検出の設定。
  6. リストに DEVICE_IP_ADDRESS:9222 と入力して、[Done](完了)をクリックします。
  7. スクリプトの下にある青色の検査リンクをクリックして Chrome DevTools を起動します。 Console タブに切り替えます。 ローカルホーム プラットフォームが、アプリのバージョンと Local Home SDK バージョンをコンソールログに出力します。ログが表示された場合は、アプリが正常に読み込まれて接続できる状態になっています。ログが表示されない場合は、Google Home または Google Nest デバイスを再起動してください。

デバッグのヒント

デバッグ中は次の点に注意してください。

  • テスト アカウントに、同じローカル ネットワーク上の複数の Google Home デバイスまたは Google Nest デバイスをリンクさせないようにしてください。ローカル フルフィルメント コマンドのターゲットを、どの Google Home デバイスまたは Google Nest デバイスにするかを制御することはできません。
  • Chrome DevTools でページを更新し、ローカル フルフィルメント アプリ コンテナを再読み込みして、開発用 URL からの最新のコードを確認します。これによってローカルホーム プラットフォームがリセットされることはありません。なお、リセットは、ローカル フルフィルメント アプリでプラットフォーム インテント(IDENTIFY など)を再トリガーするために必要になることがあります。ローカルホーム プラットフォームをリセットするには、Google Home デバイスまたは Google Nest デバイスを再起動します。
  • JavaScript アプリがエラーなしで読み込まれていることを確認します。これを行うには、DevTools ページのコンソール セクションを確認してください。問題がある場合は、「Uncaught TypeError: Cannot read property ‘open’ of null.」などのメッセージが表示されます。
  • IDENTIFY レスポンスからの verificationId は、SYNC レスポンスからの otherDeviceIds のいずれかと一致する必要があります。
  • EXECUTE ハンドラの場合は、HTTP、TCP、UDP のコマンドがデバイスで受信され、正常に動作することを確認します。
  • ハンドラから Promise が返されることを確認します。
  • メモリ内でグローバルな状態を維持しないようにします。詳しくは、アプリケーションのライフサイクルをご覧ください。
  • ローカル フルフィルメント アプリによってスローされたエラーは、プロジェクトのエラーログに表示されます。

4. 本番環境の準備と本番環境へのリリース

スマートホーム アクションをリリースする準備が整ったら、次の手順を行います。

  1. ターミナルを開きます。プロジェクト ディレクトリで、npm run build コマンドを実行します。 このコマンドは、dist ディレクトリに、アプリ用の次の JavaScript バンドルを生成します。
    project-directory/
    └── dist
     ├── web
     │    └── bundle.js
     └── node
          └── bundle.js
    
  2. コンソールで、[Develop](開発)> [Actions](アクション)の順にクリックして、JavaScript アプリをアップロードします。[Configure Local Home SDK](Local Home SDK の設定)で、[Upload JavaScript files](JavaScript ファイルのアップロード)をクリックします。
    図 3. JavaScript アプリのアップロード。
  3. [Upload files](ファイルのアップロード)ダイアログで、以前に生成したバンドル ファイルをアップロードします。必ずバンドル ファイルの両方のバージョン(ノード、ウェブ)をアップロードして、ローカル フルフィルメントがサポートするすべてのランタイム環境で正しく動作するようにアクションを設定します。
    1. [Upload your JavaScript targeting Node.js](Node.js をターゲットとする JavaScript をアップロード): dist/node ディレクトリから bundle.js ファイルをアップロードします。
    2. [Upload your JavaScript targeting Chrome (browser)](Chrome(ブラウザ)をターゲットとする JavaScript をアップロード): dist/web ディレクトリから bundle.js ファイルをアップロードします。
  4. アシスタント搭載デバイスでアクションをテストし、本番環境で想定どおり動作することを確認します。詳細については、スマートホーム アクションのテストと共有をご覧ください。
  5. アクションの動作に問題がなければ、スマートホーム アクションをリリースする手順に沿って、本番環境にデプロイするために Google に提出します。これには、セルフテストと認定申請の手順が含まれます。