forked from MattCordell/SolarMax_PVoutputLogger
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSolarmanPV-to-PVoutput-inverter-data.py
executable file
·233 lines (196 loc) · 9.74 KB
/
SolarmanPV-to-PVoutput-inverter-data.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/bin/python
# -* coding: utf-8 *-
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Based on Matt Cordell's SolarMax_PVoutLiveLog.py code
# Developed 2016 by Christopher McAvaney <[email protected]>
import sys
import argparse
import datetime
from util import DEBUG, utc_to_local
# class for talking to the Solarman PV API
from SolarmanPVAPI.solarmanpv_api import SolarmanPVAPI
# API for talking to the PVoutput inverter
from PVoutput.pvoutput import PVoutput_Connection
# class for getting Weewx info
from Weewx.weewx import WeewxInfo
import requests
import socket
if sys.version_info < (2, 7):
raise "must user Python 2.7 or greater"
# pvoutput specifics
# time to delay between API calls
apiDelay = 5
appVersion = 0.3
# parameters (with defaults)
debug = False
smpv_client_id = smpv_client_secret = smpv_plant_id = None
pvo_key = pvo_system_id = None
weewx_user = weewx_password = weewx_host = weewx_database = None
parser = argparse.ArgumentParser(prog=sys.argv[0])
parser.add_argument("-d", "--debug", help="turn on debug output", action="store_true")
parser.add_argument("-v", "--version", action="version", version="%(prog)s " + str(appVersion))
parser.add_argument("--smpv_client_id", help="SolarmanPV API client ID", required=True)
parser.add_argument("--smpv_client_secret", help="SolarmanPV API client secret", required=True)
parser.add_argument("--smpv_plant_id", help="ID of the plant (i.e. The solar PV site within SolarmanPV)", required=True)
parser.add_argument("--smpv_device_id", help="ID of the device (i.e. The solar PV inverter within the site within SolarmanPV)", required=True)
parser.add_argument("--pvo_key", help="PVoutput API key", required=True)
parser.add_argument("--pvo_system_id", help="PVoutput system ID", required=True)
parser.add_argument("--power_data", help="Use the getPower() i.e. SolarmanPVAPI plant/power method. Default is getInverterData()", required=False, action="store_true")
parser.add_argument("--weewx_user", help="Weewx MySQL username", required=True)
parser.add_argument("--weewx_password", help="Weewx MySQL password", required=True)
parser.add_argument("--weewx_host", help="Weewx MySQL host", required=True)
parser.add_argument("--weewx_database", help="Weewx MySQL database", required=True)
args = parser.parse_args()
if args.debug:
print("debug turned on")
debug = True
client_id = args.smpv_client_id
client_secret = args.smpv_client_secret
plant_id = args.smpv_plant_id
device_id = args.smpv_device_id
pvo_key = args.pvo_key
pvo_system_id = args.pvo_system_id
weewx_user = args.weewx_user
weewx_password = args.weewx_password
weewx_host = args.weewx_host
weewx_database = args.weewx_database
if args.power_data:
data_method = 'power'
else:
data_method = 'inverter'
# Create the SolarmanAPI object
smpv = SolarmanPVAPI(client_id, client_secret, plant_id)
smpv.setDebug(debug)
if smpv.connected is not True:
print('An issue with connection to the SolarmanPV API')
sys.exit(1)
# get temperature data from Weewx
# create an instance for a Weewx class and call the get "current outside temp" method
try:
weather_info = WeewxInfo(weewx_user, weewx_password, weewx_host, weewx_database)
current_outside_temp = weather_info.getCurrentOutsideTemp()
except:
current_outside_temp = None
DEBUG('current outside temp == ' + str(current_outside_temp))
# testing getInverterData() instead (see below after this if statement)
if data_method == 'power':
power_details = smpv.getPower(datetime.date.today().strftime("%Y-%m-%d"), True)
if power_details is not None:
power = power_details['power']
# need to convert date and time value from UTC to local time before uploading to pvoutput.org
power_datetime_local = utc_to_local(datetime.datetime.strptime(power_details['time'], "%Y-%m-%dT%H:%M:%SZ"))
power_date = power_datetime_local.strftime("%Y%m%d")
power_time = power_datetime_local.strftime("%H:%M")
DEBUG('power == ' + str(power) + ' and power_date == ' + power_date)
try:
# Create connection to pvoutput.org
pvout = PVoutput_Connection(pvo_key, pvo_system_id)
# update pvoutput - but only if there is a value power value (i.e. > 0)
# add_output() is for end of day output - therfore the peak power, peak time, etc
# pvout.add_output(power_date, generated=power)
if power > 0:
DEBUG('adding inverter data to PVOutput')
pvout.add_status(power_date, power_time, power_exp=power, temp=current_outside_temp)
else:
DEBUG('no need to update - power %dW' % power)
except:
print('An error with PVoutput ', sys.exc_info()[0])
raise
else:
# Changed this to a DEBUG, so that it won't output when in a CRON job
DEBUG('Invalid power data from SolarmanPV API - no further action')
elif data_method == 'inverter':
date_to_retrieve = datetime.date.today().strftime("%Y-%m-%d")
inverter_details = smpv.getInverterData(date_to_retrieve, device_id, True)
if inverter_details is not None:
DEBUG('inverter_details == ' + str(inverter_details))
DEBUG('power == %sW' % (inverter_details['power']))
DEBUG('input current 1 == %sA and 2 == %sA' % (inverter_details['iPv1'], inverter_details['iPv2']))
DEBUG('input voltage 1 == %sV and 2 == %sV' % (inverter_details['vPv1'], inverter_details['vPv2']))
DEBUG('output current 1 == %sA' % (inverter_details['iac1']))
DEBUG('output voltage 1 == %sV' % (inverter_details['vac1']))
DEBUG('frequency == %sHz' % (inverter_details['fac']))
DEBUG('time == %s' % (inverter_details['time']))
inverter_datetime_local = datetime.datetime.strptime(inverter_details['time'], "%Y-%m-%dT%H:%M:%S+10:00")
inverter_date = inverter_datetime_local.strftime("%Y%m%d")
inverter_time = inverter_datetime_local.strftime("%H:%M")
DEBUG('inverter power == ' + str(inverter_details['power']) + ' and inverter_date == ' + inverter_date + ' inverter_time == ' + inverter_time)
# power = power_details['power']
# # need to convert date and time value from UTC to local time before uploading to pvoutput.org
# power_datetime_local = utc_to_local(datetime.datetime.strptime(power_details['time'], "%Y-%m-%dT%H:%M:%SZ"))
# power_date = power_datetime_local.strftime("%Y%m%d")
# power_time = power_datetime_local.strftime("%H:%M")
# DEBUG('power == ' + str(power) + ' and power_date == ' + power_date)
try:
# Create connection to pvoutput.org
pvout = PVoutput_Connection(pvo_key, pvo_system_id)
# update pvoutput - but only if there is a value power value (i.e. > 0)
# add_output() is for end of day output - therfore the peak power, pear time, etc
# pvout.add_output(power_date, generated=power)
if inverter_details['power'] > 0:
DEBUG('adding inverter data to PVOutput')
pvout.add_status(inverter_date, inverter_time, power_exp=inverter_details['power'], vdc=inverter_details['vac1'], temp=current_outside_temp)
else:
DEBUG('no need to update - power %dW' % inverter_details['power'])
except requests.exceptions.Timeout as e:
print('PVoutput_Connection: request failed on a timeout - %s' % (e))
except socket.error as e:
print('PVoutput_Connection: request/socket failed on a timeout - %s' % (e))
except:
print('An error with PVoutput ', sys.exc_info()[0])
raise
else:
# Changed this to a DEBUG, so that it won't output when in a CRON job
DEBUG('Invalid inverter data from SolarmanPV API - no further action')
# temporary exit, looking to include voltage and current data - but need to find out why the current day data doesn't come back through the API
sys.exit(5)
# cycle through the known inverters
count = 0
for sm in smlist:
for (no, ivdata) in sm.inverters().iteritems():
try:
# Pass the parameters you wish to get from the inverter and log. Power, Voltage and Temp are all that's required for PVoutput.
(inverter, current) = sm.query(no, ['PAC', 'UL1', 'TKK'])
# create connection to pvoutput.org
pvoutz = PVoutput_Connection(pvo_key, pvo_systemid)
#use system date/Time for logging. Close enough
powerdate = time.strftime("%Y%m%d")
powerTime = time.strftime("%H:%M")
# parse the results of sm.query above
PowerGeneration = str(current['PAC'])
Temperature = str(current['TKK'])
Voltage = str(current['UL1'])
print("Date: " + str(powerdate) + " Time: " + str(
powerTime) + " W: " + PowerGeneration + " temp: " + Temperature + " volt: " + Voltage)
# update pvoutput
if (PowerGeneration): # make sure that we have actual values...
pvoutz.add_status(powerdate, powerTime, power_exp=PowerGeneration, temp=Temperature, vdc=Voltage)
print("Sucessful Log ")
#Ensure API limits adhered to
time.sleep(apiDelay)
else:
print("aint no data bitch.. make the sun come up")
count += 1
except:
print('Communication Error, WR %i' % no)
continue
#(status, errors) = sm.status(count)
#if errors:
# print('WR %i: %s (%s)' % (no, status, errors))
# try:
# print("details: ", int(PAC), int(TEMP), int(VOLTAGE))
# except:
# pass
if count < len(allinverters):
print('Not all inverters queried (%i < %i)' % (count, len(allinverters)))
print("Data Succesfully query and posted.")
time.sleep(1)