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

1. 始める前に

スマートホームの統合により、ユーザーの家にある接続済みデバイスを Google アシスタントを通じて制御できるようになります。スマートホーム アクションを作成するには、スマートホーム インテントを処理できるクラウド Webhook エンドポイントを用意する必要があります。たとえば、ユーザーが「OK Google, 電気をつけて」と言うと、アシスタントはクラウド フルフィルメントにコマンドを送信してデバイスの状態を更新します。

一方、Local Home SDK を使用すると、スマートホーム インテントを Google Home デバイスに直接ルーティングするローカルパスを追加できます。これによりスマートホームの統合を強化でき、ユーザー コマンドの処理の信頼性を向上させレイテンシを短縮できます。また、デバイスを識別するローカル フルフィルメント アプリを TypeScript や JavaScript で記述してデプロイし、Google Home スマート スピーカーや Google Nest スマートディスプレイでコマンドを実行することもできます。ユーザー コマンドの実行に既存の標準プロトコルを使用することで、アプリがローカルエリア ネットワーク経由で既存のスマート デバイスと直接通信することが可能になります。

72ffb320986092c.png

前提条件

作成するアプリの概要

この Codelab では、以前に構築したスマートホームの Firebase 統合をデプロイし、Actions Console でスキャン構成を適用し、TypeScript を使用してローカルアプリを構築し、Node.js で記述したコマンドを仮想洗濯機デバイスに送信します。

学習内容

  • Actions Console でローカル フルフィルメントを有効にして設定する
  • Local Home SDK を使用してローカル フルフィルメント アプリを記述する
  • Google Home スピーカーまたは Google Nest スマートディスプレイに読み込まれたローカル フルフィルメント アプリをデバッグする

必要なもの

2. はじめに

アクティビティ管理を有効にする

Google アシスタントを使用するには、特定のアクティビティ データを Google と共有する必要があります。このデータは Google アシスタントが適切に機能するために必要となります。ただし、データの共有は SDK に固有の要件ではありません。このデータを共有するには、Google アカウントを作成します(まだ作成していない場合)。どの Google アカウントでも使用できます。デベロッパー アカウントである必要はありません。

アシスタントで使用する Google アカウントの [アクティビティ管理] ページを開きます。

以下の切り替えスイッチが有効になっていることを確認します。

  • [ウェブとアプリのアクティビティ] - これに加えて、[Chrome の履歴と Google サービスを使用するサイト、アプリ、デバイスでのアクティビティを含める] チェックボックスがオンになっていることも確認してください。
  • [デバイス情報]
  • [音声アクティビティ]

Actions プロジェクトを作成する

  1. Actions on Google のデベロッパー コンソールに移動します。
  2. [New Project](新しいプロジェクト)をクリックし、プロジェクトの名前を入力して [CREATE PROJECT](プロジェクトを作成)をクリックします。

AWXw5E1m9zVgvVeyeL3uxwCX6DtWOCK6LRSLmOATFzjMbmE5cSWBdSVhJZDFpEFH2azZTK2eMs6OYYdMJYiGb5bKqFEzxaLyRUYuwVGBlSjXzTyy8Z9CvwpXvRwP7xdycklETzFc7Q

スマートホーム アプリを選択する

Actions Console の概要画面で [Smart Home](スマートホーム)を選択します。

36RsBUWBgbgsa5xZ7MJVMm1sIg07nXbfjv0mWCxXViaC5SlbL2gMigw9hgXsZQhNMHLLFOfiKdZsSTNXONFB1i47gksw3SBNpkVYl492WeryOlgxKjpVrLAvg-5cZqu1DI-s5kxM3g

[Smart home](スマートホーム)エクスペリエンス カードを選択すると、プロジェクト コンソールが表示されます。

pzgHPsmc2LvLoeUvJfkjKQqD_BvO4v8JOPlcrxsmyptFkkjL4PP6LqrM9r5tNvEIfT9HmK-UKw3GWFPXTjqo4nUrhD2o5shUKHBE31OT8iIA69JZCev7_0_nh-lnL2oJHoxGfqqZ4w

Firebase CLI をインストールする

Firebase コマンドライン インターフェース(CLI)を使用すると、ウェブアプリをローカルで提供し Firebase Hosting にデプロイできます。

