今回は、自宅のラズパイにAWS IoT Coreを入れ、lambdaを用いてカメラを制御するということをやったので、その設定の備忘録を残します。
自分の家のカメラとAWSが繋がるようになれば、何かできることが増えそうでワクワクしています
アーキテクチャ
以下のように、lambdaから自宅のカメラを制御する構図です。

各種機器
ラズパイ→model4B 4GB
カメラ→TP-link Tapo C210(pytapoというモジュールを使用して制御を実装しました)
https://github.com/JurajNyiri/pytapo
IoT Coreの設定
最初にこのような画面が出てきます

手順にある通り、ラズパイのターミナルからpingを叩いて、正常に叩けてるか確認をします。
そしたら「新しいモノを作成」でモノの名前を入力し、次へ進みます

OSとSDKを聞かれるので適したものを選択して、次へを押します。自分はLinuxとpythonを選択しました

完了したら接続キットをダウンロードします。

これらをラズパイで解凍する必要があるので、zipファイルを自分のPCからscpコマンドで転送します。
scp -r connect_device_package.zip pi@ラズパイのIPアドレス:~/
そしてラズパイ上で以下を実行して解凍します。
unzip connect_device_package.zip
解凍できたら、start.shというファイルがあるはずなので実行権限を与えます
chmod +x start.sh
もし、pythonをSDKとして選択していた場合、仮想環境の構築が必要になります!
以下コマンドで仮想環境を構築し、アクティベートします
python -m venv venv
source venv/bin/activate
ラズパイ上でstart.shを実行します
./start.sh
正常に動作すると、AWSコンソール上で以下のように応答が出てくると思います!

接続が確認できたら続行を押し、設定は完了です!
思ったより簡単でびっくりしました。
実際にラズパイから接続をしてみる
実際にIoT CoreからラズパイにMQTTプロトコルを介して信号を送ってみます。
ラズパイ側ではサブスクライブする必要があります。
以下の簡易的なコードをラズパイで実行します
import time
from awscrt import io, mqtt
from awsiot import mqtt_connection_builder
ENDPOINT = "xxxxxxxxxxxxxx.ap-northeast-1.amazonaws.com"
PATH_TO_CERT = "xxxxxxxxxxxxxx.cert.pem"
PATH_TO_KEY = "xxxxxxxxxxxxxx.private.key"
PATH_TO_ROOT = "root-CA.crt"
TOPIC = "tapo/control"
CLIENT_ID = "Test_Subscriber"
def on_message_received(topic, payload, dup, qos, retain, **kwargs):
print(f"★ {topic} からメッセージを受信")
print(f"中身: {payload.decode('utf-8')}")
def run_main():
mqtt_connection = mqtt_connection_builder.mtls_from_path(
endpoint=ENDPOINT,
cert_filepath=PATH_TO_CERT,
pri_key_filepath=PATH_TO_KEY,
ca_filepath=PATH_TO_ROOT,
client_id=CLIENT_ID,
clean_session=False,
keep_alive_secs=30,
)
print(f"接続中... {ENDPOINT}")
connect_future = mqtt_connection.connect()
connect_future.result()
print("接続完了")
subscribe_future, _ = mqtt_connection.subscribe(
topic=TOPIC,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
subscribe_future.result()
print(f"Listening on topic '{TOPIC}'...\n")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n終了")
mqtt_connection.disconnect().result()
print("exit")
if __name__ == "__main__":
run_main()
いざ実行!してみると以下のエラーが出ました。
awscrt.exceptions.AwsCrtError: AWS_ERROR_MQTT_UNEXPECTED_HANGUP: The connection was closed unexpectedly.
証明書のパスもエンドポイントもあってるはずなのにいなんでだ、、、?
原因・IoT Coreのポリシーで弾かれていた
IoTCoreに初期で自動的に割り当てられるポリシーは、特定のトピックに対してのみしか許可されていませんでした。
以下はサブスクライブ部分を抜粋したポリシーです
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:ap-northeast-1:xxxxxxxxxxx:topicfilter/sdk/test/java",
"arn:aws:iot:ap-northeast-1:xxxxxxxxxxx:topicfilter/sdk/test/python",
"arn:aws:iot:ap-northeast-1:xxxxxxxxxxx:topicfilter/sdk/test/js"
]
},
このようにAllowされているリソースにtopicfilterがかかっていました。
なので一旦わかりやすいように全てのtopcに対して許可するポリシーに変更します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Connect",
"iot:Publish",
"iot:Subscribe",
"iot:Receive"
],
"Resource": "*"
}
]
}
このポリシーをアクティブ化して、再度ラズパイから実行すると、通りました!!
そういえばなぜ初期セットアップの段階で./start.shはIoTCoreと繋がったんだ?と思い、start.shの中身をのぞいてみると
printf "\nRunning pub/sub sample application...\n"
python3 aws-iot-device-sdk-python-v2・・・・--topic sdk/test/python --count 0
上のようにtopicがデフォルトで許可されてるtopic名になってました。
ちなみに、AWSコンソール画面の以下の画面から、特定のトピックに対してメッセージを送れます

