スマートホームのデバッグ

1. 始める前に

IoT(モノのインターネット)デベロッパーは、ユーザーが Google Home アプリのタップ コントロールや Google アシスタントへの音声コマンドでデバイスを操作できるクラウド間統合を構築できます。

a4657871181b5ad2.gif

クラウド間インテグレーションのデバッグツールを学習することは、Google アシスタントとの本番環境品質のインテグレーションを構築するための重要なステップです。モニタリングとデバッグを容易にするため、Google Cloud Platform(GCP)の指標ロギングスマートホーム用テストスイートを利用して、統合に関する問題を特定して解決できます。

前提条件

作成するアプリの概要

この Codelab では、2 つの欠陥を含むクラウド間統合をデプロイしてアシスタントに接続し、スマートホームと Google Cloud Platform(GCP)の指標とロギングのテストスイートを使用して統合の欠陥をデバッグします。

学習内容

  • GCP の指標とロギングを使用して本番環境の問題を特定して解決する方法
  • スマートホーム用テストスイートを使用して機能と API の問題を特定する方法

必要なもの

2. 問題のあるアプリを実行する

ソースコードを取得する

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

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

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

プロジェクトについて

洗濯機アプリには次のサブディレクトリが含まれています。

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

Firebase への接続

開発マシンでターミナルを開きます。washer-faulty ディレクトリに移動し、スマートホーム デバイスを Google アシスタントに接続する Codelab で作成した統合プロジェクトに Firebase CLI を設定します。

$ cd washer-faulty
$ firebase use <firebase-project-id>

Firebase にデプロイする

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

$ cd functions
$ npm install

注: 次のメッセージが表示された場合は、無視して続行できます。この警告は古い依存関係が原因で、詳しくはこちらをご覧ください。

found 5 high severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details

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

$ firebase deploy

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

...

✔ Deploy complete!

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

HomeGraph を更新する

ブラウザで Hosting URLhttps://<firebase-project-id>.firebaseapp.com)を開き、ウェブアプリを表示します。ウェブ UI で [Refresh](更新)ae8d3b25777a5e30.png ボタンをクリックして、Request Sync を使用して、欠陥のある洗濯機アプリから最新のデバイス メタデータで HomeGraph を更新します。

6f2b1344179977cf.png

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

e357de6a7faff925.png

3. 統合をテストする

プロジェクトをデプロイしたら、統合で洗濯機を制御できるかどうかをテストします。

洗濯機をテストする

スマートフォンで以下の音声コマンドを試して、値が変化することを確認します。

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

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

「OK Google, 洗濯機を一時停止して。」

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

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

洗濯機を一時停止 / 再開すると、アシスタントが音声でエラーを報告します。

「申し訳ありませんが、<プロジェクトの表示名> にアクセスできませんでした。」

この問題をデバッグするには、まずエラーに関する詳細情報を取得して根本原因を絞り込み、特定する必要があります。

スマートホーム アナリティクス ダッシュボード

エラーを確認するのに最適な場所は、クラウド フルフィルメントの使用状況と健全性に関する指標のグラフを集計した Smarthome Analytics ダッシュボードです。

  • [使用状況] 指標には、クラウド間統合の使用状況の傾向(1 日のアクティブ ユーザー数やフルフィルメントへのリクエストの合計数など)が反映されます。
  • 健全性指標を使用すると、Cloud-to-Cloud 統合で異常が発生したかどうかをモニタリングできます。リクエスト レイテンシ、成功率、エラーの内訳を確認できます。

エラーの原因を特定するには、次の手順に沿ってプロジェクト ダッシュボードにアクセスします。

  1. デベロッパー コンソールで、プロジェクト ページに移動します。
  2. スマートホーム プロジェクトを選択します。
  3. 左側のメニューで [アナリティクス] タブをクリックします。

b1735bbe11a7aff8.png

  1. これにより、Google Cloud のプロジェクトのダッシュボードのリストが表示されます。[Google Home Analytics - Cloud Integration] ダッシュボードを選択します。

5edd3751323176dd.png

  1. [Cloud Fulfillment Errors - Status Breakdown] グラフまで下にスクロールして、ハイライト表示された期間のエラーコードを表示します。

c468743c20a11c15.png

PARTNER_RESPONSE_MISSING_DEVICE エラーコードは、根本原因のヒントになります。次に、エラーコードに基づいてイベントログを取得して、詳細を確認します。