CLI をインストールするには、ターミナルから次の npm コマンドを実行します。

npm install -g firebase-tools

CLI が正しくインストールされたことを確認するには、次のコマンドを実行します。

firebase --version

Google アカウントで Firebase CLI を承認するには、次のコマンドを実行します。

firebase login

HomeGraph API を有効にする

HomeGraph API は、ユーザーのホームグラフ内のデバイスとその状態の保存と照会を可能にします。この API を使用するには、まず Google Cloud コンソールを開き、HomeGraph API を有効にする必要があります。

Google Cloud Console でアクションの <project-id>. に一致するプロジェクトを選択し、HomeGraph API の [API ライブラリ] 画面で [有効にする]をクリックします。

5SVCzM8IZLi_9DV8M0nEklv16NXkpvM0bIzQK2hSyKyvnFHBxPOz90rbr72ayxzmxd5aNROOqC_Cp4outbdlwJdObDs0DIE_8vYzw6dovoVrP9IZWlWsZxDS7UHOi1jiRbDMG8MqUA

3.スターター アプリを実行する

開発環境の設定が完了したので、スターター プロジェクトをデプロイし、すべてが正しく設定されていることを確認しましょう。

ソースコードを取得する

下のリンクをクリックして、この Codelab のサンプルを開発マシンにダウンロードします。

または、コマンドラインから GitHub リポジトリのクローンを作成することもできます。

git clone https://github.com/google-home/smarthome-local.git

プロジェクトについて

スターター プロジェクトには、以下のサブディレクトリが含まれています。

  • public - スマート洗濯機を制御、監視するためのフロントエンド ウェブ UI
  • functions - スマートホーム アクション用のクラウド フルフィルメントを実装する Cloud Functions
  • local - index.ts にインテント ハンドラがスタブされたローカル フルフィルメント アプリのスケルトン プロジェクト

提供されるクラウド フルフィルメントの index.js には、以下の関数が含まれています。

  • fakeauth - アカウント リンク用の認証エンドポイント
  • faketoken - アカウント リンク用のトークン エンドポイント
  • smarthome - スマートホーム インテントのフルフィルメント エンドポイント
  • reportstate - デバイスの状態が変化したときに HomeGraph API を呼び出す
  • updateDevice - 仮想デバイスが Report State のトリガーに使用するエンドポイント

Firebase に接続する

app-start ディレクトリに移動し、Actions プロジェクトに Firebase CLI を設定します。

cd app-start
firebase use <project-id>

Firebase プロジェクトを設定する

Firebase プロジェクトを初期化します。

firebase init

CLI 機能、Realtime DatabaseFunctions、Firebase Hosting を含む Hosting 機能を選択します。

? Which Firebase CLI features do you want to set up for this directory? Press Space to select features, then
 Enter to confirm your choices.
❯◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◯ Firestore: Configure security rules and indexes files for Firestore
 ◉ Functions: Configure a Cloud Functions directory and its files
 ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◯ Storage: Configure a security rules file for Cloud Storage
 ◯ Emulators: Set up local emulators for Firebase products
 ◯ Remote Config: Configure a template file for Remote Config
 ◯ Extensions: Set up an empty Extensions manifest

これにより、プロジェクトに必要な API と機能が初期化されます。

プロンプトが表示されたら、Realtime Database を初期化します。データベース インスタンスのデフォルトの場所を使用できます。

? It seems like you haven't initialized Realtime Database in your project yet. Do you want to set it up?
Yes

? Please choose the location for your default Realtime Database instance:
us-central1

スターター プロジェクトのコードを使用しているため、セキュリティ ルールのデフォルト ファイルを選択します。既存のデータベース ルール ファイルを上書きしないよう注意してください。

? File database.rules.json already exists. Do you want to overwrite it with the Realtime Database Security Rules for <project-ID>-default-rtdb from the Firebase Console?
No

プロジェクトを再初期化する場合、コードベースを初期化するか上書きするか尋ねられたら [上書き] を選択します。

? Would you like to initialize a new codebase, or overwrite an existing one?
Overwrite

Functions を構成するときは、デフォルトのファイルを使用する必要があります。プロジェクト サンプルの既存の index.js ファイルと package.json ファイルを上書きしないようにしてください。

? What language would you like to use to write Cloud Functions?
JavaScript

