LoginSignup
9
8

More than 3 years have passed since last update.

お手軽防犯システム「Ban-Ken Johnny」を作ってみた🐺

Last updated at Posted at 2019-12-18

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

fullsizeoutput_e4.jpeg
監視機能はOFFになっているので、startボタンを押してONにします。
fullsizeoutput_e7.jpeg
ONになりました。

2.家に誰か侵入(ラズパイの人感センサーに反応)

監視機能がONの状態で、
5DWEZ6kzT%+aQLpI5QcDxQ.jpg
家に設置されているラズペリーパイの前を誰かが横切ります(なぜか物悲しい写真ですね...笑)

そうすると、
fullsizeoutput_df.jpeg
自分のスマホに通知が飛んできました!
まあこんな感じです笑
デモでイメージを掴んでいただいたところで、「Ban-Ken Johnny」の仕組みを紹介します。

「Ban-Ken Johnny」の概要

Ban-Ken Johnny.jpg

図だけ見てもよくわからないと思うので、ここからは仕組みを少しだけ詳しく説明して行こうと思います。
興味ないよーという方は編集後記まで読み飛ばしてください

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はこんな感じになります。

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とします)にアクセスしてみます
mattermost.png

はい、無事表示されました。
(よくわからないところで凝り性を発揮してしまい、https対応のためにAWS Certificate Manager使って証明書発行したり、ALBでHTTPtoHTTPS対応したりしたんですが、長くなるので割愛します)

とりあえずアカウントを作成してログインします。
ログインしてやったこととしては、「Ban-Ken Johnny」がアラートメッセージを送って来れるように、Incominng Webhookの設定と、プッシュ通知サーバーの設定をしました。
ただ自前でpush通知用サーバーを用意するほど余裕がなかったので、Mattermostが用意してくれているテスト確認用のサーバーを使うことにしました。
あんまり通知飛ばさないと思うのでMattermostさんご容赦を...

さて、もろもろを終えると「Ban-Ken Johnny」からWebhook経由でメッセージが送られてスマホに通知がくる、ということができるようになります。
6C8105F5-A908-49DA-906A-656923A55571.png

A4C0AB61-DA9A-4680-A77D-88C42AFF4F06.png
イイ感ジデスネ、YES!YES!

2. Flask App

オンラインからをアラート通知をon/offにするために、アラートの状態を管理してくれるアプリをFlaskで実装しました。
本当に簡単なアプリで、機能としては

  • /start/stopのGETリクエストがきたら、DBのstatusカラムの値を変更する

という本当にただそれだけのアプリです。
このアプリもdockerを使ってEC2上で動かします。

docker-compose.yml
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のスクリプトはこんな感じ。

ban-ken-johnny.py
#!/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ファイルを用意します。

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.

ちゃんと動いてますね🐺
あとは人感センサーが反応したら、ちゃんと通知がくるか確認するだけです!

notification-test.jpeg

B6ED2F5E-2211-4236-B674-9E0E2C598BEC.png

無事動作しました!!
ちゃんとON/OFFにしたがって、通知を飛ばしてきてくれます!!
ヤッタゼ!!

これで「Ban-Ken Johnny」は完成です。
お手軽と言いながら、地味に時間かかっちゃいました...
これで家の留守中も安心です😄

編集後記

完成して使い始めたは良いものの、なぜか2分置きにGPIOからinputを検知してしまうという不具合が発生しました。
そのため2分置きに「侵入者ヲ検知シマシタ!🐺」と送信してきます。
鬼メッセージ送信機へと成り下がってしまったジョニーをどうにかして直したいのですが、ラズパイ初心者の私にはお手上げ状態です...
アドベントカレンダーの締め切りもあったので、すぐに届くセンサーをAmazonで選んで購入したのですが、そのセンサーが悪かったのかもしれません...
今見たら誰も評価してないし...

一応GitHubにも公開しているので、参考までに...
トリアエズ有識者ノ皆サマ、是非オ助ケヲ!!

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8