<背景>
Amazon Echoに話しかけて、AWS IoT Core経由で機器を制御する試作を開発中、最終的には、「エアコンをつけて」と発話するとEchonetLite接続のエアコンが動くようにしたい
<要約>
先人のサンプル等を参考にしつつ、PythonでUDPプログラムを書いてEchonetLite機器との最低限の通信を確認できた。
<詳細>
Amazon Echoに「エアコンをつけて」と発話するとAWS IoT Core経由でラズパイと通信し、EchonetLite接続のエアコンが動くようにしたいと考えています(今は発話でLED点灯までできたところ)。
かなり昔にEchonetLiteをUDPで制御する試作プログラムを作ったことがあったけど、マルチキャスト通信と普通の通信(ユニキャスト)をどうやって併存させたらいいのか、Pythonでの実装方法がわからず、機器制御プログラム作成は頓挫した。音声で発話して、EchonetLiteの機器を制御させたいので、再度トライしたい。
デバイスを見つけるにはデバイスからマルチキャストでINFが配信されるのをコントローラが受ける必要があるのだが、、もしコントローラ側でのマルチキャスト通信の実装が難しかったら、デバイスは見つかったとしてユニキャストによる通信だけを実装する。EchonetLiteで通信するサンプルは結構記事があるけど、マルチキャストでINFを受ける所をどのように実装しているのか?が興味深い(INF以外にGET等もマルチキャストで実装しているのか!?)。
5年前の自分の記事を読み直すと、無線GW(メディアコンバータ)(Panasonic CF-TC7B)からは以下のプロパティが返却されるはずなのだが。。
送信元IP:192.168.11.XXX 送信元PORT:49XXX DATA:EHD1:10, EHD2:81, TID:0000, SEOJ:0EF001, DEOJ:05FF01, ESV:72, OPC:01, EPC1:D6, PDC1:07, EDT1:0205FF01013005,
Windows版SSNGはWIndows10では動かないかも。。と書かれているので、Node.JS版SSNGを使った。SEARCH機能で端末を検索したところ、以下の応答が得られた。NodeJS版SSNGの画面は以下。UDPのマルチキャストで探すので、IP:224.0.23.0,Port:3610となる。
コンソール側に出力されたログは以下
UDP receive: rinfo= { address: '172.17.37.157', family: 'IPv4', port: 53740, size: 14 } msg= <Buffer 10 81 00 03 05 ff 01 0e f0 01 62 01 d6 00> UDP receive: rinfo= { address: '172.17.37.157', family: 'IPv4', port: 35151, size: 18 } msg= <Buffer 10 81 00 03 0e f0 01 05 ff 01 72 01 d6 04 01 05 ff 01>
addressに表示されている172.17.37.157は仮想環境のUbuntuのIPだった。
メディアコンバータ(無線GW)が見つかったと思ったけど、メディアコンバータに接続しているはずのエアコンの情報が返ってこないがおかしいのと、メディアコンバータを抜いてスキャンしてもやっぱりデバイス応答があるので、これは別のEchonetLiteが返却しているのだと判断。肝心のメディアコンバータは返事していない。
LANから不要な機器をすべて外して、メディアコンバータとキャプチャPCだけの状態で、メディアコンバータを再起動した。この結果、メディアコンバータは以下のパケットを投げていた。
src:192.168.10.151 dst:224.0.23.0 :3610 EHD1 HED2 TID SEOJ DEOJ ESV OPC EPC PDC EDT 10 81 0001 0ef001 0ef001 73 01 d5 07 02 05ff01 013009 (*) *:D5 : インスタンスリスト通知
EchonetLite仕様メモ
EOJ: 05FF01: コントローラ 0EF001: ノードプロファイル 0130NN: エアコンオブジェクト 0290NN: 照明 ESV: (EchonetLite Service) 0x60 SetI 0x61 SetC 0x62 Get 0x63 INF_REQ 0x6E SetGet .................... 0x71 Set_Res (res for 0x61) 0x72 Get_Res (res for 0x62) 0x73 INF (自発 or res for 0x63) 0x74 INFC(応答要) 0x7A INFC_Res (res for 0x74) SRC DEST 1081 0007 05ff01 0ef001 62 01 d6 00 1081 0007 0ef001 05ff01 72 01 d6 0401 05ff01 EPC == エアコン == 0x80 電源 0x30:ON 0x31:OFF 0xB0 動作モード 0x41:Auto,0x42:Cool,0x43:Heat,0x44:Dry,0x45:Wind 0xB3 設定温度 符号付1B == 照明 == 0x80 電源 0x30:ON 0x31:OFF
SSNGでパケット送受信させたかったけど、何が悪いのかどうも正しくパケットが出ていないようである。メディアコンバータのIPが判明し、メディアコンバータからはEchonetLiteパケットを出力していることも分かったので、PythonでUDP送受信サンプルを書いてみる。
IPが分かっている場合に、先方の機器に何がぶら下がっているか?は、INF_REQを送ってインスタンス一覧をもらう
以下はUDBユニキャストの送信ソース
#!/usr/bin/python3 import socket ip = "192.168.10.151" # MediaConverter ECHONETport = 3610 # infreq(0x0x63) with ins list(0xD6) ask to NodeProfile(0x0EF001) msg = "1081000205FF010EF0016301D600" msg = bytes.fromhex(msg) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(msg, (ip, ECHONETport))
ノードプロファイルクラス(0x0EF001)にINF_REQを投げて、配下のオブジェクト一覧をもらおうとすると、ノードプロファイルクラス(メディアコンバータ)からはMulticastで返事を送ってくる。だから、、制御ソフト側は、MulticastとUnicastの両方を受け取れるように実装している必要がある。過去にPython版のEchonetLiteコントローラを作ろうとして挫折した原因は、MulticastとUnicastを同時に受信する実装方法が分からなかったため。まぁ課題を突破する以前のレベルで、頭の中も整理できていなくてどうアプローチしたらいいのかも検討できないまま終わった。UDPでのMulticast/Unicastを同時に受信するサンプルを書いてくださっている人(saito様)がいて参考に実装したら、確かに両方のパケットが受信できた。ソースは以下
#!/usr/bin/python3 # original source: # https://memo.saitodev.com/home/python_network_programing/#id5 import socket PORT = 3610 bufsize = 1024 LOCAL_ADDR = '192.168.10.150' MULTICAST_GROUP='224.0.23.0' sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('', PORT)) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MULTICAST_GROUP) + socket.inet_aton(LOCAL_ADDR)) while True: data = sock.recvfrom(bufsize) print(data) sock.close()
応答パケットは以下(清書できていませんが、1行目はMulticastによる受信パケットで、2行目はUnicastによる受信パケット)
(b'\x10\x81\x00\x02\x0e\xf0\x01\x05\xff\x01s\x01\xd6\x07\x02\x05\xff\x01\x010\t', ('192.168.10.151', 49154)) (b'\x10\x81\x009\x0e\xf0\x01\x0e\xf0\x01b\x02\xd6\x00\x8a\x00', ('192.168.10.151', 49154))
これができるなら、、INFREQをMulticastで投げることで、EchonetLite機器がどのIPに割り当たってるかもわかるはず。。引き続きsito様の記事を参考にして、MulticastのUDBでINF_REQを送信するサンプルを実装
#!/usr/bin/python3 # org source: # https://memo.saitodev.com/home/python_network_programing/#id5 import socket MULTICAST_GROUP='224.0.23.0' LOCAL_ADDR = '192.168.10.150' ECHONET_PORT = 3610 msg = "1081000205FF010EF0016301D600" # infreq D6(ins list) ask to node prop msg = bytes.fromhex(msg) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(LOCAL_ADDR)) sock.sendto(msg, (MULTICAST_GROUP, ECHONET_PORT)) sock.close()
上記Multicast配布後、以下のパケットが受信できた。1行目は自分が発信したマルチキャストを自分も受けてしまったということかと。2行目~3行目はメディアコンバータからの返信。パケットは詳細に見ていませんが、オブジェクトリスト等が含まれていると思う。。多分。。
(b'\x10\x81\x00\x02\x05\xff\x01\x0e\xf0\x01c\x01\xd6\x00', ('192.168.10.150', 54742)) (b'\x10\x81\x00\x02\x0e\xf0\x01\x05\xff\x01s\x01\xd6\x07\x02\x05\xff\x01\x010\t', ('192.168.10.151', 49154)) (b'\x10\x81\x00=\x0e\xf0\x01\x0e\xf0\x01b\x02\xd6\x00\x8a\x00', ('192.168.10.151', 49154)) (b'\x10\x81\x00>\x0e\xf0\x01\x0e\xf0\x01s\x01\xd5\x07\x02\x05\xff\x01\x010\t', ('192.168.10.151', 49154))
■追記(20201115)
送信されるデータを確認すると、対象ノード配下には、コントローラ(0x05FF01)とエアコン(0x013009)がぶら下がっているとの回答。
SEOJ DEOJ ESV OPC EPC PDC EDT 1081 0002 0ef001 05ff01 73 01 d6 07 02 05ff01 013009 INF * N コントローラ エアコン SEOJ DEOJ ESV OPC EPC PDC EDT 1081 000e 0ef001 0ef001 62 02 d6 00 8a00 GET * 0 (*)D6 : 自ノードインスタンスリスト
フィールドの意味(仕様書(ECHONET Lite通信ミドルウェア仕様)より引用)
SEOJ:送信元ECHONET Liteオブジェクト指定 (3B) DEOJ:送信先ECHONET Liteオブジェクト指定 (3B) ESV: ECHONET Liteサービス(1B) OPC: 処理プロパティ数(1B) EPC: ECHONET Liteプロパティ(1B) PDC: EDTのバイト数(1B) EDT: プロパティ値データ(PDCで指定)
エアコンのオブジェクトが分かった(0x013009)ので、これに向かってSET要求(0x62)を出せばエアコン操作ができるはず。。
■追記
エアコンクラスに以下を投げた(GET(0x62) / 動作状態 0x80)
# S:108100 01 05FF01 013009 62 01 80 00
以下が返却された(動作状態:0x80, 未動作中(0x31))
# R:108100 01 013009 05ff01 72 01 80 01 31
確かに、今はエアコンが切れている。
■追記(201115)
エアコンオブジェクトに以下の電文を投げてみる。自宅のエアコンがONになった*1。
1081000105FF01 013009 60 01 80 01 30
上記電文を投げるプログラムは以下(エアコンのIP,機器オブジェクトは各自の環境依存)
#!/usr/bin/python3 import socket TARGET_IP = "192.168.10.XXX" ECHONET_PORT = 3610 EHD1 = '10' HED2 = '81' TID = '0001' SEOJ = '05FF01' DEOJ = '013009' # AIRCON ESV = '60' # SET OPC = '01' EPC1 = '80' # Operating Status PDC1 = '01' EDT1 = '30' # ON #EDT1 = '31' # OFF msg = EHD1 + HED2 + TID + SEOJ + DEOJ + ESV + OPC + EPC1 + PDC1 + EDT1 print(msg) msg = bytes.fromhex(msg) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(msg, (TARGET_IP, ECHONET_PORT)) sock.close()
■過去の記事
自宅エアコンをEchonetLiteで叩いた時の記事(5年前。。)
自宅のエアコンをEchonetLiteで叩いてみる。。 - chakokuのブログ(rev4)
Echonet liteの調査 - chakokuのブログ(rev4)
■仕様書や先人の情報
EchonetLite通信仕様書
https://echonet.jp/wp/wp-content/uploads/pdf/General/Standard/ECHONET_lite_V1_12_jp/ECHONET-Lite_Ver.1.12_02.pdf
制御対象機器ごとの仕様書(各機器がどんなプロパティを持っているか?)
https://echonet.jp/wp/wp-content/uploads/pdf/General/Standard/Release/Release_J_jp/Appendix_Release_J.pdf
http://sh-center.org/120620/downloads/seminar_151002.pdf
https://github.com/SonyCSL/OpenECHO/blob/master/Processing/Tutorial.pdf?raw=true
UDPによるMulticastプログラミングサンプル・・参考になりました
Pythonでネットワークプログラミング | saito's memo
(5年前は挫折しましたが、このサンプルのおかげで解決できた)
*1:妻には今エアコンいらんと怒られる。。