Internet of Things(IoT)の開発者は、Smart Home 統合を使用することで、直接音声コマンドを通じて Google アシスタントから遠隔制御できるデバイスを構築できます。これは、Google アシスタントとあなたのサーバーとの間のクラウドとクラウドの統合によって実現されます。

Smart Home アプリは、家庭とそのデバイス群に関するコンテキストデータを格納し提供するデータベースである Home Graph に依存しています。たとえば、 Home Graph には、さまざまなメーカーの複数のタイプのデバイス(サーモスタット、ランプ、ファン、および掃除機)を含むリビングルームのコンセプトを保存できます。この情報は、適切なコンテキストに基づいて、ユーザーのリクエストを実行するためにGoogleアシスタントに渡されます。

何を作りますか

このコードラボでは、独自のクラウド統合を作成し、Google アシスタントをスマートな洗濯機に接続します。

What you'll learn

何が必要ですか

Actions on Google Consoleの利用

  1. Actions on Google Developer Console に移動します。
  2. Add Project をクリックして、プロジェクト名を入力します。そして、 CREATE PROJECT をクリックします。

Smart Home Appを選択

Actions console の Overview screen 上で、Home control をクリックし、そして Smart home をクリックします。

Firebase Command Line Interface のインストール

Firebase Command Line Interface (CLI) を使用すると、ウェブアプリをローカルでサーブし、そのウェブアプリを Firebase ホスティングにデプロイできます。

CLIをインストールするために、以下の npm コマンドを実行してください:

npm -g install firebase-tools

CLI が正しくインストールされたかどうかを検証するために、コンソールを開いて、以下を実行してください:

firebase --version

Firebase CLI のバージョンが 3.3.0 以上かどうかを確認してください。

次のコマンドを実行して、Firebase CLI を認可します:

firebase login

サンプルの入手

以下のリンクをクリックして、このコードラボのサンプルをあなたの開発マシンにダウンロードしてください。

Download source code

...もしくは、コマンドラインで GitHub リポジトリを close することができます:

$ git clone https://github.com/googlecodelabs/smarthome-washer.git

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

Firebaseに接続する

あなたが washer-start ディレクトリにいることを確認し、その後あなたの Firebase Project を使用するために Firebase CLI をセットアップします:

cd washer-start
firebase use --add

その後、あなたの Project ID を選択して、指示に従ってください。

Firebaseにデプロイする

washer-start 内の functions フォルダに移動し、npm install を実行します。

cd functions
npm install

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

cd ../
firebase deploy

これは、あなたが見るべきコンソールへの出力です:

...

✔ Deploy complete!

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

ウェブアプリは、 https://<project-id>.firebaseapp.com の形式のホスティングURLにて、現在サーブされているはずです。

このコマンドはまた、Firebase にいくつかの Cloud functions をデプロイしているでしょう。Actions on Google console にて、これらの URL を使う時です。

Actions on Google console内でプロジェクトを設定する

左側の Actions を選択して、 ADD YOUR FIRST ACTION をクリックします。Smart Home インテントのためのフルフィルメントを提供するバックエンドサーバのURLを入力して、その後 DONE をクリックします。

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

その後、DONE をクリックします。

サイドバーにて、Account Linking オプションを選択します。アカウント作成は No を選択し、そしてリンク種別が OAuth および Authentication code であることを確認してください。

以下のクライアント情報を入力します。

Client ID

ABC123

Client secret

DEF456

Authorization URL

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

Token URL

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

その後、この情報を保存するために、下にある SAVE ボタンを選択し、Overview ページに戻ります。

Simulator ページを開き、あなたのプロジェクトのセットアップを完了するために、START TESTING ボタンをクリックします。

Googleアシスタントにリンクする

セットアップを完了するために、あなたがデプロイした functions である Smart Home クラウドにあなたのGoogleアカウントをリンクする必要があります。

