スマートホームのアクションを作成する

スマートホームのアクションは、従来のアクションとは構造が異なります。ユーザーがアクションをトリガーする方法のプロセスと、アクションの会話が処理されます。必要なのは、サービスのスマートホーム インテントを処理することだけです。

スマートホームのアクションの作成には、以下の作業が必要です。

  1. アカウント リンク用 OAuth 2.0 サーバーの設定
  2. Actions on Google デベロッパー プロジェクトの作成
  3. スマートホーム オプションの選択
  4. スマートホーム インテント用フルフィルメントの提供
  5. アクションのテストと提出

OAuth 2.0 サーバーの設定

インテントがフルフィルメントに送信されるときには、ユーザーのサードパーティ OAuth 2 アクセス トークンが「Authorization」ヘッダーで渡されます。アカウントがリンクされる前に実行されることはありません。これは、デバイス情報が action.devices.SYNC インテント(アカウント リンクに必要)とともにアシスタントに送信されるためです。

アクションでは、複数の Google ユーザーから同じエージェント ユーザー アカウントへの接続をサポートすることが期待されています(たとえばユーザーが家族にアクセスを許可する場合)。サービスが複数のユーザー接続をサポートできない場合は、アカウント リンク時にエラーが発生します。

ユーザーとアプリの認証用の一般公開の OAuth 2.0 サーバーがあることを確認してください。OAuth の統合については、OAuth を使ったアカウント リンクに説明があります。

Actions on Google デベロッパー プロジェクトの作成

以下の手順でデベロッパー プロジェクトを作成します。

  1. アクション コンソールに移動します。
  2. [プロジェクトを追加 / インポート] をクリックします。
  3. プロジェクト名を入力し、[プロジェクトを作成] をクリックします。

スマートホーム オプションの選択

アクション コンソールのカテゴリ選択画面で、[スマートホーム]、[スマートホーム] の順にクリックして [概要] ページに移動します。左側のナビゲーション パネルで、[ビルド] > [アクション] をクリックします。[最初のアクションを追加] をクリックし、1 つ以上の言語を選択してから、[更新] をクリックしてアクション作成ウィザードに進みます。このウィザードは、スマートホーム プロジェクトを自動的に作成します。作成されたプロジェクトでは、action.devices インテントを実行するアクションを宣言し、スマートホーム インテントのサポートを定義します。

以下のインテントは action.devices 名前空間に含まれており、これらのインテントにはフルフィルメントを与える必要があります。

  • action.devices.SYNC - ユーザーが接続済みで使用可能なデバイスのリストを要求します。
  • action.devices.QUERY - デバイスの現在の状態を問い合わせます。
  • action.devices.EXECUTE - スマートホーム デバイスでのコマンドの実行を要求します。利用可能であれば、レスポンスの中で新しい状態が提供されます。1 つの EXECUTE インテントで、複数のコマンドを使用して、複数のデバイスをターゲットにできます。
  • action.devices.DISCONNECT - ユーザーが Google アシスタントからアプリ アカウントのリンクを解除したときにアプリに通知します。DISCONNECT インテントを受け取った後は、このユーザーのデバイスの状態を返さないでください。

フルフィルメントの実行

フルフィルメントはスマートホームのインテントを処理し、会話 Webhook で定義されているとおりにアシスタントにレスポンスを返す必要があります。リクエストとレスポンスの形式に準拠している限り、任意の言語を使用できます。フルフィルメントでは、アシスタントとクラウド API の間のレイテンシを最小にする必要があります。

ウィザードで、スマートホーム インテントのフルフィルメントを提供するバックエンド サーバーの URL を入力し、[完了] をクリックします。

次のセクションでは、アシスタントとアクションのフルフィルメント間のコミュニケーションのための、リクエストとレスポンスのフォーマットについて説明します。

会話 Webhook との違い

