シャドウ概要
AWS IoT Coreでは、ラズパイなどのエッジ端末をモノ(Thing)として登録し、その状態を保持する機能を持っています。この「状態(state)」がシャドウ(shadow)です。
アプリケーションでは、この状態の変更を通してデバイスを操作します。
状態には**”reported”、”desired”**の2つの要素が存在します。
- ”desired”: アプリケーションとして期待する状態を示すのに使用します。アプリケーションで状態の変更要求を出す場合は、この状態を更新します。
- “reported”: デバイス自身がどういった状態なのかを示すのに使用します。この状態を変更するのは基本的にはデバイス自身です。
具体例
下記例は、AWSコンソールからシャドウを追加し、シャドウの内容を確認した結果です。
-
シャドウ名の入力欄が表示されるので、任意(今回は「test」)で入力し、シャドウを追加する。
-
アプリケーションからの状態変更があったと想定して、コンソールでdesired側の"welcome"の値を"aiueo"変更すると、シャドウステータスが下記のように変化する。
deltaとは、状態に差異がある箇所を抽出し、表示してくれるもの
{ "desired": { "welcome": "aiueo" }, "reported": { "welcome": "aws-iot" }, "delta": { "welcome": "aiueo" } }
-
次にエッジから状態変更があったと想定して、コンソールでreportedの"welcome"の値を"oeuia"変更すると、シャドウステータスが下記のように変化する。
まだ状態が一致していないため、deltaは表示されたままとなる。
{ "desired": { "welcome": "aiueo" }, "reported": { "welcome": "oeiua" }, "delta": { "welcome": "aiueo" } }
-
最後に、コンソールでreportedの"welcome"の値を"aiueo"変更し状態を一致させると、シャドウステータスが下記のように変化する。
状態が一致するとdeltaが消える。
{ "desired": { "welcome": "aiueo" }, "reported": { "welcome": "aiueo" } }
状態はjson形式で管理されるため、例えば下記のように、複数の情報を持つことができ、また配列なども保持することが可能。
そのため、例えば、1つのシャドウで1つのエッジにぶら下がっている複数のデバイス(照明やエアコンなど)の複数の状態(電源ON/OFF、温度など)を管理することも可能です。
(複数のシャドウで管理することも可能ですが、1つのシャドウで管理することもできます。という意味です。)
"desired": {
"welcome": "aws-iot",
"device": [
{
"type": "light",
"id": "1",
"connect": true,
"power": "on"
},
{
"type": "aircon",
"id": "2",
"connect": true,
"ondo": "20"
}
]
},
シャドウの種類
名前なしシャドウ
前述の例では、シャドウに名前を決めていたが、名前なしでシャドウを作ることもできる。
AWSコンソールで作成する場合、前述の例の名前決定箇所で名前を入力しなければ生成される。
公式サイトによると、あらかじめ1つしかシャドウを利用しないことが決まっているのであれば、名前なしシャドウを利用すると良いらしいが、既に複数のシャドウを使用する可能性があるのであれば、後述する名前付きシャドウを利用した方が良いとのこと。
拡張性を考えるなら、名前付きシャドウを使う方が良いとの理解。
[メリット]
- 名前が無いため、利用する側(エッジやLambda)が名前を知らなくてもアクセスできる。(単純)
[デメリット]
- 名前がないため、1つしか作ることができない。
名前付きシャドウ
前述の例で記載した通り、名前を指定したシャドウ。
[メリット]
- シャドウ内で複数の状態(例えば、電源ON/OFF、温度など)を管理することもできるが、複数の名前付きシャドウを作成し、個々に管理することが可能。
[デメリット]
- 基本的(*)にはエッジ端末が直接IoT Coreからシャドウの一覧を取得することはできないため、あらかじめ名前は決めておく必要がある。
(*)Sigv4(IAMユーザのアクセスキーとシークレットキーがあれば、取得可能だが、クライアント証明書では取得することができなかった)
シャドウへのアクセス方法
シャドウへの制御は、名前付きシャドウ と 名前なしシャドウでそれぞれ異なり、それぞれで、
GET、UPDATE、DELETEの3種類の制御が可能。
-
GETは、指定したシャドウの状態を取得
-
UPDATEは、指定したシャドウの状態を更新(desired、reportedのどちらを更新するか選択できる)
-
DELETEは、指定したシャドウの削除
エッジ側からのアクセス方法
<余談>AWS のnodejsのサンプルコードにシャドウに関するサンプルはなかったため、pythonのサンプルを元にnodejsに実装して確認してみた。
前述の通り、シャドウの制御は3つしかないので比較的簡単に実装できる。
AWS SDKの"aws-iot-device-sdk-v2"をインポートするとiotshadowというオブジェクトがある。
そのオブジェクトの提供関数に、シャドウの名前付き、名前なしでそれぞれGET、UPDATE、DELETEの関数が提供されているため、それを用いる。
なお、指定したシャドウの状態が変化した場合にコールバック関数を登録できる関数もある。
//シャドウクライアント生成。connectionはMQTTのコネクションオブジェクト
let shadow = new iotshadow.IotShadowClient(this.connection);
// GET関数コール
const getNamShadow: iotshadow.model.GetNamedShadowRequest = {
thingName: client_id, //モノの名前
shadowName: shadowName, //シャドウの名前
};
await shadow.publishGetNamedShadow(getNamShadow, mqtt.QoS.AtLeastOnce);
AWS側からのアクセス方法
Lambda経由でIoT CoreにMQTTメッセージを送信することで行う。
IoT CoreへMQTTメッセージを送信するSDK(boto3)が提供されているため、それを用いる。
GET、UPDATE、DELETE制御は、それぞれTopicを指定し行う。
名前付きシャドウの場合、下記のようにTopicにモノの名前、シャドウ名を含めMQTTメッセージを送信し、受信を待つ。
iot = boto3.client('iot-data')
topic = f'$aws/things/{thingName}/shadow/name/{shadowName}/update'
response = iot.get_thing_shadow(thingName=thingName, shadowName=shadowName)
Topicについては、公式サイト参照
補足
シャドウをあらかじめ生成していなくても、UPDATE時になければ新規生成される。
最後に
シャドウの状態は、AWS IoT Coreで管理されるため、例えばエッジの電源が落ちていても状態は残り続けます。
そのため、エッジ端末が起動していなかったとしても、シャドウの更新をしておけば、エッジ起動時にシャドウの状態に合わせて、更新後の状態にエッジを動作させる。といったことが可能になります。
シャドウが無いとエッジの状態を管理するための状態をストレージに持つ必要があるので、シャドウはとても便利に思いました。
参考URL
・概要
・シャドウ動かしてみた
・シャドウサンプル
・公式サイト