【IoT DIY レシピ】IoTで朝確実に起きたい

# はじめに

本記事はソラコム株式会社による[「SORACOM IoT レシピチャレンジ！」](https://blog.soracom.com/ja-jp/2020/11/17/soracom-iot-recipe-challege-2020/)企画の投稿記事です。

できるだけ本家のIoT DIY レシピの形式に近い感じで記載します。

# IoTで朝確実に起きたい

公開日: 2020年11月

レシピ難易度：★★★☆☆

朝確実に起きたい、というのは、僕のみならず、比較的多くの人にとって課題ではないかと思います。

朝起きるのが遅れると遅刻して心配や迷惑をかけることもありますし、そこまでいかなくても出勤時間に余裕がなくなると焦ってしまいます。朝焦って出社して交通事故を起こす、というようなことが起こらないようにしないといけません。

課題としては、寝起き時の僕はめっちゃ頭悪いことです。頭悪いというか1分でも多く寝ることしか考えてません。出発までのマージンが5分だったら、あと5分寝れるな、と考えて二度寝します。起きた後に、寝れるわけないだろとセルフツッコミを入れるのですが、起きてる時の論理は寝起きの自分には通用しません。別人と考えた方がいいでしょう。目覚まし３つかければさすがに起きるだろ、と思っても、目覚まし3つ止めて寝ます。そういう奴を相手にしないといけない。

現状の解決策は、ちゃんと起きるまではモーニングコールをかけ続けてもらうことです。朝決まった時間までに起きていなければモーニングコールをかけ、起きていなければ何度でもかけます。これは何度止めても何度でもかかってきて想像以上に鬱陶しいので、寝起きの状態には持っていけます。問題は何を持って起きたことを判定するかですが、これは洗面台に置いたSORACOM LTE-Mボタンを押したことで判定するようにしています。その場では止められないというのがポイントで、布団を出て洗面台にたどり着いて洗面すればまあまあ起きます。いったん起きてる状態に入れば、寝起き状態に戻ることはそんなにありません。

このIoTシステムにおける僕の状態遷移は以下になります。ボタンがなくても時間経過で起きますが、時間ギリギリになりがちです。

![wakeup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/035d70c2-7ac7-60da-d742-3d8a38717386.png)

このレシピでは、以上の解決策を実現する「朝確実に起きるためのIoTシステム」を作ります。利用するデバイスは、電源不要でどこでも設置でき、クラウドに簡単に状態を伝えられる「SORACOM LTE-M Button for Enterprise」です。クラウド側の要素として、システムから電話をかけることができる「Amazon Connect」、クラウドにプログラムを配置するだけで実行できる「AWS Lambda」、キューに処理すべきメッセージを登録できる「Amazon SQS」、定時にイベントを発生させる「Amazon EventBridge」、ボタンをクリックした履歴を簡単に保存できる「SORACOM Harvest Data」を利用します。

IoTシステムの技術要素として、システムからの電話連絡、外部アプリケーションからのSORACOMデータ参照、定時処理の実行、タスクの終了が確認されるまで実行されるリトライを体験できます。

## システムの構成
今回作成するシステムは以下のような構成となっています。
![arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f8beca9c-2c71-5562-e378-9cb5fcef5f6d.png)

## 本レシピを行うのに必要な時間、概算費用
本レシピは以下の通りです。

必要な時間: 約90分
概算費用: 約6000円 + 約500円/月

# 準備

本レシピを行うためには以下のものをご用意ください。

**ハードウェア**

|品名|数量|価格|購入先|備考|
| :-- | :--: | :--: | :--: | :-- |
|SORACOM LTE-M Button for Enterprise|1|5,980円|[ソラコム](https://soracom.jp/products/gadgets/enterprise_button/)|[SORACOM LTE-M Button Plus](https://soracom.jp/products/gadgets/button_plus/) でも代用可能です。|
|スマートフォン/電話|1|-|-|・着信可能な電話番号を持っていること|
|パソコン|1|-|-|・インターネット接続が可能でサイトへの接続が自由であること。<br>・Google Chrome 等の最新ブラウザーが利用可能な事。|

※ 金額はレシピ作成時となります。税抜き・送料別です。

**その他必要なもの**

|品名|費用|作成方法など|
| :-- | :--: | :-- |
|SORACOMアカウント|無料※|[SORACOMアカウントの作成(JP)](https://soracom.github.io/iot-recipes/create-account-soracom-jp/#0)|
|AWSアカウント|無料※|[AWSアカウントの作成(JP)](https://soracom.github.io/iot-recipes/create-account-aws/#0)|

※ アカウント作成・維持の費用の料金です。

# SORACOM LTE-M Buttonの設定

### 1. コンソールにログイン
[SORACOM コンソール](https://console.soracom.io/)にログインします。

### 2. SIMの登録
[発注済みのSIMを登録する(JP)](https://soracom.github.io/iot-recipes/register-ordering-sim-jp/#0)を参考に、SIMを登録します

## 3. グループの作成

[Menu] > [SIM管理] とクリックしてSIM管理画面を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/97115bae-ce01-e7cc-f006-cb81ed405427.png)

購入したLTE-M ButtonのSIMにチェックをつけ、[操作] > [所属グループ変更] とクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/52212993-9235-4e40-e1ab-9d3506fd303e.png)

「新しい所属グループ」のプルダウンボックスをクリックした後、［新しいグループを作成...］をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/151abfd9-7203-31ba-e2ff-2dc0c22b8831.png)

「グループ作成」のグループ名を入力して［グループ作成］をクリックします。

|項目|例|備考|
| :-- | :--: | :-- |
|グループ名|goodmorning-button|自由に入力可能です。日本語も設定可能です。|

![groupcreate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/529c0fc4-842b-d0fc-0453-bf07793d954d.png)

新しい所属グループが先ほど作成したグループになっていることを確認したら［グループ変更］をクリックします。
![changegroup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/0b9a7211-c9bd-4313-671a-0da0a4cdffb2.png)

自動的に SIM 管理画面に戻ります。
SIM の「グループ」に先ほど作ったグループが設定されていることを確認してください。
![simlist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/1911e1c0-9d40-b191-5752-5269706770c7.png)

### 4. グループの設定
グループに「**バイナリパーサー機能**」「**SORACOM Harvest Data**」の2つの設定を行います。

SIM 管理画面から、SORACOM LTE-M Button に割り当てたグループ名をクリックします。
![simlist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/668cc048-142f-f38b-65ab-3ee3b3984592.png)

［SORACOM Air for Cellular 設定］をクリックして設定ができるように開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/0e6761df-f398-f50e-e6ae-84a5e79f89b2.png)

「SORACOM Air for Cellular 設定」で以下のように設定します。

|項目|設定値|備考|
| :-- | :-- | :-- |
|バイナリパーサー機能|ON|スイッチはクリックすることで OFF から ON に切り替えることができます。|
|バイナリ―パーサー設定 / フォーマット|@button|半角英数で入力してください。|

![airsetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f7cd4c47-c954-3b9d-b896-a1c961fcd886.png)
最後に［保存］をクリックしてください。

同じページの中にある［SORACOM Harvest Data 設定］をクリックして設定ができるように開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/69eaa7de-cfba-d3dc-02ab-0ceefb40c6ae.png)

「SORACOM Harvest Data 設定」で以下のように設定します。

|項目|設定値|備考|
| :-- | :-- | :-- |
|(スイッチ)|ON|スイッチはクリックすることで OFF から ON に切り替えることができます。|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/77665063-4a59-98b4-6c60-3a13802ac063.png)

