AWS IoTのThing Shadowsを図と実行例で理解する #reinvent

2015.10.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。
AWSのカンファレンスイベント、re:Invent 2015で発表&ベータがローンチされたAWS IoT、皆さん触ってますか?

AWS IoTはメッセージブローカーを基盤とするIoTデバイスとAWSサービスを連携させるサービスです。いくつかある重要な機能の一つに、今回のThing Shadowsがあります。今回はThing Shadowsの概念と仕組みを図と実行例を用いてご紹介します。

Thing Shadowsとは

AWS IoTでは、IoTデバイスを管理するための機能としてThing Shadowsを利用します。Internet of Thingsの名前の通り、デバイスのことをThingと呼ぶわけですが、デバイスは設置や稼働状況によりオフラインになることがあるため、それを管理するアプリケーションによるオーダーを遅延実行する仕組みとしてThing Shadows(デバイスの影像)を利用することができます。

実際の仕組みはとてもシンプルで、デバイスと管理アプリケーションのそれぞれデバイスの状態情報をAWS IoTに送信(Publish)し、AWS IoTがその内容を管理することで実現しています。

thingshadows02

状態情報は任意の項目が設定でき、デバイスが報告する項目(reported)とアプリケーション操作による望ましい項目(desired)のそれぞれが保存されます。ここでは例としてデバイスの稼働状態を示す'power'という項目を扱ってみましょう。デバイスの稼働状態がオフ、アプリケーションによってパワーオンが行われるとすると、reportedにはpower:offdesiredにはpower:onが入ることになります。

thingshadows03

状態情報はMQTT/REST APIによってデバイスと管理アプリケーションの両方からSubscribeもしくはポーリングし、変更があったらそれに合わせて処理を実行することになります。例えば、先ほどの管理アプリケーションによるパワーオンが行われる場合、デバイスはreported.powerdesired.powerが異なることを検知し、パワーオン処理を実行、完了したらreported.power:onに変更します。

先ほどのThings/Thing Shadowsの用語で言うと、reportedがThings、desiredがThing Shadowsの状態をそれぞれ指すと説明できるわけですね。

実装の確認

概念を紹介しましたので、実際に動かしてその様子を見てみましょう。MQTT/REST APIの両方で操作が可能ですが、今回はAWS CLIで簡単に試すことができるREST APIでやってみます。

まずは、AWS IoTの管理画面から、状態情報を持つThingを作成します。[Create a thing]をクリックし、[Name]に適当なデバイス名(今回はsensor1)を入力、[Create]をクリックして作成します。

thingshadows01

REST APIの場合は、デバイス名を押さえておけばOKです。MQTTの場合は、Publishするトピック名($aws/things/<デバイス名>/shadow)を押さえておきましょう。

では、まずは初期のデバイスの状態としてパワーオフを登録します。AWS CLIではaws iot-data update-thing-shadowコマンドで状態情報を更新します。ルート階層のstateは固定で、その下のreported以下に自由に要素を定義することができます。

$ aws iot-data update-thing-shadow --thing-name sensor1 \
  --payload '{"state": {"reported" : {"power" : "off"}}}' \
  outfile.json

引数に指定したファイルには、AWS IoTからのレスポンスが格納されます。レスポンスにはstateに加えてリクエストのバージョンとリクエストのタイムスタンプなどが付与されます。

outfile.json(jqで整形済み)

{
  "state": {
    "reported": {
      "power": "off"
    }
  },
  "metadata": {
    "reported": {
      "power": {
        "timestamp": 1444490470
      }
    }
  },
  "version": 1,
  "timestamp": 1444490470
}

続いて、管理アプリケーションからパワーオンで更新してみます。

$ aws iot-data update-thing-shadow --thing-name sensor1 \
  --payload '{"state": {"desired" : {"power" : "on"}}}' \
  outfile2.json

outfile2.json(jqで整形済み)

{
  "state": {
    "desired": {
      "power": "on"
    }
  },
  "metadata": {
    "desired": {
      "power": {
        "timestamp": 1444490744
      }
    }
  },
  "version": 2,
  "timestamp": 1444490744
}

デバイスと管理アプリケーションで更新するためのAPI/Topicは共通ですが、それぞれreported/desiredで区別することがわかりますね。では、AWS IoTでの状態情報がどうなっているかをaws iot-data get-thing-shadowで確認してみます。

$ aws iot-data get-thing-shadow --thing-name sensor1 outfile3.json

outfile3.json(jqで整形済み)

{
  "state": {
    "desired": {
      "power": "on"
    },
    "reported": {
      "power": "off"
    },
    "delta": {
      "power": "on"
    }
  },
  "metadata": {
    "desired": {
      "power": {
        "timestamp": 1444490744
      }
    },
    "reported": {
      "power": {
        "timestamp": 1444490470
      }
    }
  },
  "version": 2,
  "timestamp": 1444490781
}

AWS IoTによって、デバイスと管理アプリケーションの両方の情報が扱われていることがわかりますね。state.deltaにはデバイスの状態と望ましい状態の差分が抽出されるので、デバイスからのSubscribe時に対応するべき処理を容易に判断することができます。ということで、デバイス側でパワーオン処理を行ったとしてstate.reported.poweronにアップデートします。

$ aws iot-data update-thing-shadow --thing-name sensor1 \
  --payload '{"state": {"reported" : {"power" : "on"}}}' \
  outfile3.json

では、再度状態情報を見てみます。

$ aws iot-data get-thing-shadow --thing-name sensor1 outfile4.json

outfile4.json(jqで整形済み)

{
  "state": {
    "desired": {
      "power": "on"
    },
    "reported": {
      "power": "on"
    }
  },
  "metadata": {
    "desired": {
      "power": {
        "timestamp": 1444490744
      }
    },
    "reported": {
      "power": {
        "timestamp": 1444490885
      }
    }
  },
  "version": 3,
  "timestamp": 1444490932
}

state.deltaが無くなり、差分が無くなっていることがわかりますね。

おまけ: バージョン番号による楽観的ロック

更新リクエストに紐付くバージョン番号はAWS IoTによってインクリメントされるため、古いバージョンへの先祖帰りを防いだり、他のPublisherによる更新を検知する手段として利用できます。

$ aws iot-data update-thing-shadow --thing-name sensor1 --payload '{"state": {"reported" : {"power" : "on"}},"version" : 2}' outfile.json

A client error (ConflictException) occurred when calling the UpdateThingShadow operation: Version conflict

更新リクエストに"version" : 2の指定を入れたところ、既にバージョン3になっているため更新が拒否されました。

まとめ

Thing Shadowsの概念とAWS CLIでの実行例をご紹介しました。実際にはREST APIの代わりにMQTT、AWS CLIの代わりにIoT Device SDKが利用されることが多いと思いますが、概念としては今回の例で十分ご理解いただけるのではないでしょうか。