スマートホーム インテントは、対話状態ではなくデバイス ターゲットに焦点を当てています。スマートホームでは、標準の Actions on Google 会話 Webhook を実装しません。物理デバイスとの通信用に特別に設計された Webhook を実装します。会話 Webhook とのプロトコルの違いは以下のとおりです。

  1. deviceconversation は提供されません。会話プロトコルの device フィールドは、モノのインターネット(IoT)のターゲットではなく、サーフェス クライアントを表します。
  2. user フィールドは提供されません。認証トークンは、HTTP リクエストの Authorization ヘッダーに含まれます。
  3. スマートホーム インテントは、inputs に単一のオブジェクトを使用します。これは、intent 値、オートメーション固有のオブジェクトを持つ payload オブジェクトを含みます。
  4. レスポンスにも同様に、スマートホーム インテントに合わせた payload オブジェクトが含まれます。
  5. すべてのリクエストには、Google とパートナーのアクション間でデバッグを行うための任意の ID を持つ requestId フィールドが含まれます。

action.devices.SYNC

このインテントは、ユーザーのセットアップ時、またはデバイスを一括で再同期する必要があるとき(新しいトレイトが追加されたときなど)に、ユーザーがアクション(再接続または切断)で再接続したときに発生します。

アカウントのリンク解除と再リンクを必要とせずにユーザーのデバイスを更新するには、Request Sync を使用します。

このインテントがトリガーされると、アシスタントはフルフィルメントにリクエストを送ります。フルフィルメントは次のように応答します。

  • 必須。すべてのデバイスを返す。一時的に利用できないデバイスも含めます。そうしないと、オフライン デバイスが認識されなくなります。
  • 必須。各デバイスの属性を返します。
リクエスト
{
    "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
    "inputs": [{
      "intent": "action.devices.SYNC"
    }]
}
Node.jsレスポンス
const {smarthome} = require('actions-on-google');
const app = smarthome();
// ...
app.onSync((body, headers) => {
  // TODO Get devices for user
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: "1836.15267389",
      devices: [{
        id: "123",
        type: "action.devices.types.OUTLET",
        traits: [
          "action.devices.traits.OnOff"
        ],
        name: {
          defaultNames: ["My Outlet 1234"],
          name: "Night light",
          nicknames: ["wall plug"]
        },
        willReportState: false,
        roomHint: "kitchen",
        deviceInfo: {
          manufacturer: "lights-out-inc",
          model: "hs1234",
          hwVersion: "3.2",
          swVersion: "11.4"
        },
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: "foo"
        }
      }, {
        id: "456",
        type: "action.devices.types.LIGHT",
        traits: [
          "action.devices.traits.OnOff",
          "action.devices.traits.Brightness",
          "action.devices.traits.ColorTemperature",
          "action.devices.traits.ColorSpectrum"
        ],
        name: {
          defaultNames: ["lights out inc. bulb A19 color hyperglow"],
          name: "lamp1",
          nicknames: ["reading lamp"]
        },
        willReportState: false,
        roomHint: "office",
        attributes: {
          temperatureMinK: 2000,
          temperatureMaxK: 6500
        },
        deviceInfo: {
          manufacturer: "lights out inc.",
          model: "hg11",
          hwVersion: "1.2",
          swVersion: "5.4"
        },
        customData: {
          fooValue: 12,
          barValue: false,
          bazValue: "bar"
        }
      }]
    }
  };
});
JSONレスポンス
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "agentUserId": "1836.15267389",
    "devices": [
      {
        "id": "123",
        "type": "action.devices.types.OUTLET",
        "traits": [
          "action.devices.traits.OnOff"
        ],
        "name": {
          "defaultNames": [
            "My Outlet 1234"
          ],
          "name": "Night light",
          "nicknames": [
            "wall plug"
          ]
        },
        "willReportState": false,
        "roomHint": "kitchen",
        "deviceInfo": {
          "manufacturer": "lights-out-inc",
          "model": "hs1234",
          "hwVersion": "3.2",
          "swVersion": "11.4"
        },
        "customData": {
          "fooValue": 74,
          "barValue": true,
          "bazValue": "foo"
        }
      },
      {
        "id": "456",
        "type": "action.devices.types.LIGHT",
        "traits": [
          "action.devices.traits.OnOff",
          "action.devices.traits.Brightness",
          "action.devices.traits.ColorTemperature",
          "action.devices.traits.ColorSpectrum"
        ],
        "name": {
          "defaultNames": [
            "lights out inc. bulb A19 color hyperglow"
          ],
          "name": "lamp1",
          "nicknames": [
            "reading lamp"
          ]
        },
        "willReportState": false,
        "roomHint": "office",
        "attributes": {
          "temperatureMinK": 2000,
          "temperatureMaxK": 6500
        },
        "deviceInfo": {
          "manufacturer": "lights out inc.",
          "model": "hg11",
          "hwVersion": "1.2",
          "swVersion": "5.4"
        },
        "customData": {
          "fooValue": 12,
          "barValue": false,
          "bazValue": "bar"
        }
      }
    ]
  }
}

