スマートホーム アクションを拡張してセキュリティを強化する

スマートホーム アクションでは、デバイスタイプを使用して、各デバイスにどの文法を適用すべきかを Google アシスタントに伝えることができます。これらのデバイスタイプの機能を定義するのがデバイス トレイトです。デバイスは、アクションに追加されている各デバイス トレイトの状態を継承します。

dc8dce0dea87cd5c.png

ユーザーのデバイスの機能をカスタマイズするには、サポートされているトレイトを特定のデバイスタイプに関連付けます。まだデバイス スキーマで使用できないカスタム トレイトをアクションに実装する場合は、Modes トレイトと Toggles トレイトを使用することで、独自に定義したカスタム名で特定の設定をコントロールできます。

Smart Home API には、タイプやトレイトが提供する基本的なコントロール機能に加えて、ユーザー エクスペリエンスを向上させるための追加機能も用意されています。インテント レスポンスが正常に完了しなかった場合には、エラー レスポンスによって詳細なユーザー フィードバックが提供されます。2 要素認証(2FA)でこのようなレスポンスを拡張し、特定のデバイス トレイトのセキュリティを強化することもできます。アシスタントが発行したチャレンジ ブロックに特定のエラー レスポンスを送信することにより、スマートホーム アクションでコマンドを実行するための追加の承認を要求することも可能です。

前提条件

目標

この Codelab では、これまでに構築したスマートホーム統合を Firebase にデプロイし、洗濯量とターボモードの非標準トレイトをスマートホーム洗濯機に追加する方法を学びます。また、エラーと例外の報告を実装するほか、2FA を使用して洗濯機をオンにするかどうかを音声で確認できるようにします。

演習内容

  • Modes トレイトと Toggles トレイトを追加する
  • エラーと例外を報告できるようにする
  • 2FA を適用する

必要なもの

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

アシスタントで使用する Google アカウントで、次のアクティビティ管理を有効にします。

  • ウェブとアプリのアクティビティ
  • デバイス情報
  • 音声アクティビティ

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

  1. Actions on Google Developer Console に移動します。
  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 Console を開いて HomeGraph API を有効にする必要があります。

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

5SVCzM8IZLi_9DV8M0nEklv16NXkpvM0bIzQK2hSyKyvnFHBxPOz90rbr72ayxzmxd5aNROOqC_Cp4outbdlwJdObDs0DIE_8vYzw6dovoVrP9IZWlWsZxDS7UHOi1jiRbDMG8MqUA

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

ソースコードを取得する

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

ソースコードをダウンロード

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

git clone https://github.com/googlecodelabs/smarthome-traits.git

ダウンロードした zip ファイルを解凍します。

プロジェクトについて

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

  • public: スマート洗濯機の状態を簡単に制御、モニタリングするためのフロントエンド UI。
  • functions: Cloud Functions for Firebase と Firebase Realtime Database を使用してスマート洗濯機を管理する、実装が完了したクラウド サービス。

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

  • fakeauth**:** アカウント リンク用の認証エンドポイント
  • faketoken**:** アカウント リンク用のトークン エンドポイント
  • smarthome**:** スマートホーム インテントのフルフィルメント エンドポイント
  • reportstate**:** デバイスの状態が変化したときに HomeGraph API を呼び出す
  • requestsync**:** アカウントを再リンクしなくてもデバイスを更新できるようにする

Firebase に接続する

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

cd washer-start
firebase use <project-id>

Firebase にデプロイする

functions フォルダに移動し、npm. を使用して必要な依存関係をすべてインストールします。

cd functions
npm install

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

firebase deploy

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

...

✔ Deploy complete!

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

このコマンドによって、いくつかの Cloud Functions for Firebase とともにウェブアプリがデプロイされます。

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

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-.cloudfunctions.net/fakeauth

トークンの URL

https://us-central1-.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 で、デバイスの状態の変化を確認します。

これで、基本的な洗濯機デバイスをデプロイできました。次は、デバイスで使用できるモードをカスタマイズします。