最後に［保存］をクリックしてください。
その後表示される「SORACOM Harvest Data が有効になっています」のダイアログでは［OK］をクリックしてください。

# SAM ユーザーの作成

外部のプログラムからSORACOM Harvestのデータを取得するためには、SAMユーザーを作成する必要があります。

### 1. ユーザーの作成

ユーザーコンソールの右上にあるユーザー名をクリックし ［セキュリティ］をクリックします。
![security.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/c163afb4-2e9e-4dc9-bfd7-101b92fd244e.png)

[ユーザー] > [ユーザー作成]をクリックします。
![createuser.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/a995d750-cec2-226e-7d95-d0b3b1b67f06.png)

下記の情報をユーザー情報として記入し、作成します。

|項目|例|備考|
| :-- | :-- | :-- |
|名前|goodmorning-user|利用できる文字: A-Z, a-z, 0-9, -, _<br>文字数: 1 文字以上 50 文字以内|
|概要|任意に入力|自由に入力可能、省略可能です|

![user.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/9f41b7ed-76a7-fd46-8f11-eb3f8d9cde72.png)

作成が終わると自動的にユーザー一覧画面に戻ります。

### 2. 権限の設定

作成したユーザーをクリックします。
![userlist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/a6acd054-65dd-cff1-2917-47679299c7dd.png)

権限設定にて、テンプレート選択から「Harvestのデータの参照のみ許可」を選択し、権限をコピーしたら、左の枠にペーストして、保存します。

![auth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/d4db8a95-3fc5-e497-6ca2-d94fa35f399f.png)