? Do you want to use ESLint to catch probable bugs and enforce style?
No

? File functions/package.json already exists. Overwrite?
No

? File functions/index.js already exists. Overwrite?
No

プロジェクトを再初期化する場合、functions/.gitignore を初期化するか上書きするかを尋ねられたら [No] を選択します。

? File functions/.gitignore already exists. Overwrite?
No
? Do you want to install dependencies with npm now?
Yes

最後に、プロジェクト コードの public ディレクトリと既存の index.html ファイルを使用するように Hosting の設定を構成します。ESLint を使用するように求められたら [No] を選択します。

? What do you want to use as your public directory?
public

? Configure as a single-page app (rewrite all urls to /index.html)?
Yes

? Set up automatic builds and deploys with GitHub?
No

? File public/index.html already exists. Overwrite?
 No

誤って ESLint を有効にしていた場合、無効にするには、次の 2 つの方法があります。

  1. GUI を使用して、プロジェクトの下にある ../functions フォルダに移動し、隠しファイル .eslintrc.js を選択して削除します。同じような名前の .eslintrc.json と取り違えないでください。
  2. コマンドラインを使用する場合:
    cd functions
    rm .eslintrc.js
    

Firebase の構成が完全で正確であることを確認するには、firebase.json ファイルを washer-done ディレクトリから washer-start ディレクトリにコピーして、washer-start にあるファイルを上書きします。

washer-start ディレクトリで:

cp -vp ../washer-done/firebase.json .

Firebase にデプロイする

これで依存関係のインストールとプロジェクトの設定が完了し、アプリを実行する準備が整いました。

firebase deploy

コンソールに次のような出力が表示されます。

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<project-id>/overview
Hosting URL: https://<project-id>.web.app

このコマンドは、いくつかの Cloud Functions for Firebase とともにウェブアプリをデプロイします。

ブラウザで Hosting URLhttps://<project-id>.web.app)を開き、ウェブアプリを表示します。次のようなインターフェースが表示されます。

L60eA7MOnPmbBMl2XMipT9MdnP-RaVjyjf0Y93Y1b7mEyIsqZrrwczE7D3RQISRs-iusL1g4XbNmGhuA6-5sLcWefnczwNJEPfNLtwBsO4Tb9YvcAZBI6_rX19z8rxbik9Vq8F2fwg

このウェブ UI は、デバイスの状態を表示したり変更したりするためのサードパーティ プラットフォームを表したものです。データベースへのデバイス情報の入力を開始するには、[UPDATE](更新)をクリックします。ページの表示は変化しませんが、洗濯機の現在の状態がデータベースに保存されます。

Actions Console を使用して、デプロイしたクラウド サービスを Google アシスタントに接続します。

Actions Console プロジェクトを構成する

[Overview](概要)> [Build your Action](アクションの構築)で、[Add Action(s)](アクションを追加)を選択します。スマートホーム インテントのフルフィルメントを提供する Cloud Functions の URL を入力し、[Save](保存)をクリックします。

https://us-central1-<project-id>.cloudfunctions.net/smarthome

Uso-o00XQXBHvOR9vQq9tmpYDYQJKsFEstsgRFnxPAJf7zJ2FxwhISiodo3dB1Tz49Okd6ivi66fjpo7rarS_GZelglGWCT1r9FzDGUl1r67ddIcIbQrxqN8jG9F9GAKOpk0Ckc-eA

[Develop] > [Invocation](呼び出し)タブで、アクションの [Display Name](表示名)を追加して [Save] をクリックします。この名前は Google Home アプリに表示されます。

gvC-TvmKDy-D-xjwkeCjNt__9ErA7DL8hZWa1oH1yPJ9SpYOepDYjxx6WnJ56IG-t37fJ65kmHISQdh72Ot2G-0tu6Flxf4gom5kvx_3hlvFeMqYuFgXr_85pfWWn7VLFHtS55p1zw

s4yc1kOW4XtKUQN1EYegiDLU5oTqmxQ2PNbeaujm26OQmYKKpjug7j5FYmutLSAZ1zBd-ZkcZlL7zyTZqw4bge3_oOeWvJTsqJ-A08vfZwImYQrKiquLskLuTpmMqXEZD1xchhCWGQ

