とりあえず、いま、HEMSで取ってきたデータを、influx と Google Spreadsheet に投げ込んでいるスクリプトを再掲。
#!/root/venv/bin/python3.9 # # put_gcp_sheet4.py # 2024/06/08 # # merge get HEMS data and put_gcp_sheet # from __future__ import print_function # for google spread sheet from google.oauth2 import service_account from googleapiclient.discovery import build # for HEMS import sys import serial import time import argparse import datetime from datetime import datetime,timezone,timedelta #import influxdb from icecream import ic ic.disable() # from influxdb_client import InfluxDBClient,Point from influxdb_client.client.write_api import SYNCHRONOUS # from dotenv import load_dotenv import os # setting for google spread sheet SPREADSHEET_ID = os.getenv('SPREADSHEET_ID') RANGE_NAME = 'Sheet1!A:G' SCOPES = ['https://www.googleapis.com/auth/spreadsheets'] SERVICE_ACCOUNT_FILE = os.getenv('SERVICE_ACCOUNT_FILE') #creds = Credentials.from_service_account_file(CREDENTIALS_FILE, scopes=['https://www.googleapis.com/auth/spreadsheets']) creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES) service = build('sheets', 'v4', credentials=creds) # setting for HEMS ( from ../influs_sm39.py ) org = "homemetrics" bucket = "environment" token = os.getenv('token') clienturl = os.getenv('CLIENTURL') client = InfluxDBClient(url=clienturl,token=token,org=org) write_api = client.write_api(write_options=SYNCHRONOUS) query_api = client.query_api() # def jsttimestr(unix_time,tz_offset_hours=9): # 現在のUTC時刻を取得 dt_utc = datetime.fromtimestamp(unix_time,tz=timezone.utc) # 任意のタイムゾーン(例:日本標準時 JST) target_tz = timezone(timedelta(hours=tz_offset_hours)) dt_target_tz = dt_utc.astimezone(target_tz) # 汎用的なフォーマットで時刻を文字列に変換 formatted_time = dt_target_tz.strftime('%Y-%m-%d %H:%M:%S%z') # タイムゾーンオフセットを '+09:00' の形式に変換 formatted_time = formatted_time[:-2] + ':' + formatted_time[-2:] return(formatted_time) # # ################## for hems data and write influxdb # Bルート認証ID(東京電力パワーグリッドから郵送で送られてくる) rbid = os.getenv('RBID') # Bルート認証パスワード(東京電力パワーグリッドからメールで送られてくる) rbpwd = os.getenv('RBPWD') # シリアルポートデバイス名 #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)) # Bルート認証ID設定 wcom="SKSETRBID " + rbid + "\r\n" ser.write(str.encode(wcom)) #scanDuration = 4; # スキャン時間。サンプルでは6なんだけど、4でも行けるので。(ダメなら増やして再試行) scanRes = {} # スキャン結果の格納用 # scanRes["Channel"]="33" scanRes["Channel Page"]="09" scanRes["Pan ID"]="24EF" scanRes["Addr"]="00808700301224EF" scanRes["LQI"]="3C" scanRes["PairID"]="00E798AF" # スキャン結果からChannelを設定。 wcom="SKSREG S3 " + scanRes["Pan ID"] + "\r\n" ser.write(str.encode(wcom)) ser.readline() ser.readline() # # スキャン結果からPan IDを設定 wcom="SKSREG S3 " + scanRes["Pan ID"] + "\r\n" ser.write(str.encode(wcom)) #print(ser.readline(), end="") # エコーバック ser.readline() #print(ser.readline(), end="") # OKが来るはず(チェック無し) ser.readline() # 直接指定する ipv6Addr = "FE80:0000:0000:0000:0280:AABB:CCDD:EEFF" # いきなり SKJOINをしてもつながらないことがあるため、SKPINGする。 # while True: wcom="SKPING " + ipv6Addr + "\r\n" ser.write(str.encode(wcom)) time.sleep(2) pingres = ser.readline() # if pingres.startswith(b"OK"): break # PANA 接続シーケンスを開始 #time.sleep(1) wcom="SKJOIN " + ipv6Addr + "\r\n" ser.write(str.encode(wcom)) # PANA 接続完了待ち(10行ぐらいなんか返してくる) bConnected = False while not bConnected : line = ser.readline() if line.startswith(b"EVENT 24") : # print("PANA 接続失敗") sys.exit() #### 糸冬了 #### elif line.startswith(b"EVENT 25") : # 接続完了! bConnected = True # # これ以降、シリアル通信のタイムアウトを設定 ser.timeout = 2 # スマートメーターがインスタンスリスト通知を投げてくる # (ECHONET-Lite_Ver.1.12_02.pdf p.4-16) # # その瞬間値として取るべき値は、 # EPC: 0xE0 : 積算電力量計測値(正方向計測値) # 0xE3 : 積算電力量計測値(逆方向計測値) # 0xE7 : 瞬時電力計測値 # 0xE8 : 瞬時電流計測値 (R相、T相両方の値が入っている) # 0xEA : 定時積算電力量計測値(正方向計測値) これは不要?0xE0と同じ? # 0xEB : 定時積算電力量計測値(逆方向計測値) これも不要? 0xE3と同じ? # の6つでいいか?(または、最初の4つ?) # while True: # ECHONET Lite フレーム作成 # 参考資料 # ・ECHONET-Lite_Ver.1.12_02.pdf (以下 EL) : 最新版は ver 1.13_02 # ・Appendix_H.pdf (以下 AppH) : 最新版は J (2018/07/30現在) # 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 += b'\xE7' # EPC(参考:EL p.3-7 AppH p.3-275) : 瞬時電力計測値 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) # コマンド送信 com1 = "SKSENDTO 1 {0} 0E1A 1 {1:04X} ".format(ipv6Addr, len(echonetLiteFrame)) command = com1.encode() + echonetLiteFrame ser.write(command) # print(ser.readline(), end="") # エコーバック ser.readline() # print(ser.readline(), end="") # EVENT 21 が来るはず(チェック無し) ser.readline() # print(ser.readline(), end="") # OKが来るはず(チェック無し) ser.readline() line = ser.readline() # ERXUDPが来るはず # print(line, end="") # 取りこぼしたりして変なデータを拾うことがあるので # チェックを厳しめにしてます。 if line.startswith(b"ERXUDP") : line = line.decode() cols = line.strip().split(' ') res = cols[8] # UDP受信データ部分 #tid = res[4:4+4]; seoj = res[8:8+6] #deoj = res[14,14+6] ESV = res[20:20+2] #OPC = res[22,22+2] if seoj == "028801" and ESV == "72" : # スマートメーター(028801)から来た応答(72)なら EPC = res[24:24+2] if EPC == "E7" : # 内容が瞬時電力計測値(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')) outputE7=intPower # print("E7:{0}".format(outputE7)) # time.sleep(5) break # ##################################################################################################### # # for EPC = 0xE8 (瞬時電流計測値) # while True: # ECHONET Lite フレーム作成 # 参考資料 # ・ECHONET-Lite_Ver.1.12_02.pdf (以下 EL) # ・Appendix_H.pdf (以下 AppH) # for EPC = 0xE7 (瞬時電力計測値) echonetLiteFrame = b"" 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) : 瞬時電力計測値 # echonetLiteFrame += "\x00" # PDC(参考:EL p.3-9) echonetLiteFrame += b'\xE8' # EPC(参考:EL p.3-7 AppH p.3-275) : 瞬時電流計測値 R相T相 echonetLiteFrame += b'\x00' # PDC(参考:EL p.3-9) # コマンド送信 com1 = "SKSENDTO 1 {0} 0E1A 1 {1:04X} ".format(ipv6Addr, len(echonetLiteFrame)) command = com1.encode() + echonetLiteFrame ser.write(command) # print(ser.readline(), end="") # エコーバック ser.readline() # print(ser.readline(), end="") # EVENT 21 が来るはず(チェック無し) ser.readline() # print(ser.readline(), end="") # OKが来るはず(チェック無し) ser.readline() line = ser.readline() # ERXUDPが来るはず # print(line, end="") # 取りこぼしたりして変なデータを拾うことがあるので # チェックを厳しめにしてます。 if line.startswith(b"ERXUDP") : line = line.decode() cols = line.strip().split(' ') res = cols[8] # UDP受信データ部分 #tid = res[4:4+4]; seoj = res[8:8+6] #deoj = res[14,14+6] ESV = res[20:20+2] #OPC = res[22,22+2] if seoj == "028801" and ESV == "72" : # スマートメーター(028801)から来た応答(72)なら EPC = res[24:24+2] if EPC == "E8" : # 内容が瞬時電流力計測値(E8)だったら # hexPower = line[-8:] # 最後の4バイト(16進数で8文字)が瞬時電力計測値 # intPower = int(hexPower, 16) rCur = res[-8:-4] tCur = res[-4:] # print 'rCur = {0}'.format(rCur) # print 'tCur = {0}'.format(tCur) # print ('rCur = {0}'.format(rCur)) # print ('tCur = {0}'.format(tCur)) intrCur = round( float( int(rCur, 16) * 0.1 ), 1) inttCur = round( float( int(tCur, 16) * 0.1 ), 1) # print(u"瞬時電力計測値:{0}[W]".format(intPower)) # print((u"瞬時電力計測値:{0}[W]".format(intPower)).encode('utf-8')) # print((u"R相電流計測値:{0}[A]".format(intrCur)).encode('utf-8')) # print((u"T相電流計測値:{0}[A]".format(inttCur)).encode('utf-8')) # print("E8r:{0} E8t:{1}".format(intrCur,inttCur)) break ################################################################################################## # # for EPC = 0xE0 (積算電力量計測値(正方向計測値)) # while True: # ECHONET Lite フレーム作成 # 参考資料 # ・ECHONET-Lite_Ver.1.12_02.pdf (以下 EL) # ・Appendix_H.pdf (以下 AppH) # for EPC = 0xE7 (瞬時電力計測値) echonetLiteFrame = b"" 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) : 瞬時電力計測値 # echonetLiteFrame += "\x00" # PDC(参考:EL p.3-9) echonetLiteFrame += b'\xE0' # EPC(参考:EL p.3-7 AppH p.3-275) : 積算電力量計測値 echonetLiteFrame += b'\x00' # PDC(参考:EL p.3-9) # コマンド送信 com1 = "SKSENDTO 1 {0} 0E1A 1 {1:04X} ".format(ipv6Addr, len(echonetLiteFrame)) command = com1.encode() + echonetLiteFrame ser.write(command) # print(ser.readline(), end="") # エコーバック ser.readline() # print(ser.readline(), end="") # EVENT 21 が来るはず(チェック無し) ser.readline() # print(ser.readline(), end="") # OKが来るはず(チェック無し) ser.readline() line = ser.readline() # ERXUDPが来るはず # print(line, end="") # 取りこぼしたりして変なデータを拾うことがあるので # チェックを厳しめにしてます。 if line.startswith(b"ERXUDP") : line = line.decode() cols = line.strip().split(' ') res = cols[8] # UDP受信データ部分 #tid = res[4:4+4]; seoj = res[8:8+6] #deoj = res[14,14+6] ESV = res[20:20+2] #OPC = res[22,22+2] if seoj == "028801" and ESV == "72" : # スマートメーター(028801)から来た応答(72)なら EPC = res[24:24+2] if EPC == "E0" : # 内容が瞬時電流力計測値(E8)だったら hexPower = line[-8:] # 最後の4バイト(16進数で8文字)が瞬時電力計測値 # intPower = int(hexPower, 16) # floatPower = round ( float( int( hexPower, 16) * 0.1 ),1) # cacti の Data Source で COUNTER を使いたい、となるとデータは intでなくてはいけなくて、floatはだめ。 # なので、係数を使わずにそのままにする。 intPower = int( hexPower, 16) # print(u"積算電力量計測値:{0}[kWh]".format(intPower)) # print((u"瞬時電力計測値:{0}[W]".format(intPower)).encode('utf-8')) # print("E0:{0}".format(floatPower)) break ################################################################################################## # # for EPC = 0xE3 (積算電力量計測値(逆方向計測値)) # while True: # for EPC = 0xE3 echonetLiteFrame = b"" 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 += b'\xE3' # EPC(参考:EL p.3-7 AppH p.3-275) : 積算電力量計測値 echonetLiteFrame += b'\x00' # PDC(参考:EL p.3-9) # コマンド送信 com1 = "SKSENDTO 1 {0} 0E1A 1 {1:04X} ".format(ipv6Addr, len(echonetLiteFrame)) command = com1.encode() + echonetLiteFrame ser.write(command) ser.readline() ser.readline() ser.readline() line = ser.readline() # ERXUDPが来るはず # 取りこぼしたりして変なデータを拾うことがあるので # チェックを厳しめにしてます。 if line.startswith(b"ERXUDP") : line = line.decode() cols = line.strip().split(' ') res = cols[8] # UDP受信データ部分 #tid = res[4:4+4]; seoj = res[8:8+6] #deoj = res[14,14+6] ESV = res[20:20+2] #OPC = res[22,22+2] if seoj == "028801" and ESV == "72" : # スマートメーター(028801)から来た応答(72)なら EPC = res[24:24+2] if EPC == "E3" : # 内容が瞬時電流力計測値(E8)だったら hexPower = line[-8:] # 最後の4バイト(16進数で8文字)が瞬時電力計測値 # intPower = int(hexPower, 16) intPowerM = int(hexPower, 16) # print(u"積算電力量計測値:{0}[kWh]".format(intPower)) # print((u"瞬時電力計測値:{0}[W]".format(intPower)).encode('utf-8')) # print("E0:{0}".format(floatPower)) break ser.close() # ################################################ # # for influxdb2 2022/06/20 # #print("start write influxdb2") # p = Point("smartmerter")\ .tag("location","home")\ .field("outputE7",int(outputE7))\ .field("rCurrent",float(intrCur))\ .field("tCurrent",float(inttCur))\ .field("Power",int(intPower))\ .field("PowerM",int(intPowerM)) write_api.write(bucket=bucket,record=p) ################## for google spread sheet utime = int(time.time()) # データを書き込む要求を追加 #row_data = [['14:00', 550], ['15:00', 600]] row_data = [[str(utime),jsttimestr(utime),int(outputE7),float(intrCur),float(inttCur),int(intPower),int(intPowerM)]] # データを追加 request = service.spreadsheets().values().append( spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME, valueInputOption='USER_ENTERED', insertDataOption='INSERT_ROWS', body={'values': row_data} ) response = request.execute()
コメントする