a``ction.devices.traits.Modes トレイトを使用すると、デバイスにモードを追加し、任意の数の値を設定できます(ただし、同時に複数の値をモードにセットすることはできません)。ここでは、洗濯量を定義するモードを追加し、値として「small」(小)、「medium」(中)、「large」(大)を設定します。

SYNC レスポンスを更新する

新しいトレイトを追加するには、トレイトに関する情報を functions/index.jsSYNC レスポンスに追加する必要があります。追加した情報は、次のコード スニペットに示すように traits 配列と attributes オブジェクトに表示されます。

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
          // Add Modes trait
          'action.devices.traits.Modes',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          //Add availableModes
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en',
            }],
            settings: [{
              setting_name: 'small',
              setting_values: [{
                setting_synonym: ['small'],
                lang: 'en',
              }]
            }, {
              setting_name: 'medium',
              setting_values: [{
                setting_synonym: ['medium'],
                lang: 'en',
              }]
            }, {
              setting_name: 'large',
              setting_values: [{
                setting_synonym: ['large'],
                lang: 'en',
              }]
            }],
            ordered: true,
          }],
        },
      }],
    },
  };
});

新しい EXECUTE インテント コマンドを追加する

次のコード スニペットに示すように、EXECUTE インテントに action.devices.commands.SetModes コマンドを追加します。

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      Break;
    // Add SetModes command
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
}

QUERY レスポンスを更新する

次に、QUERY レスポンスを更新して洗濯機の現在の状態を取得します。

更新された変更を queryFirebase 関数と queryDevice 関数に追加して、Realtime Database に保存されている状態を取得します。

index.js

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
    // Add Modes snapshot
    load: snapshotVal.Modes.load,
  };
}

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    // Add currentModeSettings
    currentModeSettings: {
      load: data.load,
    },
  };
};

Report State を更新する

最後に reportstate 関数を更新し、現在の洗濯物量の設定がホームグラフに報告されるようにします。

index.js

const requestBody = {
  requestId: 'ff36a3cc', /* Any unique ID */
  agentUserId: USER_ID,
  payload: {
    devices: {
      states: {
        /* Report the current state of your washer */
        [context.params.deviceId]: {
          on: snapshot.OnOff.on,
          isPaused: snapshot.StartStop.isPaused,
          isRunning: snapshot.StartStop.isRunning,
          // Add currentModeSettings
          currentModeSettings: {
            load: snapshot.Modes.load,
          },
        },
      },
    },
  },
};

Firebase にデプロイする

次のコマンドを実行して、更新したアクションをデプロイします。

firebase deploy --only functions

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

bf4f6a866160a982.png

洗濯機のモードは、次のようなコマンドで設定できます。

「OK Google, 洗濯量を大にセットして。」

次のように、洗濯量を確認することもできます。

「OK Google, 洗濯量はどれくらい?」

action.devices.traits.Toggles トレイトは、デバイスの状態を true または false で示す名前付きアスペクトを表します。たとえば、洗濯機がターボモードになっているかどうかを示すことができます。

SYNC レスポンスを更新する

SYNC レスポンスで、新しいデバイス トレイトに関する情報を追加する必要があります。追加した情報は、次のコード スニペットに示すように traits 配列と attributes オブジェクトに表示されます。

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
          'action.devices.traits.Modes',
          // Add Toggles trait
          'action.devices.traits.Toggles',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en'
            }],
            settings: [{ ... }],
            ordered: true,
          }],
          //Add availableToggles
          availableToggles: [{
            name: 'Turbo',
            name_values: [{
              name_synonym: ['turbo'],
              lang: 'en',
            }],
          }],
        },
      }],
    },
  };
});

新しい EXECUTE インテント コマンドを追加する

次のコード スニペットに示すように、EXECUTE インテントに action.devices.commands.SetToggles コマンドを追加します。

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
    // Add SetToggles command
    case 'action.devices.commands.SetToggles':
      state = {Turbo: params.updateToggleSettings.Turbo};
      ref = firebaseRef.child(deviceId).child('Toggles');
      break;
  }

