AWS IoT
開発者ガイド

シャドウの使用

AWS IoT には、デバイスのシャドウを操作するための 3 つのメソッドが用意されています。

UPDATE

デバイスのシャドウが存在しない場合は作成するか、またはリクエストで渡されたデータでデバイスの内容を更新します。データには、いつ最後に更新されたかを示すタイムスタンプ情報も保存されています。メッセージは desired 状態と reported 状態との違い (差分) と共にすべてのサブスクライバーに送信されます。メッセージを受信するモノやアプリケーションは、desired 状態と reported 状態との違いに基づいてアクションを実行できます。たとえば、デバイスは自らの状態を desired 状態に更新でき、アプリケーションはデバイスの状態の変更を表示するようにその UI を更新できます。

GET

デバイスのシャドウに保存されている最新の状態を取得します (デバイスの起動時に設定やオペレーションの最終状態を取得するなど)。このメソッドは、メタデータを含む完全な JSON ドキュメントを返します。

DELETE

デバイスのシャドウを、その内容のすべてを含め、削除します。これにより、データストアから JSON ドキュメントが削除されます。削除したデバイスのシャドウを復元することはできませんが、同じ名前で新しいシャドウを作成することはできます。

プロトコルのサポート

これらのメソッドは MQTT と RESTful API over HTTPS の両方でサポートされています。MQTT はパブリッシュ/サブスクライブ通信モデルであるため、AWS IoT には一連の予約されたトピックが実装されています。モノやアプリケーションは、リクエスト - レスポンス動作を実装するために、request トピックにパブリッシュする前にこれらのトピックにサブスクライブします。詳細については、「シャドウの MQTT トピック」および「Device Shadow の RESTful API」を参照してください。

シャドウの更新

UpdateThingShadow RESTful API を使用するか、/update トピックにパブリッシュすることで、デバイスのシャドウを更新できます。更新は、リクエストで指定したフィールドにのみ反映されます。

初期状態:

{ "state": { "reported" : { "color" : { "r" :255, "g": 255, "b": 0 } } } }

更新メッセージを送信:

{ "state": { "desired" : { "color" : { "r" : 10 }, "engine" : "ON" } } }

デバイスは、前回の desired メッセージによってトリガーされる /update/delta トピックで /update 状態を受信し、目的の変更を実行します。完了したら、デバイスは Shadow JSON ドキュメント内の reported セクションにより、その更新された状態を確認します。

最終状態:

{ "state": { "reported" : { "color" : { "r" : 10, "g" : 255, "b": 0 }, "engine" : "ON" } } }

Shadow ドキュメントの取得

GetThingShadow RESTful API を使用するか、/get トピックにサブスクライブしてパブリッシュすることで、デバイスのシャドウを取得できます。これにより、ドキュメント全体に加えて、desired 状態と reported 状態との差分が取得されます。

ドキュメントの例:

{ "state": { "desired": { "lights": { "color": "RED" }, "engine": "ON" }, "reported": { "lights": { "color": "GREEN" }, "engine": "ON" } }, "metadata": { "desired": { "lights": { "color": { "timestamp": 123456 }, "engine": { "timestamp": 123456 } } }, "reported": { "lights": { "color": { "timestamp": 789012 } }, "engine": { "timestamp": 789012 } }, "version": 10, "timestamp": 123456789 } }

レスポンス:

{ "state": { "desired": { "lights": { "color": "RED" }, "engine": "ON" }, "reported": { "lights": { "color": "GREEN" }, "engine": "ON" }, "delta": { "lights": { "color": "RED" } } }, "metadata": { "desired": { "lights": { "color": { "timestamp": 123456 }, }, "engine": { "timestamp": 123456 } }, "reported": { "lights": { "color": { "timestamp": 789012 } }, "engine": { "timestamp": 789012 } }, "delta": { "lights": { "color": { "timestamp": 123456 } } } }, "version": 10, "timestamp": 123456789 }