リクエストの形式

  • requestId: 文字列。必須。トレースを容易にするためのリクエストの ID
  • 入力:
    • intent: 文字列。必須。action.devices.SYNC
    • (ペイロードなし)

レスポンスの形式

  • ペイロード:
    • errorCode: 文字列。省略可。SYNC でのシステムエラーの場合。
    • debugString: 文字列。省略可。エラーの詳細。ユーザーには表示されませんが、開発中にログに記録されるか使用される可能性があります。
    • agentUserId: 文字列(最大 256 バイト)。必須。エージェントのプラットフォームでの固有かつ不変のユーザー ID が反映されます。この文字列は Google からは見えないため、エージェント側に不変な形式と可変な形式がある場合は、不変な形式を使用します(例: メールアドレスではなくアカウント番号)。
    • devices: Array<Object>。デバイスの配列。0 個以上のデバイスを返します(0 個のデバイスは、ユーザーがデバイスを持っていないか、すべてを切断したことを意味します)。各デバイスには以下のプロパティがあります。
      • id: 文字列。必須。パートナーのクラウド内でのデバイスの ID。共有の場合に、これを使用して同じデバイスの複数のビューを 1 つにまとめることがあるため、ユーザーとパートナーにとって一意である必要があります。デバイスに対して不変でなければなりません。変更された場合、アシスタントはそれを新しいデバイスとして扱います。
      • type: 文字列。必須。デバイスのハードウェア タイプ(たとえば action.devices.types.LIGHT)。完全なデバイスタイプのリストをご覧ください。
      • traits: Array<String>。必須。このデバイスがサポートしているトレイトのリスト(たとえば action.devices.traits.OnOff)。デバイスが持っているコマンド、属性、状態を定義します。完全なデバイス トレイトのリストをご覧ください。
      • name: オブジェクト。必須。デバイスの名前。
        • defaultNames: Array<String>。省略可。ユーザーではなくパートナーによって提供された名前(メーカーの名前、SKU など)のリスト。
        • name: 文字列。必須。デバイスのプライマリ名。通常はユーザーが提供します。アシスタントがレスポンスの中でデバイスを示す名前でもあります。
        • nicknames: Array<String>。省略可。ユーザーがデバイスに付けた追加の名前。
      • willReportState: ブール値。必須。このデバイスの状態がリアルタイム フィードによって更新されるかどうかを示します(状態の報告にリアルタイム フィードを使用する場合は TRUE、ポーリング モデルを使用する場合は FALSE です)。
      • roomHint: 文字列。省略可。セットアップを簡素化するために、ユーザー宅内で現在デバイスがある部屋を示します。
      • deviceInfo: オブジェクト。省略可。1 回限りのロジックが必要な場合に、そのためのデバイスの説明が含まれるフィールド(例: 「ライト Y のファームウェア バージョン X は不具合があるため、色を調整する必要がある」、「セキュリティ上の欠陥をファームウェア Z のすべてのユーザーに通知する必要がある」)。
        • manufacturer: 文字列。特にパートナーが他のデバイスのハブである場合に便利です。たとえば TP-Link と Smartthings の両方が「osram」を同じように記述するように、Google がここでメーカーの標準リストを提供する場合があります。
        • model: 文字列。特定デバイスのモデルまたは SKU ID。
        • hwVersion: 文字列。ハードウェアのバージョン番号(ある場合)。
        • swVersion: 文字列。ソフトウェア / ファームウェアのバージョン番号(ある場合)。
      • attributes: オブジェクト。省略可。以下の属性と同じように、トレイトごとの属性に合わせます。右辺値は string | int | boolean | number です。
      • customData: オブジェクト。省略可。将来の QUERY リクエスト、EXECUTE リクエストに添付されるパートナーによって定義された特別なオブジェクトです。パートナーはこのオブジェクトにデバイスの追加情報を保存し、パフォーマンスやクラウド内のルーティングを向上させることができます(デバイスのグローバル リージョンなど)。このオブジェクトのデータにはいくつかの制約があります。
        • 個人を特定できる情報(PII)を含まないこと
        • データは、他の属性と同じように、めったに変わるべきではない(したがって、リアルタイムの状態を含めるべきではない)
        • オブジェクト全体の大きさは、1 デバイスあたり最大 512 バイト

