Amazon Dash ButtonをIoTボタンとして使う第三の方法：DHCPサーバ設定でスクリプトを起動する

Amazon Dash Buttonはボタンを押すだけで商品が届くというコンセプトのデバイスです。小さいボディですが、これ単体で無線LANネットワークに接続してAmazonにHTTPSリクエストを投げてくれます。

![2017-05-07 13.05.28.jpg](https://qiita-image-store.s3.amazonaws.com/0/46856/66006275-bc71-9cc4-2420-2626775a0c89.jpeg)

これを自作IoTボタンにする試みが半年くらい前に流行りました。ネットで検索してみると、ボタンを押すとSlack上で発言する、みたいな使い方が多いですね。

ところで、このボタンが押されたことを検出する仕組みは下記の2種類のどちらかを使っている人が大半のようです。

1. ARPやDHCPなどのブロードキャストパケットを別マシンで捕まえる方針
    * [hortinstein/node-dash-button](https://github.com/hortinstein/node-dash-button) など出来合いのものは大抵この方針
    * プロミスキャスモードで全パケットを捕まえるパターンもこの仲間と言って良いでしょう
2. DNSサーバに偽のレコードを登録して、amazon宛のHTTPSリクエストを別マシンで横取りする方針

ただ、どちらの方法も別のマシンが必要で、回りくどい方法のように個人的には感じます。Dashボタンと無線LANルータの間で通信が成立しているのだから、無線LANルータ上でプログラムを起動するのが一番素直というものでしょう。別のマシン上で普段動かさないデーモンを立てるようなやり方だと、1回遊んだら終わり、になりかねません。

本稿では第三のAmazon Dash Button検出方法として、無線LANルータ上でDHCPによるIP払い出しと同時にスクリプトを起動する方法を紹介します。

# 家庭用ルータ上で動くLinuxディストリビューション

ルータ上でプログラムを動かす方が簡単だと言いましたが、ご家庭のルータでそこまで柔軟に設定変更できるものは少ないかもしれません。こうしたニーズに応えてくれるのが[OpenWrt](https://openwrt.org/)や[LEDE](https://lede-project.org/)です。

OpenWrt/LEDEは組み込みLinuxのディストリビューションで、家庭用の有線LANルータ・無線LANルータのファームウェアを置き換えることでカスタマイズ性を高めようというプロジェクトです。Web設定画面からルータとしての設定ができるだけでなく、普通のLinuxのようにSSHログインして必要なバイナリパッケージをインストールすることもできます。最新の安定版LEDE 17.01.1では約300機種のイメージが公開されており、国内外で広く使われています。

筆者もLEDEベースのルータを常用しているため、今回紹介するような設定が可能だというわけです。このように設定の小回りが利くのはOpenWrt/LEDEの魅力だと思います。

参考までに、筆者が今回利用した構成を紹介します。

- 有線LANルータ: [BUFFALO BHR-4GRV](http://buffalo.jp/product/wired-lan/router/bhr-4grv/)
- USB無線LAN子機: [BUFFALO WLI-UC-AG300N](http://buffalo.jp/products/catalog/network/wli-uc-ag300n/)
- Linuxディストリビューション: LEDE 17.01.1

![2017-05-08 11.44.01.jpg](https://qiita-image-store.s3.amazonaws.com/0/46856/d425b9b1-5311-4ed1-537b-636d42536d78.jpeg)

筆者は有線LANルータのファームウェアを置き換えた上で、ルータのUSBポートに無線LAN子機を指して利用しています。子機といってもチップの機能としてはアクセスポイントにもなれるので、この組み合わせで無線LANルータとして使っているというわけです。

OpenWrt/LEDE自体は無線LANルータのファームウェアを置き換えることもできるのですが、日本国内ではファームウェア書き換えは無線機の改造にあたります。言い換えると、電波法違反になってしまうリスクがあります。法律を守って正しく遊ぶために、無線機器を改造しない構成で利用しているというわけです。

興味を持たれた方は「[BHR-4GRVにLEDEをインストールしたメモ - Qiita](http://qiita.com/hnw/items/e1ec0cf5ca94be916433)」などもご参照ください。

# Dashボタン専用隔離ネットワークの作成

さて、今回せっかくルータがあるわけですからネットワーク構成も工夫してみましょう。今回のDashボタンの用途ではルータとだけ通信できれば十分ですから、家庭内の他のマシンと同じネットワークに参加させる理由はありません。

そこで、Dashボタン専用のSSIDと専用の隔離ネットワークを作成してみました。設定としては次のようなものです。

- 無線SSID: amazon-dash
    - 普段のSSIDと別に新規作成
- ネットワーク: 192.0.2.0/24
    - 普段のネットワークとは別のネットワークを新規作成
- DHCPリース時間: 2分
    - dnsmasqの最小値、理由は後述

このネットワークでは、外部への通信を全てファイアウォール設定でrejectしています。これはDashボタンからのNTPやHTTPSの通信を素早く諦めさせるためです。

設定が完了していないDashボタンは注文こそしないものの、毎回`https://dash-button-jp.amazon.com/`にHTTPS通信を行います。しかし、HTTPSのハンドシェイク処理や証明書の検証処理はCPU負荷の高い処理ですから、これをrejectすることで電池持ちを良くする効果も狙えるというわけです。

ただし、Dashボタンのセットアップ中はインターネット接続が必須なので、一時的にインターネットに出られるようにしておく必要があります。セットアップ手順については他のページ（たとえば「[Amazon Dash ButtonをただのIoTボタンとして使う - Qiita](http://qiita.com/jsoizo/items/3b8bba4160f41aef20f4#%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)」など）を参考にしてください。

# DHCPの設定変更

さて、いよいよ本題です。DHCPによるIPアドレス割り当てのタイミングでスクリプトを起動してみましょう。OpenWrt/LEDEではdnsmasqがDHCPサーバ機能を担当していますので、設定ファイル`/etc/dnsmasq.conf`に次のように記述します。

```/etc/dnsmasq.conf
dhcp-script=/usr/local/bin/dnsmasq-script.sh
```

起動するスクリプトは次のようなものです。

```bash:/usr/local/bin/dnsmasq-script.sh
#!/bin/ash

/bin/echo `/bin/date +"%F %T"` $* >> /tmp/dnsmasq.script.log

# 自分のDashボタンのMACアドレスに書き換えてください
MAC_ADDR="00:00:5E:00:53:38"

if [ "$1" == "add" -a "$2" == ${MAC_ADDR} ]; then
    # 発言したい内容に応じて適宜書き換えてください
    WEBHOOK_URL="https://hooks.slack.com/services/***/***/***"
    CHANNEL="#general"
    USERNAME="空腹bot"
    ICON_EMOJI=":ghost:"
    TEXT="はらへり"
    JSON="{\"channel\": \"${CHANNEL}\", \"username\": \"${USERNAME}\", \"icon_emoji\": \"${ICON_EMOJI}\", \"text\": \"${TEXT}\" }"
    curl -s -S -X POST --data-urlencode "payload=${JSON}" ${WEBHOOK_URL} > /dev/null
fi
```

これでDashボタンを押すとSlack投稿ができるようになりました。

![image](https://qiita-image-store.s3.amazonaws.com/0/46856/bdfa7bd4-31f9-c378-297d-6bc156176d48.png)

上の画像は自分で発言した直後にDashボタンを押すという小芝居の様子です。

ただし、このスクリプトでは新規にIPアドレスを割り当てられたときしか処理が発火しません（条件文の`"$1" == "add"`の部分）。今回はDHCPのリース時間を2分に設定しているので、2分経過以降であれば新規割り当ての扱いになって期待した処理を行うことができます。

逆に言うと、現状だと2分間のうちに2回以上ボタンを押されても反応しないことになってしまいます。もっと短時間のうちに連打したいような用途であれば、スクリプト中の条件文に`"$1" == old`も加えるなどして対応できるはずです（リース期間中に再度割り当て要求があった場合は `old` となります）。

# まとめ

無線LANルータ上でDHCPのIPアドレス払い出しタイミングでスクリプトを起動し、Amazon Dash Buttonが押されたときだけ反応するスクリプトの作り方を紹介しました。無線LANルータに手を入れられない場合でも、たとえばDHCPサーバだけをRasberry Piなどで提供しても同じことが実現できるはずです。

また、Amazon Dash Button専用に新たなSSIDおよび新たなネットワークを作るようなやり方も紹介しました。このような構成にすることで、特殊な設定（DHCPリース時間を極端に短くする）もやりやすくなりますし、セキュリティ面でもメリットが得られます[^1]ので、個人的には良い設定だと思っています。

[^1]: セキュリティレベルの異なる機器は異なるネットワークに接続させましょう、という一般的な話題ではあります。特にDashボタンをドアフォンとして使う（＝屋外に置く）ようなことを考えると、普段使いのネットワークに参加させるのは危険でしょう。Dashボタンを分解すればWiFiのパスワードを簡単に読み出すことができます。

# 参考URL

この仕組みを作ったあとで検索してみたところ、Tomato Firmwareを利用してほとんど同じことをやっている海外の方を見つけました。

- 「[Amazon Dash doorbell](https://loads.pickle.me.uk/2016/12/14/amazon-dash-doorbell/)」

家庭用ルータのファームウェア入れ替えは日本だとやや下火になってきた印象ですが、海外ではまだまだ遊んでいる人が多いということかもしれません。