スマートホーム アクションを作成したら、次の手順として、スマートホーム インテントを処理してアシスタントが認識するレスポンスを返す機能をフルフィルメントに追加します。
このページでは、スマートホーム アクションにインテントのフルフィルメントを実装する手順について説明します。
ユーザーを特定する
アシスタントは、Authorization
ヘッダー内の OAuth 2.0 サーバーから提供されるアクセス トークンを使用して、スマートホーム アクションのフルフィルメントにリクエストを送信します。
POST /fulfillment HTTP/1.1 Host: smarthome.example.com Content-Type: application/json Authorization: Bearer ACCESS_TOKEN
フルフィルメント ロジックでは、リクエストに応答する前にこのトークンの認証情報が有効であることを確認し、関連付けられているユーザー アカウントを特定する必要があります。アクセス トークンが無効な場合は、フルフィルメントから HTTP 401 Unauthorized エラーが返されます。
デバイスと機能のリストを入手する
アシスタントは、action.devices.SYNC
インテントをフルフィルメントに送信し、特定のユーザーとその機能に関連付けられているデバイスのリストをリクエストします。フルフィルメントは、SYNC
レスポンスの agentUserId
フィールドでユーザーごとに一意の ID を返す必要があります。この ID は、クラウド サービスがユーザーの識別に使用する不変値でなければなりません。メールアドレスなど、ユーザーが変更可能な設定に基づく属性を ID に使用することはおすすめしません。
SYNC
レスポンスの devices
フィールドには、ユーザーがアシスタントにアクセスを許可したすべてのデバイス、サポートされているタイプとトレイト、特定のデバイスのトレイトの動作を設定するために必要な属性が格納されています。
SYNC
インテントは、アカウントをリンクしたとき、またはユーザーが手動でデバイスを再同期したときにトリガーされます。ユーザーのデバイスのリスト、サポートされているトレイト、または属性値が変更された場合は、Request Sync を使用して新しい SYNC
インテントをトリガーして変更内容を Google に報告してください。
{ "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf", "inputs": [{ "intent": "action.devices.SYNC" }] }
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" }, "otherDeviceIds": [ { "deviceId": "local-device-id" } ], "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.ColorSetting" ], "name": { "defaultNames": [ "lights out inc. bulb A19 color hyperglow" ], "name": "lamp1", "nicknames": [ "reading lamp" ] }, "willReportState": false, "roomHint": "office", "attributes": { "colorModel": "rgb", "colorTemperatureRange": { "temperatureMinK": 2000, "temperatureMaxK": 9000 }, "commandOnlyColorSetting": false }, "deviceInfo": { "manufacturer": "lights out inc.", "model": "hg11", "hwVersion": "1.2", "swVersion": "5.4" }, "customData": { "fooValue": 12, "barValue": false, "bazValue": "bar" } } ] } }
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" }, otherDeviceIds: [{ deviceId: "local-device-id" }], 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.ColorSetting" ], name: { defaultNames: ["lights out inc. bulb A19 color hyperglow"], name: "lamp1", nicknames: ["reading lamp"] }, willReportState: false, roomHint: "office", attributes: { colorModel: 'rgb', colorTemperatureRange: { temperatureMinK: 2000, temperatureMaxK: 9000 }, commandOnlyColorSetting: false }, deviceInfo: { manufacturer: "lights out inc.", model: "hg11", hwVersion: "1.2", swVersion: "5.4" }, customData: { fooValue: 12, barValue: false, bazValue: "bar" } }] } }; });
Java
@NotNull @Override public SyncResponse onSync(@NotNull SyncRequest syncRequest, @Nullable Map<?, ?> map) { Payload payload = new Payload(); payload.setAgentUserId("1836.15267389"); payload.setDevices( new Device[] { new Device.Builder() .setId("123") .setType("action.devices.types.OUTLET") .addTrait("action.devices.traits.OnOff") .setName( Collections.singletonList("My Outlet 1234"), "Night light", Collections.singletonList("Wall plug")) .setWillReportState(true) .setDeviceInfo("lights-out-inc", "hs1234", "3.2", "11.4") .setCustomData( new JSONObject() .put("fooValue", 74) .put("barValue", true) .put("bazValue", "foo")) .build(), new Device.Builder() .setId("456") .setType("action.devices.types.LIGHT") .addTrait("action.devices.traits.OnOff") .addTrait("action.devices.traits.Brightness") .addTrait("action.devices.traits.ColorTemperature") .addTrait("action.devices.traits.ColorSpectrum") .setName( Collections.singletonList("Lights Out Inc. bulb A19 color hyperglow"), "Lamp", Collections.singletonList("Reading lamp")) .setWillReportState(true) .setDeviceInfo("Lights Out Inc.", "hg11", "1.2", "5.4") .setCustomData( new JSONObject() .put("fooValue", 12) .put("barValue", false) .put("bazValue", "bar")) .build(), }); return new SyncResponse(syncRequest.getRequestId(), payload); }
その他のリファレンスについては、SYNC
インテントのリファレンス ドキュメントをご覧ください。
クエリとコマンドに応答する
ユーザーがアシスタントに現在のデバイスのステータスを尋ねると、フルフィルメントは action.devices.QUERY
インテントが受け取ります。このインテントには、SYNC
レスポンスで提供されたデバイス ID のリストが含まれています。
ユーザーがデバイス操作するためのコマンドをアシスタントに送ると、フルフィルメントは action.devices.EXECUTE
インテントを受け取ります。
QUERY
インテントを処理する
QUERY
レスポンスには、リクエストされたデバイスでサポートされている各トレイトのステータスがすべて含まれます。
{ "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" } }] } }] }
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 } } } } }
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 } } } } }; });
Java
@NotNull @Override public QueryResponse onQuery(@NotNull QueryRequest queryRequest, @Nullable Map<?, ?> map) { QueryResponse.Payload payload = new QueryResponse.Payload(); payload.setDevices( new HashMap<String, Map<String, Object>>() { { put( "123", new HashMap<String, Object>() { { put("on", true); put("online", true); } }); put( "456", new HashMap<String, Object>() { { put("on", true); put("online", true); put("brightness", 80); put( "color", new HashMap<String, Object>() { { put("name", "cerulean"); put("spectrumRGB", 31655); } }); } }); } }); return new QueryResponse(queryRequest.getRequestId(), payload); }
その他のリファレンスについては、QUERY
インテントのリファレンス ドキュメントをご覧ください。
EXECUTE
インテントを処理する
QUERY
と同様に、1 つのインテントで複数のデバイス ID をターゲットにできます。また、1 つの EXECUTE
インテントには、デバイスのグループに対するコマンドを複数含めることができます。たとえば、トリガーされた 1 つのインテントで、複数のライトの明るさと色の両方を設定することも、複数のライトをそれぞれ異なる色に設定することもできます。EXECUTE
レスポンスにより、実行後のデバイスの新しいステータスが返されます。
EXECUTE
インテントやローカル ステータスの変更(たとえばライトのスイッチが手動で切り替えられた)などにより、ユーザーのデバイスのステータスが変更された場合は、Report State を使用します。これにより、ホームグラフとクラウド サービスの同期を維持できます。
{ "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 } }] }] } }] }
JSON
{ "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf", "payload": { "commands": [ { "ids": [ "123" ], "status": "SUCCESS", "states": { "on": true, "online": true } }, { "ids": [ "456" ], "status": "ERROR", "errorCode": "deviceTurnedOff" } ] } }
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" }] } }; });
Java
@NotNull @Override public ExecuteResponse onExecute( @NotNull ExecuteRequest executeRequest, @Nullable Map<?, ?> map) { ExecuteResponse.Payload payload = new ExecuteResponse.Payload(); payload.setCommands( new Commands[] { new Commands( new String[] {"123"}, "SUCCESS", new HashMap<String, Object>() { { put("on", true); put("online", true); } }, null, null), new Commands(new String[] {"456"}, "ERROR", null, "deviceTurnedOff", null) }); return new ExecuteResponse(executeRequest.getRequestId(), payload); }
その他のリファレンスについては、EXECUTE
インテントのリファレンス ドキュメントをご覧ください。
ステータス レスポンス
QUERY
と EXECUTE
のレスポンスには、リクエストの結果を報告するための status
フィールドが含まれています。ステータス レスポンスで返される可能性のある値は次のとおりです。
SUCCESS
: リクエストが成功した。OFFLINE
: ターゲット デバイスがオフラインであるか、接続できない。EXCEPTIONS
: リクエストに関連する問題またはアラートがある。ERROR
: 対応するerrorCode
でリクエストが失敗した。
ERROR
と EXCEPTIONS
の詳細については、エラーと例外を処理するおよびエラーと例外をご覧ください。
リンク解除イベントを処理する
ユーザーがアシスタントからスマートホーム アクションのリンクを解除すると、フルフィルメントは action.devices.DISCONNECT
インテントを受け取ります。このインテントは、該当のユーザーに対するアシスタントからのインテントが送信されなくなることを意味し、クラウド サービスでのこのデバイスに対する Home Graph API(Request Sync と Report State)の呼び出しを停止する必要があります。
{ "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf", "inputs": [{ "intent": "action.devices.DISCONNECT", }] }
JSON
{}
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 {}; });
Java
@Override public void onDisconnect( @NotNull DisconnectRequest disconnectRequest, @Nullable Map<?, ?> map) { // TODO Disconnect user account from Google Assistant // This function does not return anything }
その他のリファレンスについては、DISCONNECT
インテントのリファレンス ドキュメントをご覧ください。
これで、スマートホーム アクション プロジェクトのフルフィルメントの実装が完了し、Request Sync と Report State を使用して非同期の変更を公開できるようになりました。