イベントログにアクセスする

エラーの詳細を確認するには、Cloud Logging を使用して Cloud-to-Cloud 統合のイベントログにアクセスします。

Google Cloud Platform で [ナビゲーション メニュー] を開き、[オペレーション] で [ロギング] > [ログ エクスプローラ] を選択して、プロジェクトのイベントログにアクセスします。または、検索ボックスで「ログ エクスプローラ」を検索することもできます。

[すべてのフィールドを検索] 入力フィールドにクエリ PARTNER_RESPONSE_MISSING_DEVICE を入力し、[クエリを実行] をクリックします。クエリに一致するログが [結果] セクションに表示されます。

747cca0f1249a5a.png

エラーログには、次のようなエラーの詳細を示すスマートホーム イベントが表示されます。

  • ユーザーが実行したアクションは「洗濯機の再開」(actionType:STARTSTOP_UNPAUSE」)で、最近失敗した音声コマンドに対応しています。
  • 関連するデバッグ メッセージは「JSON response does not include device.」です。

デバッグ メッセージに基づいて、洗濯機アプリが EXECUTE レスポンスに正しいデバイスを含めていない理由を確認する必要があります。

エラーの根本原因を特定する

functions/index.js で、各コマンドのステータスと更新後のデバイスの状態を返す EXECUTE ハンドラ(onExecute 配列内)を見つけます。デバイス ID を EXECUTE レスポンスに挿入するかどうかは、updateDevice 関数の解決によって決まります。

index.js

app.onExecute(async (body) => {
 ...

 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) => {
                 result.ids.push(device.id);
                 Object.assign(result.states, data);
               })
               .catch((e) =>
                 functions.logger.error('EXECUTE',
                     device.id, e.message)));
     }
   }
 }

updateDevice 関数が洗濯機の一時停止 / 再開をどのように処理するかをさらに確認すると、一時停止 / 再開コマンドに一致する文字列が正しくないことがわかります。

index.js