Request Sync

Request Sync は、指定された agentUserId が関連付けられているデバイス(元の SYNC リクエストで送信されたもの)を持つすべての Google ユーザーに対して SYNC 呼び出しを行うように Google に指示します。この識別子にリンクされているすべてのユーザーが SYNC リクエストを受け取ります。

詳細については、Request Sync の実装をご覧ください。

action.devices.QUERY

このインテントは、パートナーからデバイスの現在の状態を問い合わせます。これは、真にリアルタイムの正確さが要求される問い合わせに使用されます(たとえば、ドアロックの状態)。action.devices.QUERY がトリガーされたときは、states のみが返されます。デバイスのプロパティやトレイト、その他の永続的な要素を更新するには、action.devices.SYNC が使用されます。

リクエスト
{
    "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
    "inputs": [{
      "intent": "action.devices.QUERY",
      "payload": {
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "foo"
          }
        }, {
          "id": "456",
          "customData": {
            "fooValue": 12,
            "barValue": false,
            "bazValue": "bar"
          }
        }]
      }
    }]
}
Node.jsレスポンス
const {smarthome} = require('actions-on-google');
const app = smarthome();
// ...
app.onQuery((body, headers) => {
  // TODO Get device state
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          on: true,
          online: true
        },
        456: {
          on: true,
          online: true,
          brightness: 80,
          color: {
            name: "cerulean",
            spectrumRGB: 31655
          }
        }
      }
    }
  };
});
JSONレスポンス
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "devices": {
      "123": {
        "on": true,
        "online": true
      },
      "456": {
        "on": true,
        "online": true,
        "brightness": 80,
        "color": {
          "name": "cerulean",
          "spectrumRGB": 31655
        }
      }
    }
  }
}

リクエストの形式

  • requestId: 文字列。必須。トレースを容易にするためのリクエストの ID
  • 入力:
    • intent: 文字列。必須。action.devices.QUERY
    • payload: オブジェクト。必須
      • devices: Array<Object>。必須。
        • id: 必須。問い合わせるパートナー ID、SYNC で与えられた ID のとおり。
        • customData: 省略可。中身のわからない customData オブジェクトが SYNC で与えられている場合は、ここで送信されます。