アカウント リンクを有効にするには、左側のナビゲーションで [Develop] > [Account linking](アカウント リンク)を選択します。以下の値でアカウント リンクを設定します。

クライアント ID

ABC123

クライアント シークレット

DEF456

認証 URL

https://us-central1-<project-id>.cloudfunctions.net/fakeauth

トークンの URL

https://us-central1-<project-id>.cloudfunctions.net/faketoken

rRyZTiBSTuPk3YtJtXjDK1pPftUxsaEhs9jzpvFtbHTD6bEwYxM8jV4MWxiljKA1bKVZrIRoO9O3jtBefLKf_OyMpukPjwIj8zGvyU3UwASzMrnRskl-hVAfAmQVi4sC_zAwgYwRXw

[Save] をクリックしてアカウント リンクの設定を保存し、[Test](テスト)をクリックしてプロジェクトでのテストを有効にします。

OgUvpQfXioygkRwPcaJpzjyNQDZy6enidUC8YMPaCOrZi0YeWCFsCJV9Gqg-_UfsqTnn4KEg--uE3Ymr0QuamDonF4RyYHtRKcULXABDuaEnj2hq8i20LYj1SrGP_1lQ_UsUB90pGw

[Simulator](シミュレータ)にリダイレクトされます。[Testing on Device](デバイスでのテスト)アイコン(soCeBB1CkSIEqsBmDc8Cth6EjgcXUnrOHeOpLNlvMiiXM73Rmh8iBK1ZFLFd47kycYqIMq3Fm49ryAGUt79BXVPDyEB1IU3W0fgiL49iqTAVrpRszL10mmxzq_AQTJZVrXor-vne2w)にカーソルを移動し、プロジェクトでテストが有効になっていることを確認します。

2zbfeYpG-wEd2SFP07Wc4mJzHakLX7YvrNw3IV0_0Kd-TonfsKIvvjKWlwvrmTm5jLj3XPWqCtcDd5J2z6gwn9fnchpYVraw1j_mE4M0LVppAl5WY5cK7g0uZyhZ3VFFS25yPmyksg

スマートホーム アクションをテストするには、プロジェクトを Google アカウントにリンクする必要があります。これにより、同じアカウントにログインしている Google アシスタント画面と Google Home アプリでテストできるようになります。

  1. スマートフォンで Google アシスタントの設定を開きます。なお、コンソールと同じアカウントでログインする必要があります。
  2. [Google アシスタント] > [設定] > [スマートホーム]([アシスタント] の下)に移動します。
  3. 右上にある検索アイコンをクリックします。
  4. 接頭辞 [test] を使用してテストアプリを検索し、特定のテストアプリを見つけます。
  5. そのアイテムを選択します。Google アシスタントがサービスで認証を行い、SYNC リクエストを送信してデバイスのリストをユーザーに提供するようサービスに依頼します。

Google Home アプリを開いて、洗濯機デバイスが表示されることを確認します。

XcWmBVamBZtPfOFqtsr5I38stPWTqDcMfQwbBjetBgxt0FCjEs285pa9K3QXSASptw0KYN2G8yfkT0-xg664V4PjqMreDDs-HPegHjOc4EVtReYPu-WKZyygq9Xmkf8X8z9177nBjQ

Google Home アプリで、音声コマンドを使用して洗濯機を操作できることを確認します。また、クラウド フルフィルメントのフロントエンド ウェブ UI で、デバイスの状態の変化を確認します。

これで、アクションにローカル フルフィルメントを追加できる状態になりました。

4. クラウド フルフィルメントを更新する

ローカル フルフィルメントをサポートするには、otherDeviceIds というデバイスごとの新しいフィールドを、デバイス固有のローカル識別子を格納するクラウド SYNC レスポンスに追加する必要があります。このフィールドは、デバイスをローカルに制御できるかどうかも示します。

次のコード スニペットに示すように、otherDeviceIds フィールドを SYNC レスポンスに追加します。

functions/index.js

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '123',
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [ ... ],
        name: { ... },
        deviceInfo: { ... },
        willReportState: true,
        attributes: {
          pausable: true,
        },
        otherDeviceIds: [{
          deviceId: 'deviceid123',
        }],
      }],
    },
  };
});

更新したプロジェクトを Firebase にデプロイします。

firebase deploy --only functions