Smart Home アカウントが Google アシスタントに接続されたので、デバイスの追加やデータの送信を開始することができます。あなたのサーバーが smarthome 関数で扱う必要のある3つのインテントがあります。

デバイスのリストを動的に取得するために、Cloud Functions for Firebase を使用することができます。それはあなたが定義可能な応答でこれら3つのインテントを処理します。洗濯機の状態をホストするために、Firebase Realtime Database を使用することができます。

SYNCリクエストのアップデート

functions/index.js を開きます。これには、Google アシスタントからのリクエストに応答するコードが含まれています。まず、洗濯機が応答するために、SYNC インテントを処理する必要があります。同じ応答をいつも与えることを、今見てみましょう。

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: '123',
      devices: []
    }
  };
});

今、devices 配列には、何も入っていません。washer を表現するために、その配列に新しい項目を追加します。

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: '123', 
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle'
        ],
        name: {
          defaultNames: 'My Washer'],
          name: 'Washer',
          nicknames: ['Washer']
        },
        deviceInfo: {
          manufacturer: 'Acme Co',
          model: 'acme-washer',
          hwVersion: '1.0',
          swVersion: '1.0.1'
        },
        attributes: {
          pausable: true
        }
     }]
    }
  };
});

では、更新された関数をデプロイします。

firebase deploy

Googleアシスタントにあなたの統合を再度リンクする

新しい SYNC 応答をテストするために、統合のリンクを解除し、再度リンクする必要があります。後で、アカウントのリンクを解除せずに SYNC 要求を発生できるように、Request Sync 機能を追加します。

EXECUTE インテントを処理する

今、洗濯機の現在の状況をユーザに知らせる、あるいはそれを制御するための2つのインテントを処理しなければなりません。最初に、EXECUTE インテントから追加しましょう。

Firebase console に移動し、プロジェクトを選択します。次に、Database ページを開いて、Realtime Database を使用するために GET STARTED を選択します。コンソールを使用して、 washer という新しい子を追加します。washer の下で、トレイトの値を保存するために子供が追加されます。

washer
│
├┬ OnOff 
│└─ on: false
├┬ RunCycle
│└─ dummy: false
├┬ StartStop
│├─ isPaused: false
│└─ isRunning: false

functions/index.js 内で、以下のように EXECUTE ハンドラを編集します:

app.onExecute((body) => {
  const {requestId} = body;
  const payload = {
    commands: [{
      ids: [],
      status: 'SUCCESS',
      states: {
        online: true,
      },
    }],
  };
  for (const input of body.inputs) {
    for (const command of input.payload.commands) {
      for (const device of command.devices) {
        const deviceId = device.id;
        payload.commands[0].ids.push(deviceId);
        for (const execution of command.execution) {
          const execCommand = execution.command;
          const {params} = execution;
          switch (execCommand) {
            case 'action.devices.commands.OnOff':
              firebaseRef.child(deviceId).child('OnOff').update({
                on: params.on,
              });
              payload.commands[0].states.on = params.on;
              break;
            case 'action.devices.commands.StartStop':
              firebaseRef.child(deviceId).child('StartStop').update({
                isRunning: params.start,
              });
              payload.commands[0].states.isRunning = params.start;
              break;
            case 'action.devices.commands.PauseUnpause':
              firebaseRef.child(deviceId).child('StartStop').update({
                isPaused: params.pause,
              });
              payload.commands[0].states.isPaused = params.pause;
              break;
          }
        }
      }
    }
  }
  return {
    requestId: requestId,
    payload: payload,
  };
});

上記の実装では、各コマンドを反復し、Firebase 内の値を更新してから、デバイスの現在の状態を示すペイロードで応答します。

では、更新された関数をデプロイします。

firebase deploy

これで、音声コマンドを入力すると値の変化を確認できます。あなたはこれらのコマンドを与えるためにあなたの携帯電話を使用することができます。

"Turn on my washer"

"Pause my washer"

