SORACOMのあのボタンでIoTシステムのテストができるようにしてしまおう！

本記事は [SORACOM LTE-M Button powered by AWS Advent Calendar 2018](https://qiita.com/advent-calendar/2018/soracom)の23日目の記事です。

この記事を読んでいるということは、かなりの確率でIoTをやりたいな、やらなくちゃ、絶対やってやる！系の人ではないかと思いますが、IoTシステム(特にバックエンド側)のテストって、テスト用のデータを送るの大変じゃないですか？

動作確認をするたびにデバイスをあれこれ操作しないといけなくって、やってられんわ！ってなったり、
負荷試験まで考え出すと、そんなにデバイス用意できるか！できたとしても操作できるか！とかなったり。
デバイスシミュレーターを自作しようにも、そんな時間なかったり。

そんなお悩みを、あのボタンと[mockmock](https://mock-mock.com/ja/)というサービスを連携させて解決してみます。

## mockmockとは
その名のとおり、IoTのデバイスmockを作るサービスです。
作ったmockでテスト用のデータを生成し、自分が開発しているIoTのバックエンドにデータを送信します。

- 送信するデータ(json)のフォーマットは思いのまま
- 温度などの時系列に沿って変化する値、指定した経路を移動したときの緯度経度など、様々なデータが生成できる
- mockごとにシリアル番号などの固有の値も持たせることができる
- デバイスの状態遷移もシミュレートできる
- 大量のmockを一瞬で起動できる(5,000台まで。それ以上は要問合せ)
- データの送信先として、独自サーバー(HTTP(S), MQTT(S))のほかに、AWS IoT Core, Amazon Kinesis Data Streams, GCP Cloud IoT Core, Azure IoT Hubといった、主要クラウドのIoT系サービスにも対応している

といった特長があります。
そして先日、APIがリリースされ、プログラマブルにmockの操作ができるようになりました！
そこで、あのボタンでmockの起動、停止、再起動ができるようにして、\ﾎﾟﾁｨ/とするだけでIoTのバックエンドにテストデータが送れるようにしてやろうと思います。

## mockmockの設定
今回はウエアラブルデバイス的な感じで作ってみましょう。
送るデータはこんな感じです。

```
{
  "id": [デバイス固有ID],
  "pos": {
    "lon": [緯度],
    "lat": [経度]
  },
  "hrt": [心拍数],
  "time": [タイムスタンプ]
}
```

ざっと設定を見ていきます。

### プロジェクト設定
今回はチュートリアル用のエンドポイントに送ります。
<img width="674" alt="スクリーンショット 2018-12-22 23.05.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/4c593685-6083-eb9c-88aa-816fd6849490.png">

### バリュージェネレーター
心拍数を生成するためのグラフバリュージェネレーター。
このグラフに沿って値を変化させて送ります。周期は一時間、±5の誤差を設定しています。
<img width="901" alt="スクリーンショット 2018-12-22 23.08.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/ed7dcc68-05ed-d832-f58c-a02aa75ad637.png">

緯度経度のジェネレーター。
適当に福岡の街中をウロウロさせてみます。
<img width="844" alt="スクリーンショット 2018-12-22 23.12.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/9a8917a0-6129-e029-423f-48a0a3d14d08.png">

### データテンプレート
データのフォーマットや生成方法を定義するデータテンプレート。
この定義に沿ってjsonが生成されます。
<img width="657" alt="スクリーンショット 2018-12-22 23.20.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/5ca80fdb-499d-d51f-82ce-55d390f32da7.png">

### mockグループ
mockの最大稼働時間やmockに持たせる固有値のフィールド名を設定します。
<img width="657" alt="スクリーンショット 2018-12-22 23.20.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/a6f3fce8-a964-eb98-f493-c74f1251ab3a.png">

### mockステータス
今回は状態遷移をさせないので1つだけです。
HTTPのパスやメソッド、データの送信間隔、使用するデータテンプレートを指定します。
<img width="662" alt="スクリーンショット 2018-12-22 23.22.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/91643b5c-48e3-6771-f551-ea0f5795d489.png">

### mock作成
mockを一つ用意したところ。
<img width="823" alt="スクリーンショット 2018-12-22 23.24.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/881c9caa-74b3-8901-5f47-a44fb3d760c2.png">

念のためテスト送信してちゃんとデータが送れるか確認してみます。
<img width="476" alt="スクリーンショット 2018-12-22 23.24.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/8c582c7e-4168-f9f1-9b94-1dda24e557a1.png">

### API鍵
API鍵も作成しておきましょう。
<img width="654" alt="スクリーンショット 2018-12-22 23.25.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/666291c1-ec7c-c2d4-a7b7-87b8c22531c0.png">

これでmockmock側の準備は完了です。

## SORACOM LTE-M Button
基本的な設定は[Getting Started](https://dev.soracom.io/jp/aws_button/getting-started/)あたりをご覧ください。

Placement Attributesは以下のように設定します。
<img width="875" alt="スクリーンショット 2018-12-22 23.56.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/39565/730c0389-fcfd-0196-de14-977f66a7965a.png">

mock-groupで指定したmockグループに所属するmock全てに対して操作を行います。
1,000台用意していれば、1,000台が一気に起動したりするような感じです。

ボタンから呼ぶLambda関数は以下のとおり(python3.6)。

```python
import json
import logging
import os
import urllib.request

API_BASE = 'https://console.mock-mock.com/api/v2'

def lambda_handler(event, context):

    mock_group   = event['placementInfo']['attributes']['mock-group']
    token_id     = event['placementInfo']['attributes']['token_id']
    secret_token = event['placementInfo']['attributes']['secret_token']

    # リクエストヘッダ
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Token token={token_id};secret={secret_token}'
    }

    # mockのID一覧を取得
    req = urllib.request.Request(
        f'{API_BASE}/mock_groups/{mock_group}',
        headers=headers
    )
    res = urllib.request.urlopen(req)
    if res.getcode() != 200:
        return {'statusCode': res.getcode()}

    res_body = json.loads(res.read())
    targets = [{'code': mock_info['code']} for mock_info in res_body['result']['mocks']]

    # 操作内容
    operation = None
    if event['deviceEvent']['buttonClicked']['clickType'] == "SINGLE":
        operation = 'launch'
    if event['deviceEvent']['buttonClicked']['clickType'] == "DOUBLE":
        operation = 'terminate'
    if event['deviceEvent']['buttonClicked']['clickType'] == "LONG":
        operation = 'reboot'

    # mock操作
    data = {
      'operation': operation,
      'options': {
        'ignore_inoperable': True
      },
      'targets': targets
    }
    req = urllib.request.Request(
        f'{API_BASE}/mock_groups/{mock_group}/operate',
        data=json.dumps(data).encode("utf-8"),
        headers=headers,
        method='POST'
    )
    res = urllib.request.urlopen(req)
    if res.getcode() != 200:
        return {'statusCode': res.getcode()}

    return {'statusCode': 200}
```

## 結果
- シングルクリック: mock起動
- ダブルクリック : mock停止
- 長押し : mock再起動

という動作が実現しました。

ちなみに送られてきたデータはこんな感じです。

```
$ curl https://tutorial-api.mock-mock.com/data/xxxxxxxxxxxx
2018-12-22 16:50:28.162544 {"id":"mk00001","pos":{"lon":33.58325137601978,"lat":33.58325137601978},"hrt":134,"time":1545497427}
2018-12-22 16:50:37.075294 {"id":"mk00001","pos":{"lon":33.58347080996428,"lat":33.58347080996428},"hrt":132,"time":1545497437}
2018-12-22 16:50:47.080990 {"id":"mk00001","pos":{"lon":33.58371461329075,"lat":33.58371461329075},"hrt":126,"time":1545497447}
2018-12-22 16:50:57.079951 {"id":"mk00001","pos":{"lon":33.58390608076425,"lat":33.58390608076425},"hrt":129,"time":1545497457}
2018-12-22 16:51:07.077066 {"id":"mk00001","pos":{"lon":33.58366228749682,"lat":33.58366228749682},"hrt":130,"time":1545497467}
2018-12-22 16:51:17.082268 {"id":"mk00001","pos":{"lon":33.583418481421425,"lat":33.583418481421425},"hrt":131,"time":1545497477}
2018-12-22 16:51:27.094228 {"id":"mk00001","pos":{"lon":33.58314226914939,"lat":33.58314226914939},"hrt":126,"time":1545497487}
2018-12-22 16:51:37.089284 {"id":"mk00001","pos":{"lon":33.58261584160005,"lat":33.58261584160005},"hrt":123,"time":1545497497}
2018-12-22 16:51:47.081681 {"id":"mk00001","pos":{"lon":33.58263765026497,"lat":33.58263765026497},"hrt":132,"time":1545497507}
2018-12-22 16:51:57.079058 {"id":"mk00001","pos":{"lon":33.583164077388226,"lat":33.583164077388226},"hrt":127,"time":1545497517}
```

## まとめ
なかなかテストが大変なIoTのバックエンドですが、ボタン\ﾎﾟﾁｨ/で簡単にテストができるようになりました。開発が楽になりますね！

mockmockには無料枠もありますので、ぜひお試しください！
https://mock-mock.com/ja/