AWS IoT エンタープライズボタンで掃除の記録をGoogle Spreadsheetに残す

AWS IoT エンタープライズボタンがついに日本でも手に入るようになったので、浴室やトイレに設置して掃除の記録をGoogle Spreadsheetに記録するボタンを作った。

# 全体像

![flow.png](https://qiita-image-store.s3.amazonaws.com/0/14097/ddb20ed9-c692-fc46-4498-fca61a5ad56c.png)


掃除が終わったらAWS IoT エンタープライズボタンを押す
↓
AWS Lambdaの関数が呼ばれる
↓
Google Apps Scriptの公開URLが叩かれる
↓
Google Spreadsheetに掃除した場所の記録が残る

**なお、この投稿では実験目的/わかりやすさ重視のため、認証については実装を省略しています。**もし実際に似たようなシステムを運用する場合は、適宜アクセス制限の設定と実装を行ってください。

# AWS IoT エンタープライズボタンの初期設定
AWS IoTボタンのセットアップについては、長くなるので割愛します。下記の記事を参照してください。

[【国内販売開始】AWS IoT Enterprise Button試してみたらホンマに簡単にLambda関数を実行できた](https://dev.classmethod.jp/cloud/aws/aws-iot-enterprise-button/)

# SpreadsheetとApps Scriptの設定
## Spreadsheetの作成
掃除をした日時と場所を記録していくためのSpreadsheetを新しく作成します。

<img width="1199" alt="スクリーンショット 2018-05-27 12.29.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/a51ad777-6528-185b-762f-459ab09ee89b.png">

一行目に「日時」「場所」というヘッダーとなる行を入力したら残りの行は削除しておきます。

## スクリプトの作成
「ツール」「スクリプトエディタ」を開いて、スクリプトを作成します。

```javascript
function writeHouseworkLog(place) {  
  var sheet = SpreadsheetApp.getActiveSheet();
  var maxRows = sheet.getMaxRows();
  sheet.insertRowAfter(maxRows);
  var range = sheet.getRange(maxRows + 1, 1, 1, 2);
  var value = range.setValues([[new Date(), place]]);
}

function doPost(e){
  var params = JSON.parse(e.postData.getDataAsString());
  place = params.place;
  
  var res = ContentService.createTextOutput();
  res = res.setMimeType(ContentService.MimeType.JAVASCRIPT);
  
  if (place != undefined) {
    writeHouseworkLog(place);
    
    res = res.setContent(JSON.stringify({"place": place}));
    return res
  } else {
    res = res.setContent(JSON.stringify({"error": "place is empty."}));
    return res
  }
}

function test() {
  writeHouseworkLog("洗面所の掃除");
}
```
POST形式のHTTPリクエストを受け取ります。パラメータはJSON形式で渡すようにして、`place`という引数の中身と現在時刻をSpreadsheetの最終行の後に追加します。

<img width="726" alt="スクリーンショット 2018-05-27 12.39.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/32a77193-eae2-05c1-07b5-73e57aefc6d6.png">

最終的な動作テストは`CURL`などを使って行う必要がありますが、それ以前の動作確認(今回の場合であれば、Spreadsheetに新しく行を追加する処理など)はテスト用の関数(今回の場合は`test`)を用意しておくとスクリプトエディタ上で即座に実行/確認をできるので便利です。

## ウェブアプリケーションとして公開
「公開」「ウェブアプリケーションとして導入...」から、公開設定を行います。

<img width="484" alt="スクリーンショット 2018-05-27 12.40.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/d1343fc3-f35a-b149-5ff8-7be47e8f48be.png">

**不正利用防止のため、本来は「アプリケーションにアクセスできるユーザー」を制限するべきですが、今回は動作確認をわかりやすくするため、匿名ユーザーでのアクセスを許可しています。**(つまり、URLがわかれば誰でも実行できてしまう)

もし実際に運用する場合は、アクセスできるユーザーを制限した上で認証の設定を行ってください。

公開したら、`CURL`などを使ってウェブアプリケーションのURLを叩いて、ちゃんと記録されるか確かめておきましょう。

<img width="835" alt="スクリーンショット 2018-05-27 12.46.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/9a6037e1-3a62-0375-dcca-eba083c48648.png">

# Lambdaの設定

## Lambda関数の作成

AWS Lambdaコンソールに入って、新しくLambda関数を作成します。

![スクリーンショット 2018-05-27 12.58.24.png](https://qiita-image-store.s3.amazonaws.com/0/14097/2fc4a7df-334e-7da5-80ea-9017856cf21d.png)

ランタイムはなんでも良いですが、今回はPythonで書きます。今回はSESなど他のサービスは利用しないので、ロールに特にポリシーテンプレートを追加する必要はないです。

## Lambda関数の編集

```python
from __future__ import print_function
import json
import logging
import urllib.request

GAS_URL = "{GASウェブアプリケーションのURL}"

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logging.info('Received event: ' + json.dumps(event))

    attributes = event['placementInfo']['attributes']
    place = attributes['place']
    
    headers = {
            'Content-Type': 'application/json'
        }
    request_body = {"place": place}
    
    logger.info("Request body: " + json.dumps(request_body))
    
    req = urllib.request.Request(GAS_URL, data=json.dumps(request_body).encode('utf-8'), method='POST', headers=headers)
    with urllib.request.urlopen(req) as res:
        logger.info(res.read().decode("utf-8"))

    logger.info('Done')
```

上で公開したGoogle Apps ScriptのURLにPOSTリクエストを送るようにします。

ボタンごとに設定した属性は`event['placementInfo']['attributes']`以下に含まれて呼ばれます。今回はボタンごとに`place`という属性に「浴室の掃除」「トイレの掃除」のような値を設定しておくことにします。

そして、その値をHTTPリクエストの引数にしてGoogle Apps ScriptのURLを叩くようにします。

# Lambda関数のテスト

画面右上「テストイベントの設定」から、ボタンが押された際に呼ばれるリクエストを再現したテストイベントを用意します。

<img width="824" alt="スクリーンショット 2018-05-27 13.05.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/4b3b958c-035e-865c-abc8-a22664e606b7.png">

作成したテストイベントを選んで、「テスト」してみます。

<img width="1465" alt="スクリーンショット 2018-05-27 13.07.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/99bb92a4-39a9-e8da-ee0f-72a243671cba.png">

画面下部のコード編集画面でログを見ると、無事にリクエストを送信できたようです。

<img width="1408" alt="スクリーンショット 2018-05-27 13.08.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/66b497ad-dc84-2ca1-051e-a34f8def59e5.png">

Spreadsheetの方を確認すると、ちゃんと行が追加されています。

<img width="836" alt="スクリーンショット 2018-05-27 13.09.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/51ebe34f-b4b8-00ba-2676-3d9dd9eacf17.png">

# IoTボタンの設定
## テンプレートの設定

IoTボタンのプロジェクトのテンプレート編集画面で、ボタンが押された時に上で作ったLambda関数が呼ばれるように設定します。

<img width="874" alt="スクリーンショット 2018-05-27 13.10.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/913f893d-3c31-aae6-21f7-3b0a1bdefa38.png">

## ボタンごとの設定
最後にプレイスメントの設定画面から、ボタンごとの設定を行います。このプレイスメント(ボタン)では`place`に「浴室の掃除」を設定しています。別のプレイスメント(ボタン)では、`place`に「トイレの掃除」を設定しました。

<img width="870" alt="スクリーンショット 2018-05-27 13.12.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/a8c9a8d9-4311-1f41-37ec-7adf6b069b8a.png">

# 動作確認
最後に実際にボタンを押して見ます。

<img width="830" alt="スクリーンショット 2018-05-27 13.14.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/14097/c522aa1b-425a-7935-2303-99e79ccdfada.png">

保存された！

# まとめ
スマホを持ち歩かなくてもネット上の何かに対してアクションを実行できる、のは楽しい。

ボタンを押した際のアクションをLambdaではなく、直接Google Apps Scriptを指定できればもっと嬉しいのだけど、そこは会社が違うから仕方がない…。

なお、この記事の内容は上述の通り、実験目的/わかりやすさ重視のため認証関連の実装を省略しているため、本格的にこのようなシステムをする場合は認証関連の設定をお忘れなく！