TwilioとAWS IoTボタンを使った子供のトイレ訓練

AWS IoT Button for Potty Training

2人の幼子の父親として、私は1日のうちのバカにならない時間をうんちに捧げています。大量の、大量のうんちです。

上の子がトイレのトレーニングを始めた時、夜中でも、もよおしたら起きてトイレを使うようになりました。ただ、そんな時、子供はもよおしたことを大きな声で私に知らせるので、近くで寝ている下の子が起きてしまうのではないかとヒヤヒヤしたものです。そんなわけで、何らかの対策が必要だなと感じていました。

私はいつも、子供の協力を得ることができる、楽しくかつ斬新な方法はないものかと考えています。そしてそれが、自分のエンジニアリングプロジェクトをいじくり回すことで実現できるなら、なお良いでしょう。

うんちボタンを押す

笑顔のうんちキャラクターが貼られた装置は、Amazon Dash ButtonをベースにしたAmazon IoTボタンです。子供がこのボタンを押すと、AWS Lambda関数が呼び出され、Twilio Pythonヘルパライブラリを使用してTwilio番号で私のiPhoneに発信します。Twilio番号は、アプリの連絡先の設定で”緊急時は鳴らす”をオンにしているので、たとえ午前2時で”おやすみモード”をオンにしていたとしても着信します。

はじめに:IoTうんちボタンに必要なもの

それでは、うんちボタンを作ってみましょう。以下に、必要な手順を挙げます。

AWS IoTボタンを買う

IoTうんちボタンのハードウェアは、第2世代のAWS IoTボタンです。こちらで売られています

ローカル開発環境を構築する

Lambdaのコードを書くには、Python 3.6がインストールされたコンピュータが必要です。Python 2が入ったMacの場合は、Homebrewで3.6をインストールすることをお勧めします。

brew install python

また、Python 2がインストールされている場合は、PYTHONPATHにPython 2のパッケージが指定されていないことを確認してください。

if [ $(echo $PYTHONPATH | grep python2) ]; then export PYTHONPATH=; fi

Twilioアカウントにサインアップする

Twilioアカウントを持っていない場合は作成します。その後、電話番号を購入してください。購入した電話番号やアカウントSID、Auth Token(認証トークン)は、後で必要になるので忘れないようにメモしておきましょう。

次に、Verified Caller ID(検証済み発信者番号)のリストを開いて、うんちボタンから発信する番号が登録されているかを確認し、なければ追加します。

AWSアカウントにサインアップする

AWSアカウントを持っていない場合は作成します。ちなみに、AWSアカウントは、Amazonのアカウントとは異なります。そのため、Amazonアカウントをお持ちの場合でも、AWSアカウントは別途、必要です。

PythonでTwilioを使って電話を掛ける

ここまではいいですか? それでは、次はコードを見てみましょう。

プロジェクトのディレクトリと依存関係

Lambdaにはコードの依存関係を管理する方法がないため、依存関係の全てを直接プロジェクトにインクルード(vendors)する必要があります。そして、全ての準備ができたら、Lambda関数を設定する際にAWSにアップロードできるzipファイルを作成します。

この関数では、Pythonの依存関係はTwilioクライアントライブラリのみです。それでは、プロジェクトディレクトリを設定してインストールしましょう。

mkdir twilio_poop_button
cd twilio_poop_button
printf "[install]nprefix=" > setup.cfg
pip3 install twilio --target .

プロジェクトディレクトリに複数のサブディレクトリとPythonファイルが表示されたでしょうか? なお、twilioには固有の依存関係があるので、requestspyzのようなものもあります(setup.cfgは、Homebrew経由でPython 3をインストールした場合にのみ必要です。HomebrewのPythonによる厄介なバグを回避します)。

電話を発信するためのPythonのソースコード

index.pyというファイルを作成し、そこに以下のコードをペーストします。

import os
from urllib.parse import urlencode

from twilio.rest import Client


def handler(event, context):
    client = Client(
        os.environ['TWILIO_ACCOUNT'],
        os.environ['TWILIO_TOKEN'],
    )

    call = client.calls.create(
        to=os.environ['ALERT_PHONE'],
        from_=os.environ['TWILIO_PHONE'],
        url='http://twimlets.com/message?{}'.format(urlencode({
            'Message[0]': "Baby needs to go potty",
        })),
    )

    print(call.sid)