レスポンスの形式

  • ペイロード:
    • errorCode: 文字列。省略可。トランザクション全体のエラーコード(認証が失敗した場合、パートナー システムが利用できない場合)個々のデバイスのエラーには、デバイス オブジェクトの errorCode を使用してください。
    • debugString: 文字列。省略可。エラーの詳細。ユーザーには表示されませんが、開発中にログに記録または使用される可能性があります。
    • devices: オブジェクト。デバイスのマップ。各プロパティには、次の名前と値があります。
      • id: オブジェクト。必須。パートナー デバイス ID を、下の states のセクションで定義されているように、状態プロパティのオブジェクトにマッピングします。
        • online: ブール値。必須。デバイスがオンライン(つまり到達可能)かどうかを示します。
      • errorCode: 文字列。省略可。ERROR 状態を、必要に応じて、プリセットのエラーコードから拡張します。ユーザーに表示されるエラーに対応付けられます。
      • debugString: 文字列。省略可。エラーの詳細。ユーザーには表示されませんが、開発中にログに記録または使用される可能性があります。

Report State

Report State はデバイスの状態を Home Graph に直接報告します。詳細については、Report State の実装をご覧ください。

action.devices.EXECUTE

このインテントは、スマートホームのデバイスで実行するコマンドを与えるためにトリガーされます。利用可能であれば、レスポンスの中で新しい状態を返す必要があります。1 つのトリガーされたインテントで、複数のコマンドを使用して、複数のデバイスをターゲットにできます。たとえば、トリガーされた 1 つのインテントは、複数のライトの組に明るさと色の両方を設定することも、複数のライトのそれぞれに異なる色を設定することもできます。

リクエスト
{
    "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
    "inputs": [{
      "intent": "action.devices.EXECUTE",
      "payload": {
        "commands": [{
          "devices": [{
            "id": "123",
            "customData": {
              "fooValue": 74,
              "barValue": true,
              "bazValue": "sheepdip"
            }
          }, {
            "id": "456",
            "customData": {
              "fooValue": 36,
              "barValue": false,
              "bazValue": "moarsheep"
            }
          }],
          "execution": [{
            "command": "action.devices.commands.OnOff",
            "params": {
              "on": true
            }
          }]
        }]
      }
    }]
}
Node.jsレスポンス
const {smarthome} = require('actions-on-google');
const app = smarthome();
// ...
app.onExecute((body, headers) => {
  // TODO Send command to device
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ["123"],
        status: "SUCCESS",
        states: {
          on: true,
          online: true
        }
      }, {
        ids: ["456"],
        status: "ERROR",
        errorCode: "deviceTurnedOff"
      }]
    }
  };
});
JSONレスポンス
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "123"
        ],
        "status": "SUCCESS",
        "states": {
          "on": true,
          "online": true
        }
      },
      {
        "ids": [
          "456"
        ],
        "status": "ERROR",
        "errorCode": "deviceTurnedOff"
      }
    ]
  }
}

リクエストの形式

  • requestId: 文字列。必須。トレースを容易にするためのリクエストの ID
  • inputs
    • intent: 文字列。必須。action.devices.EXECUTE
    • payload:
      • commands: Array<Object>。必須。各オブジェクトには、ターゲットにするデバイスが 1 つ以上と、それに添付されたコマンドが含まれます。
        • devices: Array<Object>。必須。
          • id: 必須。問い合わせるパートナー ID、SYNC で与えられた ID のとおり。
          • customData: 省略可。中身のわからない customData オブジェクトが SYNC で与えられている場合は、ここで送信されます。
        • execution: Array<Object>。必須。添付された ID の実行コマンドの順序付きリスト。
          • command: 文字列。必須。実行するコマンド(下記を参照)。通常はパラメータが付きます。
          • params: Map<string, Object> 省略可。ただし、各コマンドのパラメータに一致します(下記)。
            • <name>: 文字列。必須。パラメータの名前。
            • <value>: String | Number | Boolean