つまりあとは、このトリガーとペイロード部分をlambdaに置き換えれば送りたい信号を送れそうです
lambdaからIoT Coreにリクエストを送る
今度はlambdaからIoT Coreに流します。抜粋ですがboto3を使えば簡単にiotcoreへpublishできます。
ENDPOINT = "https://xxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com"
TOPIC = "tapo/control"
iot_client = boto3.client('iot-data', endpoint_url=ENDPOINT)
iot_client.publish(
topic=TOPIC,
qos=1,
payload=json.dumps(iot_message)
)
ここでまた注意しなければならないのが、lambdaについてるポリシーの編集です。ここも以下のようなインラインポリシーを追加します。
(設定→アクセス権限→ロール名のところをクリック)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Publish"
"Resource": "*"
}
]
}
これによって正常にlambdaからiot coreにpublishすることができました!!
ラズパイからtapoカメラを制御する
次に、ラズパイからtapoカメラを制御する方法を紹介します。
今回使うtapoカメラには、「pytapo」という非公式なカメラ制御ができるオープンソースライブラリがあるのでこちらを使用します。
詳しいカメラのセットアップ方法や画面などは割愛しますが、以下に重要な押さえておきたい点だけ記します。
ちなみに以下だけで接続を試せます!簡単!
import json
from pytapo import Tapo
CAMERA_IP = "192.168.x.xxx"
CAMERA_USER = "admin"
CAMERA_PASS = "xxxxxxxxxxx"
tapo = Tapo(CAMERA_IP, CAMERA_USER, CAMERA_PASS)
tapo.setPrivacyMode(True)
ユーザー名は統一でadminとしたほうがいいです。
これによって、カメラのプライバシーモード(映像を写すか否か)をコードで制御できます。
つまりこのコードを、ラズパイがサブスクライブしているコードに組み込めばIoTCoreからの信号をキャッチし、そのペイロードを見てプライバシーモードにするかどうか制御可能となります!
以下は陥った沼です
tapoアプリから、「サードパーティ連携」をオンにする必要がある
ここは一番沼りました。tapoアプリを開いて、設定画面から「音声アシスタント」を開くとサードパーティ連携というボタンがあるのでここをオンにする必要があります。
(ボタンの位置分かりずらすぎるし、初見殺しすぎたので気をつけてください。)
リクエストに必要なパスワードはカメラを管理してるアカウントのパスワード
上記のCAMERA_PASSですが、カメラのアカウントではなく管理してるユーザーアカウントのパスワードを入れる必要がありました。ユーザー名はadminで問題ないです。
まとめ
今回は、lambda,AWS IoT Core,ラズパイ,tapoカメラを使用してクラウド上から自宅のカメラを制御するやり方を実践してみました。
これができたから何?となるかもですが、デバイスがクラウドに繋がると無限の可能性が広がると思っているので、また癖のあるプロダクトを作ってみたいなーと思います!