### 3. 認証キーの生成

認証設定のタブを選択し、「認証キーを生成」をクリックします。
![authkey.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/04f476ad-1c1c-37b6-91ba-e2e9091ccd45.png)

生成ダイアログにて表示される認証キーをコピーし、保存しておきます。
![createkey.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/c7aeee37-c756-94cb-77f5-dfbd27eedf67.png)

これでSORACOM側の設定は終わりです。

# Amazon Connectの設定

電話が発信できるようAmazon Connectの設定をします。

### 1. Amazon Connect インスタンスの作成

AWSのコンソールにログインします。

サービス一覧からAmazon Connectを選択します。
![serviceselect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/b16fd663-80b6-b779-1ecb-5f72bee78630.png)

Amazon Connectを未使用の状態であれば以下の画面になります。「今すぐ始める」をクリックします。
![startconnect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/5fc45653-707b-84db-e874-43f6081a9ba1.png)

ID情報を以下のように入力して次のステップへ

|項目|例|備考|
| :-- | :-- | :-- |
|アクセスURL|goodmorning|半角英数字とURLで使用可能な記号で入力します|

![id.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/86ff0be9-b216-90ab-3f55-178b0b3cd992.png)

管理者情報を以下のように入力して次のステップへ

|項目|例|備考|
| :-- | :-- | :-- |
|名|太郎|自由に入力可能|
|姓|山田|自由に入力可能|
|ユーザー名|Administrator|使用可能文字: 半角英数-_.<br>文字数制限: 1文字以上20文字以下|
|パスワード|ランダムなパスワード|使用可能文字: 半角英大文字、小文字、数字を1文字以上<br>文字数制限: 8文字以上64文字以下|
|パスワード(確認用)|ランダムなパスワード|パスワードと同じ文字とする|
|Eメールアドレス|Administrator@example.com|メールアドレス形式|

![admin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/eed627e3-60eb-92ec-a648-f66a4065d21c.png)

テレフォニーオプションを以下のように設定して次のステップへ

|項目|設定値|備考|
| :-- | :-- | :-- |
|着信|OFF|着信を使う用途があればONにしても良い|
|発信通話|ON||

![telephony.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/72d21721-0331-b523-c952-4cbb6cf4f17a.png)

データストレージ設定は特に設定の必要はありません。内容を確認して次のステップへ

![datastorage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/32512b21-1651-f735-8826-4b904e6d096b.png)

内容を確認して、「インスタンスの作成」をクリックします。
![review.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/84200e4f-b3f5-0d0a-1ce7-622f47d920a9.png)

しばらく待ちます。
![setup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/de746e39-987a-61d4-b51d-120be5b39151.png)

成功画面が表示されたら、「今すぐ始める」をクリックします。
![success.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/a2054b9a-2a21-a977-1150-cd25174bf4bc.png)

### 2. Amazon Connectの電話番号を取得

英語で表示されていたら日本語に変更します。

![language.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f3b75780-ab60-81a1-faf9-1646bca6ffba.png)

今すぐ始めるをクリックします。
![startnow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f886a74f-c043-4013-b034-511512936fc6.png)

電話番号を取得します。2020/11/21現在において、日本の番号は申請しないと取得できないようでした。代わりにUSの番号を取得します。(こちらの方が料金も安い。)以下の項目を入力して次へをクリックします。

|項目|設定値|備考|
| :-- | :-- | :-- |
|国/地域|US +1|取得できる国であればどちらでも良い|
|タイプ|DID(直通ダイヤル)||
|電話番号|表示された番号から選択||

![phonenumber.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/c284b7d3-2406-f9c4-e669-4b93d30dd8b9.png)

### 3. 問い合わせフローの作成

Amazon Connectでは、「問い合わせフロー」という処理に従って、発信、受信を制御します。今回は発信だけなので、発信用の問い合わせフローを作成します。完成すると以下のようになります。

![contactflowall.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f4592a68-009b-b89d-436a-72a115b51051.png)

ここから問い合わせフローの作り方を説明します。
画面左側の上から3番目のアイコンにマウスオーバーし、表示された問い合わせフローをクリックします。
![contactflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/b6be9720-9d9f-5c1d-fb1e-7ba5360d0094.png)

「問い合わせフローの作成」をクリックします。
![createflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/856600ef-698f-beb1-ee4d-6fc7bb76c546.png)

名前の入力の右側の鉛筆アイコンをクリックし、フロー名を入力します。

|項目|例|備考|
| :-- | :-- | :-- |
|問い合わせフロー名|goodmorning|1文字以上で自由に設定可能|

