Help us understand the problem. What is going on with this article?

スマートロックを自作してAlexaに鍵を開けてもらう。

今回作るもの

こんな感じで鍵が開きます。


はじめに

大まかな仕組み

hsikuimi.png
既存のサービスをいろいろつないで形にしています。

なぜスマートロックを作ろうかと思ったか?
  • 市販のものは値段が高い
  • レスポンスが悪い
  • 自由度が低い
  • 調べてみたら簡単にできそう
  • せっかくAlexa買ったのに使ってない気がする

説明とかどうでもいい人はここに鍵本体のソースが乗ってます。

使用したもの

ハードウェア
ソフトウェア
  • TeraTerm
  • Blynk
  • node-red

1.raspiの設定

今回使用するraspiはOTGケーブルでパソコンとつなぐだけでヘッドレス運用ができるのでとても便利です。
:paperclip:こちらのリンク を参考にしてセットアップしましょう。

セットアップ出来たらPythonの設定です。
なぜかPythonに標準搭載されているパッケージ管理ツールのpipがないので

RaspberryPi
~$ sudo apt install python3-dev
~$ sudo apt install python3-pip

と入力してpipを入れます。

RaspberryPi
~$ pip3 -V
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.5)

と出てれば成功です。
もし、アップグレードしてください。と言われたら

RaspberryPi
~$ python -m pip3 install --upgrade pip3

でアップグレードすることができます。

次に今回使うモジュールをインストールしていきます。
インストールが必要なモジュールは

  • slackbot
  • slacker
  • RPi.GPIO
  • datetme

です。
順番にインストールしていきましょう。

RaspberryPi
~$ pip3 install slackbot
~$ pip3 install slacker
~$ pip3 install RPi.GPIO
~$ pip3 install datetime

2.サーボを制御する

それではサーボを制御していきます。
主に使うのは、サーボモーターとRPi.GPIOです。

サーボモーターの配線は下記のようになっています。
98 (2).jpg

サーボモーターはPWMで制御するのでraspiのPWM出力ができるpinに信号線を接続しなければなりません。
また、:paperclip:今回使うサーボは4.8V以上で駆動するのでraspiの5Vのpinにつなぎましょう。

今回は、信号線をpin33にしています。
servo.png

コードです。

servo.py
import RPi.GPIO as GPIO
import time

# BOARDでpinで指定
GPIO.setmode(GPIO.BOARD)

#pin33を出力に設定
gp_out = 33
GPIO.setup(gp_out, GPIO.OUT)

#PMWを50Hzにセット
servo = GPIO.PWM(gp_out, 50)
servo.start(0)

GPIO.setup(gp_out, GPIO.OUT)
time.sleep(0.5)

#初期値
servo.ChangeDutyCycle(7.25)
time.sleep(0.5)

servo.ChangeDutyCycle(12)
time.sleep(0.5)

#位置の初期化
servo.ChangeDutyCycle(7.25)
time.sleep(0.5)

#サーボの出力ピンの設定をリセット
GPIO.cleanup(gp_out)

90度動いて元に戻るようにしてあります。動いたでしょうか?
GitHubのコードでは開けるための動きと閉めるときの動きを関数にしてlock.txtに状態を書き込んであります。
ついでにLEDも光らせてます。

3.botをつくる。

今回はPythonで簡単にbotを実装できるslackbotを使っていきます。
slackbotに関しては:paperclip:こちらの方の記事がとても分かりやすく参考になります。

GitHubから一部ですがコードを載せておきます。

key_plugin.py
@listen_to(u'(鍵|カギ|かぎ)+.*(開け|あけ|空け)+')
@respond_to(u'(鍵|カギ|かぎ)+.*(開け|あけ|空け)+')
@respond_to(u'(解錠)+')
@respond_to('(open)+.*(door)+')
@respond_to(u'(扉|トビラ|とびら)+.*(開け|あけ|空け)+')
def openKeyOrder(message, *something):
    userID = message.channel._client.users[message.body['user']][u'id']
    userName = message.channel._client.users[message.body['user']][u'name']

    # message.reply(userName)
    with open('/home/pi/slackbot/lock.txt', mode='r') as f:
        for row in f:
            key = int(row.strip())
            global lock
            lock = key

    if lock == 1:

        message.reply(u'わかりました。解錠します。')
        open_key()
        print(userName + "さんの命令でカギを開けます。")
    else:
        message.reply(u'鍵が開いているようです。')

lock.txtを読み込むことで鍵が開いているか閉まっているかを判断して条件分岐してます。

userIDはコマンドラインからメンション、DMをするために必要になるものです。
また、userNameは登録した名前になります。
これらを組み合わせてuserの制限をしていきます。

ユーサー制限
def openKeyOrder(message, *something):
    userID = message.channel._client.users[message.body['user']][u'id']
    userName =message.channel._client.users[message.body['user']][u'name']

    #message.reply(userName)
    my_attachments = {"fallback": "test", \
                      "actions": [{\
                                   "type": "button", \
                                   "text": "はい", \
                                   "url": "https://slack.com/api/chat.postMessage?token="+commander_token+"&channel="+smart_lock_ID+"&text=add%20%3C%40"+userID+"%3E%20instant&as_user=Ture&pretty=1"\
                                   },\
                                  {\
                                   "type": "button", \
                                   "text": "いいえ", \
                                   "url": "https://slack.com/api/chat.postMessage?token="+commander_token+"&channel="+smart_lock_ID+"&text=rm%20%3C%40"+userID+"%3E%20instant&as_user=Ture&pretty=1"}]}
    attachment = [my_attachments]

    if userID in root_user or userID in instant_user:
        message.reply("権限があります。")
    else:
        message.reply("権限がありません。")
        slacker.chat.post_message(master_ID,'<@'+userID+'>さんが鍵を開けようとしました。\n許可しますか?', as_user=True,  attachments=attachment)

