2019

12

30

Raspberry PiとMQTT使ってマンションのオートロックを解錠してやんよ!!!(前編)

タグ: | |

Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

前々から興味のあったラズパイを使ってIoTしてみたい!
それと自宅にスマートロックを導入したもののマンション自体のオートロックを解錠させるのに結局カギは持ち歩かなきゃならないので
何とか鍵を持たずスマホだけでエントランスから自室までの解錠したい!!という個人的欲求を年末年始で解決してみた。

今回の目的と使用アイテム&サービス

きっかけと目的

自宅マンションのオートロックは通常通りにエントランスから鍵を使うか、インターホンで部屋番号を呼び出し、自室から「解錠」ボタンを押さないと開かない。

これをSwitcBotというスイッチガジェットをベースに考案。
別途4000円ほどで専用ハブを購入すればwifi接続して外部からでも操作できるみたいですが、ここをできるだけ自力IoTしてみます。

今回使用するアイテムとサービス

  • ・GoogleHomeMini (起動トリガー)
  • ・IFTTT (webhook)
  • ・Beebotte (MQTTブローカー)
  • ・Raspberry Pi 3B (中継マシン)
  • ・SwitchBot (スイッチ役)

おおまかな流れ

ラズパイ設定

今回は以前から持っていたもののなかなか触る機会がなかったので改めてセットアップ。
初期セットアップなどはいろんな所で紹介されているのでそちらを参考に。

とりあえず最低限としてOSにはRaspbianを入れてメインPCからSSH接続できるように設定
今回は安定して外部からの接続ができるようにwifiではなくLANケーブルを直接繋げてます。

MQTT(Beebotte)の設定

今回初めて利用しましたがMQTTってなんぞや?
Message Queue Telemetry Transportの略で、publish/subscribeモデルという仕組みに基づいてつくられた軽量なメッセージプロトコルです。
ネットワークが不安定な場所や、性能が低いデバイスでも動くように軽量化されているのが特徴で、TCP/IP ネットワークをベースに作られています。

今回はこのMQTTを利用できるサービスを利用します。
beebotte Beebotte

新規チャンネルの作成

アカウントを作成したら右上メニューから「Channels」を選択して
「Create New」ボタンをクリック。

上記のようなチャンネル名とリソース名を設定して、「Create Channel」ボタンで新規作成します。
リソース項目数などは後から編集はできないのでオートロック解除の仕様上、まずインターホンの応答ボタンを押さないと解錠ボタンのプッシュが効かない様なパターンもあるので、
1アクション=1ボットとして、それぞれの動作に複数の操作(スイッチ)が必要な場合はそれぞれ用にリソース数も設定が必要です。

チャンネルを作成すると専用のチャンネルトークンが作成されます(token_○○○○

作成したチャンネルは右メニューの「Console」からテスト送信ができます。
Secret Keyが必要ですが、これがどこにあるかわかりにくいんですがここにあります

Beebotte証明書をダウンロード

ラズパイからメイン処理でBeebotteへリクエストをするためのBeebotteの証明書をダウンロードしておきます。

https://beebotte.com/certs/mqtt.beebotte.com.pem

IFTTTの設定

続いてスマートスピーカーへのトリガーの設定ですが、こちらは有名なIFTTTを利用します。IFTTT

アプレットの新規作成

わかりにくいですが、画像の「+」をクリックして新規アプレットを作成します。

Thisの作成

まずは"コレ"から"ソレ"の部分の"コレ"を設定します。

「This」をクリックしたら対応サービス一覧画面に変わるので「google assistant」と入力し、
トリガー項目一覧が出るのでその中から「Say a simple phrase」をクリック。
初回はGoogleアカウントのログインが必要だったと思います

上記の様な発動条件を設定して「Create trigger」をクリック

Thatの作成

次は"コレ"から"ソレ"の部分の"ソレ"を設定します。

「That」をクリックしたら対応サービス一覧画面に変わるので「web hooks」と入力し、「Make a web request」をクリック。

URLにはBeebotteで作成したリクエストURLを入力して各項目は上記の様に設定して「Create action」をクリック

Bodyの部分はGoogle Homeアプリで設定してあるデバイス情報がアプレット作成後に自動で入ると思います。

# GoogleHomeで設定済みのbody設定例
{"data":[{"room":"living","device":"release","action":"on"}]}

アプレット名をわかりやすい名前に変更してFinishをクリックするとアプレットが作成されます。

これでIFTTTを経由して"This"(GoogleHomeへの音声命令)から"That"(Beebotteへの外部リクエスト)が開通して、自宅のラズパイにリクエストを送る準備ができました。

ラズパイのメイン処理ファイルを作成

いよいよラズパイで動作させるメイン処理です。ラズパイ本体をディスプレイに繋いでターミナルアプリを起動するか、メインPCからSSH接続してファイルを作成します。

各パッケージのインストール

※ラズパイ上のpythonはデフォで2系のため、pyenv経由で3系を入れてる前提です。
そしてpython3系でBluetoothを使うのに必要なパッケージを入れます。

# Pyenv関連パッケージのインストール
$ sudo apt-get install -y git openssl libssl-dev libbz2-dev libreadline-dev libsqlite3-dev
# pyenv設定
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv
$ vim .bash_profile
$ export PYENV_ROOT="$HOME/.pyenv"
$ export PATH="$PYENV_ROOT/bin:$PATH"
$ eval "$(pyenv init -)"
# Python3系のインストールとデフォ設定
$ pyenv install 3.6.6
$ pyenv global 3.6.6
$ python --version
--> 3.6.6
# 必要なパッケージをインストール
$ pip install pybluez
$ apt-get install libboost-python-dev
$ apt-get install libboost-thread-dev
$ pip install gattlib

gattlibのインストールが色々と手間取ると思いますが、自分の環境は以下のコマンドで成功しました

pip download gattlib
tar xvzf ./gattlib-0.20150805.tar.gz
cd gattlib-0.20150805/
sed -ie 's/boost_python-py34/boost_python-py36/' setup.py
pip install .

コマンドでラズパイからSwitchBotを操作

上記設定が完了したら実際にSwitchBotのボタンが押されるか確認してみます。

# 操作したいスイッチのMACアドレスで1回プッシュ
sudo python switchbot_py3.py -d xx:xx:xx:xx:xx:xx Press

MACアドレスはSwitchBotアプリのスイッチ一覧から歯車マークで各デバイス情報の右上アイコンの情報ページに記されています。

メインファイルの作成

主な変更点はトークン情報とSwitchBot本体のMACアドレス部分です。

各ファイルディレクトリは"/usr/local/lib/switchbot/"をベースに置いていきます。 事前にダウンロードしたBeebotteの証明書も同ディレクトリ内に置いておきます。

python
import os
import sys
import time
import json
import logging
import time

import paho.mqtt.client as mqtt
import binascii
from bluepy.btle import Peripheral

TOKEN = "トークン情報"
HOSTNAME = "mqtt.beebotte.com"
PORT = 8883
TOPIC = "Entrance/release"
CACERT = "/usr/local/lib/switchbot/mqtt.beebotte.com.pem"

SWITCH_MAC = "SwitchBotのMACアドレス"
SWITCH_NAME = "release"

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG,format='%(asctime)s- %(name)s - %(levelname)s - %(message)s')