デプロイが完了したら、ウェブ UI に移動してツールバーの更新ボタン ae8d3b25777a5e30.png をクリックします。これにより Request Sync 操作がトリガーされ、更新された SYNC レスポンス データがアシスタントに送信されます。

bf4f6a866160a982.png

5. ローカル フルフィルメントを設定する

このセクションでは、ローカル フルフィルメントに必要な設定オプションをスマートホーム アクションに追加します。開発中は、ローカル フルフィルメント アプリを Firebase Hosting に公開し、Google Home デバイスからアクセスしてダウンロードできるようにします。

Actions Console で、[Develop] > [Actions] を選択し、[Configure Local home SDK] セクションを見つけます。テスト URL として次の URL を入力し、プロジェクト ID を挿入して [Save] をクリックします。

https://<project-id>.web.app/local-home/index.html

7d59b31f8d2a988.png

次に、Google Home デバイスがローカルのスマート デバイスを検出する方法を定義する必要があります。ローカルホーム プラットフォームは、mDNS、UPnP、UDP のブロードキャストなど、さまざまなデバイス検出プロトコルに対応しています。ここでは、UDP ブロードキャストを使用してスマート洗濯機を検出します。

[Add Device Scan configuration](デバイス スキャン設定の追加)で、[New scan config](新しいスキャン設定)をクリックして新しいスキャン設定を追加します。プロトコルとして UDP を選択し、以下の属性を入力します。

フィールド

説明

推奨値

ブロードキャスト アドレス

UDP ブロードキャスト アドレス

255.255.255.255

ブロードキャスト ポート

Google Home が UDP ブロードキャストを送信するポート

3311

リッスンポート

Google Home がレスポンスをリッスンするポート

3312

検出パケット

UDP ブロードキャスト データのペイロード

48656c6c6f4c6f63616c486f6d6553444b

4777bf63c53b6858.png

最後に、ウィンドウの上部にある [Save] をクリックして変更内容を公開します。

6. ローカル フルフィルメントを実装する

Local Home SDK のタイピング パッケージを使用して、TypeScript でローカル フルフィルメント アプリを開発します。スターター プロジェクトで提供されているスケルトンの中身を見てみましょう。

local/index.ts

/// <reference types="@google/local-home-sdk" />

import App = smarthome.App;
import Constants = smarthome.Constants;
import DataFlow = smarthome.DataFlow;
import Execute = smarthome.Execute;
import Intents = smarthome.Intents;
import IntentFlow = smarthome.IntentFlow;

...

class LocalExecutionApp {

  constructor(private readonly app: App) { }

  identifyHandler(request: IntentFlow.IdentifyRequest):
      Promise<IntentFlow.IdentifyResponse> {
    // TODO: Implement device identification
  }

  executeHandler(request: IntentFlow.ExecuteRequest):
      Promise<IntentFlow.ExecuteResponse> {
    // TODO: Implement local fulfillment
  }

  ...
}

const localHomeSdk = new App('1.0.0');
const localApp = new LocalExecutionApp(localHomeSdk);
localHomeSdk
  .onIdentify(localApp.identifyHandler.bind(localApp))
  .onExecute(localApp.executeHandler.bind(localApp))
  .listen()
  .then(() => console.log('Ready'))
  .catch((e: Error) => console.error(e));

ローカル フルフィルメントの中心となるコンポーネントは smarthome.App クラスです。スターター プロジェクトでは、IDENTIFY インテントと EXECUTE インテントのハンドラをアタッチし、listen() メソッドを呼び出してアプリの準備ができたことを Local Home SDK に通知します。

IDENTIFY ハンドラを追加する

Local Home SDK は、Actions Console でのスキャン設定に基づいて、ローカル ネットワーク上の未確認のデバイスを検出して IDENTIFY ハンドラをトリガーします。

一致するデバイスが検出された場合は、スキャン結果のデータに基づいて identifyHandler を呼び出します。アプリでは、UDP ブロードキャストを使用してスキャンを行い、ローカル デバイスによって送信されたレスポンス ペイロードを含むスキャンデータを IDENTIFY ハンドラに提供します。

ハンドラは、ローカル デバイス固有の識別子を含む IdentifyResponse インスタンスを返します。次のコードを identifyHandler メソッドに追加することで、ローカル デバイスからの UDP レスポンスを処理し、適切なローカル デバイス ID を特定できます。

