MAN WITH A MISSION
頭はオオカミ、体は人間の究極の生命体。
彼らは自分たちの音楽で世界征服を目指す、最高にアツい集団だ。
そんな「MAN WITH A MISSION」のGt,Vo,Rap ジャン・ケン・ジョニー(Jean-Ken Johnny)と番犬をかけあわせた、というのが「Ban-Ken Johnny(番犬ジョニー)」の由来である。
オオカミと番犬、イヌ科の奇跡のコラボレーション!
センスしかないですね(笑)
ファンクラブ会員限定の10周年記念ツアー当たって欲しいなぁ...
はい、前置きはこれぐらいにして「Ban-Ken Johnny」の機能を紹介します。
###「Ban-Ken Johnny」の機能
- 人感センサーで人の気配を感知したら、スマホにアラートメッセージを送る
- 自分が家にいる間はセンサーが反応して欲しくないので、監視機能の
ON/OFF
を切り替えできる。 - 毎回家出る時にon/offするのはメンドイので、家の外からオンラインでアラート通知を
ON/OFF
できる
「Ban-Ken Johnny」にできることはざっとこれぐらいです。
イメージがつきやすいように「Ban-ken Johnny」のデモをしました。
####1.監視機能をON
監視機能はOFFになっているので、start
ボタンを押してONにします。
ONになりました。
####2.家に誰か侵入(ラズパイの人感センサーに反応)
監視機能がONの状態で、
家に設置されているラズペリーパイの前を誰かが横切ります(なぜか物悲しい写真ですね...笑)
そうすると、
自分のスマホに通知が飛んできました!
まあこんな感じです笑
デモでイメージを掴んでいただいたところで、「Ban-Ken Johnny」の仕組みを紹介します。
図だけ見てもよくわからないと思うので、ここからは仕組みを少しだけ詳しく説明して行こうと思います。
興味ないよーという方は編集後記
まで読み飛ばしてください
###1.Mattermost
Mattermostは自分が侵入者検知メッセージを受け取るために使用します。
別にSlackでも良かったのですが、勉強のためにも自分でチャット環境を用意してみることにしました。
今回MattermostはEC2上でdocker
を使って動かします。
個人利用なので、preview版を使うことにしました。
mattermost/mattermost-preview
preview版は一つのコンテナ内に、mattermostアプリとmysqlが動作していて、コンテナ一つを動かすだけでmattermostを使用できます。
加えてmattermostの前段にnginxも置いてみることにしました。(特に理由はないです笑)
Mattermostは親切なので、Mattermost用のnginx.confを用意してくれています。
これをありがたく使わせてもらいます。
それらを踏まえると、mattermostを動かすためのdocker-compose.ymlはこんな感じになります。
version: "3"
services:
nginx:
image: nginx:1.17
container_name: nginx
volumes:
- "./etc/nginx:/etc/nginx"
- "./var/log/nginx:/var/log/nginx"
ports:
- 8080:80
links:
- mattermost
mattermost:
image: mattermost/mattermost-preview
container_name: mattermost
volumes:
- "./mysql:/var/lib/mysql"
ディレクトリ構成はこんな感じです。
./mattermost
├── ./docker-compose.yml
├── ./etc
│ └── ./etc/nginx
│ └── ./etc/nginx/nginx.conf
├── ./mysql
└── ./var
└── ./var/log
└── ./var/log/nginx
今回はEC2で動かして、その前段にELBとRoute53を置きます(ELB置くならnginxいるのか?というツッコミはなしで)
Route53で設定したドメイン名(ban-ken-johnny.example.com
とします)にアクセスしてみます
はい、無事表示されました。
(よくわからないところで凝り性を発揮してしまい、https対応のためにAWS Certificate Manager
使って証明書発行したり、ALBでHTTPtoHTTPS
対応したりしたんですが、長くなるので割愛します)
とりあえずアカウントを作成してログインします。
ログインしてやったこととしては、「Ban-Ken Johnny」がアラートメッセージを送って来れるように、Incominng Webhookの設定と、プッシュ通知サーバーの設定をしました。
ただ自前でpush通知用サーバーを用意するほど余裕がなかったので、Mattermostが用意してくれているテスト確認用のサーバーを使うことにしました。
あんまり通知飛ばさないと思うのでMattermostさんご容赦を...
さて、もろもろを終えると「Ban-Ken Johnny」からWebhook経由でメッセージが送られてスマホに通知がくる、ということができるようになります。
###2. Flask App
オンラインからをアラート通知をon/off
にするために、アラートの状態を管理してくれるアプリをFlaskで実装しました。
本当に簡単なアプリで、機能としては
-
/start
や/stop
のGETリクエストがきたら、DBのstatusカラムの値を変更する
という本当にただそれだけのアプリです。
このアプリもdocker
を使ってEC2上で動かします。
version: "3"
services:
nginx:
image: nginx:1.17
container_name: nginx_flask
volumes:
- "./etc/nginx:/etc/nginx"
ports:
- 8081:80
links:
- flask-app
flask-app:
build: ../
image: flask-app
container_name: flask-app
environment:
- DB_USER=root
- DB_PASSWORD=setting_password
- DB_HOST=mysql_flask
- DB_NAME=flaskdb
links:
- mysql
mysql:
container_name: mysql_flask
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=setting_password
- MYSQL_DATABASE=flaskdb
volumes:
- ./sql:/docker-entrypoint-initdb.d
command: "mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci"
ディレクトリ構成はこんな感じです。
./flask-app
├── ./Dockerfile
├── ./config
│ └── ./config/config.py
├── ./definitions
│ ├── ./definitions/__init__.py
│ └── ./definitions/database.py
├── ./docker
│ ├── ./docker/docker-compose.yml
│ ├── ./docker/etc
│ │ └── ./docker/etc/nginx
│ │ └── ./docker/etc/nginx/nginx.conf
│ └── ./docker/sql
│ └── ./docker/sql/init_ddl.sql
├── ./models
│ ├── ./models/__init__.py
│ ├── ./models/alert_status.py
│ └── ./models/dao
│ ├── ./models/dao/__init__.py
│ └── ./models/dao/alert_status.py
├── ./requirements.txt
└── ./server.py
これにより、アラートの状態を管理してくれる簡単なAPIが完成しました。
これぐらいならAmazon API Gateway → AWS lambda → Amazon DynamoDB
の完全サーバレス構成にできそうなので、時間ができたらサーバレス構成に変更して見たいと思います。
###3. S3(Front End)
スマホからオンラインでアラートのON/OFF
を命令するためのフロントエンドは、Amazon S3 での静的ウェブサイトのホスティングを使って動かすことにしました。
正直フロント技術はほとんど触ってきていないところで、かつデザインのセンスもないのでパパッと手軽に実装して、S3にデプロイしちゃいました。
本当はVue.jsとか使いたいし、CSSも初心者感を無くしたいし...
そんなのは夢のまた夢で、イケてない見た目になっちゃいました...
ごめんねジョニー...
###4. RaspberryPi
さあ最後に、ラズパイを触っていきます。
「Ban-Ken Johnny」を作るために、今回初めてラズパイを購入しました。
今年一年頑張った自分へのクリスマスプレゼントですね
ラズパイには、「人感センサーをつけて、人の気配を検知したらMattermostにアラート通知する」という、根幹の部分を担ってもらいます。
このプロセスは常時動きながら家を監視しておいて欲しいので、systemd
を使ってデーモンプロセスとして動かすことにしました。
プロセスとして動かすpythonのスクリプトはこんな感じ。
#!/usr/bin/python3
import requests
import json
from time import sleep
from datetime import datetime
import dateutil.tz as tz
import sys
import RPi.GPIO as GPIO
GPIO_PIN = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN, GPIO.IN)
STOPPED_STATUS = 2
WEBHOOK_URL = 'https://ban-ken-johnny.example.com/hooks/abcdefghijklmnopqrstuvwxyz'
def is_alert_stopped():
response = requests.get("https://ban-ken-johnny.example.com:8080/getAlertStatus")
content = json.loads(str.strip(response.content.decode("utf-8")))
if int(content['status']) == STOPPED_STATUS:
return True
return False
def post_alert():
headers = {'content-type': 'application/json; charset=UTF-8'}
now = datetime.today().astimezone(tz.gettz('Asia/Tokyo')).strftime('%Y/%m/%d %H:%M')
payload = {
"text": "侵入者ヲ検知シマシタ!:wolf:",
"channel": "town-square",
"attachments": [
{
"text": "**Time:** "+now,
"mrkdwn_in": ["text"]
}
]
}
response = requests.post(WEBHOOK_URL, data=json.dumps(payload), headers=headers)
res = {
"status" : str(response.status_code),
"mesage" : response.content.decode("utf-8")
}
return json.dumps(res)
if __name__ == "__main__":
try:
while True:
if GPIO.input(GPIO_PIN) == GPIO.HIGH:
if is_alert_stopped() == False:
post_alert()
sleep(10)
except Exception as err:
print(str(err))
sys.exit(1)
finally:
GPIO.cleanup()
このpythonプロセスをsystemd
を使ってデーモンプロセスとして登録するために、ban-ken-johnny.service
ファイルを用意します。
[Unit]
Description=Ban-Ken Johnny
Requires=network.target
[Service]
Type=simple
ExecStart=/opt/ban_ken_johnny.py
Restart=always
[Install]
WantedBy=multi-user.target
このban-ken-johnny.service
を/etc/systemd/system
直下に置き、ban-ken-johnny.py
は、serviceファイルのExecStart
で指定しているように、/opt
直下に置きます。
準備ができたらコマンドを実行して、ban-ken-johnny.service
を起動します
#rootにpython-dateutilをインストール
$ sudo pip3 install python-dateutil
$ sudo systemctl daemon-reload
$ sudo systemctl start ban-ken-johnny.service
#OSが起動したらプロセスも自動起動する様にする
$ sudo systemctl enable ban-ken-johnny.service
プロセスのステータスを確認してみます。
$ sudo systemctl status ban-ken-johnny.service
● ban-ken-johnny.service - Ban-Ken Johnny
Loaded: loaded (/etc/systemd/system/ban-ken-johnny.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2019-12-12 21:20:20 JST; 8s ago
Main PID: 3010 (ban_ken_johnny.)
Tasks: 1 (limit: 2200)
Memory: 11.7M
CGroup: /system.slice/ban-ken-johnny.service
└─3010 /usr/bin/python3 /opt/ban_ken_johnny.py
12月 12 21:20:20 raspberrypi systemd[1]: Started Ban-Ken Johnny.
ちゃんと動いてますね🐺
あとは人感センサーが反応したら、ちゃんと通知がくるか確認するだけです!
無事動作しました!!
ちゃんとON/OFF
にしたがって、通知を飛ばしてきてくれます!!
ヤッタゼ!!
これで「Ban-Ken Johnny」は完成です。
お手軽と言いながら、地味に時間かかっちゃいました...
これで家の留守中も安心です😄
###編集後記
完成して使い始めたは良いものの、なぜか2分置きにGPIO
からinput
を検知してしまうという不具合が発生しました。
そのため2分置きに「侵入者ヲ検知シマシタ!🐺」
と送信してきます。
鬼メッセージ送信機へと成り下がってしまったジョニーをどうにかして直したいのですが、ラズパイ初心者の私にはお手上げ状態です...
アドベントカレンダーの締め切りもあったので、すぐに届くセンサーをAmazonで選んで購入したのですが、そのセンサーが悪かったのかもしれません...
今見たら誰も評価してないし...
一応GitHubにも公開しているので、参考までに...
トリアエズ有識者ノ皆サマ、是非オ助ケヲ!!