AWS IoT Events は IoT デバイスの「ステートマシン」 ― IoTデバイスの状態管理に使えるサービス

[AWS IoT Hero になった](https://aws.amazon.com/jp/developer/community/heroes/kohei-matsushita/)ので、やっぱ AWS IoT 関連のアウトプットは必要でしょう、ということでお送りしております。

## AWS IoT Events は「ステートマシン」

AWS では[目的に応じた IoT 向けサービス](https://aws.amazon.com/jp/iot/)が数多く存在しています。
その中でも今回取り上げる [AWS IoT Events](https://aws.amazon.com/jp/iot-events/) は~~説明で損をしている[^1][^2]~~素晴らしいサービスですので、皆さんに知ってもらいたいと思い、紹介しています。

以下は AWS IoT Events の実装画面です。見たことありませんか？そうです、「**ステートマシン**」です。AWS IoT Events はIoTデバイスの状態管理(ステート管理)を行うことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/b4f5bb9e-5bc5-756a-9975-97a48eb33412.png)

[^1]: ["IoT センサーやアプリケーションで発生したイベントを容易に検出し対応する"](https://aws.amazon.com/jp/iot-events/) 🤔🤔🤔

[^2]: 前はもっとアレでした "変更についてデバイスフリートをモニタリングし、アラートをトリガーして応答します"

### ステートマシンって？

いわゆる「状態遷移(図)」です。
例として挙げられるのが、 OS 内のプロセス遷移です。[^3] プロセスが New されてから Terminated されるまでの流れが描かれています。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F22754%2F59e43411-b735-d515-3d6b-5ed0ef0ef3a1.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&w=1400&fit=max&s=97d3dc5846d3e80068d2eb6ecda58dac)

たとえば "Ready" からは決して "Waiting" や "Terminated" に遷移しないとなっているわけです。（もちろん、そうやってコードの実装をする必要があります）

[^3]: 情報処理技術者試験とかでお世話になりましたね。

## IoT では「状態」が常に発生している

現在はステートレス、システム内部に「状態」を持たずにシステムを作る考え方が、特にサーバレス界隈では常識(要出典)となっています。理由は「スケールしやすい」からです。
ステートレスとステートフルの解説や、ステートレスだとなぜスケールしやすいのかは [ステートレスとは何か](http://yohei-y.blogspot.com/2007/10/blog-post.html) がわかりやすいのでご覧ください。

話を戻すと、IoT は必ず物理世界に「モノ」があり、モノは常に「状態」を持っています。例えば扉は「開いてる(Open)」と「閉まっている(Close)」のステートがあります。
ここで困るのが、**ステートってどこに保存しておこうか？** という問題です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/cf85b852-c46f-0f2d-1058-cb8bfd2e730d.png)

## AWS IoT Core の「デバイスシャドウ」をにステートを保存するアプローチ

AWS IoT Core にはデバイスシャドウという、デジタルツイン[^2]の実装ができる機能があり、これをステートの保存先として利用するアプローチが考えられます。
[^2]: デジタルツインについては [いまさら聞けない「デジタルツイン」 - MONOist](https://monoist.atmarkit.co.jp/mn/articles/1808/08/news043.html) がわかりやすいです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/61b49e43-f71f-d717-98c3-d577262df7f6.png)

シャドウステータスを更新する方法はクラウド側、デバイス側、それぞれあります。

* クラウド上から
    * AWS Lambda (Python なら [`update_thing_shadow(**kwargs)`](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot-data.html#IoTDataPlane.Client.update_thing_shadow) です)
* デバイス上から
    * AWS Greengrass (の中の AWS Lambda)

結局 AWS Lambda ってことですね。

### 「状態」の実装で面倒な事

「状態」をコードで実現しようとすると、面倒なことが 3つ出てきます。

* コード側
    * どのようなステートが存在するのか？
    * ステートの遷移条件は？
* ストレージ側
    * ステートをどこにどうやって保存するのか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/f3330c86-f29b-b2e1-e207-97e55a7af7f3.png)

このうち、「コード」については、実は [GoF の State パターン](https://qiita.com/i-tanaka730/items/49ee4e3daa3aeaf6e0b5)があるのですが、ダンプカーでコンビニに行く感覚くらいの実装量になります。
かといって、たとえば "open" から "close" へは遷移できるけど "open" から "open" は遷移できない(無視する)という実装を考えると、必殺 `if` 文の登場となりますが、ステートが増えた時や遷移条件が変更された時の保守が「うっ」ってなるのは、ちょっと考えれば想像できる話です。

前回どこまで遷移したか（＝次回はどの「状態」からスタートするか）を保存しておく「ストレージ」もコードが必要となります。
デバイスシャドウであれば `update_thing_shadow()` を使いますし、Amazon DynamoDB や Amazon ElastiCache ... まあ色々と考えられますけど、結局コード内に「読みだして、保存する」実装しなければいけませんし、「キーをどうする」とか「データ構造どうしよう」とか、、、もう面倒！！

要するに、面倒なんです。

## そこで AWS IoT Events

以下のように「状態 (ステート)」と「遷移条件」を指定することができます。
また、「今回はどの状態まで進んだか ＝ 次回はどこからスタートするか」を保存してくれるうえ、次回のスタート場所を自動的に設定してくれます。

どの様に動作するかは、アニメーションをご覧ください。
![a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/fc96c69c-efaf-18ea-c7d3-00603460182c.gif)

### 遷移時に「アクション」を呼び出すことができる

状態と遷移条件の実装だけでなく、遷移時の「アクション」も指定できます。例えば "open から close に遷移したときに Amazon SNS 経由で通知を送る" といった事もできます。

イベントは onEnter , onInput , onExit の3つです。(ドキュメントは[ここ](https://docs.aws.amazon.com/iotevents/latest/apireference/API_State.html))

* onEnter : その状態になった時に発火
* onExit : その状態じゃなくなった時に発火
* onInput : データ入力を受け取ったら発火

基本的には「データを受け取って(onInput)」→「その状態じゃなくなった(onExit)」→「次の状態になった(次の状態の onEnter)」という形で発火していきます。(たぶん)
ますは onEnter を使えばわかりやすく実装できるでしょう。

指定できるアクションは [ドキュメント](https://docs.aws.amazon.com/ja_jp/iotevents/latest/apireference/API_Action.html) を見てください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/2a522906-807b-2dd0-e9e1-7deb5165dce1.png)

### 結局 AWS Lambda じゃねぇか！！

そ、そうですね。
とはいえ、Lambda 関数には状態の移行条件の `if/switch` 文を排除できるので、Lambda 関数自体をステートレスに実装できます。

まさに「Lambda が Lambda であるために！」

### AWS IoT Core と AWS IoT Events はどうやって連携する？

AWS IoT Core のルールに "IoT Events 入力にメッセージを送信する" で OK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/7f239f86-89c2-7b27-debc-dee6f391b9e4.png)

### ほかにはどんな使い方が？

AWS IoT Events は内部でタイマーを持っており、これをイベントとして発火させることもできるので [AWS IoT Events入門 一定期間通信途絶時にイベント発火してみた](https://dev.classmethod.jp/articles/alive_monitoring_using_aws_iot_events/) こんな使い方もできます。

これも状態管理ですね。

### 検知器(= 要するにステートマシン) のバックアップは CLI が使える

GUI 特有の「バックアップとかどうするんじゃい」問題は AWS CLI を使うことで解決はできます。

```console
$ aws iotevents list-detector-models
{
    "detectorModelSummaries": [
        {
            "detectorModelName": "DoorState",
            "creationTime": 1568089449.837
        }
    ]
}
$ aws iotevents describe-detector-model --detector-model-name "DoorState" > model.json
```

あとは `aws iotevents create-detector-model` で `model.json` から復元する形となります。(ごめん、ここは未検証)

## まとめ

AWS IoT Events はステートマシーンです。

ステート管理は AWS IoT Events に任せ、その他のロジックは別で(っていうか、実質 AWS Lambda)と役割を分離しよう。

AWS IoT Events は検知器(要するにステートマシン)の実行回数で課金される、完全従量課金モデルなので、いつデータが発生するかわからないような IoT デバイスと相性が抜群に良いかと思います。

## あとがき

じつはこれ、JAWS-UG 広島 14回目@酒まつりの時に、ピンチヒッターとして登壇したときのネタなんです。。。使いまわしでスマセンスマセン。
今回あらたにアニメーション作ったから許してください。

当時のスライドはこちらです。悪天候で本当に大変でしたよね。。。でも楽しかった！！
[![thumbnail](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/9c3e7424-2991-f806-a57d-227bfda767c7.png)](https://speakerdeck.com/ma2shita/aws-iot-events-is-statemachine-for-iot-devices)
[Speaker Desk / AWS IoT Events はステートマシンですから。](https://speakerdeck.com/ma2shita/aws-iot-events-is-statemachine-for-iot-devices)

これからも AWS IoT Hero の名に恥じぬような情報(使いまわしはいいのか？)を皆さんにお届けできたらいいなと思いますし、また、いろいろ教えていただきたく思いますので、お気軽に絡んでいただければと思います！

IoT 芸人として "Still Day One" をいつも心に、 Max 松下でしたー。

#### バックアップのくだりは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/87c591d0-a058-fa41-5ef4-ed906583e720.png)
さすが。あざます！

EoT