![editname.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/e81dea6a-120e-9b68-ad99-4fa7ba21ab72.png)

設定タブを開く
![settingtab.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/93b3922d-4fe6-9ee9-c558-6e354c0272a8.png)

メニュー内の「音声の設定」をフロー内にドラッグ&ドロップし、エントリポイントの「開始」の右側にある○印からドラッグして線をつなぎます。フロー内の「音声の設定」をクリックします。

![voicesetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/928fe671-f2bf-5e65-ba09-dcbcd05c843e.png)

音声の設定を以下のように設定し、保存します

|項目|設定値|備考|
| :-- | :-- | :-- |
|言語|日本語||
|音声|Mizuki|Mizuki(女性音声)とTakumi(男性音声)のいずれかを選択します|

![voicesettingedit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/2409cf2e-59e6-2d00-cae2-c6c3f3e3ae80.png)

操作タブを開き、メニュー内の「プロンプトの再生」をフロー内にドラッグ&ドロップし、音声の設定の「成功」の右側にある○印からドラッグして線をつなぎます。フロー内の「プロンプトの設定」をクリックします。

![audio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/17692568-73bd-8a12-f9fc-5c5dd189de32.png)

電話の内容をプログラムから指定できるよう、以下のように設定し、保存します。

|項目|設定値|備考|
| :-- | :-- | :-- |
|プロンプト|テキストの読み上げまたはチャットテキスト||
|入力|動的に入力する||
|タイプ|ユーザー定義||
|属性|message|プログラムから指定する属性名。任意に入力可能|
|解釈する|テキスト|抑揚をつけたい場合などはSSMLを選択|

![audioedit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/b91aff07-5b7c-a644-f79d-2bd249818687.png)

終了/転送タブを開き、メニュー内の「切断」をフロー内にドラッグ&ドロップし、プロンプトの再生の「OK」の右側にある○印からドラッグして線をつなぎます。
![disconnect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/cbc427e6-9fec-91bc-a915-be8e3cb9b98d.png)

画面右上の「公開」ボタンをクリックします。確認ダイアログが表示されるので、確認して「公開」をクリックします。
![publish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/1c47066d-e1ac-bcb7-7121-66bccbd74905.png)

公開されたら画面左上の「追加のフロー情報の表示」をクリックし、ARNを確認します。instanceの右に表示されるインスタンスID、contact-flowの右側に表示される問い合わせフローIDが電話発信の際に必要になります。
![information.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/9d8bd47b-45f3-22a6-6bbe-30af259e09c8.png)

以上でAmazon Connectの設定は終了です。

# Amazon SQSの設定

メッセージを登録するキューを管理するサービスであるSQSを利用します。
これを使うことで、「正常に処理が終わるまでリトライする」といった処理が簡単に実装できます。

### 1. キューを作成

サービス一覧からAmazon SQSを選択します。
![sqs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/991a52cf-691e-c758-68a7-cf6bdf8e41fb.png)

「キューを作成」をクリックします。
![createqueue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/54a3cdcc-ae1e-b8a1-b478-1dc6c442ec9b.png)

以下の項目を入力して、キューを作成します。他の項目はデフォルト値とします。

|項目|設定値 / 例|備考|
| :-- | :-- | :-- |
|キュータイプ|標準||
|名前|GoodMorningQueue|使用可能文字: 半角英数-_<br>文字数制限: 1文字以上80文字以下|
|可視性タイムアウト|1分|処理されなかった時に再処理するまでの時間を設定します。このシステムの場合は電話される間隔になります|

![createqueueoption.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/46bbaab6-dcca-5d65-350f-11cf0724a1ae.png)

以上でAmazon SQSの設定は終了です。

# AWS Lambdaの設定

AWS Lambdaはサーバーなどのプログラム実行環境を自分で構築、維持することなくプログラムが実行できる、いわゆるサーバレスな実行環境です。今回はプログラムをここに配置します。

### 1. 関数の作成

サービス一覧からAWS Lambdaを選択します。
![Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/0dd6bd81-ac81-5bad-ac79-b7d9c63021d2.png)

「関数の作成」をクリックします。
![createfunction.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/47a8baab-96be-ab96-d9f9-477b0b82f478.png)

以下の情報を入力し、「関数を作成」をクリックします。詳細設定はデフォルトとします。