オプティミスティックロック

状態ドキュメントのバージョンを使用して、デバイスのシャドウドキュメントの最新バージョンを更新していることを確認できます。更新リクエストでバージョンを渡したとき、そのバージョンと状態ドキュメントの現在のバージョンとが一致しない場合、サービスは HTTP 409 conflict レスポンスコードでリクエストを拒否します。

(例:

初期ドキュメント:

{ "state" : { "desired" : { "colors" : ["RED", "GREEN", "BLUE" ] } }, "version" : 10 }

更新: (バージョンが一致しないと、リクエストは拒否される)

{ "state": { "desired": { "colors": [ "BLUE" ] } }, "version": 9 }

結果:

409 Conflict

更新: (バージョンが一致すると、リクエストは受け入れられる)

{ "state": { "desired": { "colors": [ "BLUE" ] } }, "version": 10 }

最終状態:

{ "state": { "desired": { "colors": [ "BLUE" ] } }, "version": 11 }

データの削除

/update トピックにパブリッシュして、削除するフィールドを null に設定することで、デバイスのシャドウからデータを削除できます。値が null のフィールドはすべてドキュメントから削除されます。

初期状態:

{ "state": { "desired" : { "lights": { "color": "RED" }, "engine" : "ON" }, "reported" : { "lights" : { "color": "GREEN" }, "engine" : "OFF" } } }

更新メッセージを送信:

{ "state": { "desired": null, "reported": { "engine": null } } }

最終状態:

{ "state": { "reported" : { "lights" : { "color" : "GREEN" } } } }

デバイスのシャドウからすべてのデータを削除するには、その状態を null に設定する必要があります。たとえば、以下のメッセージを送信すると、状態データはすべて削除されますが、デバイスのシャドウは残ります。

{ "state": null }

デバイスのシャドウは、その状態が null であっても残ります。次の更新が発生すると、Shadow のバージョンがインクリメントされます。

シャドウの削除

DeleteThingShadow RESTful API を使用するか、/delete トピックにパブリッシュすることで、デバイスのシャドウドキュメントを削除できます。

注記

デバイスのシャドウを削除しても、モノは削除されません。モノを削除しても、対応するデバイスのシャドウは削除されません。

初期状態:

{ "state": { "desired" : { "lights": { "color": "RED" }, "engine" : "ON" }, "reported" : { "lights" : { "color": "GREEN" }, "engine" : "OFF" } } }

空のメッセージが /delete トピックにパブリッシュされます。

最終状態:

HTTP 404 - resource not found

delta 状態

delta 状態は、desired 状態と reported 状態の違いを含む virtual 型の状態です。desired セクションに含まれるが、reported セクションに含まれないフィールドは、差分に含まれます。reported セクションに含まれるが、desired セクションに含まれないフィールドは、差分に含まれます。差分にはメタデータが含まれ、その値は desired フィールドのメタデータに一致します。(例:

{ "state": { "desired": { "color": "RED", "state": "STOP" }, "reported": { "color": "GREEN", "engine": "ON" }, "delta": { "color": "RED", "state": "STOP" } }, "metadata": { "desired": { "color": { "timestamp": 12345 }, "state": { "timestamp": 12345 }, "reported": { "color": { "timestamp": 12345 }, "engine": { "timestamp": 12345 } }, "delta": { "color": { "timestamp": 12345 }, "state": { "timestamp": 12345 } } }, "version": 17, "timestamp": 123456789 } }

ネストされたオブジェクトが異なると、差分にはルートへのパスが含まれます。

{ "state": { "desired": { "lights": { "color": { "r": 255, "g": 255, "b": 255 } } }, "reported": { "lights": { "color": { "r": 255, "g": 0, "b": 255 } } }, "delta": { "lights": { "color": { "g": 255 } } } }, "version": 18, "timestamp": 123456789 }

Device Shadow サービスは、desired 状態の各フィールドを反復処理し、reported 状態と比較することで、差分を計算します。

配列は値のように扱われます。desired セクションの配列が reported セクションの配列に一致しない場合は、desired 配列全体が差分にコピーされます。

状態の変更の監視

デバイスのシャドウが更新されると、メッセージが 2 つの MQTT トピックにパブリッシュされます。

  • $aws/things/thing-name/shadow/update/accepted

  • $aws/things/thing-name/shadow/update/delta

update/delta トピックに送信されるメッセージは、自らの状態が更新されるモノに使用されます。このメッセージには、デバイスのシャドウドキュメントの desired セクションと reported セクションとの差分のみが含まれます。このメッセージを受信すると、デバイスはリクエストされた変更を行うかどうかを決定します。デバイスの状態が変更されると、新しい現在の状態を $aws/things/thing-name/shadow/update トピックにパブリッシュします。

デバイスやアプリケーションは、ドキュメントの状態が変更されたときに通知されるように、これらのトピックのいずれかにサブスクライブできます。

以下に示しているのは、そのフローの例です。

  1. デバイスはその状態をレポートします。

  2. システムはその永続データストア内の状態ドキュメントを更新します。

  3. システムは差分メッセージをパブリッシュします。このメッセージは差分を含み、サブスクライブしているデバイスがターゲットになります。デバイスはこのトピックにサブスクライブして、更新を受信します。

  4. デバイスのシャドウは、受信したメッセージをパブリッシュします。このメッセージには、受信したドキュメント全体 (メタデータなど) が含まれます。アプリケーションは、更新を受信するために、このトピックにサブスクライブします。

メッセージの順序

AWS IoT サービスからのメッセージが特定の順序でデバイスに到達するとは限りません。

初期状態ドキュメント:

{ "state" : { "reported" : { "color" : "blue" } }, "version" : 10, "timestamp": 123456777 }

更新 1:

{ "state": { "desired" : { "color" : "RED" } }, "version": 10, "timestamp": 123456777 }

更新 2:

{ "state": { "desired" : { "color" : "GREEN" } }, "version": 11, "timestamp": 123456778 }

最終状態ドキュメント:

{ "state": { "reported": { "color" : "GREEN" } }, "version": 12, "timestamp": 123456779 }

これにより、2 つの差分メッセージが生成されます。

{ "state": { "color": "RED" }, "version": 11, "timestamp": 123456778 }
{ "state": { "color" : "GREEN" }, "version": 12, "timestamp": 123456779 }

デバイスは順不同でこれらのメッセージを受信する場合があります。これらのメッセージ内の状態は累積的であるため、デバイスは追跡しているものより古いバージョン番号が含まれるメッセージをすべて安全に破棄できます。デバイスはバージョン 11 の前にバージョン 12 の差分を受信した場合、バージョン 11 のメッセージを安全に破棄できます。

シャドウメッセージのトリム

デバイスに送信されるシャドウメッセージのサイズを小さくするには、デバイスに必要なフィールドのみを選択してから、デバイスのリッスン対象の MQTT トピックにメッセージを再パブリッシュするルールを定義します。

ルールは JSON で指定し、以下のようになります。

{ "sql": "SELECT state, version FROM '$aws/things/+/shadow/update/delta'", "ruleDisabled": false, "actions": [{ "republish": { "topic": "${topic(2)}/delta", "roleArn": "arn:aws:iam::123456789012:role/my-iot-role" } }] }

SELECT ステートメントにより、指定したトピックにメッセージのどのフィールドが再パブリッシュされるかが決まります。すべての Shadow 名に一致させるには、"+" のワイルドカードを使用します。ルールでは、一致するすべてのメッセージが指定したトピックに再パブリッシュされるように定義しています。この場合、"topic()" 関数を使用して、再パブリッシュする先のトピックを指定しています。topic(2) は、元のトピック内のモノ名に評価されます。ルールの作成の詳細については、「ルール」を参照してください。