const updateDevice = async (execution, deviceId) => {
 const {params, command} = execution;
 let state; let ref;
 switch (command) {
   ...
   case 'action.devices.commands.PauseUnpausePause':
      const data = await queryDevice(deviceId);
      state = (data.isPaused === false && data.isRunning === false)
        ? {isRunning: false, isPaused: false}
        : {isRunning: !params.pause, isPaused: params.pause};
      ref = getFirebaseRef().child(deviceId).child('StartStop');
      break;
 }

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

エラーを修正する

エラーの根本原因を特定できたので、一時停止 / 再開コマンドの文字列を修正できます。

index.js

const updateDevice = async (execution, deviceId) => {
 const {params, command} = execution;
 let state; let ref;
 switch (command) {
   ...
   case 'action.devices.commands.PauseUnpause':
      const data = await queryDevice(deviceId);
      state = (data.isPaused === false && data.isRunning === false)
        ? {isRunning: false, isPaused: false}
        : {isRunning: !params.pause, isPaused: params.pause};
      ref = getFirebaseRef().child(deviceId).child('StartStop');
      break;
 }

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

修正をテストする

Firebase CLI を使用して、更新したコードをデプロイします。

firebase deploy --only functions

次の音声コマンドを再試行すると、洗濯機の一時停止 / 再開時にアシスタントが正しく応答するようになります。

「OK Google, 洗濯機を一時停止して。」

=>

「はい、洗濯機を一時停止します。」

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

=>

「承知いたしました。洗濯機を再開します。」

また、洗濯機の現在の状態を確認することもできます。

「OK Google, 洗濯機はオンになってる?」

「OK Google, 洗濯機は動いてる?」

「OK Google, 洗濯機の今のステップを教えて」

4. テストスイートで統合をテストする

手動でテストできるだけでなく、自動のスマートホーム用テストスイートを使用して、デバイスタイプと、統合に関連付けられたトレイトに基づいてユースケースを検証できます。テストスイートは一連のテストを実行して統合の問題を検出し、失敗したテストケースに関する情報メッセージを表示して、イベントログを調べる前にデバッグを迅速に行えるようにします。

スマートホーム用テストスイートを実行する

テストスイートで Cloud-to-cloud 統合をテストするには、次の手順に沿って操作します。

  1. ウェブブラウザで、スマートホーム用テストスイートを開きます。
  2. 右上にあるボタンから Google にログインします。これにより、テストスイートから Google アシスタントに直接コマンドを送信できるようになります。
  3. [プロジェクト ID] フィールドに、クラウド間統合のプロジェクト ID を入力します。[次へ] をクリックして続行します。
  4. [Test Settings] ステップで、Test Suite に洗濯機のデバイスタイプとトレイトが表示されます。

78ed6a1ebdb581bf.png

  1. サンプル洗濯機アプリには洗濯機を追加、削除、名前変更する UI がないため、[Test Request Sync](Request Sync のテスト)オプションを無効にします。本番環境システムでは、ユーザーがデバイスを追加、削除、名前変更するたびに Request Sync をトリガーする必要があります。
  2. [次へ] をクリックして、テストの実行を開始します。

Test Suite の実行が完了したら、テストケースの結果を表示します。次の 2 つのテストケースが失敗し、それぞれのエラー メッセージが表示されます。

5838d10631c98ed2.png

クラウド間統合の失敗をデバッグするには、まずエラー メッセージを分析してエラーの根本原因を特定する必要があります。

エラー メッセージを分析する

デベロッパーが根本原因を特定できるように、テストスイートでは、失敗した各テストケースのエラー メッセージに失敗の理由が表示されます。

上記の最初の失敗したテストケースの場合、

99e4e5d06965a8a7.png

エラー メッセージは、テストスイートがクラウド間インテグレーションから報告された状態に "isPause": true を想定しているが、実際の状態には "isPause": false のみが含まれていることを示しています。

また、2 番目の失敗したテストケースのエラー メッセージは、Cloud-to-Cloud 統合からの QUERY レスポンスの状態に "isPause": true が含まれていることを示しています。これは、Cloud-to-Cloud 統合から報告された状態の "isPause": false とは異なります。

fdb5124102e3a37.png

どちらのエラー メッセージにも、統合レポートで isPaused が正しい値で報告されているかどうかを確認するよう記載されています。

エラーの根本原因を特定する

functions/index.js を開きます。このファイルには、Report State を使用して状態の変化をホームグラフに投稿する reportstate 関数が含まれています。レポートの状態のペイロードを調べると、ペイロードに isPaused 状態がないことがわかります。これは、テストスイートが失敗したテストケースでチェックした内容とまったく同じです。

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      ...

      const requestBody = {
        requestId: 'ff36a3cc', /* Any unique ID */
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              /* Report the current state of our washer */
             [context.params.deviceId]: {
                online: snapshot.online,
                on: snapshot.OnOff.on,
                isRunning: snapshot.StartStop.isRunning,
                currentRunCycle: [{
                  currentCycle: 'rinse',
                  nextCycle: 'spin',
                  lang: 'en',
                }],
                currentTotalRemainingTime: 1212,
                currentCycleRemainingTime: 301,
              },
            },
          },
        },
      };

      const res = await homegraph.devices.reportStateAndNotification({
        requestBody,
      });
      ...
    });

エラーを修正する

エラーの根本原因を特定したので、Report State ペイロードに isPaused 状態を追加して functions/index.js を修正します。

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      ...

      const requestBody = {
        requestId: 'ff36a3cc', /* Any unique ID */
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              /* Report the current state of our washer */
             [context.params.deviceId]: {
                online: snapshot.online,
                on: snapshot.OnOff.on,
                isPaused: snapshot.StartStop.isPaused,
                isRunning: snapshot.StartStop.isRunning,
                currentRunCycle: [{
                  currentCycle: 'rinse',
                  nextCycle: 'spin',
                  lang: 'en',
                }],
                currentTotalRemainingTime: 1212,
                currentCycleRemainingTime: 301,
              },
            },
          },
        },
      };
      ...
    });

修正をテストする

Firebase CLI を使用して、更新したコードをデプロイします。

$ firebase deploy --only functions

スマートホーム用テストスイートを再実行すると、すべてのテストケースが合格していることがわかります。

148837f85d377dd6.png

5. 完了

17d485868a6771bc.png

おめでとうございます!スマートホームと GCP の指標とロギングのテストスイートを使用して、クラウド間統合の問題をトラブルシューティングする方法を学習しました。

その他の情報

この Codelab で学んだことを活かして、追加リソースを参照しながら以下の演習に挑戦してみましょう。

インテグレーションの審査(インテグレーションをユーザーに公開するための認定プロセスを含む)を受ける前に行うテストと送信についての詳細もご確認ください。