環境から4つの変数が取得されていることにお気付きになりましたか?

  • TWILIO_ACCOUNT:TwilioアカウントダッシュボードからコピーしたアカウントSID
  • TWILIO_TOKEN:同じ場所からコピーしたAuth Token
  • TWILIO_PHONE:購入した電話番号(E.164形式
  • ALERT_PHONE:ボタンを押した時に発信する電話番号(同じくE.164形式)

スクリプトはTwilioクライアントをインスタンス化し、上記の環境変数に従って新しい発信を作成します。発信の内容については、Simple Message Twimlet(クエリ文字列内のメッセージリストを取得し、TwiML文書を返すURL)を使用しており、Twilioはその文書を読み込んで、そこから音声メッセージを生成します。

PythonとTwimletで電話を発信してみる

では、ローカルでコードを試してみて、ちゃんと動くか確認してみましょう。なお、その際は、下記のフェイク値を実際の値に置き換えてください。

TWILIO_ACCOUNT=xxxx TWILIO_TOKEN=xxxx ALERT_PHONE= 15017122661 TWILIO_PHONE= 15555555555 python3 -c 'import index; index.handler(None, None)'

電話は鳴りましたか? 鳴らない場合は、環境変数が間違っていないか見直し、Twilio電話番号の発信ログに電話番号が表示されているかチェックしてみてください。Twilioの無料トライアルアカウントをご使用の場合は、その電話番号がVerified Caller ID(検証済み発信者ID)のリストに入っているかも確認してみましょう。

電話が鳴った方は、おめでとうございます。無事、Twilioを動作させることができました。あとは、AWSにアップロードするためのzipファイルを用意すれば、コードに関しては終わりです。

zip -r ~/poop-button.zip .

AWS IoTとLambdaの統合

まだ開封していない場合は、ピカピカのAmazon IoTボタンを取り出しましょう。そして、裏に印刷されたDSNをメモしておきます(恐らく”G030″から番号が始まっているはずです)。これはボタンの識別子で、AWSが作成する自動生成リソースの多くには、この番号が表示されます。

各電話用OSのネイティブアプリを使用してボタンを設定する

次に、iOSまたはAndroidにAWS IoT Button Devアプリをインストールして起動し、AWS IoTボタンを設定します。私が使っているのはiPhoneなので、スクリーンショットはiOS用アプリからのものです。以下、アプリでボタンを接続する方法を説明します。

  1. AWS IoTボタンを設定するために、ボタンをクリックします。

iOS Native App to register IoT Button

  1. ボタンのDSNを入力して名前を付けます(箱のバーコードをスキャンできる場合は、バーコードを読み取ってください)。
  2. ライトが青く点滅するまでボタンを押し続けます(約6秒間)。

Enter an Amazon IoT Button DSN
Pairing process for a phone and Amazon IoT button

  1. “Copy password and go to settings(パスワードをコピーして設定に進む)”リンクをタップし、アプリに表示されたSSIDに接続、そしてクリップボードに保存されたパスワードを貼り付けてボタンのWi-Fi回線に接続します。
  2. アプリに戻ります。ボタンのWi-Fi回線が認識されると、自宅の(ボタンが使用する)Wi-Fi回線のSSIDとパスワードを設定する画面に切り替わります。

Configuring a WiFi network for the IoT button to use

  1. その後で、ボタンアクションを設定します。Lambda関数はまだ作成していないので、まずはひな形を使って作成してみましょう。”Send Email(python)(Eメールを送信)”を選択し、”Set action(アクションを設定)”を押します。

Basic python demo with the Amazon IoT button

  1. ダイアログボックスにEメールアドレスを入力すれば設定完了です。

Amazon IoT button send email with Python

試してみましょう。ボタンを押してみてください。要求が成功した場合、ライトが白く点滅してから緑に変わります。初回時は、設定先のEメールについて確認するため、AWSからそのアドレスに確認メールが届くはずです。ボタンがきちんと機能しているかは、このメールが届いたことでも分かります。記載の確認リンクをクリックしてもう一度ボタンを押すと、”Hello from your IoT button”という件名のメールが、改めて送られてきます。

Lambda関数を新規に作成する

AWSコンソールにログインし、Lambdaサービスを検索します。

Create a Lambda function

先ほど電話のアプリで作成した関数が表示されているはずです。ここで”Create function(関数の作成)”ボタンをクリックします。

Search for a created Lambda function

“Author from scratch(新規に編集)”を選択して関数に名前を付け、Runtime(ランタイム)にPython 3.6を選択してくだい。次にRole(ロール)の欄で”Create new role from template(s)(テンプレートから新規ロールを作成)”を選択し、ロールに名前を付けて、Policy templates(ポリシーテンプレート)のドロップダウンリストから”AWS IoT Button Permissions(AWS IoTボタンの権限)”を選択します。最後に、”Create function(関数を作成)”をクリックすれば完了です。ローディングインジケータが回転し、新しい関数に移動します。

Role and naming for a poop button Lambda function

関数のトリガを設定する

Designer(デザイナ)のセクションで、左側にあるトリガのリストからAWS IoTを選択します。

AWS IoT as a Lambda trigger

Configure triggers(トリガ設定)のセクションで、IoT type(IoT種別)に”Custom IoT rule(カスタムIoTルール)”を選択し、Rule(ルール)の欄から、最初の関数を作成した時に作られたルールを見つけてください(ルール名にDSNが表示されているはずです)。”Enable trigger(トリガを有効化)”にチェックが入っていることを確認し、”Add(追加)”をクリックします。

Configuring a Lambda trigger

ソースコードのアーカイブをアップロードする

Designerセクションに戻ってLambda関数をクリックすると、”Function code(関数コード)”のセクションがDesignerの下に表示されます。”Code entry type(コードエントリ種別)”を”Upload a .ZIP file(ZIPファイルをアップロード)”に変更し、RuntimeがPython 3.6に設定されていることを確認して、Handler path(ハンドラパス)に”index.handler”と入力してください。そして、”Upload(アップロード)”ボタンをクリックして、先ほど作成したzipファイルを選択します(~/poop-button.zip)。

Upload a zip file to Lambda

環境変数を設定する

“Function code”セクションの下には”Environment Variables(環境変数)”セクションがあります。前述のローカルテストで使用したのと同じ4つの変数名と値を、このセクションに入力してください。変数は、KMSキーで暗号化することが望ましいですが、この記事では、そこまでは触れません。

Enter environment variables to Lambda

ここまで終わったら、画面をスクロールしてウィンドウの上部にある”Save(保存)”ボタンをクリックし、全ての変更を保存します。

Where to 'save' a Lambda function

テストイベントを設定して実行する

ウィンドウ上部の”Select a test event(テストイベントを選択)”ドロップダウンリストをクリックし、”Configure test event(テストイベントを設定)”を選択します。ここでは、テストの内容については何でも構いません。デフォルトの”Hello world”イベントで十分に事足ります。名前を付けて”Create(作成)”をクリックしてください。

Configure a Lambda test event

“Test(テスト)”ボタンをクリックします。テストが終わると、Execution result(実行結果、望ましくは、発信を含む成功した実行結果)が表示されるはずです。電話が鳴らない場合は、コードがきちんとアップロードされ、環境変数がローカルテストの値と同じかどうか確認してください。

Successful Lambda function execution

ボタンを試してみる

ここまででボタンの設定が終了しました。早速、試しにボタンを押してみましょう。

電話は鳴りましたか? 鳴らない場合は、AWS IoTトリガの設定ができているか、ボタン背面のDSNが識別子に含まれているか、そしてトリガが有効になっているかを確認してください。

電話が鳴った方はおめでとうございます。うんちボタンの完成です。

この時、電話メールの両方を受け取りましたよね? IoTボタンは、一度に複数のLambda関数をトリガすることができます。

メールを無効にしたい場合は、元のLambda関数を削除するか、該当するAWS IoTトリガを無効にします。

幼児のトラブルシューティング

ボタンを使ったこの方法のいい点は、全ての技術が第三者によって構築され運用されており、信頼性も非常に高いということです。私はこれを日常的に数ヶ月使っていますが、ハードウェアに関してもソフトウェアに関しても、不具合は今のところ見られません。一方で、お子さんを持つ親御さんならご存じかとは思いますが、一般的に子供の行動は予測がつかず、いわば非常に厳しいQA担当者とも言えるでしょう。そこで、遭遇する可能性が高い故障モード、不測事態に対して、私なりの対策を提案します。

子供は、ベッドに入った時にはボタンを持っていたが、目が覚めると毛布に紛れてボタンがなくなっていた/部屋のどこかに放り投げていた/マットレスとベッドの間に挟まっていた。おしっこがしたいのに、ボタンがなくて子供がパニックになっている。

なくしたりする事態に対しては、ベルクロが有効です。ボタンの裏側にベルクロの一方を貼り、対のベルクロを壁やベッドの隅に貼り付けるといいでしょう。寝ている時にぶつかって、落ちてしまう可能性がある場所は避けた方が賢明です。また、手が届きやすい方がいいですが、あまり近すぎると、寝ている時に誤って押す恐れもあるので、その辺の案配にも気を付けてください。

まだ眠りたくない子供は、ベッドに入って15分程度たつと、ベッドから出る言い訳としてボタンを押すようになった。

ご多分に漏れず、私の子供もすぐにこれに気付き、そうするようになりました。これに対して私は、子供が寝る前にトイレに行く機会を複数回は与えるようにし、一方で彼らが眠りにつくまではボタンは私が保持するようにしました(眠った後で、こっそりと所定の位置にセットします。)。それ以降、子供は自然と寝る前にトイレに行く習慣を身に付けました。

子供がボタンを押すのを拒否する。理由を聞いても”したくないから”としか答えない。

私の家でも、子供がボタンに慣れるのには時間が掛かりました。そこでまずは、朝起きてベッドから出る前にボタンを押すように頼んで、それを日常化しました。ただしこれは、結果的には上記の”ベッドから出る”という問題のきっかけになった可能性もありますが…

うんちボタンを使い始めて

うんちボタンには、とても助けられています。子供たちはお漏らしをせずに目が覚めるようになりましたし、起きた時に大声で親を呼ぶこともなくなったため、もう一方の子供の睡眠も邪魔されずに済むようになりました。この些細なプログラミング的工夫の効果には驚かされるばかりです。


*注釈:David Gouldin@dgouldin
上の子がトイレ訓練を始めた時に、この”うんちボタン”を子供に与えた。ベッドに入った後で、もしトイレに行きたくなったら、このボタンを押してもらう。

AmazonのIoTボタンで、@twilioで電話が掛かる仕組みだ。本当に助かってる。

ステッカーは@pavel_lishin*

Twitter上で、多くの人からShark Tank(事業立ち上げを支援するテレビ番組)に応募したり、Kickstarter(キックスターター)を始めたりしたらいいのにと声を掛けてもらいましたが、もし誰かがうんちキャラクターの貼られたボタンでビジネスを立ち上げるなら、それは@internetofshitでしょう。


注釈:Internet of Shit@internetofshit
うん、確かに。15万個欲しいんだけど、どこで注文したらいい?
https://twitter.com/dgouldin/status/967232808488812544 …

AWSに関しては、Amazonの技術のこうした愚かな再利用を彼らが残念に思っている可能性もなきにしもあらずですが、反応を見る限り、そんなことはないと言って差し支えないと思います。


注釈:Werner Vogels@Werner
1)構築ブロックを作成する。 2)予期せぬ革新が起こる。
#AWS
https://twitter.com/dgouldin/status/967232808488812544 …経由

私に関して言うと、これからも引き続き父親業の問題に対して実用的な(時にはそうでないこともあるでしょうが)工夫を追求していきたいと思っています。PythonやTwilio、AWS IoT、Lambdaを使って夜間の不測事態を回避しようとしたり、InfluxDBとRaspberry Piを使ってGrafanaフロントエンドで体温をモニターしたりと、常に複数のプロジェクトが進行中です。もし何かいいアイデアがあれば、ぜひ連絡してください。一緒に何かを作れたら素晴らしいですね。