|項目|設定値 / 例|備考|
| :-- | :-- | :-- |
|作成方法|一から作成||
|関数名|goodMorningFunction|使用可能文字: 半角英数-_<br>文字数制限: 1文字以上64文字以下|
|ランタイム|Ruby 2.7|自由に選んで良いですが、サンプルコードはRubyで記載しています|
|実行ロール|AWSポリシーテンプレートから新しいロールを作成||
|ロール名|goodMorningFunctionRole|使用可能文字: 半角英数-_<br>文字数制限: 1文字以上51文字以下|
|ポリシーテンプレート|Amazon SQSポーリングアクセス権限|Connectの権限はここではつけられないため、後で追加します|

![createFunctionRole.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f78705df-876a-d38f-50c0-e3991c36ff2c.png)

### 2. コードの編集

関数コードの編集領域に以下のコードをペーストし、「デプロイ」ボタンをクリックします。

![code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/541c1c0b-bffb-3cb0-7a77-fdb3ac82767e.png)

```ruby
require 'json'
require 'aws-sdk-connect'
require 'net/http'
require 'uri'

def lambda_handler(event:, context:)
  token = get_soracom_token
  event['Records'].each do |record|
    message = JSON.parse(record["body"])
    if sleeping?(imsi: message['imsi'], token: token)
      call(call_to: message['callTo'], message: message['message'])
      raise 'still sleeping' # 例外を発生させるとメッセージは未処理のまま残る
    end
  end
  puts 'awake'
end

def sleeping?(imsi:, token:)
  now = Time.now
  from = Time.mktime(now.year, now.mon, now.day)
  data = get_harvest_data(imsi: imsi, from: from.to_i * 1000, token: token)
  data.length == 0
end

def get_soracom_token
  uri = URI.parse("https://api.soracom.io/v1/auth")
  request = Net::HTTP::Post.new(uri)
  request.content_type = "application/json"
  request["Accept"] = "application/json"
  request.body = JSON.generate({
    "authKeyId" => ENV['SORACOM_AUTH_KEY_ID'],
    "authKey" => ENV['SORACOM_AUTH_KEY_SECRET']
  })
  response = Net::HTTP.start(uri.hostname, uri.port, { use_ssl: true } ) do |http|
    http.request(request)
  end
  raise "fail to get soracom token" if response.code != '200'
  JSON.parse(response.body)
end

def get_harvest_data(imsi:, from:, token:)
  uri = URI.parse("https://api.soracom.io/v1/data/Subscriber/#{imsi}?from=#{from}")
  request = Net::HTTP::Get.new(uri)
  request["Accept"] = "application/json"
  request["X-Soracom-Api-Key"] = token['apiKey']
  request["X-Soracom-Token"] = token['token']

  response = Net::HTTP.start(uri.hostname, uri.port, { use_ssl: true } ) do |http|
    http.request(request)
  end
  raise "fail to get harvest data" if response.code != '200'
  JSON.parse(response.body)
end

def call(call_to:, message:)
  client = Aws::Connect::Client.new
  client.start_outbound_voice_contact({
    destination_phone_number: call_to,
    contact_flow_id: ENV['CONNECT_CONTACT_FLOW_ID'],
    instance_id: ENV['CONNECT_INSTANCE_ID'],
    source_phone_number: ENV['CONNECT_SOURCE_PHONE_NUMBER'],
    attributes: {
      "message" => message
    }
  })
end
```

簡単にプログラムの説明をします。

