1日天気が良かったときの発電量グラフ

  •  
  •  

11月3日と4日の48時間の発電量のグラフ。3日は太陽光を日の出から日の入りまでずっと受けていた発電にはベストの日だったか。4日も良い感じ。

3日は 8.6 kWh, 4日は 8.1 kWh の発電量だった模様。

HEMS SolarPanel 太陽光パネル 2 - Grafana_2.png

grafanaで、太陽光発電のグラフをdashboardに。

  •  
  •  

データを 5分毎にinfluxDB2に送り込んだのでこれを グラフ化する。

太陽光発電で、発電量について瞬間発電量と、積算電力量の差分の2通りでグラフ化してみた。

ある日のサンプル

HEMS SolarPanel 太陽光パネル 2 - Grafana.png

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つを直接比較できるように、単位計算のところでちょっと工夫している。

蓄電池関連のデータを python で取得して、influxdb2 に放り込む。

  •  
  •  
こちらも同様に 5分に1回、influxdbにデータを格納。
#
# 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())

太陽光発電量量を python で取得して、influxdb2 に放り込む。

  •  
  •  

とりあえず、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 レベルは必要ないだろう。

HEMS gatewayに echonetlite frame を投げつけてみる(蓄電池:機器オブジェクトスーパークラス)

  •  
  •  

続けて、蓄電池のほうのスーパークラスを確認してみる。

まず、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(不可応答)が多いのはどういうことだろうと思う。

HEMS gatewayに echonetlite frame を投げつけてみる(住宅用太陽光発電:機器オブジェクトスーパークラス)

  •  
  •  

ちょっと先走って、住宅用太陽光発電機器クラス規定(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

エアコンの HEMS応答

  •  
  •  

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に入れることにするか。。。

HEMS gatewayに echonetlite frame を投げつけてみる(蓄電池) その2

  •  
  •  

夜にまた ゲートウェイにリクエストしてみた。

プロパティ名称 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(%)

蓄電池設定(SOC)が勝手に変更される件

  •  
  •  

いまいろいろ設定を試して見ているが、時間帯価格変動のサービスに変えることを前提に、価格が安い時間(普通は夜間)に蓄電池に充電する機能を試してみる。

今のシステムだと、グリーンモードの場合に蓄電池の容量の何%まで充電するかを設定することができて、0%~50%まで10%刻みで設定できる。

とりあえず50%に設定してみたが、なぜか勝手にこれがリセットされて0%になっていることが続いた。

で、オムロンに電話してみたところ、これは「AI制御の設定」で「翌日の天気予報で充電制御」というのを有効にしていた場合に、翌日の天気が良いときは充電が期待されるので勝手に変更することがあるとのこと。なるほど。。。

でも、これって、充電能力と蓄電池の容量と、消費電力との兼ね合いになるんだろうけどそこまで賢くやってくれるのだろうか?

うちは一般家庭と比べて消費電力が多い、充電能力は低くてバッテリー100%になることはほぼ無いのではと思われるので、できるだけ安い時間帯で溜め込むのが良いと考えているのだが。。。

HEMS gatewayに echonetlite frame を投げつけてみる(蓄電池)

  •  
  •  

マルチキャストの 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 なので、数値がマイナス(放電)とプラス(充電)がある。