local/index .ts

identifyHandler(request: IntentFlow.IdentifyRequest):
    Promise<IntentFlow.IdentifyResponse> {
  console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));

  const scanData = request.inputs[0].payload.device.udpScanData;
  if (!scanData) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_request', 'Invalid scan data');
    return Promise.reject(err);
  }

  // In this codelab, the scan data contains only local device id.
  const localDeviceId = Buffer.from(scanData.data, 'hex');

  const response: IntentFlow.IdentifyResponse = {
    intent: Intents.IDENTIFY,
    requestId: request.requestId,
    payload: {
      device: {
        id: 'washer',
        verificationId: localDeviceId.toString(),
      }
    }
  };
  console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));

  return Promise.resolve(response);
}

verificationId フィールドは SYNC レスポンスの otherDeviceIds 値のいずれかと一致する必要があります。これにより、ユーザーのホームグラフでローカル フルフィルメントを利用できるデバイスのフラグが設定されます。一致しているデバイスは検証済みとなり、ローカル フルフィルメントで利用できるものと見なされます。

EXECUTE ハンドラを追加する

Local Home SDK は、ローカル フルフィルメントをサポートするデバイスがコマンドを受け取ると EXECUTE ハンドラをトリガーします。ローカル インテントの内容は、クラウド フルフィルメントに送信される EXECUTE インテントと同等であるため、インテントをローカルで処理するロジックは、クラウドでインテントを処理する方法と似ています。

アプリは、TCP/UDP ソケットまたは HTTP(S)リクエストを使用してローカル デバイスと通信できます。この Codelab では、プロトコルとして HTTP を使用して仮想デバイスを制御します。ポート番号は、index.tsSERVER_PORT 変数として定義されています。

次のコードを executeHandler メソッドに追加することで、受信コマンドを処理して HTTP でローカル デバイスに送信できます。

local/index.ts

executeHandler(request: IntentFlow.ExecuteRequest):
    Promise<IntentFlow.ExecuteResponse> {
  console.log("EXECUTE intent: " + JSON.stringify(request, null, 2));

  const command = request.inputs[0].payload.commands[0];
  const execution = command.execution[0];
  const response = new Execute.Response.Builder()
    .setRequestId(request.requestId);

  const promises: Array<Promise<void>> = command.devices.map((device) => {
    console.log("Handling EXECUTE intent for device: " + JSON.stringify(device));

    // Convert execution params to a string for the local device
    const params = execution.params as IWasherParams;
    const payload = this.getDataForCommand(execution.command, params);

    // Create a command to send over the local network
    const radioCommand = new DataFlow.HttpRequestData();
    radioCommand.requestId = request.requestId;
    radioCommand.deviceId = device.id;
    radioCommand.data = JSON.stringify(payload);
    radioCommand.dataType = 'application/json';
    radioCommand.port = SERVER_PORT;
    radioCommand.method = Constants.HttpOperation.POST;
    radioCommand.isSecure = false;

    console.log("Sending request to the smart home device:", payload);

    return this.app.getDeviceManager()
      .send(radioCommand)
      .then(() => {
        const state = {online: true};
        response.setSuccessState(device.id, Object.assign(state, params));
        console.log(`Command successfully sent to ${device.id}`);
      })
      .catch((e: IntentFlow.HandlerError) => {
        e.errorCode = e.errorCode || 'invalid_request';
        response.setErrorState(device.id, e.errorCode);
        console.error('An error occurred sending the command', e.errorCode);
      });
  });

  return Promise.all(promises)
    .then(() => {
      return response.build();
    })
    .catch((e) => {
      const err = new IntentFlow.HandlerError(request.requestId,
          'invalid_request', e.message);
      return Promise.reject(err);
    });
}

TypeScript アプリをコンパイルする

TypeScript コンパイラをダウンロードしてアプリをコンパイルするため、local/ ディレクトリに移動して次のコマンドを実行します。

cd local
npm install
npm run build

これにより、index.ts(TypeScript)のソースがコンパイルされ、以下のファイルが public/local-home/ ディレクトリに格納されます。

  • bundle.js - ローカルアプリと依存関係を含むコンパイル済み JavaScript の出力。
  • index.html - デバイスでのテストでアプリの配信に使用するローカル ホスティング ページ。

