Slack APIを用いたスマートホームアプリ作成(玄関施錠/開錠システム)

Python

始めに

本記事は、以下で公開した記事に関連する補足情報をまとめるため、以下のリンク先と同様の内容を転記したものです。

elchika.com/article/612f4e01-33ee-4292-87b4-bd6bbf84c2d5/

作ったもの

別記事で解説した通り、電子的にスイッチを押せる装置を作った。しかしながら、単に電子的にスイッチを押せるだけでは便利ではなく、やはりネットワーク経由でスイッチを操作したい。もっと言うと、自宅内/外を問わず、というか外出先から自宅内のネットワークに接続された機器を制御したいというのが一般的であろう。

本記事では、Slack-APIを用いることで、外出先と自宅内の機器との通信を簡単に実現する方法を紹介する。本記事の方法を用いることの利点は、以下の通りである。

難しいネットワーク設定がいらない
(契約しているインターネット回線/使用しているルーターの機種、などによらず同じ手順でできる)
Slackの基本的な操作さえできれば、システムを使用することができる
(自分の家族など、知識がない人でも利用だけはできる。)
・セキュリティ面の設定・対策などを、自分でやる必要があまりない
(基本的にSlackにお任せすることができる)
・自宅内の機器への制御信号の送信/自宅内の機器からの応答(※)の確認ができる
※:テキストだけでなく、Slackに添付可能な全てのファイル(画像・動画等)を応答として利用できる。

自宅外の環境から自宅内の機器を制御する仕組み

ウェブブラウジングをするときの通信のイメージ

自宅からウェブブラウジングなどをする際の一般的な通信のイメージを以下に示す。これは、自宅内にあるデバイスから、自宅外のサーバ等にリクエストを送信し、自宅外のサーバ等から自宅内のデバイスにレスポンスと返す様子を表したものである。

分かりやすく具体的な例で説明すると、自宅内にあるデバイス(スマホ等)でウェブページを閲覧するときは、目的とするウェブページのアドレス(○○.comなど)を指定し、httpのGETメソッドなどを用いて、ウェブページのサーバに対しウェブページの情報を送るようリクエストを送信する。

ここで、ユーザーのデバイスは、各種DNSサーバに問い合わせることで、目的とするウェブページのアドレス(○○.comなど)からグローバルIPアドレスに変換し、目的とするサーバ(図中の「どこかのサーバ」)へたどり着くことができる。

一方、目的とするサーバからのレスポンスは、リクエストを送信してきた人のデバイスのグローバルIPアドレスが分かっているため、簡単に送信することができる。またhttp通信等、一般的な通信で使用するポートは決まった値であるため、ルーター等でこれらの通信が遮られることはない。

自宅からウェブブラウジングするときの通信のイメージ

自宅外から自宅内への通信の課題

次に、自宅外から自宅内のネットワークにアクセスする際のイメージを以下に示す。自宅外から自宅内のデバイスに直接通信をするには、以下の2つの課題がある。
①自宅のネットワークに接続された機器のグローバルIPアドレスを何らかの手段で取得する必要がある。
→一般的に、各家庭のグローバルIPアドレスは、一定期間ごとに変化する。よって、自宅外から自宅内にアクセスするには、定期的に変わるIPアドレスを、何らかのサーバ等を用いて調べる(※)か、プロバイダの提供するオプションプランに加入してIPアドレスを固定する(固定IPアドレス)必要がある。
②自宅ネットワークのモデム/ルーターに対して、①の通信をブロックしないよう設定する
→任意の通信を素通りさせることは、データを盗まれたり、自宅のネットワーク機器を乗っ取られたり、といったセキュリティ上の問題がある。

※:このあたりの課題を解決するサービスも世の中にはある。が、いちいちログインしないと接続先のアドレスが分からなかったりして、自分以外の家族でも使えるようにしようと考えると、とても難しかったりする。
ja.remote.it/

電子工作をする多くの人は、自宅外から自宅内の装置へ通信を試みた経験があるかと思うが、上述のような課題、特に①の課題によりあきらめた経験があるのではないかと思う。

自宅外から自宅内にアクセスするときの課題

解決方法

以上の課題を簡単に解決する方法のイメージを以下に示す。これは、自宅内外に関わらず、簡単にアクセス可能なSlackのサーバを介して自宅の内外の通信を実現する様子を表したものである。

自宅内に消費電力の少ない装置(Raspberry Piなど)を設置・常時起動させ、Slack-APIを用いてSlackへの操作を監視させることで、自宅外からSlackの操作があったときに操作内容を把握することができる。これにより、数秒程度の遅れを許容できるような操作(例えば自宅内のリモコン/スイッチを操作する、など)であれば問題なく実現可能である。

Slackを介することで自宅内の機器と通信

Slack-APIを用いた自宅内外通信システムの作成方法

前置きが長くなったが、ここからSlack-APIを用いて自宅内外の通信を実現する方法を紹介したいと思う。なお、本記事は、「Slack-APIを使うと、簡単に自宅内外で通信ができるよ」「自分以外の家族などが簡単に使えるシステムが作れるよ」「テキストだけじゃなく、画像なんかも取得できるよ」ということを紹介することが本題であるため、詳細な手順は別途調べるか参考リンクを参照いただきたい。

