11月3日と4日の48時間の発電量のグラフ。3日は太陽光を日の出から日の入りまでずっと受けていた発電にはベストの日だったか。4日も良い感じ。
3日は 8.6 kWh, 4日は 8.1 kWh の発電量だった模様。
データを 5分毎にinfluxDB2に送り込んだのでこれを グラフ化する。
太陽光発電で、発電量について瞬間発電量と、積算電力量の差分の2通りでグラフ化してみた。
ある日のサンプル
10月のある日。お昼前までは順調に発電していてピークを迎えたが、その後は天候が少し悪くなったようで、ガタガタしている。
瞬時データのグラフを作成する influxQL:from(bucket: "environment") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "hems_02791f") |> filter(fn: (r) => r["location"] == "home") |> filter(fn: (r) => r["_field"] =~ /datae0/)積算データの差分から瞬時データのグラフを作成する influxQL:
from(bucket: "environment") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "hems_02791f") |> filter(fn: (r) => r["location"] == "home") |> filter(fn: (r) => r["_field"] == "datae1") |> aggregateWindow(every: 5m, fn: last) |> derivative(unit: 1h, nonNegative: true) |> map(fn: (r) => ({ r with _value: r._value * 1000.0 }))ここで、この2つを直接比較できるように、単位計算のところでちょっと工夫している。
# # https://echonet.jp/wp/wp-content/uploads/pdf/General/Standard/Release/Release_R/Appendix_Release_R_r2.pdf # # # 蓄電池クラス規定: 2-236 # クラスグループコード: 0x02 # クラスコード: 0x7D # インスタンスコード: 0x01-0x7F # # データ取得対象を、以下の epcにする。 # 0x80 動作状態 # 0x89 異常状態 # 0x97 現在時刻 # 0x98 現在年月日 # # 0xA4 AC充電可能量 # 0xA5 AC放電可能量 # # 0xA8 AC積算充電電力量計測値 # 0xA9 AC積算放電電力量計測値 # # 0xCF 運転動作状態 # # 0xD3 瞬時充放電電力計測値(プラス:充電、マイナス:放電?) # # 0xDA 運転モード設定 # # 0xE2 蓄電残量1 # 0xE4 蓄電残量3 # # import asyncio import netifaces import socket import sys # # for influxdb # from influxdb_client import InfluxDBClient,Point,WritePrecision from influxdb_client.client.write_api import SYNCHRONOUS # UDP_RESES = [] UDP_PORT = 3610 # for UDP GWIP = "xxx.xxx.xxx.xxx" # Gateway IP address INTERVAL = 0.03 # udp 送信のインターバル (sec) # for influxdb2 org = "homemetrics" bucket = "environment" token = "api key" client = InfluxDBClient(url="http://xxx.xxx.xxx.xxx:8086",token=token,org=org) write_api = client.write_api(write_options=SYNCHRONOUS) query_api = client.query_api() # async def main(): # 自身のipアドレスを探索 local_ip = UdpSender.find_local_ip_addr() if local_ip is None: print("ローカルIPアドレスが見つかりませんでした。") return #print(f"ローカルIPアドレス: {local_ip}") # echonetのコマンドをudp送信 sender = UdpSender(local_ip) # echonetのコマンド受信用 recv = UdpReceiver() await recv.start() echonet_cmd = echonet_cmd_pro("80") # 0x80: 動作状態 :unsigned char: 1 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("89") # 89: 異常内容 :unsigend short: 2 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("97") # 97: 現在時刻設定 :unsigend char: 2 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("98") # 98: 現在年月日設定 :unsigend char: 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("A4") # A4: AC充電可能量 :unsigend long: 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("A5") # A5: AC放電可能量 :unsigend long: 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("A8") # A8: AC積算充電電力量計測値 :unsigend long: 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("A9") # A9: AC積算放電電力量計測値 :unsigend long: 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("CF") # CF: 運転動作状態 :unsigend char: 1 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("D3") # D3: 瞬時充放電電力計測値 :sigend long : 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("DA") # DA: 運転モード設定 :unsigend char: 1 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("E2") # E2: 蓄電残量1 :unsigend long : 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("E4") # E4: 蓄電残量3 :unsigend char : 1 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) # #print("メッセージ送信完了") sender.close() #await asyncio.sleep(10) await asyncio.sleep(7) #recv.stop() # これは awaitはつけずに実効する。、 # UDP_RESES に各機器からのレスポンスが、echonet電文とIPで入ってます for udp_res in UDP_RESES: echonet_res = parse_echonet_res(udp_res[0]) #print(udp_res) #print(echonet_res) await recv.stop() #print("end") for udp_res in UDP_RESES: echonet_res = parse_echonet_res(udp_res[0]) # #print(echonet_res[6]) # [6] is epc, [8] is raw hexdata match echonet_res[6]: case "80": # 動作状態: 0x30 on, 0x31 off print("80 start") data80 = echonet_res[8] print(data80) case "89": # 異常状態: 上位1バイト 異常内容小分類,下位1バイト 異常内容大分類 print("89 start") data89 = echonet_res[8] print(data89) case "97": # 現在時刻: print("97 start") data97 = str(int(echonet_res[8][:2],16)).zfill(2) + ':' + str(int(echonet_res[8][2:],16)).zfill(2) print(data97) case "98": # 現在年月日: print("98 start") data98 = str(int(echonet_res[8][:4],16)) + '-' + str(int(echonet_res[8][4:6],16)).zfill(2) + '-' + str(int(echonet_res [8][6:],16)).zfill(2) print(data98) case "a4": # AC充電可能量(Wh): 0x00000000-0x03B9AC9FF = 0 - 999,999,999 Wh print("a4:") dataa4 = int(echonet_res[8],16) print(dataa4) case "a5": # AC放電可能量(Wh): 0x00000000-0x03B9AC9FF = 0 - 999,999,999 Wh print("a5:") dataa5 = int(echonet_res[8],16) print(dataa5) case "a8": # AC積算充電電力量計測値(0.001kWh): 0x00000000-0x3b9AC9FF = 0 - 999,999.999 kWh) print("a8:") dataa8 = float(int(echonet_res[8],16)/1000) print(dataa8) case "a9": # AC積算放電電力量計測値(0.001kWh): 0x00000000-0x3b9AC9FF = 0 - 999,999.999 kWh) print("a9:") dataa9 = float(int(echonet_res[8],16)/1000) print(dataa9) case "cf": # 運転動作状態:0x41 急速充電,0x42 充電,0x43 放電,0x44 待機,0x45 テスト,0x46 自動,0x48 再起動,0x49 実効容量再計算処理,0x40 その他 print("cf:") datacf = echonet_res[8] print(datacf) case "d3": # 瞬時充放電電力計測値:0x0000001-0x3B9AC9FF 1-999,999,999 W(充電時 プラス値), 0xFFFFFFFF - 0xC4653601 - 1 - -999,999,999 W(放電時 マイナス値) print("d3:") datad3 = int(echonet_res[8],16) if datad3 >= 0x8000000: datad3 -= 0x100000000 print(datad3) case "da": # 運転モード設定;0x41 急速充電,0x42 充電,0x43 放電,0x44 待機,0x45 テスト,0x46 自動,0x48 再起動,0x49 実効容量再計算処理,0x40 その他 print("da:") datada = echonet_res[8] print(datada) case "e2": # 蓄電残量1(Wh):0x00000000 - 0x3B9AC9FF 0-999,999,999 Wh print("e2") datae2 = int(echonet_res[8],16) print(datae2) case "e4": # 蓄電残量3(%) :0x00-0x64 0-100 % print("e4") datae4 = int(echonet_res[8],16) print(datae4) # for influxdb p = Point("hems_027d1f")\ .tag("location","home")\ .field("data80",data80)\ .field("data89",data89)\ .field("data97",data97)\ .field("data98",data98)\ .field("dataa4",dataa4)\ .field("dataa5",dataa5)\ .field("dataa8",dataa8)\ .field("dataa9",dataa9)\ .field("datacf",datacf)\ .field("datad3",datad3)\ .field("datada",datada)\ .field("datae2",datae2)\ .field("datae4",datae4) write_api.write(bucket=bucket,record=p,write_precision=WritePrecision.S) 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 echonet_res[28:] # 0105ff01 = 1個(01)x05ff01 ] return res_cols def echonet_cmd_pro(property): cmd_cols = ["1081", # echonetであることの宣言 "0000", # 自由欄 "05ff01", # SEOJ(送信元機器) 05ff01=コントローラ #"0ef001", # DEOJ(送信先機器) 0ef001=ノード #"02791f", # DEOJ(送信先機器) 02791f=太陽光発電? #"02877f", # DEOJ(送信先機器) 0287 =分電盤メータリングクラス。 さて、インスタンスコードは何? "027d1f", # DEOJ(送信先機器) 027d =蓄電池 インスタンスは 1fっぽい "62", # 60=set, 61=set(要:応答), 62=get "01", # 処理プロパティ数 #"d6", # プロパティ名 d6=自ノードlist. property, # "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)) async def send_msg(self, ip, message): msg = bytes.fromhex(message) loop = asyncio.get_running_loop() await loop.run_in_executor(None, 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 is None: return ip_addr elif iface_name == find_iface_name: return ip_addr return None class UdpReceiver(): def __init__(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind(("0.0.0.0", UDP_PORT)) self.sock.settimeout(5.0) self.task = asyncio.create_task(self.receive_loop()) self.running = True async def start(self): loop = asyncio.get_running_loop() self.task = loop.create_task(self.receive_loop()) async def receive_loop(self): loop = asyncio.get_running_loop() while self.running: try: data, addr = await asyncio.get_running_loop().run_in_executor(None, self.sock.recvfrom, 4096) global UDP_RESES UDP_RESES.append([data.hex(), addr[0]]) #print(f"データ受信: {data.hex()}, 送信元アドレス: {addr[0]}") except socket.timeout: print("socket timeout") continue except (OSError, asyncio.CancelledError): break async def stop(self): self.running = False self.sock.close() self.task.cancel() try: await self.task except asyncio.CancellerdError: pass if __name__ == '__main__': asyncio.run(main())
とりあえず、hems のデータを gatewayから取得できることがわかったので、pythonのscriptで influxdbにデータを送り込むスクリプトを作成し、5分毎に起動することに。
# # # https://echonet.jp/wp/wp-content/uploads/pdf/General/Standard/Release/Release_R/Appendix_Release_R_r2.pdf # 住宅用太陽光発電クラス規定: 3-206 # クラスグループコード: 0x02 # クラスコード : 0x79 # インスタンスコード : 0x01 - 0x7F # import asyncio import netifaces import socket import sys # # for influxdb # from influxdb_client import InfluxDBClient,Point,WritePrecision from influxdb_client.client.write_api import SYNCHRONOUS # UDP_RESES = [] UDP_PORT = 3610 # for UDP GWIP = "xxx.xxx.xxx.xxx" # Gateway IP address INTERVAL = 0.03 # udp 送信のインターバル (sec) # # for influxdb2 # org = "homemetrics" bucket = "environment" token = "api token" client = InfluxDBClient(url="http://xxx.xxx.xxx.xxx:8086",token=token,org=org) write_api = client.write_api(write_options=SYNCHRONOUS) query_api = client.query_api() # async def main(): # 自身のipアドレスを探索 local_ip = UdpSender.find_local_ip_addr() if local_ip is None: print("ローカルIPアドレスが見つかりませんでした。") return #print(f"ローカルIPアドレス: {local_ip}") # echonetのコマンドをudp送信 sender = UdpSender(local_ip) # echonetのコマンド受信用 recv = UdpReceiver() await recv.start() echonet_cmd = echonet_cmd_pro("80") # 0x80: 動作状態 :unsigned char: 1 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("89") # 89: 異常内容 :unsigend short: 2 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("97") # 97: 現在時刻設定 :unsigend char: 2 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("98") # 98: 現在年月日設定 :unsigend char: 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("E0") # E0: 瞬時発電電力計測値(W) :unsigend short : 2 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) echonet_cmd = echonet_cmd_pro("E1") # E1: 積算発電電力量計測値(kWh) :unsigend short : 4 Byte await sender.send_msg(GWIP, echonet_cmd) # 動作状態を確認 await asyncio.sleep(INTERVAL) sender.close() # 複数機器からレスポンスがある場合があるので、少々、待つ await asyncio.sleep(5) # UDP_RESES に各機器からのレスポンスが、echonet電文とIPで入ってます for udp_res in UDP_RESES: echonet_res = parse_echonet_res(udp_res[0]) #print(udp_res) print(echonet_res) await recv.stop() print("end") for udp_res in UDP_RESES: echonet_res = parse_echonet_res(udp_res[0]) # match echonet_res[6]: case "80": # 動作状態: 0x30 on, 0x31 off print("80 start") data80 = echonet_res[8] print(data80) case "89": # 異常内容 : 上位1バイト 小分類 、下位1バイト 大分類 print("89 start") data89 = echonet_res[8] print(data89) case "97": # 現在時刻設定 0x00-0x17=0-23, 0x00-0x3B = 0-59 print("97 start") data97 = str(int(echonet_res[8][:2],16)).zfill(2) + ':' + str(int(echonet_res[8][2:],16)).zfill(2) print(data97) case "98": # 現在年月日設定 1-0x270F = 1-9999, 1-0x0C = 1-12, 1-0x1F = 1-31 print("98 start") data98 = str(int(echonet_res[8][:4],16)) + '-' + str(int(echonet_res[8][4:6],16)).zfill(2) + '-' + str(int(echonet_res[8][6:],16)).zfill(2) print(data98) case "e0": # 瞬時発電電力量計測値(W) 0x0000-0xfffd = 0-65533 print("e0 start") datae0 = int(echonet_res[8],16) print(datae0) case "e1": # 積算発電電力量計測値(0.001kWh) 0x00000000-0x3b9ac9ff = 0 - 999,999.999 kWh print("e1 start") datae1 = float(int(echonet_res[8],16)/1000) print(datae1) # # for influxdb # p = Point("hems_02791f")\ .tag("location","home")\ .field("data80",data80)\ .field("data89",data89)\ .field("data97",data97)\ .field("data98",data98)\ .field("datae0",datae0)\ .field("datae1",datae1) write_api.write(bucket=bucket,record=p,write_precision=WritePrecision.S) 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:] # 0105ff01 = 1個(01)x05ff01 ] return res_cols def echonet_cmd_pro(property): cmd_cols = ["1081", # echonetであることの宣言 "0000", # 自由欄 "05ff01", # SEOJ(送信元機器) 05ff01=コントローラ "02791f", # DEOJ(送信先機器) 02791f=太陽光発電? "62", # 60=set, 61=set(要:応答), 62=get "01", # 処理プロパティ数 property, # "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)) async def send_msg(self, ip, message): msg = bytes.fromhex(message) loop = asyncio.get_running_loop() await loop.run_in_executor(None, 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 is None: return ip_addr elif iface_name == find_iface_name: return ip_addr return None class UdpReceiver(): def __init__(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind(("0.0.0.0", UDP_PORT)) self.sock.settimeout(5.0) self.task = asyncio.create_task(self.receive_loop()) self.running = True async def start(self): loop = asyncio.get_running_loop() self.task = loop.create_task(self.receive_loop()) async def receive_loop(self): loop = asyncio.get_running_loop() while self.running: try: data, addr = await asyncio.get_running_loop().run_in_executor(None, self.sock.recvfrom, 4096) global UDP_RESES UDP_RESES.append([data.hex(), addr[0]]) #print(f"データ受信: {data.hex()}, 送信元アドレス: {addr[0]}") except socket.timeout: print("socket timeout") continue except (OSError, asyncio.CancelledError): break async def stop(self): self.running = False self.sock.close() self.task.cancel() try: await self.task except asyncio.CancellerdError: pass if __name__ == '__main__': asyncio.run(main())UDPの受けのアドレスは '0.0.0.0' を明示的に指定しないと受信してくれなかった。ちょっとはまったかも。 あと、influxdb 側のデータ受信日時が、どこかで丸められていて 分単位になっていることがちょっとあったので、 明示的に、WritePrecision.S で秒単位で記録させることに。さすがに、ms,ns,us レベルは必要ないだろう。
続けて、蓄電池のほうのスーパークラスを確認してみる。
まず、EPC:0x9Fに投げてみると次の応答があった。
Send: 1081 0001 05FF01 027D1F 62 01 9F 00
Recv: 1081 0001 027D1F 05FF01 72 01 9F 11 40A595D5A7C4C4C5869795A7E471339392
ということで今回は EPCは小さい順に
33,
40,
71,
86,
92,93,95,95,97,
A5,A7,A7,
C4,C4,C5,
D5
E4
の 17(=0x11) 個 のようだ。
なぜか、95,A7,C4 が2回現れている。
このうち、機器オブジェクト詳細規定のほうに説明があるのは、
0x97: 現在時刻(uc 2b HH:MM)
0xA5: 放電可能量(ul 4b Wh)
0xA7: AC放電下限設定 (uc 1b %)
0xD5: 瞬時充放電電圧計測値(ss 2b V)
0xE4: 蓄電残量3 (uc 1b %)
となっていて、残りの、'33,40,71,86,92,93,95,C4,C5' についての記述は見当たらない。
スーパークラスのほうを見ると
0x86:メーカー異常コード
0x93: 遠隔操作設定
とあるので、たぶんこれでOK。それにしてもあと、'33,40,71,92,95,C4,C5' は何だろう。
プロパティ名称 | EPC | データ型・サイズ | 取得データ | 結果 |
? | 0x33 | 52013300 | ||
? | 0x40 | 52014000 | ||
? | 0x71 | 52017100 | ||
メーカー異常コード | 0x86 | uc x 4b | 7201860400000064 | |
? | 0x92 | 52019200 | ||
? | 0x93 | 7201930141 | ||
? | 0x95 | 52019500 | ||
現在時刻 | 0x97 | uc x 2b | 72019702100f | 16:15 |
放電可能量 | 0xA5 | ul x 4b | 7201a5040001412 | 5138 Wh |
AC放電下限設定 | 0xA7 | uc x 1b | 7201a70100 | 0 % |
? | 0xC4 | 5201c400 | ||
? | 0xC5 | 5201c500 | ||
瞬時充放電電圧計測値 | 0xD5 | ss x 2b | 5201d500 | |
蓄電残量3 | 0xE4 | uc x 1b | 7201e4013d | 61 % |
Getマッププロパティで GETでデータが取れるといいながら、ESVが 52(不可応答)が多いのはどういうことだろうと思う。
ちょっと先走って、住宅用太陽光発電機器クラス規定(0x0279) を見ていたが、そもそもこれは「住宅・設備関連クラスグループ(0x02)」というクラスグループコードに属しており、それは機器オブジェクトスーパークラスのプロパティを継承している。
つまり、このスーパークラスのプロパティもデータが取れるのをちょっと見落としていた。
個別の機器クラス規定と被る EPCもあるが、データが SET/GET できるかという「Setプロパティマップ(EPC:0x9E)」「Getプロパティマップ(0x9F)」があり、これを見ることで、SET/GET できる EPCがわかる。
とりあえず、この GET プロパティだけ取得して見てみると、
'72019f111fe1c18183004001024341830001038382'
と帰ってきた。これを読み解くと、
'72019f11' の最後の 0x11 =17で、この後17個の データが続くことを意味していて、
その最初が '1f' , 0x1F=31 で、GET できる EPCが全部で31個ある!ということになる。
残りの 16byteが、EPCのマッピングで、付録1に書いてあるとあるので、読み解くと、
e1 = b'1110-0001' = F0,E0,D0,80
c1 = b'1100-0001' = F1,E1,81
81 = b'1000-0001' = F2,82
83 = b'1000-0011' = F3,93,83
00 = b'0000-0000' = (nothing)
40 = b'0100-0000' = E5
01 = b'0000-0001' = 86
02 = b'0000-0010' = 87
43 = b'0100-0011' = E8,98,88
41 = b'0100-0001' = E9,89
83 = b'1000-0011' = FA,9A,8A
00 = b'0000-0000' = (nothing)
01 = b'0000-0001' = 8C
03 = b'0000-0011' = 9D,8D
83 = b'1000-0011' = FE,9E,8E
82 = b'1000-0010' = FF,9F
ということらしい。EPCの小さい順に並べてみると、
80,81,82,83,86,87,88,89,8A,8C,8D,8E,
93,98,9A,9D,9E,9F
D0,
E0,E1,E5,E8,E9,
F0,F1,F2,F3FA,FE,FF
の 31個で、これは 個数 0x1f とあったのと一致している。
この中に規格書には無い、'F0,F1,F2,F3,FA,FE,FF' がある。
メーカー独自仕様のものと思われるが、ドキュメントは公開されてないっぽい。
とりあえず、スーパークラスのデータを出してみると次の通り。
プロパティ名称 | EPC | データ型・サイズ | 取得データ | 結果 |
設置場所 | 0x81 | uc x 1b or 17b | 0100 | |
規格Version 情報 | 0x82 | uc x 4b |
0400004a00 |
Release J |
メーカー異常コード | 0x86 | uc x max 225b | 0400000064 | |
電流制限設定 | 0x87 | uc x 1b | 00 | 0 % |
異常発生状態 | 0x88 | uc x 1b | 0142 | 異常発生無 |
会員ID/メーカーコード | 0x8a | uc x 3b | 03000064 | |
製造番号 | 0x8d | uc x 12b | 31313131313120202020 | 111111 |
製造年月日 | 0x8e | uc x 4b | 07e8010b | 2024-01-10 |
遠隔操作設定 | 0x93 | uc x 1b | 0141 | 公衆回線未経由操作 |
積算運転時間 | 0x9a | uc 1b + ul 4b | 05410028afaf | 2,666,415 秒 = 44440分15秒 = 740時間40分15秒 = 30 日 20 時間 40 分 15 秒 |
状変アナウンスプロパティマップ | 0x9d | 0403808188 | EPC: 80,81,88 | |
Set プロパティマップ | 0x9e | 060581939798fe | EPC: 81,93,97,98,fe | |
Get プロパティマップ | 0x9f | 111fe1c18183004001024341830001038382 | (上記参照) |
|
? | 0xf0 | 06138104040408 | ||
? | 0xf1 | 0104 | 何かの status ? | |
? | 0xf2 | 0a00000000000000000000 | 日中は '0' ではない。2byte x 5 ? | |
? | 0xf3 | 0a00000000000000000000 | 日中は '0' ではない。2byte x 5 ? | |
? | 0xfa | 0400000000 | ||
? | 0xfe | 0100 | これは setもできる。 | |
? | 0xff | 0100 |
scanしたら応答した、パナソニックのエアコン、0x013001 で応答してきたので、こちらでもどういうデータが取れるのかを確認してみた。
HEMSの応答については、"3.2.1 家庭用エアコンクラス規定" を参照し、30個くらいのEPCを投げてみた。
UDPで帰ってきた結果だけ記載すると、
['1081', '0000', '013001', '05ff01', '72', '01', '80', '01', '30']
['1081', '0000', '013001', '05ff01', '72', '01', '8f', '01', '42']
['1081', '0000', '013001', '05ff01', '72', '01', 'b0', '01', '41']
['1081', '0000', '013001', '05ff01', '52', '01', 'b1', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'b2', '00', '']
['1081', '0000', '013001', '05ff01', '72', '01', 'b3', '01', '1b']
['1081', '0000', '013001', '05ff01', '52', '01', 'b4', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'b5', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'b6', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'b7', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'b8', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'b9', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'bc', '00', '']
['1081', '0000', '013001', '05ff01', '72', '01', 'ba', '01', '2d']
['1081', '0000', '013001', '05ff01', '72', '01', 'bb', '01', '1d']
['1081', '0000', '013001', '05ff01', '52', '01', 'bd', '00', '']
['1081', '0000', '013001', '05ff01', '72', '01', 'be', '01', '22']
['1081', '0000', '013001', '05ff01', '52', '01', 'bf', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'a3', '00', '']
['1081', '0000', '013001', '05ff01', '72', '01', 'a0', '01', '41']
['1081', '0000', '013001', '05ff01', '72', '01', 'a1', '01', '43']
['1081', '0000', '013001', '05ff01', '52', '01', 'aa', '00', '']
['1081', '0000', '013001', '05ff01', '72', '01', 'a4', '01', '43']
['1081', '0000', '013001', '05ff01', '52', '01', 'a5', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'ab', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c0', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c1', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c2', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c4', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c6', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c7', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c8', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'c9', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'ca', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'cb', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'cc', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'cd', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'ce', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', 'cf', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', '90', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', '91', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', '92', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', '94', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', '95', '00', '']
['1081', '0000', '013001', '05ff01', '52', '01', '96', '00', '']
と、ほとんどが '52' を返してきて、HEM経由ではあまりデータ取得や操作はできなさそう。
純正のエオリアアプリを使えということか。
一応取れているのは、
80: 動作状態 (30: ON)
8f: 節電動作設定 (42: 通常動作中)
b0: 運転モード設定 (41:自動)
ba: 室内相対湿度計測値 (2d: 45%)
bb: 室内温度計測値 ( 1d: 29度)
be: 外気温度計測値 (22: 34度)
a0: 風量設定( 41: 自動設定)
a1: 風向自動設定 (43: 上下 AUTO)
a4: 風向上下設定 (43: 中央)
だった。一応、これも取得して、infuxdbに入れることにするか。。。
夜にまた ゲートウェイにリクエストしてみた。
プロパティ名称 | EPC | 取得データ | 結果 |
動作状態 | 0x80 | 0130 | 30:ON |
識別番号 | 0x83 | fe0000643000NNNNNNNNNNNN0000000000 | 17byte MACアドレス |
異常内容 | 0x89 | 0000 | 0000 異常無し |
商品コード | 0x8C | 5043532d5250314120202020 | PCS-RP1A |
現在時刻設定 | 0x97 | 1526 | 21:38 |
現在年月日設定 | 0x98 | 07e80809 | 2024:08:09 |
AC実効容量(充電) | 0xA0 | 7201a00400002424 | 9252 Wh |
AC実効容量(放電) | 0xA1 | 000020e7 | 8423 Wh |
AC充電可能容量 | 0xA2 | 00002424 | 9252 Wh |
AC放電可能容量 | 0xA3 | 000020e7 | 8423 Wh |
AC充電可能量 | 0xA4 | 00001da2 | 7586 Wh |
AC放電可能量 | 0xA5 | 000005ec | 1516 Wh |
AC充電上限設定 | 0xA6 | 64 | 100 % |
AC放電下限設定 | 0xA7 | 00 | 0 % |
AC積算充電電力量計測値 | 0xA8 | 00017763 | 96.099 kWh |
AC積算放電電力量計測値 | 0xA9 | 0001436e | 82.798 kWh |
AC充電量設定値 | 0xAA | 00000000 | 0:未設定 |
AC放電量設定値 | 0xAB | 00000000 | 0:未設定 |
充電方式 | 0xC1 | 03 | 03:指定電力充電 |
放電方式 | 0xC2 | 03 | 03:指定電力放電 |
最小最大充電電力値 | 0xC8 | 0000000000000fa0 | 最小: 0 W 最大:4000 W |
最小最大放電電力値 | 0xC9 | 0000000000000fa0 | 最小: 0W 最大: 4000W |
最小最大充電電流値 | 0xCA | 5201ca00 | 52:不可応答 |
最小最大放電電流値 | 0xCB | 5201cb00 | 52:不可応答 |
再連係許可設定 | 0xCC | 41 | 41:許可 |
運転許可設定 | 0xCD | 41 | 41:許可 |
自立運転許可設定 | 0xCE | 41 | 41:許可 |
運転動作状態 | 0xCF | 44 | 44: 待機 |
AC定格電力量 | 0xC7 | 5201c700 | 52:不可応答 |
定格電力量 | 0xD0 | 7201d00400002648 | 9800 Wh |
定格容量 | 0xD1 | 5201d100 | 52:不可応答 |
定格電圧 | 0xD2 | 5201d200 | 52:不可応答 |
瞬時充放電電力計測値 | 0xD3 |
7201d30400000000 |
0 Wh |
瞬時充放電電流計測値 | 0xD4 | 5201d400 | 52:不可応答 |
瞬時充放電電圧計測値 | 0xD5 | 5201d500 | 52:不可応答 |
積算放電電力量計測値 | 0xD6 | 5201d600 | 52:不可応答 |
積算充電電力量計測値 | 0xD8 | 5201d800 | 52:不可応答 |
運転モード設定 | 0xDA | 46 | 46:自動 |
系統連係状態 | 0xDB | 02 |
02:系統連係(逆潮流不可) |
最小最大充電電力値(独立時) | 0xDC | 0000000000000fa0 | 最小: 0 W 最大: 4000 W |
最小最大放電電力値(独立時) | 0xDD | 0000000000000fa0 | 最小: 0 W 最大: 4000 W |
最小最大充電電流値(独立時) | 0xDE | 5201de00 | 52:不可応答 |
最小最大放電電流値(独立時) | 0xDF | 5201df00 | 52:不可応答 |
充放電量設定値1 | 0xE0 | 5201e000 | 52:不可応答 |
充放電量設定値2 | 0xE1 | 5201e100 | 52:不可応答 |
蓄電残量1 | 0xE2 | 00000633 | 1587 Wh |
蓄電残量2 | 0xE3 | 5201e300 | 52:不可応答 |
蓄電残量3 | 0xE4 | 12 | 18 % |
劣化状態 | 0xE5 | 64 | 100 % |
蓄電池タイプ | 0xE6 | 04 | type: 04 |
充電量設定値1 | 0xE7 | 5201e700 | 52:不可応答 |
放電量設定値1 | 0xE8 | 5201e800 | 52:不可応答 |
充電量設定値2 | 0xE9 | 5201e900 | 52:不可応答 |
放電量設定値2 | 0xEA | 5201ea00 | 52:不可応答 |
充電電力設定値 | 0xEB | 7201eb0400000fa0 | 最小: 0 W 最大: 4000 W |
放電電力設定値 | 0xEC | 7201ec0400000fa0 | 最小: 0 W 最大: 4000 W |
充電電流設定値 | 0xED | 5201ed00 | 52:不可応答 |
放電電流設定値 | 0xEE | 5201ee00 | 52:不可応答 |
定格電圧(独立時) | 0xEF | 5201ef00 | 52:不可応答 |
夜で発電無し、バッテリー残容量が20%以下となっていて、充電も放電もせず、系統の電気をそのまま使っている状態。
この中で、時系列でデータを取得して意味がありそうなのは、A4,A5,A8,A9,CF,D3,E2,E4 くらい?
D3(瞬時充放電電力計測値)は signed long なので、数値がマイナス(放電)とプラス(充電)がある。
とりあえず、時系列で変化が有りそうな以下のデータをモニタすることにする。
0x80 動作状態
0x89 異常状態
0x97 現在時刻
0x98 現在年月日
0xA4 AC充電可能量(Wh)
0xA5 AC放電可能量(Wh)
0xA8 AC積算充電電力量計測値(kWh)
0xA9 AC積算放電電力量計測値(kWh)
0xCF 運転動作状態
0xD3 瞬時充放電電力計測値(プラス:充電、マイナス:放電)(W)
0xDA 運転モード設定
0xE2 蓄電残量1(Wh)
0xE4 蓄電残量3(%)
いまいろいろ設定を試して見ているが、時間帯価格変動のサービスに変えることを前提に、価格が安い時間(普通は夜間)に蓄電池に充電する機能を試してみる。
今のシステムだと、グリーンモードの場合に蓄電池の容量の何%まで充電するかを設定することができて、0%~50%まで10%刻みで設定できる。
とりあえず50%に設定してみたが、なぜか勝手にこれがリセットされて0%になっていることが続いた。
で、オムロンに電話してみたところ、これは「AI制御の設定」で「翌日の天気予報で充電制御」というのを有効にしていた場合に、翌日の天気が良いときは充電が期待されるので勝手に変更することがあるとのこと。なるほど。。。
でも、これって、充電能力と蓄電池の容量と、消費電力との兼ね合いになるんだろうけどそこまで賢くやってくれるのだろうか?
うちは一般家庭と比べて消費電力が多い、充電能力は低くてバッテリー100%になることはほぼ無いのではと思われるので、できるだけ安い時間帯で溜め込むのが良いと考えているのだが。。。
マルチキャストの HEMSのINFメッセージを見て、蓄電池のインスタンスが '1f' と分かったので、とりあえずこのノードに GET できる情報を投げてみた。
いつもの"ECHONET 機器オブジェクト詳細規定 Release R rev.2" の蓄電池のところをみて EPCを変えながら応答を見てみた。
プロパティ名称 | EPC | データ型・サイズ | 取得データ | 結果 |
動作状態 | 0x80 | uc 1b | 7201800130 | 30:ON |
識別番号 | 0x83 | uc 9b or 17b | 72018311fe0000643000NNNNNNNNNNNN0000000000 | 17byte MACアドレス |
異常内容 | 0x89 | us 2b | 720189020000 | 0000 異常無し |
商品コード | 0x8C | uc 12b | 72018c0c5043532d5250314120202020 | PCS-RP1A |
現在時刻設定 | 0x97 | uc 2b | 720197020c03 | 12:03 |
現在年月日設定 | 0x98 | uc 4b | 7201980407e8071a | 2024:07:26 |
AC実効容量(充電) | 0xA0 | ul 4b | 7201a00400002424 | 9252 Wh |
AC実効容量(放電) | 0xA1 | ul 4b | 7201a104000020e7 | 8423 Wh |
AC充電可能容量 | 0xA2 | ul 4b | 7201a20400002424 | 9252 Wh |
AC放電可能容量 | 0xA3 | ul 4b | 7201a304000020e7 | 8423 Wh |
AC充電可能量 | 0xA4 | ul 4b | 7201a40400001158 | 4440 Wh |
AC放電可能量 | 0xA5 | ul 4b | 7201a5040000111b | 4379 Wh |
AC充電上限設定 | 0xA6 | uc 1b | 7201a60164 | 100 % |
AC放電下限設定 | 0xA7 | uc 1B | 7201a70100 | 0 % |
AC積算充電電力量計測値 | 0xA8 | ul 4B | 7201a804000022d1 | 8.913 kWh |
AC積算放電電力量計測値 | 0xA9 | ul 4b | 7201a904000015b2 | 5.554 kWh |
AC充電量設定値 | 0xAA | ul 4b | 7201aa0400000000 | 0:未設定 |
AC放電量設定値 | 0xAB | ul 4b | 7201ab0400000000 | 0:未設定 |
充電方式 | 0xC1 | uc 1b | 7201c10103 | 03:指定電力充電 |
放電方式 | 0xC2 | uc 1b | 7201c20103 | 03:指定電力放電 |
最小最大充電電力値 | 0xC8 | ul x2 8b | 7201c8080000000000000fa0 | 最小: 0 W 最大:4000 W |
最小最大放電電力値 | 0xC9 | ul x2 8b | 7201c9080000000000000fa0 | 最小: 0W 最大: 4000W |
最小最大充電電流値 | 0xCA | us x2 4b | 5201ca00 | 52:不可応答 |
最小最大放電電流値 | 0xCB | us x2 4b | 5201cb00 | 52:不可応答 |
再連係許可設定 | 0xCC | uc 1b | 7201cc0141 | 41:許可 |
運転許可設定 | 0xCD | uc 1b | 7201cd0141 | 41:許可 |
自立運転許可設定 | 0xCE | uc 1b | 7201ce0141 | 41:許可 |
運転動作状態 | 0xCF | uc 1b | 7201cf0142 | 42:充電 |
AC定格電力量 | 0xC7 | ul 4b | 5201c700 | 52:不可応答 |
定格電力量 | 0xD0 | ul 4b | 7201d00400002648 | 9800 Wh |
定格容量 | 0xD1 | ul 4b | 5201d100 | 52:不可応答 |
定格電圧 | 0xD2 | us 2b | 5201d200 | 52:不可応答 |
瞬時充放電電力計測値 | 0xD3 | sl 4b | 7201d304000004d1 | 1233 Wh |
瞬時充放電電流計測値 | 0xD4 | ss 2b | 5201d400 | 52:不可応答 |
瞬時充放電電圧計測値 | 0xD5 | ss 2b | 5201d500 | 52:不可応答 |
積算放電電力量計測値 | 0xD6 | ul 4b | 5201d600 | 52:不可応答 |
積算充電電力量計測値 | 0xD8 | ul 4b | 5201d800 | 52:不可応答 |
運転モード設定 | 0xDA | uc 1b | 7201da0146 | 46:自動 |
系統連係状態 | 0xDB | uc 1b | 7201db0102 |
02:系統連係(逆潮流不可) |
最小最大充電電力値(独立時) | 0xDC | ul x2 8b | 7201dc080000000000000fa0 | 最小: 0 W 最大: 4000 W |
最小最大放電電力値(独立時) | 0xDD | ul x2 8b | 7201dd080000000000000fa0 | 最小: 0 W 最大: 4000 W |
最小最大充電電流値(独立時) | 0xDE | us x2 4b | 5201de00 | 52:不可応答 |
最小最大放電電流値(独立時) | 0xDF | us x2 4b | 5201df00 | 52:不可応答 |
充放電量設定値1 | 0xE0 | sl 4b | 5201e000 | 52:不可応答 |
充放電量設定値2 | 0xE1 | sl 4b | 5201e100 | 52:不可応答 |
蓄電残量1 | 0xE2 | ul 4b | 7201e204000011ea | 4586 Wh |
蓄電残量2 | 0xE3 | us 2b | 5201e300 | 52:不可応答 |
蓄電残量3 | 0xE4 | uc 1b | 7201e40134 | 52 % |
劣化状態 | 0xE5 | uc 1b | 7201e50164 | 100 % |
蓄電池タイプ | 0xE6 | uc 1b | 7201e60104 | type: 04 |
充電量設定値1 | 0xE7 | ul 4b | 5201e700 | 52:不可応答 |
放電量設定値1 | 0xE8 | ul 4b | 5201e800 | 52:不可応答 |
充電量設定値2 | 0xE9 | ul 2b | 5201e900 | 52:不可応答 |
放電量設定値2 | 0xEA | ss 2b | 5201ea00 | 52:不可応答 |
充電電力設定値 | 0xEB | ul 4b | 7201eb0400000fa0 | 最小: 0 W 最大: 4000 W |
放電電力設定値 | 0xEC | ul 4b | 7201ec0400000fa0 | 最小: 0 W 最大: 4000 W |
充電電流設定値 | 0xED | us 2b | 5201ed00 | 52:不可応答 |
放電電流設定値 | 0xEE | us 2b | 5201ee00 | 52:不可応答 |
定格電圧(独立時) | 0xEF | us 2b | 5201ef00 | 52:不可応答 |
uc: unsigned char,us: unsgined short ,ul: unsigned long, sl: signed long, ss: signed short,
この中で、時系列でデータを取得して意味がありそうなのは、A4,A5,A8,A9,CF,D3,E2,E4 くらい?
D3(瞬時充放電電力計測値)は signed long なので、数値がマイナス(放電)とプラス(充電)がある。