テスト プロジェクトをデプロイする

更新したプロジェクト ファイルを Firebase Hosting にデプロイします。これにより、Google Home デバイスからアクセスできるようになります。

firebase deploy --only hosting

7. スマート洗濯機を起動する

次に、ローカル フルフィルメント アプリとスマート洗濯機の間の通信をテストします。この Codelab スターター プロジェクトでは、vNode.js で記述した仮想のスマート洗濯機を使って、スマート洗濯機のローカルでの操作をシミュレーションします。

デバイスを設定する

Actions Console でデバイス検出のスキャン設定に適用したのと同じ UDP パラメータを使用するため、仮想デバイスを設定する必要があります。さらに、報告するローカル デバイス ID と、デバイスの状態が変更されたときに Report State イベントに使用する Actions プロジェクト ID を、仮想デバイスに伝える必要があります。

パラメータ

推奨値

deviceId

deviceid123

discoveryPortOut

3311

discoveryPacket

HelloLocalHomeSDK

projectId

Actions プロジェクトの ID

デバイスを起動する

virtual-device/ ディレクトリに移動し、引数として設定パラメータを渡してデバイス スクリプトを実行します。

cd virtual-device
npm install
npm start -- \
  --deviceId=deviceid123 --projectId=<project-id> \
  --discoveryPortOut=3311 --discoveryPacket=HelloLocalHomeSDK

デバイス スクリプトが、想定どおりのパラメータで実行されたことを確認します。

(...): UDP Server listening on 3311
(...): Device listening on port 3388
(...): Report State successful

8. TypeScript アプリをデバッグする

次のセクションでは、Google Home デバイスがローカル ネットワーク経由で仮想スマート洗濯機をスキャンして適切に識別し、コマンドを送信できることを確認します。Google Chrome デベロッパー ツールを使用すると、Google Home デバイスへの接続、コンソールログの表示、TypeScript アプリのデバッグを行えます。

Chrome デベロッパー ツールを接続する

次の手順に沿って、デバッガをローカル フルフィルメント アプリに接続します。

  1. Actions Console プロジェクトへのアクセス権限を持つユーザーに Google Home デバイスをリンクしていることを確認します。
  2. Google Home デバイスを再起動します。これにより、Actions Console で設定した HTML の URL とスキャン設定が取得されます。
  3. 開発マシンで Chrome を起動します。
  4. 新しい Chrome タブを開き、アドレス フィールドに「chrome://inspect」と入力して、インスペクタを起動します。

ページ上にデバイスのリストが表示され、Google Home デバイスの名前の下にアプリの URL が表示されます。

567f97789a7d8846.png

インスペクタを起動する

アプリの URL の下にある [Inspect](検査)をクリックして Chrome デベロッパー ツールを起動します。[Console](コンソール)タブを選択し、TypeScript アプリによって出力された IDENTIFY インテントの内容が表示されることを確認します。

6b67ded470a4c8be.png

この出力を見ると、ローカル フルフィルメント アプリが仮想デバイスを正常に検出して識別できたことがわかります。

ローカル フルフィルメントをテストする

Google Home アプリのタップ コントロールまたは音声コマンドを使用して、Google Home デバイスにコマンドを送信します。次に例を示します。

「OK Google, 洗濯機をオンにして。」

「OK Google, 洗濯を開始して。」

「OK Google, 洗濯機を止めて。」

これにより、プラットフォームから TypeScript アプリに EXECUTE インテントが送信されます。

bc030517dacc3ac9.png

それぞれのコマンドによって、ローカル スマート洗濯機の状態が変化することを確認します。

...
***** The washer is RUNNING *****
...
***** The washer is STOPPED *****

9. 完了

764dbc83b95782a.png

これで完了です。Local Home SDK を使用したローカル フルフィルメントとスマートホーム アクションの統合が完了しました。

詳細

他にも以下のことを試してみてください。

  • スキャン設定を変更しても機能するかどうか。たとえば、別の UDP ポートや検出パケットを使用してみます。
  • 仮想スマート デバイスのコードベースを修正する。たとえば Raspberry Pi のような組み込みデバイス上で実行できるようにしたり、LED やディスプレイを使って現在の状態を可視化したりしてみます。