SORACOM Enterprise Buttonの簡易位置情報を用い地域外での押しミスを防ぐ(CloudFunctions/Python版)

# TL;DR(まとめ)

* SORACOM Enterprise Buttonの簡易位置情報を用いて、ある地域でのみ動作するボタンを構築した
* 2019/7/2 SORACOM Discoveryにて新発表されたSORACOM Funkを用い、GCPのCloudFunctionsを動作させた
* 「地域外」はCloudFunctions側で判断しているので、ボタン動作回数は通常通り消費される
 
# 機材
SORACOM Enterprise Button (初期ボタン登録は済んでいるものとします)

# 主に使ったもの
SORACOM Funk
CloudFunctions(GCP)
pyjwt(JWT(Json Web Token)Python版ライブラリ)

# 構築内容
SORACOM Enterprise Buttonを押した際に、SORACOM Funk経由でCloudFunctionsを起動させます。
CloudFunctionsの中で位置情報を取得します。（このときにjwt使用）
地域外であれば動作、そうでなければ動作しないようにします。
判断するロジックの部分のみを記事にします。

# 構築手順
## CloudFunctions設定

後でSORACOMのコンソールから起動関数を指定するため、先にCloudFunctionsを記載します。
CloudFunctionsの画面から関数の作成を選択し、以下の通りに入力しましょう。ここで★の部分のURLはSORACOM Funk設定にて用いるため取得しておきます。
関数の名前はなんでも良いですが、`enterprise_button`とでもしておきましょう。
デフォルトのNode.jsでも良いですが、Pythonでも記載できるのでPythonを選択しました。

<img width="498" alt="01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15264/0e11dc6d-ce57-3265-f206-2278dc7684a4.png">


続けてrequirements.txtの編集です。
pyjwtを用いるため、`requirements.txt`に以下記載をしましょう。(ここに記載した内容が pip installされるイメージです)

```python:CloudFunctions(requirements.txt)
pyjwt
```

以下が実行関数です。デフォルトの起動関数名`hello_world`から`exec_button`に変えているため、
実行関数名と環境変数の設定がこの後必要です。

[SORACOMの説明](https://dev.soracom.io/jp/funk/how-it-works/)では、httpヘッダに`x-soracom-token`があり、それをJWTでデコードできるとのこと。
というわけでこうなります。

```python:CloudFunctions(main.py)
import jwt
import os

def exec_button(request):
    if 'x-soracom-token' not in request.headers:
        return 'Not from soracom button.'
    
    # Decode jwt token. (algorithm : RS256)
    data = jwt.decode(request.headers.get("x-soracom-token"), verify=False)
    if not is_inside_area(data['ctx']['location']):
        return 'Out of area.'

    # Write your code here.
    return 'Inside area.'

def is_inside_area(position):
    northwest = {'lat': float(os.environ.get('northwest_lat', 1.0001)), 'lon': float(os.environ.get('northwest_lon', 178.0001))}
    southeast = {'lat': float(os.environ.get('southeast_lat', 0.0001)), 'lon': float(os.environ.get('southeast_lon', 179.0001))}
    if 'lat' not in position or not(isinstance(position['lat'], float)):
        return False
    if not(southeast['lat'] <= position['lat'] and position['lat'] <= northwest['lat']):
        return False
    if not(northwest['lon'] <= position['lon'] and position['lon'] <= southeast['lon']):
        return False
    return True
```

補足：is_inside_area関数では、取得した簡易位置情報(緯度経度)がその領域に含まれているかどうかを判断しています。

```
この枠内にあるか？を判断している
northwest - - - - -
-                 -
-                 -
-                 -
- - - - - southeast
```

実行関数名を`exec_button`にします。
詳細設定が必要なので、その下の`Environment variables, networking, timeouts and more`をクリックします。

<img width="708" alt="02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15264/9c8a1e7b-e82c-3c6c-37f6-969698de5ad9.png">


環境変数のみ以下の通りに指定します。（その他はデフォルトでOK）
デプロイしておきましょう。（関数を初回に作成する場合は「作成」です）

<img width="700" alt="03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15264/3e25728d-1d0c-c0ee-e299-3cb1fb965296.png">


これでCloudFunctionsの設定は完了です。

## SORACOM Enterprise Button設定

SORACOMコンソールから、Funkと位置情報の有効化の設定を行います。
該当するEnterprise Buttonが所属するSIMグループの設定ページを開いてください。(私はenterprisebuttonというグループ名)
設定する箇所は赤枠で囲った２箇所です。

<img width="994" alt="04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15264/5fb08c6b-b53c-7fd2-4e1e-dc97e2bf0040.png">


では順に設定していきましょう。
SORACOM Air for Cellular設定 : 位置情報の取得を有効化します。また、バイナリパーサとして`@button`を設定します。

<img width="1101" alt="05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15264/bb42c08a-dc95-f224-767d-e290fb76c7a6.png">


SORACOM Funk設定 : FunkでCloudFunctionsを実行するようにします。★で取得した関数のエンドポイントを記載します。

<img width="809" alt="06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15264/a0cdb3db-e792-0430-8a2f-c8c2b4578192.png">


これで位置情報が取得できた場合に、地域外を判定することができます。
これにより地域外で誤って押してしまった際に動作をさせない、なんてことができますね。
参考になれば幸いです。