STEP1:Slackのワークスペースを作成する
まず最初に、「自宅内外で通信するシステム」のアクセス権限を考慮し、Slackのワークスペースやチャネルを作成する。このワークスペースやチャネルのアクセス権限が、すなわち「自宅内外で通信するシステム」のアクセス権限となるため、システムの管理者はワークスペースやチャネルの管理者権限を有する必要がある。

STEP2:Slack-APIを利用するための設定を行う
以下のリンク先を参考に、Slack-APIに必要な権限の設定や、アクセストークンを取得する。
qiita.com/seratch/items/1a460c08c3e245b56441

必要となる権限は、たぶん以下のあたりだと思う(うろ覚え)。いらないやつも混じっていると思うので、適当に判断ください。
・channels:history
・channels:join
・channels:read
・chat:write
・commands

また、Slack-APIを利用するにあたり必要なアクセストークンを再確認する際は、以下の画像のように、「OAuth & Permissions」画面から確認できる。

赤丸のリンクをクリックして出てくる画面の、赤で塗りつぶした部分に表示される。

STEP3:以下のプログラムを常時実行したままにする

我が家では、Raspberry Piで以下のプログラムを実行することとした。なお、本プログラムは以下のページを参考に作成した、玄関の施錠/開錠/状態の確認をするプログラムである。
qiita.com/seratch/items/1a460c08c3e245b56441

以下のプログラムを実行すると、次のような動作をする。
・Slackのワークスペースのいずれかのチャネルに「開」または「閉」の文字が含まれる書き込みが行われたとき、RaspberryPiはリモコンスイッチ制御装置(※)にシリアル通信で「Open」「Close」を送信する
→リモコンスイッチ制御装置は、「Open」「Close」の信号に応じて、スイッチを押す
・Slackのワークスペースにある「#玄関」チャネルのショートカットから、Open/Close指令を送る、または玄関の鍵の施錠状態を表す画像を送る

※:今どきの玄関のカギは、車のカギと似たようなリモコンキーから操作できるものがあり、大変便利である。なお、このリモコンスイッチ制御装置は、別の記事にて紹介した方法を応用して作成した。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#■基本的に下記のページの手順に沿って作成
#https://qiita.com/seratch/items/1a460c08c3e245b56441
#・必要なライブラリのインストールは、「pip install slack_bolt」でOK
#・以下のコマンドは、自分の環境ではうまく環境変数が設定できなかった
#>export SLACK_APP_TOKEN=xapp-<自分のトークンの値>
#>export SLACK_BOT_TOKEN=xoxb-<自分のトークンの値>
#→なので、「os.environ["SLACK_APP_TOKEN"]」の部分に直接トークンを記載した(本当はよくない)
#・「Enable Socket Mode」をONにした時に表示されるアプリレベルトークンを、
#”再度”取得する手順↓
#https://deep.tacoskingdom.com/blog/156

import logging
import os
from slack_sdk import WebClient
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import serial

logging.basicConfig(level=logging.DEBUG)

SLACK_APP_TOKEN="★xapp-で始まるトークンを入力★"
SLACK_BOT_TOKEN="★xoxb-で始まるトークンを入力★"
Genkan_ch="★当該チャネルのURLの最後「/」以降の文字列を入力★"

#シリアル通信をする場合に必要な処理:シリアル通信開始の処理
#各自の環境に応じてシリアル通信のポート名(「/dev/ttyUSB1」のところ)を設定すること
ser = serial.Serial('/dev/ttyUSB1', 115200)

app = App(token=SLACK_BOT_TOKEN)
client = WebClient(SLACK_BOT_TOKEN)

# イベント API:受信したメッセージに含まれる文字列に応じた処理を実行
@app.message("開")
def handle_messge_evnts(message, say):
	ser.write("Open")#シリアル通信をする場合に必要な処理:コマンドをシリアル通信で送信
	say(f" <@{message['user']}> 開錠しました")
@app.message("閉")
def handle_messge_evnts(message, say):
	ser.write("Close")#シリアル通信をする場合に必要な処理:コマンドをシリアル通信で送信
	say(f" <@{message['user']}> 施錠しました")

# ショートカットとモーダル:ショートカット操作に応じた処理を実行
@app.shortcut("unlock_door")
def handle_shortcut(ack, body: dict, client: WebClient, say):
	ack()
	ser.write("Open")#シリアル通信をする場合に必要な処理:コマンドをシリアル通信で送信
	say(channel=Genkan_ch, text="unlocked")

@app.shortcut("lock_door")
def handle_shortcut(ack, body: dict, client: WebClient, say):
	ack()
	ser.write("Close")#シリアル通信をする場合に必要な処理:コマンドをシリアル通信で送信
	say(channel=Genkan_ch, text="locked")
	
# ショートカットとモーダル:TODO:カギの開閉状況を何らかの方法で検出して送信
@app.shortcut("check_door")
def handle_shortcut(ack, body: dict, client: WebClient, say):
	ack()
	#say(channel=Genkan_ch, text="checked")
    #TODO:玄関の写真を撮影する処理
	#添付ファイルを送信する方法の例
	upload_text_file = client.files_upload(
		channels=Genkan_ch,
		title="Test text data",
		file="./genkan.jpg",
		initial_comment="checked:",
	)

if __name__ == "__main__":
	handler = SocketModeHandler(app,SLACK_APP_TOKEN)
	handler.start()


まとめ

SlackのAPIの権限設定周りが結構難しくて面倒であるが、一回設定さえしてしまえば非常に簡単に自宅の内外で通信するシステムを構築できる。

コメント