QUERY レスポンスを更新する

最後に QUERY レスポンスを更新して、洗濯機のターボモードを報告できるようにする必要があります。更新された変更を queryFirebase 関数と queryDevice 関数に追加して、Realtime Database に保存されているトグルの状態を取得します。

index.js

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
    load: snapshotVal.Modes.load,
    // Add Toggles snapshot
    Turbo: snapshotVal.Toggles.Turbo,
  };
}

const queryDevice = async (deviceId) => {
  const data = queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    currentModeSettings: {
      load: data.load,
    },
    // Add currentToggleSettings
    currentToggleSettings: {
      Turbo: data.Turbo,
    },
  };
};

Report State を更新する

最後に reportstate 関数を更新し、洗濯機がターボモードに設定されているかどうかがホームグラフに報告されるようにします。

index.js

const requestBody = {
  requestId: 'ff36a3cc', /* Any unique ID */
  agentUserId: USER_ID,
  payload: {
    devices: {
      states: {
        /* Report the current state of your washer */
        [context.params.deviceId]: {
          on: snapshot.OnOff.on,
          isPaused: snapshot.StartStop.isPaused,
          isRunning: snapshot.StartStop.isRunning,
          currentModeSettings: {
            load: snapshot.Modes.load,
          },
          // Add currentToggleSettings
          currentToggleSettings: {
            Turbo: snapshot.Toggles.Turbo,
          },
        },
      },
    },
  },
};

Firebase にデプロイする

次のコマンドを実行して、更新した関数をデプロイします。

firebase deploy --only functions

デプロイが完了したら、ウェブ UI の [Refresh](更新) ae8d3b25777a5e30.png ボタンをクリックして Request Sync をトリガーします。

これで、次のようなコマンドにより洗濯機をターボモードに設定できるようになりました。

「OK Google, 洗濯機のターボをオンにして。」

次のようなコマンドで、洗濯機がターボモードになっているかどうかを確認することもできます。

「OK Google, 洗濯機はターボモードになってる?」

スマートホーム アクションのエラー処理を使用すると、EXECUTE レスポンスや QUERY レスポンスが失敗したことをユーザーに報告できます。スマート デバイスやアクションの操作中に適切に通知することで、ユーザー エクスペリエンスを向上させることができます。

EXECUTE リクエストや QUERY リクエストが失敗したときには、必ずアクションからエラーコードを返すようにしてください。たとえば、ユーザーが蓋を開けたまま洗濯機を始動しようとしたときにエラーを返す場合は、EXECUTE レスポンスを次のコード スニペットのようにします。

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "456"
        ],
        "status": "ERROR",
        "errorCode": "deviceLidOpen"
      }
    ]
  }
}

これで、ユーザーが蓋を開けたまま洗濯機を始動しようとすると、アシスタントは次のように応答します。

「洗濯機の蓋が開いています。閉じてからもう一度試してください。」

例外はエラーと似ていますが、コマンドに関連するアラートを示すためのもので、コマンドの実行をブロックする場合としない場合があります。例外では、StatusReport トレイトを使用して関連情報(電池残量、状態の変化など)を提供できます。実行をブロックしない例外コードの場合は SUCCESS ステータスが返され、実行をブロックする例外コードの場合は EXCEPTIONS ステータスが返されます。

次のコード スニペットに、例外を含むレスポンスの例を示します。

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [{
      "ids": ["123"],
      "status": "SUCCESS",
      "states": {
        "online": true,
        "isPaused": false,
        "isRunning": false,
        "exceptionCode": "runCycleFinished"
      }
    }]
  }
}

アシスタントは次のように応答します。

「洗濯は終了しています。」

洗濯機からのエラーを報告できるようにするには、functions/index.js を開いてエラークラスの定義を追加します。次のコード スニペットを参照してください。

index.js

app.onQuery(async (body) => {...});

