自宅外から自宅内のデバイスを簡単に操作する方法の紹介
本記事のまとめ
本記事で紹介したいことは、以下のとおりである。
・Slack APIを使用すると、自宅内外で通信するシステムが簡単に作れる
・Raspberry Piの起動時に、プログラムを自動で実行させる方法として、「rc.local」「cron」などの方法がある
作ったもの
別記事で解説した通り、電子的にスイッチを押せる装置を作った。しかしながら、単に電子的にスイッチを押せるだけでは便利ではなく、やはりネットワーク経由でスイッチを操作したい。もっと言うと、自宅内/外を問わず、というか外出先から自宅内のネットワークに接続された機器を制御したいというのが一般的であろう。
本記事では、SlackのAPIを用いることで、外出先と自宅内の機器との通信を簡単に実現する方法を紹介する。本記事の方法を用いることの利点は、以下の通りである。
・難しいネットワーク設定がいらない
(契約しているインターネット回線/ルーターの機種、などによらず同じ手順でできる)
・Slackの基本的な操作さえできれば、システムを使用することができる
(自分の家族など、知識がない人でも利用できる)
・セキュリティやアクセス権限周りを、Slackのアカウントで管理できる
・自宅内の機器への制御信号の送信/自宅内の機器からの応答(※)の確認ができる
※:Slackに添付可能な全てのファイル(画像・動画等)を、応答メッセージに添付できる
自宅外の環境から自宅内の機器を制御する仕組み
ウェブブラウジングをするときの通信のイメージ
自宅からウェブブラウジングなどをする際の一般的な通信のイメージを以下に示す。これは、自宅内にあるデバイスから、自宅外のサーバ等にリクエストを送信し、自宅外のサーバ等から自宅内のデバイスにレスポンスを返す様子を表したものである。
分かりやすく具体的な例で説明すると、自宅内にあるデバイス(スマホ等)でウェブページを閲覧するときは、目的とするウェブページのアドレス(○○.comなど)を指定し、httpのGETメソッドなどを用いて、ウェブページのサーバに対しウェブページの情報を送るようリクエストを送信する。
ここで、ユーザーのデバイスは、各種DNSサーバに問い合わせることで、目的とするウェブページのアドレス(○○.comなど)からグローバルIPアドレスに変換し、目的とするサーバ(図中の「どこかのサーバ」)へたどり着くことができる。
一方、目的とするサーバからのレスポンスは、リクエストを送信してきた人のデバイスのグローバルIPアドレスが分かっているため、簡単に送信することができる。またhttp通信等、一般的な通信で使用するポートは決まった値であるため、ルーター等でこれらの通信が遮られることはない。
自宅外から自宅内への通信の課題
次に、自宅外から自宅内のネットワークにアクセスする際のイメージを以下に示す。自宅外から自宅内のデバイスに直接通信をするには、以下の2つの課題がある。
①自宅のネットワークに接続された機器のグローバルIPアドレスを何らかの手段で取得する必要がある。
→一般的に、各家庭のグローバルIPアドレスは、一定期間ごとに変化する。よって、自宅外から自宅内にアクセスするには、定期的に変わるIPアドレスを、何らかのサーバ等を用いて調べる(※)か、プロバイダの提供するオプションプランに加入してIPアドレスを固定する(固定IPアドレス)必要がある。
②自宅ネットワークのモデム/ルーターに対して、①の通信をブロックしないよう設定する
→任意の通信を素通りさせることは、データを盗まれたり、自宅のネットワーク機器を乗っ取られたり、といったセキュリティ上の問題がある。
※:このあたりの課題を解決するサービスとして、[remote it](筆者はremote itなども利用している)など、便利なものが世の中にはある。が、ログインしないと接続先のアドレスが分からないなど、自分以外の家族でも簡単に使えるようなサービスは見当たらなかった。
→ただし「remote it」は、家庭内で動くサーバ等に手を加えず、自宅外からアクセスできるようにすることができる、という面で非常に便利である。我が家では、RPi-Cam-Web-Interface(Raspberry pi カメラの映像を遠隔で見たり、撮影・録画ができるとっても便利なシステム)を、remote itにより外出先からみられるようにしている。
(参考)
・remote it
https://ja.remote.it/
・RPi-Cam-Web-Interface
https://elinux.org/RPi-Cam-Web-Interface
電子工作をする多くの人は、自宅外から自宅内の装置へ通信を試みた経験があるかと思うが、上述のような課題、特に①の課題によりあきらめた経験があるのではないかと思う。
解決方法
以上の課題を簡単に解決する方法のイメージを以下に示す。これは、自宅内外に関わらず、簡単にアクセス可能なSlackのサーバを介して自宅の内外の通信を実現する様子を表したものである。
自宅内に消費電力の少ない装置(Raspberry Piなど)を設置・常時起動させ、Slack-APIを用いてSlackへの操作を監視させることで、自宅外からSlackの操作があったときに操作内容を把握することができる。これにより、数秒程度の遅れを許容できるような操作(例えば自宅内のリモコン/スイッチを操作する、など)であれば問題なく実現可能である。
Slack-APIを用いた自宅内外通信システムの作成方法
前置きが長くなったが、ここからSlack-APIを用いて自宅内外の通信を実現する方法を紹介したいと思う。なお、本記事は、「Slack-APIを使うと、簡単に自宅内外で通信ができるよ」「自分以外の家族などが簡単に使えるシステムが作れるよ」「テキストだけじゃなく、画像なんかも取得できるよ」ということを紹介することが本題であるため、詳細な手順は別途調べるか参考リンクを参照いただきたい。
STEP1:Slackのワークスペースを作成する
まず最初に、「自宅内外で通信するシステム」のアクセス権限を考慮し、Slackのワークスペースやチャネルを作成する。このワークスペースやチャネルのアクセス権限が、すなわち「自宅内外で通信するシステム」のアクセス権限となるため、システムの管理者はワークスペースやチャネルの管理者権限を有する必要がある。
STEP2:Slack-APIを利用するための設定を行う
以下のリンク先を参考に、Slack-APIに必要な権限の設定や、アクセストークンを取得する。
https://qiita.com/seratch/items/1a460c08c3e245b56441
必要となる権限は、たぶん以下のあたりだと思う(うろ覚え)。いらないやつも混じっていると思うので、適当に判断ください。
・channels:history
・channels:join
・channels:read
・chat:write
・commands
また、Slack-APIを利用するにあたり必要なアクセストークンを再確認する際は、以下の画像のように、「OAuth & Permissions」画面から確認できる(赤丸のリンクをクリックして出てくる画面の、赤で塗りつぶした部分に表示される)。
STEP3:「pip install slack_bolt」を実行する
タイトルそのままであるが、pythonからSlack APIを利用するためのパッケージをインストールする。具体的には、「slack_bolt」をインストールする。
STEP4:以下のプログラムを常時実行したままにする
我が家では、Raspberry Piで下に示すプログラムを実行することとした。なお、本プログラムはリンク先のページを参考に作成したものであり、玄関の施錠/開錠/状態の確認をするプログラムである。
https://qiita.com/seratch/items/1a460c08c3e245b56441
以下のプログラムを実行すると、次のような動作をする。
・Slackのワークスペースのいずれかのチャネルに「開」または「閉」の文字が含まれる書き込みが行われたとき、RaspberryPiはリモコンスイッチ制御装置(※)にシリアル通信で「Open」「Close」を送信する
→リモコンスイッチ制御装置は、「Open」「Close」の信号に応じて、スイッチを押す
・Slackのワークスペースにある「#玄関」チャネルのショートカットから、Open/Close指令を送る、または玄関の鍵の施錠状態を表す画像を送る
※:今どきの玄関のカギは、車のカギと似たようなリモコンキーから操作できるものがあり、大変便利である。なお、このリモコンスイッチ制御装置は、別の記事にて紹介した方法を応用して作成した。
Raspberry Piで、プログラムを自動的に起動する方法
本記事で紹介したような、常時起動しておきたいプログラムを自動的に実行する方法は、以下の2通りの方法が一般的なようである。
- 「/etc/rc.local」に実行したいコマンドを記載する
→ここに記載したコマンドが、Raspberry Pi起動時に1回だけ実行される
→別の方法の方が良いという話もあるが、個人的には一番設定が楽なのでお気に入り。 - 「cron」の設定を行う
→cronで設定した周期で、コマンドが定期的に実行される
→今回のような常時起動させておきたいプログラムでは、1の方法をとった方がよいことが多い。が、例えば「プログラムが異常終了することがある」場合に、cronを用いて一定時間ごとに死活監視を行い、再起動する、という方法もある。
その他、詳細なことや他の方法については詳しくないため、例えば以下のページなど、参考となる記事をご確認いただきたい。
https://qiita.com/karaage0703/items/ed18f318a1775b28eab4
app.py
#!/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の権限設定周りが結構難しくて面倒であるが、一回設定さえしてしまえば非常に簡単に自宅の内外で通信するシステムを構築できる。この方法は、スイッチのON/OFFをする、センサー/カメラの値を取得する、といった程度の用途であれば、十分実用的であると思う。
投稿者の人気記事
-
dangomushi115
さんが
2023/02/18
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する