As I Please

MTのいんすとーるの練習と、その他びぼうろく・・・

タグ「RL7023」が付けられているもの


スマートメーター HEMS データの取得 for python3

次に、取得した接続情報を元に、HEMSのデータを取得する。HEMSスマートメーターからの出力で例示した ECHONETの「低圧スマート電力量メータクラス規定」で、素のデータを読み出すシンプルなscriptを。前ページで取得した Channel,Channel Page, Pan ID, PairID,ついでにLQI(これは不要と思うけど・・・),IPv6アドレスを指定してすみやかにデータ取得するようにした。コメントが重複している部分が多いが、自分用の備忘録として。。。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  see https://qiita.com/kanon700/items/d4df13d45c2a9d16b8b0
#
from __future__ import print_function
import sys
import serial
import time
# Bルート認証ID(東京電力パワーグリッドから郵送で送られてくる)
rbid  = "AAAABBBBCCCCDDDD"
# Bルート認証パスワード(東京電力パワーグリッドからメールで送られてくる)
rbpwd = "PASSWORDDDDDDD"
# シリアルポートデバイス名
#serialPortDev = 'COM3'  # Windows の場合
serialPortDev = '/dev/ttyUSB0'  # Linux(ラズパイなど)の場合
#serialPortDev = '/dev/cu.usbserial-A103BTPR'    # Mac の場合
#
ser = serial.Serial(serialPortDev, 115200)
#
# Bルート認証パスワード設定
wcom="SKSETPWD C " + rbpwd + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
# Bルート認証ID設定
wcom="SKSETRBID " + rbid + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
scanDuration = 4;   # スキャン時間。サンプルでは6なんだけど、4でも行けるので。(ダメなら増やして再試行)
scanRes = {} # スキャン結果の入れ物
scanRes["Channel"]="33"
scanRes["Channel Page"]="09"
scanRes["Pan ID"]="24EF"
scanRes["Addr"]="00FFEEDDCCBBAA99"
scanRes["LQI"]="32"
scanRes["PairID"]="12345678"
# スキャン結果からChannelを設定。
wcom="SKSREG S2 " + scanRes["Channel"] + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
# スキャン結果からPan IDを設定
wcom="SKSREG S3 " + scanRes["Pan ID"] + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
# MACアドレス(64bit)をIPV6リンクローカルアドレスに変換。
# (BP35A1の機能を使って変換しているけど、単に文字列変換すればいいのではという話も??)
#ser.write("SKLL64 " + scanRes["Addr"] + "\r\n")
#print(ser.readline(), end="") # エコーバック
#ser.readline()
#time.sleep(2)
#ipv6Addr = ser.readline().strip()
#
# 直接指定しちゃう
ipv6Addr = "FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99"
#
# いきなり SKJOINをしてもつながらないことがあるため、SKPINGしてみます。
#
while True:
    wcom="SKPING " + ipv6Addr + "\r\n"
    ser.write(str.encode(wcom))
    print(ser.readline().decode('utf-8'), end="",flush=True) # echo
#    print(ser.readline(), end="") # res
    pingres = ser.readline() #
    if pingres.startswith(b'OK'):
       print("come OK break")
       break
    pingres = ser.readline() #
    if pingres.startswith(b'OK'):
       print("come OK break")
       break
#
#
# PANA 接続シーケンスを開始します。
#
time.sleep(1)
wcom="SKJOIN " + ipv6Addr + "\r\n"
ser.write(str.encode(wcom))
time.sleep(1)
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
#ser.readline()
#print(ser.readline(), end="") # OKが来るはず(チェック無し)
#ser.readline()
# PANA 接続完了待ち(10行ぐらいなんか返してくる)
bConnected = False
while not bConnected :
    line = ser.readline()
    print(line.decode('utf-8'), end="",flush=True)
    if line.startswith(b'EVENT 24') :
#        print("PANA 接続失敗")
        sys.exit()  #### 糸冬了 ####
    elif line.startswith(b'EVENT 25') :
        # 接続完了!
        print("connect PANA OK",flush=True)
        bConnected = True