- まだ寝ているかどうかを判定し、寝ていれば電話をしてエラー終了、起きていれば正常終了します。
- LambdaとSQSを組み合わせて使用した場合、キューの内容はeventに渡され、正常終了するとメッセージが削除される、という動作になります。プログラム内でSQSを使う必要はありません。
- 異常終了するとメッセージは削除されずに残り、可視性タイムアウト時間経過後、再実行されます。このプログラムでは通常のプログラムのエラーのほか、SORACOMへのアクセス結果が異常であったり、時間になったのに僕が寝たままであったりしたことを異常と判断して異常終了します。
- SORACOM HarvestからLTE-MボタンのIMSIに関する今日のデータを取得し、今日のデータがなければ寝ている、あれば起きていると判定します。
- SORACOM Harvestからデータを取得するため、認証してトークンを取得しています。
- 電話発信は[Amazon ConnectのSDK](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Connect/Client.html#start_outbound_voice_contact-instance_method)にて呼び出しています。
- システム全体に関わるパラメータは環境変数から、個別の電話内容は入力から取得しています。これにより他のボタン、他の電話番号を追加することが簡単になっています。

### 3. 環境変数の設定

Lambdaで実行するプログラムの環境変数を設定します。
環境変数のエリアにある「編集」ボタンをクリックします。

![environment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/19f47917-d9b8-732f-51b8-228b2dcd6f85.png)

「環境変数の追加」ボタンをクリックし、以下の環境変数を追加して、「保存」ボタンをクリックします。

|キー|設定値|備考|
| :-- | :-- | :-- |
|CONNECT_CONTACT_FLOW_ID|Amazon Connectの問い合わせフローID||
|CONNECT_INSTANCE_ID|Amazon ConnectのインスタンスID||
|CONNECT_SOURCE_PHONE_NUMBER|Amazon Connectの電話番号|ハイフンおよびスペースは取り除く。<br>例：電話番号が+1 123-456-7890であれば、+11234567890と入力|
|SORACOM_AUTH_KEY_ID|SORACOMのSAMユーザーの認証キーID||
|SORACOM_AUTH_KEY_SECRET|SORACOMのSAMユーザーの認証キーシークレット||
|TZ|Asia/Tokyo|使用する場所でのタイムゾーン|

![envvalue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/9380911e-12f9-dfb9-5316-12bba1c33a0b.png)

### 4. アクセス権限の設定

このプログラムにはAmazon SQSのキューをポーリングする権限と、Amazon Connectから電話を発信する権限が必要です。SQSへの権限は作成時に付与しましたので、Amazon Connectの権限を追加します。

アクセス権限のタブをクリックし、ロール名のリンクをクリックします。IAMのコンソールがブラウザの別タブで開きます。
![access.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/87c060d9-8c98-6c7c-bd5d-a5c027b7249e.png)

「ポリシーをアタッチします」をクリックします
![attachpolicy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/2e5c276c-76ee-c55c-539f-88cb9aec9201.png)

ポリシーのフィルタに「AmazonConnect」と入力し、表示された「AmazonConnect_FullAccess」にチェックを入れ、「ポリシーのアタッチ」をクリックします。(ここでは簡単にするためのFullAccess権限をつけていますが、もし業務で使うのであれば権限を狭くした方がいいでしょう)
![connectauth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/32088d8c-a31b-4795-07e0-ad10f25ff7c6.png)

成功した旨が表示されればOKです。
![successattach.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/dfa18e38-3165-5130-cfae-eb10d167e516.png)

### 5. トリガーの設定

SQSにメッセージが入ればこのLambda関数が実行されるよう、トリガーを設定します。

設定タブを開き、デザイナーエリアの「トリガーを追加」をクリックします。
![opentrigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/015c6f83-fa71-09c6-cc3e-40a6262570f1.png)

以下の情報で項目を設定し、「追加」をクリックします。

|項目|設定値|備考|
| :-- | :-- | :-- |
|トリガー種類|SQS||
|SQSキュー|作成したSQSキューを選択||
|バッチサイズ|1|実行ごとにメッセージを消すか残すかを決めるため、1つのメッセージしか処理しないよう1とします。|
|トリガーの有効化|ON|テスト後ONにしても良い|

![trigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/55f33d98-dd54-c650-153d-f3535379f67b.png)

# Amazon EventBridgeの設定

最後にAmazon EventBridgeにて、指定した時間にSQSキューにメッセージを送信するイベントを登録します。

### 1. イベントを作成

サービス一覧からAmazon EventBridgeを選択します。
![eventBridge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/595eba8b-8c24-b164-d49d-e2660bd5d55e.png)

ルールのメニューから「ルールを作成」ボタンをクリックします。
![opencreaterule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/61a1115a-4ce1-b160-1cdb-dbaf745adea6.png)

以下の内容で項目を入力し、「作成」をクリックします。

|項目|設定値 / 例|備考|
| :-- | :-- | :-- |
|名前|GoodMorningRule|使用可能文字: 半角英数.,-_<br>文字数制限: 1文字以上64文字以下|
|パターン|スケジュール|定時に実行するためスケジュールを選択します|
|スケジュール|cron式|決まった時間に入力するためcron式を使います|
|cron 式|30 21 * * ? *|分 時 日 月 曜日 年をスペース区切りで入力します。「時」はUTCで入力することに注意します。*はすべての値を含むワイルドカードです。日が*であれば何日でも、月が*であれば何月でも、年が*であれば何年でも、を意味します。?は任意を意味します。日と曜日は両方を*にできず、いずれかを?にする必要があります。例は毎日6:30を意味します|
|ターゲット|SQSキュー||
|キュー|作成したキューを選択||
|入力の設定|定数(JSONテキスト)||
|定数(JSONテキスト)|テストにてSQSに送信したメッセージと同じ||

![createrule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/aac87984-f35b-9754-fde1-877cfc38669f.png)

これですべての設定が終わりました。明日の6時半までにLTM-Mボタンを押さなければ、電話がかかってくるはずです。

# 動作確認

ちゃんと動作するかを各段階で確認しておきましょう。

### 1. Lambda コンソールでの動作確認

テストイベントを設定します。
![opentestevent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/b9aa487b-70ee-acf9-5441-52c2748d05b4.png)

以下のように項目を入力します。テスト内容には下のJSONテキストをペーストし、bodyの中にある各項目を設定します。

|項目|設定値 / 例|備考|
| :-- | :-- | :-- |
|作成/編集|新しいテストイベントの作成||
|イベント名|GoodMorningTest|入力可能文字:半角英数<br>文字数制限:1文字以上25文字以下|
|imsi|LTE-MボタンのIMSI||
|callTo|通話先の電話番号|通常の電話番号の先頭の0を+81と変更し、ハイフンを取り除く。<br>例：電話番号が090-1234-5678であれば、+819012345678と入力|
|message|おはようございます|電話を取った時に自動音声で話される内容。任意に入力可能|
bodyの中にある設定値を以下のように設定します。

```json
{
  "Records": [
    {
      "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
      "receiptHandle": "MessageReceiptHandle",
      "body": "{\"imsi\":\"440************\",\"callTo\":\"+8190********\",\"message\":\"おはようございます\"}",
      "attributes": {
        "ApproximateReceiveCount": "1",
        "SentTimestamp": "1523232000000",
        "SenderId": "123456789012",
        "ApproximateFirstReceiveTimestamp": "1523232000001"
      },
      "messageAttributes": {},
      "md5OfBody": "{{{md5_of_body}}}",
      "eventSource": "aws:sqs",
      "eventSourceARN": "arn:aws:sqs:ap-northeast-1:123456789012:MyQueue",
      "awsRegion": "ap-northeast-1"
    }
  ]
}
```

![testevent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/700cac11-9b28-c733-7896-a16b6802e134.png)

画面右上の「テスト」ボタンをクリックするとテストが実行されます。
![testerror.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/3d6625fb-29eb-cd4e-2452-ed0e26c8ff4e.png)

ここまでLTE-Mボタンを押していなければ、実行結果は失敗になり、errorMessageに"still sleeping"と表示され、電話がかかってくるはずです。他のエラーが出ていたり、電話がかかってこなかった場合は、コードや環境変数、テストの入力を見直しましょう。

ここで成功してしまう場合は、すでにボタンをクリックしてSORACOM Harvestに保存されていると考えられます。この場合は、SORACOMコンソールにてデータを削除しましょう。

データ削除は、SORACOMコンソールのSIM管理にて、対象のLTE-MボタンのSIMをチェックし、[操作] > [データを確認]をクリックします。

![deletedata.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/9cf2ce01-5c5d-f984-d497-19bcaf58541e.png)

今日のデータにチェックを入れ、「削除」ボタンをクリックします。

![deleteharvestdata.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/be08bf19-e259-66bd-29ee-175016726f54.png)

確認ダイアログにて「削除する」をクリックします。

![confirmdelete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/805aca48-26a7-fd4b-de3b-bf4a67807990.png)

今日のデータが全て削除されれば、Lambdaのテスト結果は失敗になるはずです。

次にLTE-Mボタンをクリックしていて、実行結果が成功になる場合をテストします。LTE-Mボタンをクリックし、LEDが緑点灯することを確認します。その後再度テストボタンをクリックすると、今度は成功になるはずです。

![successtest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/ed6486ef-1aef-470b-54ea-b171e2198336.png)

ここで失敗する場合は、IMSIが間違えている可能性があります。Harvest Dataを取得するAPIでは存在しないIMSIを指定した場合応答がステータスコード200、レスポンスが[]となり、IMSIを間違えた場合と正しいIMSIでデータがない場合が区別できないためです。成功になることもテストしておいた方が良いでしょう。

当日中にLTE-Mボタンがクリックされていなければ電話がかかってきて処理が失敗し、LTE-Mボタンがクリックされていれば電話はかかってこず処理は正常終了することが確認できました。

### 2. SQS にメッセージを送信して動作確認

SQS経由でのLambda実行が動作するかを確認しましょう。
まずSORACOM Harvestの当日のデータを削除します。

SQSのコンソールにて、作成したキュー名のリンクをクリックします。
![queuelist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f8bf184e-6a55-8ec0-a880-a7f0a00bb829.png)

画面右上の「メッセージを送受信」ボタンをクリックします。
![queuedetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/008cedcd-8f7f-b779-eef9-64307fc8260e.png)

メッセージ本文に以下のようなJSONテキストを入力します。「メッセージ送信」をクリックします。テストイベントではJSONをエスケープしていましたが、こちらではエスケープせず入力します。imsiとcallToは実際使用するものを入力してください。

```json
{"imsi":"440************","callTo":"+8190********","message":"おはようございます"}
```

![sendmessage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f494fee7-817c-d26d-c5a2-a8422cb71112.png)

送信してしばらくしたら電話がかかってくるはずです。電話を取っても取らなくても、時間が経つとまたかかってきます。LTE-Mボタンをクリックして、Harvestにデータが届くと電話がかかってこなくなるはずです。

電話がかかってこない場合はcallToが、電話が止まらない場合はimsiが間違っている可能性が高いです。電話が止まらない場合は、まずはLambdaのトリガーのSQSを無効化しましょう。

![invalidatetrigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/f0811dfe-8ff2-7c58-43da-95f2bfa3b168.png)

その後SQSコンソールにてメッセージをポーリングし、受信したメッセージを削除すればよいです。

![deletemessage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/e7babed7-4e30-090a-aa9d-1272dc62191a.png)

### 3. スケジュール実行の動作確認

指定した時間に電話がかかってくることを確認しましょう。

![Screenshot_20201123_073353_com.android.dialer.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/0b442f9a-ee12-eb1f-3248-baff4b65a757.jpeg)

取得した電話番号の他に、非通知でかかってくることもあるようです。

SQS -> Lambda起動がうまくいっているのに電話がかかってこない場合、イベントルールが間違っている可能性が高いです。ルールのcron式やテキスト内容を見直しましょう。

# あとかたづけと注意事項

本レシピでは費用がかかるサービスを利用しています。
必要であれば解除作業を行い、想定外の費用がかからないようにしてください。
以下に料金をまとめました。金額は2020/11/22時点、SORACOMは日本カバレッジ、AWSは東京リージョンでの金額としております。

|サービス / 機能|料金|解除方法|
| :-- | :-- | :-- |
|LTE-M Button SIM (plan-KM1)|基本料金: 100円/ 月、通信料金: 約0.3円 /回|利用開始待ちに変更すれば基本料金を0円 /月にできる。再開には200円かかる |
|SORACOM Harvest Data|5円 /日 /SIM|SIMグループを解除するか、グループ設定でHarvest Dataをオフにするかいずれかを設定する|
|Amazon Connect|電話番号(US): \$0.06 /日<br>発信通話(日本): \$0.0844 /分|電話番号を解約する|
|AWS Lambda|呼び出し: \$0.0000002 /回<br>実行時間(128MBにて): <br>\$0.000002083 /秒|Lambda 関数を削除する|
|Amazon SQS|\$0.0000004 /回|キューを削除する|
|Amazon EventBridge|\$0.000001 /回|イベントルールを削除する|

AWS Lambda、Amazon SQS、Amazon EventBridgeの料金は実行されなければ課金されないため、EventBridgeのルールを削除すれば料金はかかりません。
一方電話番号の保持やSORACOM Harvestには基本料金がかかるため、必要なければ解除しておいた方が良いでしょう。

# 次のステップ

本レシピでは、「ボタンを押したら通知する」ではなく、「ボタンを押されるまで通知し続ける」という通知の形を取りました。今回はワンパターンな通知をし続ける構成でしたが、就業時間に間に合わない時間であることを検知したら自動的に会社に通知を送る、などのエスカレーションを考えた方が良いでしょう。指定回数失敗したらメッセージを別のキューに移すSQSのデッドレターキュー機能が使えます。

起こす方法を電話としましたが、電話番号の維持にはお金がかかるので、より費用のかからない起こし方を考えてもいいかもしれません。スマートスピーカーやマイコンボードのブザーなどが使えるかと思います。何か問題があった時に現場にどうやって伝えるか、という方法のアイディアになります。

SORACOM Harvest Dataは簡単に使えますが、1日1回クリックされるボタンのストレージとしてはちょっともったいないです。(5円 /日には2000回分の送信費用が含まれています。)AWSのデータストアが使えるのであれば、S3やDynamoDB、TimestreamなどとSORACOM Funnel、Funkを組み合わせることでより費用を抑えることができます。

また、外部のアプリケーションからHarvest Dataのデータを取り出す方法を実践しました。IoTのデータをどこに保存して、どうやってデータを入れて、どうやってデータを閲覧、解析するか、はIoTの大事なテーマです。今回は起きた時間がHarvest Dataにたまるので、それを分析することを考えてもいいかもしれません。(ただしデータの保持期間は40日間なので注意しましょう)

身近な課題をIoTで解決していきましょう！