またまたGitHubから抜粋ですが、root_user,instant_userのリストにuserIDがないと鍵が開けられないようにしています。
また、権限がないuserが鍵を開けようとすると自分宛に下のようなメッセージが送られてきます。
slackbutton_LI.jpg
再起動するとinstant_userのリストは初期化されるので定期的に再起動させるようにすると勝手に利用制限がかかって楽です。

4.ボタンで鍵を開ける。

さすがに室内にいるときにslackで「鍵開けて」というのは不便なので本体にボタンをつけて開け閉めできるようにしましょう。
ということで、pinを入力に設定して信号を受け取りましょう。

push_button.png

このように接続するとボタンを押すと8pinに入力信号が入ります。
入力信号を受け取ったときにサーボを動かす関数を発火させられれば鍵が開きます。
slackbotのbot.pyに細工をして同時に起動させましょう。
GitHubからですが載せておきます。
また、今回は入力を11pinにしています。

bot.py
def check_button():
    while True:
        with open('/home/pi/slackbot/lock.txt', mode='r') as f:
            for row in f:
                key = int(row.strip())
                lock = key

        if GPIO.input(sw1_in) == 1 and lock == 1:
            open_key()

        elif GPIO.input(sw1_in) == 1 and lock == 0:
            close_key()

    servo.stop()
    GPIO.cleanup()

def main():
    bot = Bot()
    # ボタン監視用のスレッドを起動する
    th_me = threading.Thread(target=check_button, name="th_check_button")
    th_me.setDaemon(True)
    th_me.start()
    try:
        slacker.chat.post_message(c_name, '起動しました。', as_user=True)
        # botを起動する
        bot.run()
    except Exception as e:
        print(e)


if __name__ == "__main__":
    print('start slackbot')
    main()

これでslackbotとボタン監視用のスレッドが同時に立ち上がってボタンでもslackからでも開けられるようになりました。

5.Alexaに鍵を開けてもらう。

仕組みは単純でAlexaに「鍵開けて」と言うとSlack上に「鍵開けて」と送信することができればよいわけです。
そのためにNode-redと:paperclip:Alexa Home Skill Bridgeを使います。
Node-redの導入は:paperclip:こちらを参考にセットアップしていきます。
Alexa Home Skill Bridgeの導入は:paperclip:こちらの方のものがわかりやすく参考になりました。

今回追加で使うノードは3つです。
alexa用のノード
alexa_node.png
slack用のノード
slack_node.png
タイムスタンプ用のノード(なくてもいい)
moment_node.png

Alexa Home Skill Bridgeは次のように設定します。
home.png
これでAlexa上でON/OFFをいじれるのでそれに応じてslackに送るメッセージを分岐させます。
AlexaアプリからNode-redスキルを有効化するここを忘れずに!!

これらをいい感じ組み合わせてこんな感じにします。
node-red.png

一応フローを:paperclip:ここに置いておきます。
これをraspi上で起動させておけばいい感じになりますね。

また、Alexaアプリの定型アクションをこんな感じにしておくと自然に開けることができます。
iOS の画像.png

6.再起動時に自動起動する.

raspiのOSであるRaspbianはLinux系のOSなのでcrontabでいろいろいじれます。
ついでにみんなに再起動することを知らせるためにbotにしゃべらせましょう!

bye.py
# coding: utf-8
import subprocess
from slacker import Slacker
from slackbot_settings  import API_TOKEN

# API token
token = API_TOKEN

# 投稿するチャンネル名
c_name = '自分のチャンネル'

#投稿
slacker = Slacker(token)
slacker.chat.post_message(c_name, '1分後に再起動します。', as_user=True)
subprocess.run(('/sbin/shutdown','-r','1'))

そしてbye.pyをcrontabで定期実行しましょう!

terminal
crontab -e

と入力してから選択肢がでると思うので"nano"を選択して下記のように書いてみてください。

crontab
@reboot sudo python3 hoge/bot.py

59 3 * * * sudo python3 hoge/bye.py

終了するときは'Ctrl + x'→'y'です。
これで再起動時にbotが起動して3:59にメッセージが送信、1分後に再起動します。
再起動するとinstant_userが初期化されるのでinstantらしくてちょうどいいですね。

7.最後に

一番最初の動画でお気づきかもしれませんが、うちの鍵が固くSG92Rではトルクがたりず、RaspberryPi Zero Wでは電流が足りないという事態となり泣く泣くいろいろ買い換えました...

それに伴いこんなこともありましたね…


slackに特定のメッセージを送信すれば鍵を開けることができるので100均一で売ってるbluetoothボタンを使って



こんなものや

:paperclip:こんなものを組み合わせると応用が利きますね。
またAPIを叩いてslackにメッセージを送信すれば鍵が開くのでこんな感じでスマホからも簡単にできますね。
iOS の画像.jpg
ショートカット機能万歳!!

参考にした記事

Raspberry Piでスマートロックをつくった

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away