とりあえず、いま、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()
コメントする