_LOGGER = logging.getLogger(__name__)

class SwitchBot:
    def __init__(self, name, mac):
        self._mac = mac
        self._name = name

    def action(self, act):
        for connection in range(1,6):
            try:
                p = Peripheral(self._mac, "random")
            except:
                _LOGGER.error('Connection attempt failed after {connection} tries')
                time.sleep(1)
                continue
            break
        else:
            _LOGGER.error('Connection to Switchbot failed')

        try:
            hand_service = p.getServiceByUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b")
            hand = hand_service.getCharacteristics("cba20002-224d-11e6-9fb8-0002a5d5c51b")[0]
            if act == "on":
                hand.write(binascii.a2b_hex("570101"))
            elif act == "off":
                hand.write(binascii.a2b_hex("570102"))
            elif act == "press":
                hand.write(binascii.a2b_hex("570100"))
            p.disconnect()
        except:
            _LOGGER.error("Cannot connect to switchbot.")

switch_bot = SwitchBot(SWITCH_NAME, SWITCH_MAC)


def on_connect(client, userdata, flags, respons_code):
    print('status {0}'.format(respons_code))
    client.subscribe(TOPIC)

def on_message(client, userdata, msg):
    data = json.loads(msg.payload.decode("utf-8"))["data"][0]
    data = {key:value.strip() for key, value in data.items()}
    if "action" in data.keys():
        switch_bot.action(data["action"])

def fork():
    pid = os.fork()
    if pid > 0:
        f = open('/var/run/switchbotd.pid','w')
        f.write(str(pid)+"\n")
        f.close()
        sys.exit()

    if pid == 0:
        main()

def main():
    client = mqtt.Client()
    client.username_pw_set("token:%s"%TOKEN)
    client.on_connect = on_connect
    client.on_message = on_message
    client.tls_set(CACERT)
    client.connect(HOSTNAME, port=PORT, keepalive=60)
    client.loop_forever()

if __name__=='__main__':
    fork()

このファイルを実際に動かしてswitchbotが動けば準備はほぼ完了です。

ラズパイにサービス登録設定

ほぼ完了ではありますが、現状ではファイルをいちいち実行しないと動かないので
一例の処理をサービスとして登録して、ラズパイを起動したらそのままサービスも起動する様に設定します。

# ディレクトリ構造をラズパイにデプロイ
$ tree /usr/local/lib/switchbot/

サービス設定ファイル

[Unit]
Description=SwitchBot

[Service]
ExecStart=/usr/bin/python /usr/local/lib/switchbot/switchbot.py
Restart=always
Type=forking
PIDFile=/var/run/switchbot.pid

[Install]
WantedBy=multi-user.target

自動起動設定

# サービス再読み込み
$ sudo systemctl daemon-reload
# switchbotデーモンスタート
$ sudo systemctl start switchbot
# OS起動時有効設定
$ sudo systemctl enable switchbot
# 状態確認
$ systemctl status switchbot
# 自動起動
$ systemctl is-enabled switchbot
enabled

もしエラーが出る場合はラズパイを再起動したり、サービスファイルのPIDFileパスの権限を変更するなど試してみてください。
自分の場合は一度PIDFileの行をコメントアウトしたらエラーが出なくなったので、その後修正しました。

前編まとめ

これでGoogle Homeから音声で操作できるようになりましたが、室内からの操作だったら最初からSwitchBotのアプリでできるよ、正直意味ないよって事でちゃんと外からでもオートロックを施錠できるように
後編では室外からでも操作できるようにします。

Raspberry PiとMQTT使ってマンションのオートロックを解錠してやんよ!!!(後編)
Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

トップへ