Cloud 間インテグレーションを強化して保護する

この Codelab について
schedule50 分
subject最終更新: 2024年11月20日
account_circleGoogle 社員により作成

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

dc8dce0dea87cd5c.png

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

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

前提条件

作成するアプリの概要

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

学習内容

  • Modes トレイトと Toggles トレイトを統合に追加する方法
  • エラーと例外を報告できるようにする
  • 二次的なユーザー確認を申請する方法

必要なもの

2. はじめに

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

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

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

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

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

Cloud 間インテグレーション プロジェクトを作成する

  1. Developer Console に移動します。
  2. [プロジェクトを作成] をクリックし、プロジェクトの名前を入力して [プロジェクトを作成] をクリックします。

プロジェクトに名前を付ける

クラウド間インテグレーションを選択する

デベロッパー コンソールの [プロジェクトのホーム] で、[クラウド間] の [クラウド間統合を追加] を選択します。

クラウド間インテグレーションを追加する

Firebase CLI をインストールする

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

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

npm install -g firebase-tools

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

firebase --version

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

firebase login

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

  1. Firebase に移動します。
  2. [プロジェクトを作成] をクリックし、プロジェクト名を入力します。
  3. 同意チェックボックスをオンにして、[続行] をクリックします。同意チェックボックスがない場合は、この手順をスキップできます。
    Firebase プロジェクトを作成する
  4. Firebase プロジェクトが作成されたら、プロジェクト ID を確認します。[プロジェクトの概要] に移動し、設定アイコン > [プロジェクトの設定] をクリックします。
    プロジェクト設定を開く
  5. プロジェクトが [全般] タブに表示されます。
    プロジェクトの一般設定

HomeGraph API を有効にする

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

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

ee198858a6eac112.png

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

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

ソースコードを取得する

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

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

git clone https://github.com/google-home/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 ディレクトリに移動し、統合プロジェクトに Firebase CLI を設定します。

cd washer-start
firebase use <firebase-project-id>

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

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

firebase init

CLI 機能の [Realtime Database]、[Functions]、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

関数を設定する際は、デフォルト ファイルを使用する必要があります。プロジェクト サンプル内の既存の 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

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

? 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 deploy

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

...

✔ Deploy complete!

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

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

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

5845443e94705557.png

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

次は、デベロッパー コンソールを使用して、デプロイしたクラウド サービスを Google アシスタントにリンクします。

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

[Develop] タブで、インタラクションの [Display Name](表示名)を追加します。この名前は Google Home アプリに表示されます。

表示名を追加する

[アプリのブランディング] で、アプリアイコンの png ファイルをアップロードします。サイズは 144 x 144 ピクセルで、名前は .png にします。

アプリアイコンを追加する

アカウント リンクを有効にするには、以下のアカウント リンク設定を使用します。

クライアント ID

ABC123

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

DEF456

認証 URL

https://us-central1-
.cloudfunctions.net/fakeauth

トークンの URL

https://us-central1-
.cloudfunctions.net/faketoken

アカウントのリンク URL を更新する

[Cloud fulfillment URL](クラウド フルフィルメント URL)に、スマートホーム インテントのフルフィルメントを提供する Cloud Functions の関数の URL を入力します。

https://us-central1--cloudfunctions.net/smarthome

Cloud Functions の関数の URL を追加する

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

クラウド間統合をテストする

これで、デバイスの状態とアシスタントをリンクするために必要な Webhook の実装を開始できるようになりました。

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

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

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

ae252220753726f6.png

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

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

4. モードを追加する

action.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, 洗濯量はどれくらい?」

5. トグルを追加する

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, 洗濯機はターボモードになってる?」

6. エラーと例外を報告する

Cloud 間インテグレーションのエラー処理を使用すると、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(
        updateDevice
(execution, device.id)
       
.then((data) => {
         
...
       
})
       
//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;
         
}
       
})
     
);
   
}
 
}
}

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

7. 二次的なユーザー確認を追加する

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

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

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

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

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

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

index.js

class SmartHomeError extends Error { ... }

// Add secondary user verification error handling
class ChallengeNeededError extends SmartHomeError {
 
/**
   * Create a new ChallengeNeededError
   * @param {string} suvType secondary user verification challenge type
   */

  constructor
(suvType) {
   
super('challengeNeeded', suvType);
   
this.suvType = suvType;
 
}
}

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(

        updateDevice
(execution, device.id)
       
.then((data) => {
         
...
       
})
       
.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 error response handling
           
if (error instanceof ChallengeNeededError) {
              result
.challengeNeeded = {
                type
: error.suvType
             
};
           
}
         
}
       
})
     
);
   
}
 
}
}

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

index.js

const updateDevice = async (execution,deviceId) => {
 
const {challenge,params,command} = execution; //Add secondary user challenge
  let state
, ref;
 
switch (command) {
   
case 'action.devices.commands.OnOff':
     
//Add secondary user verification 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 ログを開くと、セカンダリ ユーザー確認フローの各ステップでの詳細なレスポンスを確認することもできます。

289dbe48f4bb8106.png

8. 完了

674c4f4392e98c1.png

これで、Modes トレイトと Toggles トレイトでクラウド間インテグレーションの機能を拡張し、セカンダリ ユーザー確認で実行時のセキュリティを強化しました。

その他の情報

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

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