"Stop my washer"

疑問の質問をサポートするために、"Is my washer on?" のように、QUERY インテントを実装する必要があります。

QUERYインテントの処理

QUERY インテントは、デバイスのセットを含みます。各デバイスにて、それの現在の状況を応答する必要があります。

functions/index.js 内で、QUERY ハンドラを以下のように編集します:

app.onQuery((body) => {
  const {requestId} = body;
  const payload = {
    devices: {},
  };
  const queryPromises = [];
  for (const input of body.inputs) {
    for (const device of input.payload.devices) {
      const deviceId = device.id;
      queryPromises.push(queryDevice(deviceId)
        .then((data) => {
          // Add response to device payload
          payload.devices[deviceId] = data;
        }
        ));
    }
  }
  // Wait for all promises to resolve
  return Promise.all(queryPromises).then((values) => ({
    requestId: requestId,
    payload: payload,
  })
  );
});

これで、質問を尋ねることによって、あなたの洗濯機の現在の状況を知ることができます。

"Is my washer on?"

"Is my washer running?"

今、3つ全てのインテントを実装したので、あなたは洗濯機に追加の機能を実装することができます。

モードとトグルを使用すると、開発者によって定義された名前でデバイスの特定のコンポーネントを制御することができます。このコードラボでは、洗濯機は洗濯物のサイズを小さくするか大きくするかを定義するモードを備えています。

開始するのですが、モードを表示するために index.html のセクションのコメントを外します:

<div id='demo-washer-modes-main'>
    <label>Washer Mode</label>
    <br>
    <label id="demo-washer-modes-small" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="demo-washer-modes-small-in">
      <input checked class="mdl-radio__button" id="demo-washer-modes-small-in" name="load" type="radio"
               value="on">
      <span class="mdl-radio__label">Small</span>
    </label>
    <label id="demo-washer-modes-large" class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="demo-washer-modes-large-in">
      <input class="mdl-radio__button" id="demo-washer-modes-large-in" name="load" type="radio" value="off">
      <span class="mdl-radio__label">Large</span>
    </label>
    <br>
    <br>
</div>

UPDATE ボタンを押した際に、モードが Firebase に格納されます。

SYNC 応答にて、この新しいモードについての情報を追加する必要があります。これは、以下のレスポンスに示されるように、 attributes オブジェクトにて表示されます。

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: '123',
      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',
        ],
        name: {
          defaultNames: ['My Washer'],
          name: 'Washer',
          nicknames: ['Washer']
        },
        deviceInfo: {
          manufacturer: 'Acme Co',
          model: 'acme-washer',
          hwVersion: '1.0',
          swVersion: '1.0.1'
        },
        attributes: {
          pausable: true,
          availableModes: [{
              name: 'load',
              name_values: [{
                  name_synonym: ['load'],
                  lang: 'en'
                }],
              settings: [{
                  setting_name: 'small',
                  setting_values: [{
                      setting_synonym: ['small'],
                      lang: 'en'
                    }]
                  }, {
                  setting_name: 'large',
                  setting_values: [{
                      setting_synonym: ['large'],
                      lang: 'en'
                    }]
                }],
              ordered: true
            }]        
        }
    }]
    }
  };
});

EXECUTE インテントにて、以下のように action.devices.commands.SetModes コマンドを追加する必要があります。