レスポンスの形式

  • ペイロード:
    • errorCode: 文字列。省略可。トランザクション全体のエラーコード(認証が失敗した場合、パートナー システムが利用できない場合)個々のデバイスのエラーには、デバイス オブジェクトの errorCode を使用してください。
    • debugString: 文字列。省略可。エラーの詳細。ユーザーには表示されませんが、開発中にログに記録されるか使用される可能性があります。
    • commands: Array<Object>。必須。各オブジェクトには、レスポンスの詳細を持つデバイスが 1 つ以上含まれています。注意: リクエストと同じ形ではグループ化されない可能性があります。たとえば、7 つのライトをオンにするリクエストに対して、3 つのライトが成功し、4 つが失敗した場合、レスポンスでは 2 つのグループになります。
      • ids: Array<String>。必須。レスポンスのパートナー デバイス ID
      • status: 文字列。必須。現在のステータスの種類:
        • SUCCESS -コマンド(複数可)の成功を確認した。
        • PENDING - コマンドはキューに入り、成功が期待される。
        • OFFLINE - ターゲット デバイス(複数可)がオフライン状態あるいは到達不可能。
        • ERROR - コマンドを実行できない。
      • errorCode: 文字列。省略可。ERROR 状態を、必要に応じて、プリセットのエラーコードから拡張します。ユーザーに表示されるエラーに対応付けられます。
      • debugString: 文字列。省略可。エラーの詳細。ユーザーには表示されませんが、開発中にログに記録されるか使用される可能性があります。
      • states: オブジェクト。省略可。ただし、以下の属性と同じように、トレイトごとの状態に合わせます。利用可能であれば、実行後の状態を表します。
        • online: ブール値。省略可。デバイスがオンライン(つまり到達可能)かどうかを示します。
        • name:value 文字列。必須。状態の名前と、それに続けて、状態の値(String、Number、Boolean が指定可能)。

action.devices.DISCONNECT

このインテントは、ユーザーが Google アシスタントからアプリ アカウントのリンクを解除したときに、アプリに通知されるようにトリガーされます。DISCONNECT インテントを受け取った後は、このユーザーのデバイスの状態を返さないでください。

リクエスト
{
    "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
    "inputs": [{
      "intent": "action.devices.DISCONNECT",
    }]
}
Node.jsレスポンス
const {smarthome} = require('actions-on-google');
const app = smarthome();
// ...
app.onDisconnect((body, headers) => {
  // TODO Disconnect user account from Google Assistant
  // You can return an empty body
  return {};
});
JSONレスポンス
{}

リクエストの形式

  • requestId: 文字列。必須。トレースを容易にするためのリクエストの ID
  • inputs
    • intent: 文字列。必須。action.devices.DISCONNECT

レスポンスの形式

  • 必要なのは、空の JSON 本文を持つ 200 Ok だけです。

エラー レスポンス

以下は、レスポンスで返される可能性があるエラーコードです。

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "errorCode": "notSupported"
  }
}
  • authExpired: 認証情報は期限切れ。
  • authFailure : 認証の失敗(一般的な場合)。
  • deviceOffline: ターゲットに到達できない。
  • timeout: 内部タイムアウト。
  • deviceTurnedOff: デバイスが完全にオフになっているとわかっている(到達不能と区別できる場合)。
  • deviceNotFound: デバイスがパートナー側に存在しない。これは通常、データ同期の失敗または競合状態を示します。
  • valueOutOfRange: パラメータが範囲外。
  • notSupported: サポートされていないコマンドまたはそのパラメータ(トレイトとビジネス ロジックで回避するため、一般的には発生しない)。
  • protocolError: リクエスト処理の失敗。
  • unknownError: 上記以外のすべて。ただし、これをスローするものはすべて実際のエラーコードに置き換えてください。

エラーと例外の全リストをご覧ください。

タップ操作

視覚的なアシスタント サーフェス上および Google Home アプリでは、ユーザーはグラフィカル インターフェースを使って自宅のデバイスを制御できます。サーモスタットの温度を制御するためのスライダ、スイッチをオン / オフするためのボタンなどです。タップ操作は、音声コマンドの補完となります。

このコントロールは自動的に有効になります。開発作業は不要です。

タップ操作は、以下のトレイトと組み合わせて使用される以下のデバイスでサポートされています。

サポートされているデバイスの種類

サポートされているトレイト