end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転します

pythonで socket + udp通信し、echonet機器一覧を探索

以下、何となく書いて、何となく、動いた程度。 何となくpythonで書いてみたけど、javaでも書くかな? (気が向いたら)

#!/usr/local/bin/python  ## for python 2.7
# -*- coding: utf-8 -*-

import asyncore
import codecs
import netifaces
import socket
import threading
from time import sleep

UDP_RESES = []
UDP_PORT = 3610 # for UDP

def main():
    # 自身のipアドレスを探索
    local_ip = UdpSender.find_local_ip_addr()
    if local_ip == None:
        return None
    
    # echonetのコマンドをudp送信
    sender = UdpSender(local_ip)

    # echonetのコマンド受信用thread
    recv = UdpReceiverThread()
    recv.start()

    echonet_cmd = echonet_cmd_ls() ##echonet機器の一覧取得用コマンド生成

    # 224.0.23.0 とは、ECHONET専用のマルチキャストアドレス
    sender.send_msg("224.0.23.0", echonet_cmd)

    # 複数機器からレスポンスがある場合があるので、少々、待つ
    sleep(3)
    recv.stop()

    # UDP_RESES に各機器からのレスポンスが、echonet電文とIPで入ってます
    for udp_res in UDP_RESES:
        echonet_res = parse_echonet_res(udp_res[0])
        print(udp_res)
        print(echonet_res)

    # 後はお好きに...



def parse_echonet_res(echonet_res):
    res_cols = [echonet_res[ 0: 4],  ## echonetであることの宣言
                echonet_res[ 4: 8],  ## 自由欄
                echonet_res[ 8:14],  ## SEOJ(送信元機器) 0ef001=ノード
                echonet_res[14:20],  ## DEOJ(送信先機器) 05ff01=コントローラ
                echonet_res[20:22],  ## 応答code. 71=set 72=get
                echonet_res[22:24],  ## 処理プロパティ数
                echonet_res[24:26],  ## プロパティ名 d6=自ノードlist.
                echonet_res[26:28],  ## PDC. 後のbyte数
                echonet_res[28:36]]  ## 0105ff01 = 1個(01)x05ff01
    return res_cols


## http://qiita.com/miyazawa_shi/items/725bc5eb6590be72970d
def echonet_cmd_ls():
 
    cmd_cols = ["1081",   ## echonetであることの宣言
                "0000",   ## 自由欄
                "05ff01", ## SEOJ(送信元機器) 05ff01=コントローラ
                "0ef001", ## DEOJ(送信先機器) 0ef001=ノード
                "62",     ## 60=set, 61=set(要:応答), 62=get
                "01",     ## 処理プロパティ数
                "d6",     ## プロパティ名 d6=自ノードlist.
                          ## https://echonet.jp/spec_v112_lite/ にある
                          ## 第2部 ECHONET Lite 通信ミドルウェア仕様の
                          ## 6.11.1 ノードプロファイル詳細規定 参照
               "00"]
    echonet_cmd = "".join(cmd_cols)
    return echonet_cmd



class UdpSender():
    def __init__(self, local_ip):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.IPPROTO_IP,
                             socket.IP_MULTICAST_IF,
                             socket.inet_aton(local_ip))
        
    def send_msg(self, ip,message):
        decode_hex = codecs.getdecoder("hex_codec")
        msg = decode_hex( message )[0]
        self.sock.sendto(msg, (ip, UDP_PORT))

    def close(self):
        self.sock.close()

    @staticmethod
    def find_local_ip_addr(find_iface_name=None):
        for iface_name in netifaces.interfaces():
            iface_data = netifaces.ifaddresses(iface_name)
            af_inet = iface_data.get(netifaces.AF_INET)
        
            if not af_inet: continue

            ip_addr = af_inet[0]["addr"]
            
            if find_iface_name == None:
                return ip_addr
            elif iface_name == find_iface_name:
                return ip_addr
        return None

class UdpReceiverThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.server = UdpReceiver()

    def run(self):
        asyncore.loop()

    def stop(self):
        self.server.close()
        self.join()

        
class UdpReceiver(asyncore.dispatcher):
    def __init__(self):

        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.bind(("0.0.0.0",UDP_PORT))
        return

    def handle_read(self):
        data = self.recvfrom(4096)
        encode_hex = codecs.getencoder("hex_codec")
        global UDP_RESES
        UDP_RESES.append([encode_hex(str(data[0]))[0], data[1][0]])
        return

if __name__ == '__main__':
    main()

↑こう書くと、↓こう出力されます

C:\home\endo\tmp>\Python27\python.exe foo.py
['108100000ef00105ff017201d6040105ff05', '192.168.0.15']
['1081', '0000', '0ef001', '05ff01', '72', '01', 'd6', '04', '0105ff05']

たまにエラー…windows環境だから?

たまに、次のようなエラーが出力されますので、気が向いたら、調べます。

C:\home\endo\tmp>\Python27\python.exe foo.py
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Python27\lib\threading.py", line 801, in __bootstrap_inner
    self.run()
  File "foo.py", line 106, in run
    asyncore.loop()
  File "C:\Python27\lib\asyncore.py", line 216, in loop
    poll_fun(timeout, map)
  File "C:\Python27\lib\asyncore.py", line 145, in poll
    r, w, e = select.select(r, w, e, timeout)
error: (10038, '\x83\\\x83P\x83b\x83g\x88\xc8\x8aO\x82\xcc\x82\xe0\x82\xcc\x82\xc9\x91\xce\x82\xb5\x82\xc4\x91\x80\x8d\xec\x82\xf0\x8e\xc0\x8ds\x82\xb5\x82\xe6\x82\xa4\x82\xc6\x82\xb5\x82\xdc\x82\xb5\x82\xbd\x81B')

参考にさせて頂いたurl