// Add SmartHome error handling
class SmartHomeError extends Error {
  constructor(errorCode, message) {
    super(message);
    this.name = this.constructor.name;
    this.errorCode = errorCode;
  }
}

EXECUTE レスポンスを次のように変更し、エラーコードとエラー ステータスが返されるようにします。

index.js

const executePromises = [];
const intent = body.inputs[0];
for (const command of intent.payload.commands) {
  for (const device of command.devices) {
    for (const execution of command.execution) {
      executePromises.push( ... )
          //Add error response handling
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
            }
          })
      );
    }
  }
}

これで、定義したエラーコードが、アシスタントからユーザーに通知されるようになりました。次のセクションでは、具体的な例に沿って説明します。

たとえばソフトウェアの更新やロックの解除など、デバイスのモードにセキュリティを設定したり、特定の承認済みユーザー グループに限定したりする必要がある場合は、アクションに 2 要素認証(2FA)を実装します。

すべてのデバイスタイプとデバイス トレイトに 2FA を実装し、セキュリティ チャレンジを毎回行うか、特定の条件を満たした場合のみ行うかをカスタマイズできます。

以下の 3 種類のチャレンジがサポートされています。

  • No challenge - 認証チャレンジを使用しないリクエストとレスポンス(デフォルト)
  • ackNeeded - 明示的な承認を必要とする 2FA(はい / いいえ)
  • pinNeeded - 個人識別番号(PIN)を必要とする 2FA

この Codelab では、洗濯機の電源をオンにする際の ackNeeded チャレンジと、2FA チャレンジが失敗した場合にエラーを返す機能を追加します。

functions/index.js を開き、次のコード スニペットに示すように、エラーコードとチャレンジ タイプを返すエラークラスの定義を追加します。

index.js

class SmartHomeError extends Error { ... }

// Add 2FA error handling
class ChallengeNeededError extends SmartHomeError {
  constructor(tfaType) {
    super('challengeNeeded', tfaType);
    this.tfaType = tfaType;
  }
}

EXECUTE レスポンスを次のように変更し、challengeNeeded エラーが返されるようにします。

index.js

const executePromises = [];
const intent = body.inputs[0];
for (const command of intent.payload.commands) {
  for (const device of command.devices) {
    for (const execution of command.execution) {
      executePromises.push( ... )
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
              //Add 2FA error handling
              if(error instanceof ChallengeNeededError) {
                result.challengeNeeded = {
                  type: error.tfaType
                };
              }
            }
          })
      );
    }
  }
}

最後に updateDevice を変更し、洗濯機の電源をオンまたはオフにする際に、明示的な承認が必要となるようにします。

index.js

const updateDevice = async (execution,deviceId) => {
  const {challenge,params,command} = execution; //Add 2FA challenge
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      //Add 2FA challenge
      if (!challenge || !challenge.ack) {
        throw new ChallengeNeededError('ackNeeded');
      }
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    ...
  }

  return ref.update(state)
      .then(() => state);
};

Firebase にデプロイする

次のコマンドを実行して、更新した関数をデプロイします。

firebase deploy --only functions

デプロイが完了した後は、洗濯機の電源をオンまたはオフに切り替えようとすると、アシスタントから次のように承認を求められるようになります。

あなた: 「OK Google, 洗濯機をオンにして。」

アシスタント: 「洗濯機をオンにしてもよろしいですか?」

あなた: 「はい。」

Firebase ログを開くと、2FA フローの各ステップでの詳細なレスポンスを確認することもできます。

289dbe48f4bb8106.png

674c4f4392e98c1.png

これで完了です。Modes トレイトと Toggles トレイトでスマートホーム アクションの機能を拡張し、2FA でデバイス実行時のセキュリティを強化することができました。

詳細

さらに詳しく学びたい方は、以下のことをお試しください。

  • デバイスにローカル実行機能を追加する。
  • 別の 2FA チャレンジを使用してデバイスの状態を変更する。
  • RunCycle トレイトの QUERY レスポンスに変更を加えて動的に更新する。
  • GitHub サンプルを見る。