app.onExecute((body) => {
  const {requestId} = body;
  const payload = {
    commands: [{
      ids: [],
      status: 'SUCCESS',
      states: {
        online: true,
      },
    }],
  };
  for (const input of body.inputs) {
    for (const command of input.payload.commands) {
      for (const device of command.devices) {
        const deviceId = device.id;
        payload.commands[0].ids.push(deviceId);
        for (const execution of command.execution) {
          const execCommand = execution.command;
          const {params} = execution;
          switch (execCommand) {
            case 'action.devices.commands.OnOff':
              firebaseRef.child(deviceId).child('OnOff').update({
                on: params.on,
              });
              payload.commands[0].states.on = params.on;
              break;
            case 'action.devices.commands.StartStop':
              firebaseRef.child(deviceId).child('StartStop').update({
                isRunning: params.start,
              });
              payload.commands[0].states.isRunning = params.start;
              break;
            case 'action.devices.commands.PauseUnpause':
              firebaseRef.child(deviceId).child('StartStop').update({
                isPaused: params.pause,
              });
              payload.commands[0].states.isPaused = params.pause;
              break;
            case 'action.devices.commands.SetModes':
              firebaseRef.child(deviceId).child('Modes').update({
                load: params.updateModeSettings.load,
              });
              break;
          }
        }
      }
    }
  }
  return {
    requestId: requestId,
    payload: payload,
  };
});

今、あなたは洗濯機にモードをセットするためにコマンドを与えることができます。

"Set the washer to a large load"

最終的に、あなたは洗濯機の現在の状況についての質問に答えるために、QUERY レスポンスを更新する必要があります。モードを取得するために、queryFirebase および queryDevice 関数に更新された変更を追加します。

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

const queryDevice = (deviceId) =>    queryFirebase(deviceId).then((data) => ({
  on: data.on,
  isPaused: data.isPaused,
  isRunning: data.isRunning,
  currentRunCycle: [{
    currentCycle: 'rinse',
    nextCycle: 'spin',
    lang: 'en',
  }],
  currentTotalRemainingTime: 1212,
  currentCycleRemainingTime: 301,
  currentModeSettings: {
    load: data.load,
  },
}));

以下のようにして、あなたの洗濯機について質問を尋ねることができます:

"Is my washer small load?"

トグルは、洗濯機がターボモードであるかどうかなど、true/false の状態を持つデバイスの名前付きアスペクトを表します。

開始するのですが、モードを表示するために index.html のセクションのコメントを外します。UPDATE ボタンを押した際には、Firebase にモードが格納されます。

<div id='demo-washer-toggles-main'>
  <label id="demo-washer-toggles" class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="demo-washer-toggles-in">
    <input type="checkbox" id="demo-washer-toggles-in" class="mdl-switch__input">
    <span class="mdl-switch__label">Is in Turbo</span>
  </label>
</div>

SYNC レスポンスでは、この新しいモードに関する情報を追加する必要があります。これは、以下のレスポンスに示すように、attributes オブジェクトに表示されます。

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: '123',
      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',
          'action.devices.traits.Toggles',
        ],
        name: {
          defaultNames: ['My Washer'],
          name: 'Washer',
          nicknames: ['Washer']
        },
        deviceInfo: {
          manufacturer: 'Acme Co',
          model: 'acme-washer',
          hwVersion: '1.0',
          swVersion: '1.0.1'
        },
        attributes: {
          pausable: true,
          availableModes: [{
              name: 'load',
              name_values: [{
                  name_synonym: ['load'],
                  lang: 'en'
                }],
              settings: [{
                  setting_name: 'small',
                  setting_values: [{
                      setting_synonym: ['small'],
                      lang: 'en'
                    }]
                  }, {
                  setting_name: 'large',
                  setting_values: [{
                      setting_synonym: ['large'],
                      lang: 'en'
                    }]
                }],
              ordered: true
            }],
          availableToggles: [{
              name: 'Turbo',
              name_values: [{
                  name_synonym: ['turbo'],
                  lang: 'en'
              }]
          }]
        }
    }]
    }
  };
});

あなたの EXECUTE インテントにおいて、以下に示すように、 action.devices.commands.SetToggles コマンドを追加する必要があります。