# これ以降、シリアル通信のタイムアウトを設定
ser.timeout = 2
# スマートメーターがインスタンスリスト通知を投げてくる
# (ECHONET-Lite_Ver.1.12_02.pdf p.4-16)
#print(ser.readline(), end="") #無視
#
#
#  Appendix_H.pdf
#
#
# python3 で変更。key を str として定義。
# bytes.fromhex(key) にて、byte1文字に変換し、コマンドとして送信するよ
echonetproperty = { '80':["status","80","動作状態"],  'D3':["keisuu","D3","係数"],'D7':["sekisanpower","D7","積算電力有効桁数"], \
                    'E0':["sekisanpowerplus","E0","積算電力量計測値(正方向計測値)"]    ,'E1':["sekisanpowerplusminus","E1","積算電力量単位"],\
                    'E2':["sekisanpowerplushis","E2","積算電力量計測値履歴1"] ,'E3':["sekisanpowerminus","E3","積算電力量計測値(逆方向計測値)"],\
                    'E4':["sekisanpowerminushis","E4","積算電力量計測値履歴1(逆方向計測値)"],'E5':["sekisanhiscollectday","E5","積算履歴収集日1"],\
                    'E7':["nowpower","E7","瞬時電力計測値"], 'E8':["nowcurrent","E8","瞬時電流計測値"],'EA':["teijisekisanpowerplus","EA","定時積算電力量計測値(正方向計測値)"],\
                    'EB':["teijisekisanpowerminus","EB","定時積算電力量計測値(逆方向計測値)"],'EC':["teijisekisanpowerplusminus","EC","積算電力量計測値履歴1(正方向,逆方向)"],\
                    'ED':["sekisanhiscollectday2","ED","積算履歴収集日2"]  }
#
#
#
for key,value in sorted(echonetproperty.items()):
  keystr  = str(echonetproperty[key][1])
  keydesc = echonetproperty[key][2]
  print(key,keystr,keydesc,end="",flush=True)
#  print(key,keystr,keydesc)
#  print (u"EPC = {0}".format(keyecho))
  while True:
    # ECHONET Lite フレーム作成
    #  参考資料
    #  ・ECHONET-Lite_Ver.1.12_02.pdf (以下 EL)
    #  ・Appendix_H.pdf (以下 AppH)
    #  for EPC = 0xE7  (瞬時電力計測値)
    echonetLiteFrame = b"" # echonetLiteFrame type is byte
    echonetLiteFrame += b'\x10\x81'      # EHD (参考:EL p.3-2) : 2byte目は \x81(電文形式1) or \x82(電文形式2)
    echonetLiteFrame += b'\x00\x01'      # TID (参考:EL p.3-3) : TID= Transaction ID
    # ここから EDATA
    echonetLiteFrame += b'\x05\xFF\x01'  # SEOJ (参考:EL p.3-3 AppH p.3-408~) : コントローラクラス規定
    echonetLiteFrame += b'\x02\x88\x01'  # DEOJ (参考:EL p.3-3 AppH p.3-274~) : 低圧スマート電力量メータクラス規定
    echonetLiteFrame += b'\x62'          # ESV(62:プロパティ値読み出し要求) (参考:EL p.3-5)  : Get
    echonetLiteFrame += b'\x01'          # OPC(1個)(参考:EL p.3-7)
#    echonetLiteFrame += "\x02"          # OPC(2個)(参考:EL p.3-7)   : 2つのデータを取得する。
#    echonetLiteFrame += "\xE7"          # EPC(参考:EL p.3-7 AppH p.3-275) : 瞬時電力計測値
    key2=bytes.fromhex(key)
#    print("key2 = ",key2,flush=True)
    echonetLiteFrame += key2
    echonetLiteFrame += b'\x00'          # PDC(参考:EL p.3-9)
#    echonetLiteFrame += "\xE8"          # EPC(参考:EL p.3-7 AppH p.3-275) : 瞬時電流計測値 R相T相
#    echonetLiteFrame += "\x00"          # PDC(参考:EL p.3-9)
    # コマンド送信
#    command = "SKSENDTO 1 {0} 0E1A 1 {1:04X} {2}".format(ipv6Addr, len(echonetLiteFrame), echonetLiteFrame)
    com1 = "SKSENDTO 1 {0} 0E1A 1 {1:04X} ".format(ipv6Addr, len(echonetLiteFrame))
    command = com1.encode() + echonetLiteFrame
#    print(command,end="",flush=True)
    ser.write(command)
#    ser.write(str.encode(command))
#    print(readl.decode('utf-8').encode(),end="",flush=True)
    print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
    print(ser.readline().decode('utf-8'), end="",flush=True) # EVENT 21 が来るはず(チェック無し)
    print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
