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

#今回作るもの
こんな感じで鍵が開きます。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">こんな感じ <a href="https://t.co/gBETpm7GiU">pic.twitter.com/gBETpm7GiU</a></p>&mdash; dai💸 (@dainbe1231) <a href="https://twitter.com/dainbe1231/status/1055815991433646081?ref_src=twsrc%5Etfw">2018年10月26日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#はじめに
######大まかな仕組み
![hsikuimi.png](https://qiita-image-store.s3.amazonaws.com/0/296935/e8a68296-40ca-ef16-6c60-821d875f8821.png)
既存のサービスをいろいろつないで形にしています。

######なぜスマートロックを作ろうかと思ったか？

* 市販のものは値段が高い
* レスポンスが悪い
* 自由度が低い
* 調べてみたら簡単にできそう
* せっかくAlexa買ったのに使ってない気がする

説明とかどうでもいい人は[ここに](https://github.com/dainbe/smartkey)鍵本体のソースが乗ってます。

#使用したもの
#####ハードウェア
* windows10 PC
* [raspberry pi zero WH (長いので以下raspi)](https://www.switch-science.com/catalog/3646/)
* [サーボモーター(SG92R)](http://www.aitendo.com/product/17146)
* [タミヤ 楽しい工作シリーズ ユニバーサルアームセット](http://amzn.asia/d/9WFcEU7)
* OTGケーブル Micro-B
* 100均で買えるリードスイッチ付きのライト
* 基板、LED、抵抗、etc.

#####ソフトウェア
* TeraTerm
* Blynk
* node-red

#1.raspiの設定
今回使用するraspiはOTGケーブルでパソコンとつなぐだけでヘッドレス運用ができるのでとても便利です。
:paperclip:[こちらのリンク](https://qiita.com/msquare33/items/dc6fc9098f50d9b9dbf2) を参考にしてセットアップしましょう。

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

```terminal:RaspberryPi
~$ sudo apt install python3-dev
~$ sudo apt install python3-pip
```
と入力してpipを入れます。

```terminal:RaspberryPi
~$ pip3 -V
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.5)
```
と出てれば成功です。
もし、アップグレードしてください。と言われたら

```terminal:RaspberryPi
~$ python -m pip3 install --upgrade pip3
```
でアップグレードすることができます。

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

* slackbot
* slacker
* RPi.GPIO
* datetme

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

```terminal:RaspberryPi
~$ pip3 install slackbot
~$ pip3 install slacker
~$ pip3 install RPi.GPIO
~$ pip3 install datetime
```
#2.サーボを制御する
それではサーボを制御していきます。
主に使うのは、サーボモーターとRPi.GPIOです。

サーボモーターの配線は下記のようになっています。
![98 (2).jpg](https://qiita-image-store.s3.amazonaws.com/0/296935/8acda8fe-a4ca-843f-ea93-7e3c722e4419.jpeg)

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

今回は、信号線をpin33にしています。
![servo.png](https://qiita-image-store.s3.amazonaws.com/0/296935/7b13d97a-5205-50ec-38f2-e920ba031568.png)

コードです。

```python: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:[こちら](https://qiita.com/sukesuke/items/1ac92251def87357fdf6)の方の記事がとても分かりやすく参考になります。

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

```python: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の制限をしていきます。

```python:ユーサー制限
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](https://qiita-image-store.s3.amazonaws.com/0/296935/7363986d-ada3-2d61-5f58-d186e9026ea5.jpeg)
再起動するとinstant_userのリストは初期化されるので定期的に再起動させるようにすると勝手に利用制限がかかって楽です。

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

![push_button.png](https://qiita-image-store.s3.amazonaws.com/0/296935/6c6ecb5b-7544-afad-4952-e02833d190d8.png)

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

```python: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](https://alexa-node-red.bm.hardill.me.uk/)を使います。
Node-redの導入は:paperclip:[こちら](https://nodered.jp/docs/hardware/raspberrypi)を参考にセットアップしていきます。
Alexa Home Skill Bridgeの導入は:paperclip:[こちらの](https://qiita.com/kikuzo/items/753b5065dde9633bda18)方のものがわかりやすく参考になりました。

今回追加で使うノードは3つです。
alexa用のノード
![alexa_node.png](https://qiita-image-store.s3.amazonaws.com/0/296935/91d42b4c-f7db-5f20-967c-149682f259d8.png)
slack用のノード
![slack_node.png](https://qiita-image-store.s3.amazonaws.com/0/296935/4c0f9fd8-23e6-66a3-95df-70f0037f9257.png)
タイムスタンプ用のノード(なくてもいい)
![moment_node.png](https://qiita-image-store.s3.amazonaws.com/0/296935/eb1f3c52-dea5-2545-1dfa-55b382774a8c.png)

Alexa Home Skill Bridgeは次のように設定します。
![home.png](https://qiita-image-store.s3.amazonaws.com/0/296935/19e161f2-94de-69d0-3fee-84d5c2336231.png)
これでAlexa上でON/OFFをいじれるのでそれに応じてslackに送るメッセージを分岐させます。
AlexaアプリからNode-redスキルを有効化するここを忘れずに!!

これらをいい感じ組み合わせてこんな感じにします。
![node-red.png](https://qiita-image-store.s3.amazonaws.com/0/296935/5d77f5dd-90aa-c36c-1f2a-6e9c2f4c731d.png)

一応フローを:paperclip:[ここに](https://gist.github.com/dainbe/51b924cdfcbfca72316786fda1fdd207)置いておきます。
これをraspi上で起動させておけばいい感じになりますね。

また、Alexaアプリの定型アクションをこんな感じにしておくと自然に開けることができます。
<img width="375" alt="iOS の画像.png" src="https://qiita-image-store.s3.amazonaws.com/0/296935/4f18785f-f4a4-a4c3-091b-4b556909e486.png">
#6.再起動時に自動起動する.
raspiのOSであるRaspbianはLinux系のOSなのでcrontabでいろいろいじれます。
ついでにみんなに再起動することを知らせるためにbotにしゃべらせましょう!

```python: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:terminal
crontab -e
```
と入力してから選択肢がでると思うので"nano"を選択して下記のように書いてみてください。

```terminal: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では電流が足りないという事態となり泣く泣くいろいろ買い換えました...

それに伴いこんなこともありましたね…
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">やる気あんのかこいつ <a href="https://t.co/cH4ySwGmYn">pic.twitter.com/cH4ySwGmYn</a></p>&mdash; dai💸 (@dainbe1231) <a href="https://twitter.com/dainbe1231/status/1050737824696160257?ref_src=twsrc%5Etfw">2018年10月12日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


slackに特定のメッセージを送信すれば鍵を開けることができるので100均一で売ってるbluetoothボタンを使って
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">こんなん <a href="https://t.co/VFaecuQ0f5">pic.twitter.com/VFaecuQ0f5</a></p>&mdash; dai💸 (@dainbe1231) <a href="https://twitter.com/dainbe1231/status/1049280128738443264?ref_src=twsrc%5Etfw">2018年10月8日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
こんなものや

:paperclip:[こんな](http://blog.livedoor.jp/victory7com/archives/48316461.html)ものを組み合わせると応用が利きますね。
またAPIを叩いてslackにメッセージを送信すれば鍵が開くのでこんな感じでスマホからも簡単にできますね。
<img width="200" alt="iOS の画像.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/296935/7c5111b1-4687-b1e4-73f6-3b472af1ae43.jpeg">
ショートカット機能万歳!!

##追記(2020/01/23)
RaspberryPiやめてM5Stack(ESP32)で作り直しました。
[M5Stackでスマートロックを作った](https://qiita.com/dainbe/items/9c21f82c245dacd72876#_reference-e7963a799ffe840ca230)

#####参考にした記事
[Raspberry Piでスマートロックをつくった](https://qiita.com/undo0530/items/669b6ee6b2277ea64b33)