app.onExecute((body) => {
  const {requestId} = body;
  const payload = {
    commands: [{
      ids: [],
      status: 'SUCCESS',
      states: {
        online: true,
      },
    }],
  };
  for (const input of body.inputs) {
    for (const command of input.payload.commands) {
      for (const device of command.devices) {
        const deviceId = device.id;
        payload.commands[0].ids.push(deviceId);
        for (const execution of command.execution) {
          const execCommand = execution.command;
          const {params} = execution;
          switch (execCommand) {
            case 'action.devices.commands.OnOff':
              firebaseRef.child(deviceId).child('OnOff').update({
                on: params.on,
              });
              payload.commands[0].states.on = params.on;
              break;
            case 'action.devices.commands.StartStop':
              firebaseRef.child(deviceId).child('StartStop').update({
                isRunning: params.start,
              });
              payload.commands[0].states.isRunning = params.start;
              break;
            case 'action.devices.commands.PauseUnpause':
              firebaseRef.child(deviceId).child('StartStop').update({
                isPaused: params.pause,
              });
              payload.commands[0].states.isPaused = params.pause;
              break;
            case 'action.devices.commands.SetModes':
              firebaseRef.child(deviceId).child('Modes').update({
                load: params.updateModeSettings.load,
              });
              break;
            case 'action.devices.commands.SetToggles':
              firebaseRef.child(deviceId).child('Toggles').update({
                Turbo: params.updateToggleSettings.Turbo,
              });
              break;
          }
        }
      }
    }
  }
  return {
    requestId: requestId,
    payload: payload,
  };
});

これで、あなたは洗濯機のモードをセットするためにコマンドを与えることができます。

"Turn on turbo for the washer"

最終的に、あなたは洗濯機のターボモードについての質問に答えるために、QUERY レスポンスを更新する必要があります。トグルの状況を取得するために、queryFirebase および queryDevice 関数に更新された変更を追加します。

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

const queryDevice = (deviceId) => queryFirebase(deviceId).then((data) => ({
  on: data.on,
  isPaused: data.isPaused,
  isRunning: data.isRunning,
  currentRunCycle: [{
    currentCycle: 'rinse',
    nextCycle: 'spin',
    lang: 'en',
  }],
  currentTotalRemainingTime: 1212,
  currentCycleRemainingTime: 301,
  currentModeSettings: {
    load: data.load,
  },
  currentToggleSettings: {
    Turbo: data.turbo,
  },
}));

あなたは以下のように、洗濯機についての質問を尋ねることができます。

"Is my washer in turbo mode?"

今、洗濯機は完全に実装されています。あなたは、この洗濯機の現在の状況を得て、制御することができます。しかしながら、新しい機能を追加する度に、あなたは統合のリンク解除を行い、そして再リンクしなければなりませんでした。

SYNC リクエストは、このリンク手順の間であなたのサーバに届くのみです。Request Sync APIの追加によって、SYNCリクエストをトリガーすることができ、ユーザアカウントのリンク解除および再リンクを行うことなしに、ユーザのデバイスリストを更新することが可能になります。

Request Sync APIは、パラメータとしてユーザIDを受け取ります。これは、あなたのサーバにおいてユーザを示すユーザIDであるべきです。このコードラボでは、その値は "123" とハードコードされます。

  1. Cloud Platform Console にて、Projects ページに移動してください。そして、あなたの Smart Home プロジェクトIDに一致するプロジェクトを選択します。
  2. HomeGraph API を有効にします。
  3. API キーを生成します。左のナビゲーションから、APIs & Services にある Credentials を選択します。Create Credentials ボタンをクリックし、そして API キーを選択します。
  4. functions/index.js 内の Smart Home コンストラクタに、API キーを追加します。
const app = smarthome({
  API_KEY: '<api-key>'
});

フロントエンドウェブ UI にて、ヘッダにリフレッシュアイコンがあります。Request Sync 呼び出しをさせるために、クリックリスナーを UI に追加することができます。

main.js を以下のようにします:

    this.requestSync = document.getElementById('request-sync');
    this.requestSync.addEventListener('click', () => {
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          console.log("Request SYNC success!");
        }
      };
      xhttp.open("POST", "https://us-central1-<project-id>.cloudfunctions.net/requestsync", true);
      xhttp.send();
    });