#    line = ser.readline()         # ERXUDPが来るはず
    line = ser.readline()  # 本来のデータ?をこちらで取得する
    # 取りこぼしたりして変なデータを拾うことがあるので
    # チェックを厳しめにしてます。
    if line.startswith(b'ERXUDP') :
#        print("ERXUDP found",flush=True)
        print(line.decode('utf-8'),end="",flush=True)
        line = line.decode()
        cols = line.strip().split(' ')
#        print(cols)
        res = cols[8]   # UDP受信データ部分
#        print("res=",res)
        #tid = res[4:4+4];
        seoj = res[8:8+6]
        #deoj = res[14,14+6]
        ESV = res[20:20+2]
        if seoj == "028801" and ESV == "72" :
            # スマートメーター(028801)から来た応答(72)なら
            EPC = res[24:24+2]
#            if EPC == "E7" :
            if EPC == keystr :
                # 内容が瞬時電力計測値(E7)だったら
#                hexPower = line[-8:]    # 最後の4バイト(16進数で8文字)が瞬時電力計測値
#                intPower = int(hexPower, 16)
#                print(u"瞬時電力計測値:{0}[W]".format(intPower))
#                print((u"瞬時電力計測値:{0}[W]".format(intPower)).encode('utf-8'))
                break
#
ser.close()
コメントにも書いているが、SKJOINをいきなり行っても繋がらないケースがままあった。SKPING を叩いて寝ているデバイス?インターフェースを起こす(?)必要がある感じ。
出力結果は次の通り(一部省略)
SKSETPWD C PASSWORDDDDDDD
OK
SKSETRBID AAAABBBBCCCCDDDD
OK
SKSREG S2 33
OK
SKSREG S3 24EF
OK
SKPING FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
come OK break
EPONG FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
SKJOIN FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
OK
(省略)
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
EVENT 25 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
connect PANA OK
80 80 動作状態SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FF02:0000:0000:0000:0000:0000:0000:0001 0E1A 0E1A 00808700301224EF 1 0012 108100000EF0010EF0017301D50401028801
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201800130
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201800130
D3 D3 係数SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201800130
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201D30400000001
D7 D7 積算電力有効桁数SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201D30400000001
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201D70106
E0 E0 積算電力量計測値(正方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 01
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201D70106
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E00400015208
E1 E1 積算電力量単位SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 01
OK
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201E10101
E2 E2 積算電力量計測値履歴1SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 00D0 1081000102880105FF017201E2C200FFFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE
E3 E3 積算電力量計測値(逆方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E3040000000A
E4 E4 積算電力量計測値履歴1(逆方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 00D0 1081000102880105FF017201E4C200FFFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE
E5 E5 積算履歴収集日1SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201E501FF
E7 E7 瞬時電力計測値SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 01
OK
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E70400000274
E8 E8 瞬時電流計測値SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E80400320014
EA EA 定時積算電力量計測値(正方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0019 1081000102880105FF017201EA0B07E4030E081E0000015206
EB EB 定時積算電力量計測値(逆方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0019 1081000102880105FF017201EB0B07E4030E081E000000000A
EC EC 積算電力量計測値履歴1(正方向,逆方向)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 001D 1081000102880105FF017201EC0FFFFFFFFFFFFF01FFFFFFFEFFFFFFFE
ED ED 積算履歴収集日2SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0015 1081000102880105FF017201ED07FFFFFFFFFFFF01

スマートメーター HEMS データの取得 for python3

次に、取得した接続情報を元に、HEMSのデータを取得する。HEMSスマートメーターからの出力で例示した ECHONETの「低圧スマート電力量メータクラス規定」で、素のデータを読み出すシンプルなscriptを。前ページで取得した Channel,Channel Page, Pan ID, PairID,ついでにLQI(これは不要と思うけど・・・),IPv6アドレスを指定してすみやかにデータ取得するようにした。コメントが重複している部分が多いが、自分用の備忘録として。。。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  see https://qiita.com/kanon700/items/d4df13d45c2a9d16b8b0
#
from __future__ import print_function
import sys
import serial
import time
# Bルート認証ID(東京電力パワーグリッドから郵送で送られてくる)
rbid  = "AAAABBBBCCCCDDDD"
# Bルート認証パスワード(東京電力パワーグリッドからメールで送られてくる)
rbpwd = "PASSWORDDDDDDD"
# シリアルポートデバイス名
#serialPortDev = 'COM3'  # Windows の場合
serialPortDev = '/dev/ttyUSB0'  # Linux(ラズパイなど)の場合
#serialPortDev = '/dev/cu.usbserial-A103BTPR'    # Mac の場合
#
ser = serial.Serial(serialPortDev, 115200)
#
# Bルート認証パスワード設定
wcom="SKSETPWD C " + rbpwd + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
# Bルート認証ID設定
wcom="SKSETRBID " + rbid + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
scanDuration = 4;   # スキャン時間。サンプルでは6なんだけど、4でも行けるので。(ダメなら増やして再試行)
scanRes = {} # スキャン結果の入れ物
scanRes["Channel"]="33"
scanRes["Channel Page"]="09"
scanRes["Pan ID"]="24EF"
scanRes["Addr"]="00FFEEDDCCBBAA99"
scanRes["LQI"]="32"
scanRes["PairID"]="12345678"
# スキャン結果からChannelを設定。
wcom="SKSREG S2 " + scanRes["Channel"] + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
# スキャン結果からPan IDを設定
wcom="SKSREG S3 " + scanRes["Pan ID"] + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
# MACアドレス(64bit)をIPV6リンクローカルアドレスに変換。
# (BP35A1の機能を使って変換しているけど、単に文字列変換すればいいのではという話も??)
#ser.write("SKLL64 " + scanRes["Addr"] + "\r\n")
#print(ser.readline(), end="") # エコーバック
#ser.readline()
#time.sleep(2)
#ipv6Addr = ser.readline().strip()
#
# 直接指定しちゃう
ipv6Addr = "FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99"
#
# いきなり SKJOINをしてもつながらないことがあるため、SKPINGしてみます。
#
while True:
    wcom="SKPING " + ipv6Addr + "\r\n"
    ser.write(str.encode(wcom))
    print(ser.readline().decode('utf-8'), end="",flush=True) # echo
#    print(ser.readline(), end="") # res
    pingres = ser.readline() #
    if pingres.startswith(b'OK'):
       print("come OK break")
       break
    pingres = ser.readline() #
    if pingres.startswith(b'OK'):
       print("come OK break")
       break
#
#
# PANA 接続シーケンスを開始します。
#
time.sleep(1)
wcom="SKJOIN " + ipv6Addr + "\r\n"
ser.write(str.encode(wcom))
time.sleep(1)
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
#ser.readline()
#print(ser.readline(), end="") # OKが来るはず(チェック無し)
#ser.readline()
# PANA 接続完了待ち(10行ぐらいなんか返してくる)
bConnected = False
while not bConnected :
    line = ser.readline()
    print(line.decode('utf-8'), end="",flush=True)
    if line.startswith(b'EVENT 24') :
#        print("PANA 接続失敗")
        sys.exit()  #### 糸冬了 ####
    elif line.startswith(b'EVENT 25') :
        # 接続完了!
        print("connect PANA OK",flush=True)
        bConnected = True
# これ以降、シリアル通信のタイムアウトを設定
ser.timeout = 2
# スマートメーターがインスタンスリスト通知を投げてくる
# (ECHONET-Lite_Ver.1.12_02.pdf p.4-16)
#print(ser.readline(), end="") #無視
#
#
#  Appendix_H.pdf
#
#
# python3 で変更。key を str として定義。
# bytes.fromhex(key) にて、byte1文字に変換し、コマンドとして送信するよ
echonetproperty = { '80':["status","80","動作状態"],  'D3':["keisuu","D3","係数"],'D7':["sekisanpower","D7","積算電力有効桁数"], \
                    'E0':["sekisanpowerplus","E0","積算電力量計測値(正方向計測値)"]    ,'E1':["sekisanpowerplusminus","E1","積算電力量単位"],\
                    'E2':["sekisanpowerplushis","E2","積算電力量計測値履歴1"] ,'E3':["sekisanpowerminus","E3","積算電力量計測値(逆方向計測値)"],\
                    'E4':["sekisanpowerminushis","E4","積算電力量計測値履歴1(逆方向計測値)"],'E5':["sekisanhiscollectday","E5","積算履歴収集日1"],\
                    'E7':["nowpower","E7","瞬時電力計測値"], 'E8':["nowcurrent","E8","瞬時電流計測値"],'EA':["teijisekisanpowerplus","EA","定時積算電力量計測値(正方向計測値)"],\
                    'EB':["teijisekisanpowerminus","EB","定時積算電力量計測値(逆方向計測値)"],'EC':["teijisekisanpowerplusminus","EC","積算電力量計測値履歴1(正方向,逆方向)"],\
                    'ED':["sekisanhiscollectday2","ED","積算履歴収集日2"]  }
#
#
#
for key,value in sorted(echonetproperty.items()):
  keystr  = str(echonetproperty[key][1])
  keydesc = echonetproperty[key][2]
  print(key,keystr,keydesc,end="",flush=True)
#  print(key,keystr,keydesc)
#  print (u"EPC = {0}".format(keyecho))
  while True:
    # ECHONET Lite フレーム作成
    #  参考資料
    #  ・ECHONET-Lite_Ver.1.12_02.pdf (以下 EL)
    #  ・Appendix_H.pdf (以下 AppH)
    #  for EPC = 0xE7  (瞬時電力計測値)
    echonetLiteFrame = b"" # echonetLiteFrame type is byte
    echonetLiteFrame += b'\x10\x81'      # EHD (参考:EL p.3-2) : 2byte目は \x81(電文形式1) or \x82(電文形式2)
    echonetLiteFrame += b'\x00\x01'      # TID (参考:EL p.3-3) : TID= Transaction ID
    # ここから EDATA
    echonetLiteFrame += b'\x05\xFF\x01'  # SEOJ (参考:EL p.3-3 AppH p.3-408~) : コントローラクラス規定
    echonetLiteFrame += b'\x02\x88\x01'  # DEOJ (参考:EL p.3-3 AppH p.3-274~) : 低圧スマート電力量メータクラス規定
    echonetLiteFrame += b'\x62'          # ESV(62:プロパティ値読み出し要求) (参考:EL p.3-5)  : Get
    echonetLiteFrame += b'\x01'          # OPC(1個)(参考:EL p.3-7)
#    echonetLiteFrame += "\x02"          # OPC(2個)(参考:EL p.3-7)   : 2つのデータを取得する。
#    echonetLiteFrame += "\xE7"          # EPC(参考:EL p.3-7 AppH p.3-275) : 瞬時電力計測値
    key2=bytes.fromhex(key)
#    print("key2 = ",key2,flush=True)
    echonetLiteFrame += key2
    echonetLiteFrame += b'\x00'          # PDC(参考:EL p.3-9)
#    echonetLiteFrame += "\xE8"          # EPC(参考:EL p.3-7 AppH p.3-275) : 瞬時電流計測値 R相T相
#    echonetLiteFrame += "\x00"          # PDC(参考:EL p.3-9)
    # コマンド送信
#    command = "SKSENDTO 1 {0} 0E1A 1 {1:04X} {2}".format(ipv6Addr, len(echonetLiteFrame), echonetLiteFrame)
    com1 = "SKSENDTO 1 {0} 0E1A 1 {1:04X} ".format(ipv6Addr, len(echonetLiteFrame))
    command = com1.encode() + echonetLiteFrame
#    print(command,end="",flush=True)
    ser.write(command)
#    ser.write(str.encode(command))
#    print(readl.decode('utf-8').encode(),end="",flush=True)
    print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
    print(ser.readline().decode('utf-8'), end="",flush=True) # EVENT 21 が来るはず(チェック無し)
    print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
#    line = ser.readline()         # ERXUDPが来るはず
    line = ser.readline()  # 本来のデータ?をこちらで取得する
    # 取りこぼしたりして変なデータを拾うことがあるので
    # チェックを厳しめにしてます。
    if line.startswith(b'ERXUDP') :
#        print("ERXUDP found",flush=True)
        print(line.decode('utf-8'),end="",flush=True)
        line = line.decode()
        cols = line.strip().split(' ')
#        print(cols)
        res = cols[8]   # UDP受信データ部分
#        print("res=",res)
        #tid = res[4:4+4];
        seoj = res[8:8+6]
        #deoj = res[14,14+6]
        ESV = res[20:20+2]
        if seoj == "028801" and ESV == "72" :
            # スマートメーター(028801)から来た応答(72)なら
            EPC = res[24:24+2]
#            if EPC == "E7" :
            if EPC == keystr :
                # 内容が瞬時電力計測値(E7)だったら
#                hexPower = line[-8:]    # 最後の4バイト(16進数で8文字)が瞬時電力計測値
#                intPower = int(hexPower, 16)
#                print(u"瞬時電力計測値:{0}[W]".format(intPower))
#                print((u"瞬時電力計測値:{0}[W]".format(intPower)).encode('utf-8'))
                break
#
ser.close()
コメントにも書いているが、SKJOINをいきなり行っても繋がらないケースがままあった。SKPING を叩いて寝ているデバイス?インターフェースを起こす(?)必要がある感じ。
出力結果は次の通り(一部省略)
SKSETPWD C PASSWORDDDDDDD
OK
SKSETRBID AAAABBBBCCCCDDDD
OK
SKSREG S2 33
OK
SKSREG S3 24EF
OK
SKPING FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
come OK break
EPONG FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
SKJOIN FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
OK
(省略)
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
EVENT 25 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99
connect PANA OK
80 80 動作状態SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FF02:0000:0000:0000:0000:0000:0000:0001 0E1A 0E1A 00808700301224EF 1 0012 108100000EF0010EF0017301D50401028801
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201800130
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201800130
D3 D3 係数SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201800130
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201D30400000001
D7 D7 積算電力有効桁数SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201D30400000001
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201D70106
E0 E0 積算電力量計測値(正方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 01
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201D70106
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E00400015208
E1 E1 積算電力量単位SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 01
OK
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201E10101
E2 E2 積算電力量計測値履歴1SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 00D0 1081000102880105FF017201E2C200FFFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE
E3 E3 積算電力量計測値(逆方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E3040000000A
E4 E4 積算電力量計測値履歴1(逆方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 00D0 1081000102880105FF017201E4C200FFFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE
E5 E5 積算履歴収集日1SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 000F 1081000102880105FF017201E501FF
E7 E7 瞬時電力計測値SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 01
OK
SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E70400000274
E8 E8 瞬時電流計測値SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0012 1081000102880105FF017201E80400320014
EA EA 定時積算電力量計測値(正方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0019 1081000102880105FF017201EA0B07E4030E081E0000015206
EB EB 定時積算電力量計測値(逆方向計測値)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0019 1081000102880105FF017201EB0B07E4030E081E000000000A
EC EC 積算電力量計測値履歴1(正方向,逆方向)SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 001D 1081000102880105FF017201EC0FFFFFFFFFFFFF01FFFFFFFEFFFFFFFE
ED ED 積算履歴収集日2SKSENDTO 1 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 00
OK
ERXUDP FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99 FE80:0000:0000:0000:02FF:EEDD:CCBB:AA88 0E1A 0E1A 00808700301224EF 1 0015 1081000102880105FF017201ED07FFFFFFFFFFFF01

スマートメーター HEMS デバイススキャン for python3

以前書いた、HEMS 機器からのデータの取り出し関連で、とても参考にさせてもらった スマートメーターの情報を最安ハードウェアで引っこ抜くをベースにプログラムを書いた。
このサンプルプログラムはpython2 でのプログラムだったので、今後のためにもpython3 用に書き直しておいたほうが良かったので、作成してみた。例示してあるプログラムはデバイスを探索してデータを取得するのだが、定期的にデータを取得して時系列データを作成するのであれば、デバイス探索は毎回繰り返さなくても良い(スマートメーターのサイトローカルのIPv6アドレスで特定できる)ので、(1)デバイスを探索し、アクセスするための諸元データを取得する。(2)データを定期的に取得する。の2つにわけてしまうほうが良さそうで、それを実施してみた。 まずは (1)の python3 に自分なりに書き換えたものを。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import serial
import time
# Bルート認証ID(東京電力パワーグリッドから郵送で送られてくる)
rbid  = "AAAABBBBCCCCDDDD"
# Bルート認証パスワード(東京電力パワーグリッドからメールで送られてくる)
rbpwd = "PASSWORDDDDDDD"
# シリアルポートデバイス名
#serialPortDev = 'COM3'  # Windows の場合
serialPortDev = '/dev/ttyUSB0'  # Linux(ラズパイなど)の場合
#serialPortDev = '/dev/cu.usbserial-A103BTPR'    # Mac の場合
#
ser = serial.Serial(serialPortDev, 115200)
#
wcom = "SKVER\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # バージョン
#
# Bルート認証パスワード設定
wcom = "SKSETPWD C " + rbpwd + "\r\n"
ser.write(str.encode(wcom))
ser.readline() # エコーバック
# Bルート認証ID設定
wcom = "SKSETRBID " + rbid + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
ser.readline()
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
ser.readline()
scanDuration = 4;   # スキャン時間。サンプルでは6なんだけど、4でも行けるので。(ダメなら増やして再試行)
scanRes = {} # スキャン結果の入れ物
# スキャンのリトライループ(何か見つかるまで)
#while not scanRes.has_key("Channel") :  python2
while not 'Channel' in scanRes :
        # アクティブスキャン(IE あり)を行う
        # 時間かかります。10秒ぐらい?
        #ser.write(str.encode("SKSCAN 2 FFFFFFFF " + str(scanDuration) + "\r\n"))
        wcom = "SKSCAN 2 FFFFFFFF " + str(scanDuration) + "\r\n"
        ser.write(str.encode(wcom))
        # スキャン1回について、スキャン終了までのループ
        scanEnd = False
        while not scanEnd :
            line = ser.readline()
            print(line.decode(), end="",flush=True)
#
            line = line.decode()
            if line.startswith("EVENT 22") :
#                # スキャン終わったよ(見つかったかどうかは関係なく)
                scanEnd = True
            elif line.startswith("  ") :
                # スキャンして見つかったらスペース2個あけてデータがやってくる
                # 例
                #  Channel:39
                #  Channel Page:09
                #  Pan ID:FFFF
                #  Addr:FFFFFFFFFFFFFFFF
                #  LQI:A7
                #  PairID:FFFFFFFF
                cols = line.strip().split(':')
                scanRes[cols[0]] = cols[1]
        scanDuration+=1
        if 7 < scanDuration and not scanRes.has_key("Channel"):
            # 引数としては14まで指定できるが、7で失敗したらそれ以上は無駄っぽい
            print("スキャンリトライオーバー")
            sys.exit()  #### 糸冬了 ####
これを実行すると次のような結果が出てくる。
raspberrypi:~/smartmeter3# ./test-bs_scan.py
SKVER
EVER 1.2.8
SKSETPWD C PASSWORDDDDDDD
SKSETRBID AAAABBBBCCCCDDDD
SKSCAN 2 FFFFFFFF 4
OK
EVENT 20 FE80:0000:0000:0000:1234:5678:9ABC:DEF0
EPANDESC
  Channel:33
  Channel Page:09
  Pan ID:24EF
  Addr:00FFEEDDCCBBAA99
  LQI:32
  PairID:12345678
EVENT 22 FE80:0000:0000:0000:1234:5678:9ABC:DEF0
Addr は64bit、これを IPv6のリンクローカルアドレスに変換するには SKLL64 コマンドを使う、ようだが 単純に、MACアドレス 48bit を、EUI-64で変換する場合の、'FF:FE'の挿入をやめただけのよう。先頭の7bit目を反転させて、後半64bit の IPv6 アドレスにしてしまえば良いみたい。例の 'Addr:00FFEEDDCCBBAA99' なら、IPv6アドレスは 'FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99' になる、と思われる。
これで取得できた Channel, Channel Page, Pan ID,PairID, 生成した IPv6アドレスを利用して定期的なデータ取得 scriptを動かす。

スマートメーター HEMS デバイススキャン for python3

以前書いた、HEMS 機器からのデータの取り出し関連で、とても参考にさせてもらった スマートメーターの情報を最安ハードウェアで引っこ抜くをベースにプログラムを書いた。
このサンプルプログラムはpython2 でのプログラムだったので、今後のためにもpython3 用に書き直しておいたほうが良かったので、作成してみた。例示してあるプログラムはデバイスを探索してデータを取得するのだが、定期的にデータを取得して時系列データを作成するのであれば、デバイス探索は毎回繰り返さなくても良い(スマートメーターのサイトローカルのIPv6アドレスで特定できる)ので、(1)デバイスを探索し、アクセスするための諸元データを取得する。(2)データを定期的に取得する。の2つにわけてしまうほうが良さそうで、それを実施してみた。 まずは (1)の python3 に自分なりに書き換えたものを。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import serial
import time
# Bルート認証ID(東京電力パワーグリッドから郵送で送られてくる)
rbid  = "AAAABBBBCCCCDDDD"
# Bルート認証パスワード(東京電力パワーグリッドからメールで送られてくる)
rbpwd = "PASSWORDDDDDDD"
# シリアルポートデバイス名
#serialPortDev = 'COM3'  # Windows の場合
serialPortDev = '/dev/ttyUSB0'  # Linux(ラズパイなど)の場合
#serialPortDev = '/dev/cu.usbserial-A103BTPR'    # Mac の場合
#
ser = serial.Serial(serialPortDev, 115200)
#
wcom = "SKVER\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
print(ser.readline().decode('utf-8'), end="",flush=True) # バージョン
#
# Bルート認証パスワード設定
wcom = "SKSETPWD C " + rbpwd + "\r\n"
ser.write(str.encode(wcom))
ser.readline() # エコーバック
# Bルート認証ID設定
wcom = "SKSETRBID " + rbid + "\r\n"
ser.write(str.encode(wcom))
print(ser.readline().decode('utf-8'), end="",flush=True) # エコーバック
ser.readline()
print(ser.readline().decode('utf-8'), end="",flush=True) # OKが来るはず(チェック無し)
ser.readline()
scanDuration = 4;   # スキャン時間。サンプルでは6なんだけど、4でも行けるので。(ダメなら増やして再試行)
scanRes = {} # スキャン結果の入れ物
# スキャンのリトライループ(何か見つかるまで)
#while not scanRes.has_key("Channel") :  python2
while not 'Channel' in scanRes :
        # アクティブスキャン(IE あり)を行う
        # 時間かかります。10秒ぐらい?
        #ser.write(str.encode("SKSCAN 2 FFFFFFFF " + str(scanDuration) + "\r\n"))
        wcom = "SKSCAN 2 FFFFFFFF " + str(scanDuration) + "\r\n"
        ser.write(str.encode(wcom))
        # スキャン1回について、スキャン終了までのループ
        scanEnd = False
        while not scanEnd :
            line = ser.readline()
            print(line.decode(), end="",flush=True)
#
            line = line.decode()
            if line.startswith("EVENT 22") :
#                # スキャン終わったよ(見つかったかどうかは関係なく)
                scanEnd = True
            elif line.startswith("  ") :
                # スキャンして見つかったらスペース2個あけてデータがやってくる
                # 例
                #  Channel:39
                #  Channel Page:09
                #  Pan ID:FFFF
                #  Addr:FFFFFFFFFFFFFFFF
                #  LQI:A7
                #  PairID:FFFFFFFF
                cols = line.strip().split(':')
                scanRes[cols[0]] = cols[1]
        scanDuration+=1
        if 7 < scanDuration and not scanRes.has_key("Channel"):
            # 引数としては14まで指定できるが、7で失敗したらそれ以上は無駄っぽい
            print("スキャンリトライオーバー")
            sys.exit()  #### 糸冬了 ####
これを実行すると次のような結果が出てくる。
raspberrypi:~/smartmeter3# ./test-bs_scan.py
SKVER
EVER 1.2.8
SKSETPWD C PASSWORDDDDDDD
SKSETRBID AAAABBBBCCCCDDDD
SKSCAN 2 FFFFFFFF 4
OK
EVENT 20 FE80:0000:0000:0000:1234:5678:9ABC:DEF0
EPANDESC
  Channel:33
  Channel Page:09
  Pan ID:24EF
  Addr:00FFEEDDCCBBAA99
  LQI:32
  PairID:12345678
EVENT 22 FE80:0000:0000:0000:1234:5678:9ABC:DEF0
Addr は64bit、これを IPv6のリンクローカルアドレスに変換するには SKLL64 コマンドを使う、ようだが 単純に、MACアドレス 48bit を、EUI-64で変換する場合の、'FF:FE'の挿入をやめただけのよう。先頭の7bit目を反転させて、後半64bit の IPv6 アドレスにしてしまえば良いみたい。例の 'Addr:00FFEEDDCCBBAA99' なら、IPv6アドレスは 'FE80:0000:0000:0000:02FF:EEDD:CCBB:AA99' になる、と思われる。
これで取得できた Channel, Channel Page, Pan ID,PairID, 生成した IPv6アドレスを利用して定期的なデータ取得 scriptを動かす。