これで、そのアイコンをクリックすると、あなたのサーバのログに SYNC リクエストが出てきます。デバイスを更新する、新しいデバイスを追加する、またはこのデバイスを削除する度に、新しい Request Sync 呼び出しが送信されて、その際にはデバイスの一覧が表示されるのがわかります。

応答はまた、あなたのログに表示されます。そのログは、Firebase を通じて見ることができます。

Report State API を使って、Smart Home 統合は Home graph にデバイスの状況を積極的に送信することができます。これにより、ユーザの問い合わせを迅速に完了でき、そして携帯端末やスマートディスプレイ上のリッチな UI を使って、全てのユーザのデバイスの状況を知ることができるようになります。

洗濯機の状況を変更するために UPDATE ボタンをクリックした際に、あなたは Home graph にこれをレポートする追加の手順を加えることができます。

JWTの生成

私たちの関数を書くには、データが安全に送信されることを確認する必要があります。これはJWT (JSON web tokens) を介して行われます。このコードラボでは、このトークンの作成を容易にしてくれる googleapis npm 依存ライブラリを使用します。

あなたのプロジェクトの Google Cloud Console に行きます。APIs & Services セクション内の Credentials を選択します。Create Credentials ボタンをクリックし、そして Service account key を選択します。Role 設定のために、Project -> Editor を選択します。

Service account の作成後に、JSON ファイルをダウンロードします。このファイルを、プロジェクト内の functions フォルダに key.json という名前で保存してください。

API呼び出しの追加

これは、Firebase データベーストリガーを使用して行うことができます。特定の書き込みイベントが発生すると、自動的に状態を報告することができます。

functions/index.js 内を以下のようにします。

exports.reportstate = functions.database.ref('{deviceId}').onWrite((event) => {
  console.info('Firebase write event triggered this cloud function');
  const https = require('https');
  const {google} = require('googleapis');
  const key = require('./key.json');
  const jwtClient = new google.auth.JWT(
    key.client_email,
    null,
    key.private_key,
    ['https://www.googleapis.com/auth/homegraph'],
    null
  );

  const snapshotVal = event.data.val();

  const postData = {
    requestId: 'ff36a3cc', /* Any unique ID */
    agentUserId: '123', /* Hardcoded user ID */
    payload: {
      devices: {
        states: {
          /* Report the current state of our washer */
          [event.params.deviceId]: {
            on: snapshotVal.OnOff.on,
            isPaused: snapshotVal.StartStop.isPaused,
            isRunning: snapshotVal.StartStop.isRunning,
          },
        },
      },
    },
  };

  jwtClient.authorize((err, tokens) => {
    if (err) {
      console.error(err);
      return;
    }
    const options = {
      hostname: 'homegraph.googleapis.com',
      port: 443,
      path: '/v1/devices:reportStateAndNotification',
      method: 'POST',
      headers: {
        Authorization: ` Bearer ${tokens.access_token}`,
      },
    };
    return new Promise((resolve, reject) => {
      let responseData = '';
      const req = https.request(options, (res) => {
        res.on('data', (d) => {
          responseData += d.toString();
        });
        res.on('end', () => {
          resolve(responseData);
        });
      });
      req.on('error', (e) => {
        reject(e);
      });
      // Write data to request body
      req.write(JSON.stringify(postData));
      req.end();
    }).then((data) => {
      console.info(data);
    });
  });
});

おめでとうございます!あなたは Smart Home を使った自身のデバイスを Google アシスタントに統合することに成功しました。

ここに、あなたがより深く実装することができるいくつのアイディアがあります。

もしあなたがデバイスメーカーの方だった場合は、デバイスを Google アシスタントに統合した後、審査のためにあなたのアクションを提出できます。あなたの統合は、認証プロセスを経て提出されます。

What we've covered