/
solaredge.5m.py
executable file
·244 lines (204 loc) · 10.2 KB
/
solaredge.5m.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
233
234
235
236
237
238
239
240
241
242
243
244
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# <xbar.title>SolarEdge Monitoring</xbar.title>
# <xbar.version>v2.0</xbar.version>
# <xbar.author>Shaun Grady</xbar.author>
# <xbar.author.github>shaungrady</xbar.author.github>
# <xbar.desc>Displays SolarEdge inverter power and energy generation data from your solar energy system. Also calculates system efficiency for the current day and total CO2 offset.</xbar.desc>
# <xbar.image>https://i.imgur.com/wPRb9dj.png</xbar.image>
# <xbar.dependencies>solaredge,python</xbar.dependencies>
# <xbar.var>string(SITE_ID): Please provide your SolarEdge installation SiteId</xbar.var>
# <xbar.var>string(API_KEY): Please provide your SolarEdge installation API Key (https://www.solaredge.com/node/88689)</xbar.var>
# <xbar.var>string(BATTERY_PRESENT): Does your SolarEdge installation include a battery? Y/N</xbar.var>
# <xbar.var>string(SYSTEM_WATTAGE): Total panel DC watt capacity</xbar.var>
# <xbar.var>string(SHOW_ENVIRONMENTAL_BENEFIT): Do you want to show your environmental benfit in the dropdown? Y/N (Default Y)</xbar.var>
# <xbar.var>select(ENVIRONMENTAL_BENEFIT_UNIT="Metrics"): Units to display benefit in? [Imperial, Metrics]</xbar.var>
# <xbar.var>string(FONT_SIZE): Select a font size (Default 13)</xbar.var>
####################
# Import Statements
import urllib.request, urllib.error, urllib.parse
import json
import os
####################
# User Configuration
solaredge_site_id = os.getenv("SITE_ID", "")
solaredge_api_key = os.getenv("API_KEY", "")
units = os.getenv("ENVIRONMENTAL_BENEFIT_UNIT", "Metrics")
font_size = os.getenv("FONT_SIZE", "13")
# Optional. Deafults to 0 - disabled. Total panel DC watt capacity
system_wattage = int(os.getenv("SYSTEM_WATTAGE", 0))
# Optional. Default "Y". Displays lifetime environmental benefit of the installation.
show_env_benefit = os.getenv("SHOW_ENVIRONMENTAL_BENEFIT", "Y")
# Optional. Set either as empty string to disable.
awake_icon = "☀︎"
asleep_icon = "☾"
battery_icon = "🔋"
low_battery_icon = "🪫"
# Optional. Set to "n" to disable, "y" to enable. Display current battery charge state
battery_present = os.getenv("BATTERY_PRESENT", "")
##############
# Begin Script
# Build Base URLs
overview = "https://monitoringapi.solaredge.com/site/" + solaredge_site_id + "/overview?api_key=" + solaredge_api_key
power = "https://monitoringapi.solaredge.com/site/" + solaredge_site_id + "/currentPowerFlow?api_key=" + solaredge_api_key
environment = "https://monitoringapi.solaredge.com/site/" + solaredge_site_id + "/envBenefits?&systemUnits=" + units + "&api_key=" + solaredge_api_key
# Handle empty SiteId or API Key
if solaredge_site_id == "":
raise SystemExit("SiteId is required")
if solaredge_api_key == "":
raise SystemExit("API Key is required, see here: https://www.solaredge.com/node/88689")
# Functions
def convertKwToW(kW):
kW = float(kW)
return kW * 1000
def formatWatts(Wh, unit_suffix=""):
Wh = float(Wh)
if Wh < 900:
energy = Wh
unit = "W"
elif Wh < 900000:
energy = Wh / 1000
unit = "kW"
elif Wh < 900000000:
energy = Wh / 1000000
unit = "MW"
elif Wh < 900000000000:
energy = Wh / 1000000000
unit = "GW"
if energy < 10:
energy = round(energy, 2)
elif energy < 100:
energy = round(energy, 1)
else:
energy = int(round(energy))
return str(energy) + " " + unit + unit_suffix
def check_import_export(connections_state):
for connection in connections_state:
if connection["from"] == "LOAD" and connection["to"] == "Grid":
return "EXPORTING"
elif connection["from"] == "Grid" and connection["to"] == "LOAD":
return "IMPORTING"
try:
overviewResult = urllib.request.urlopen(overview, timeout=10).read()
jsonOverview = json.loads(overviewResult)
powerResult = urllib.request.urlopen(power, timeout=10).read()
jsonPower = json.loads(powerResult)
environmentResult = urllib.request.urlopen(environment, timeout=10).read()
jsonEnvironment = json.loads(environmentResult)
except Exception as err:
print((asleep_icon + " <err>"))
print("---")
raise SystemExit(err)
raw_power = jsonPower['siteCurrentPowerFlow']['PV']['currentPower']
raw_energy = jsonOverview['overview']['lastDayData']['energy']
inverter_connections_state = jsonPower['siteCurrentPowerFlow']['connections']
if show_env_benefit == "Y":
treesPlanted = jsonEnvironment['envBenefits']['treesPlanted']
co2_saved= jsonEnvironment['envBenefits']['gasEmissionSaved']['co2']
nox_saved= jsonEnvironment['envBenefits']['gasEmissionSaved']['nox']
so2_saved= jsonEnvironment['envBenefits']['gasEmissionSaved']['so2']
display_units= jsonEnvironment['envBenefits']['gasEmissionSaved']['units']
if battery_present == "Y":
battery_status = jsonPower['siteCurrentPowerFlow']['STORAGE']['status']
battery_power = jsonPower['siteCurrentPowerFlow']['STORAGE']['currentPower']
battery_charge_level = jsonPower['siteCurrentPowerFlow']['STORAGE']['chargeLevel']
battery_charge_status = jsonPower['siteCurrentPowerFlow']['STORAGE']['status']
if system_wattage > 0:
raw_efficiency = raw_energy / system_wattage
raw_energy_mtd = jsonOverview['overview']['lastMonthData']['energy']
raw_energy_ytd = jsonOverview['overview']['lastYearData']['energy']
raw_energy_total = jsonOverview['overview']['lifeTimeData']['energy']
inverter_load = jsonPower['siteCurrentPowerFlow']['LOAD']['currentPower']
inverter_grid_load = jsonPower['siteCurrentPowerFlow']['GRID']['currentPower']
# Handle strange API bug where energy total can be much less than YTD
if raw_energy_ytd > raw_energy_total:
raw_energy_total = raw_energy_ytd
energy_mtd = formatWatts(raw_energy_mtd, "h")
energy_ytd = formatWatts(raw_energy_ytd, "h")
energy_total = formatWatts(raw_energy_total, "h")
# Human-friendly power, energy, efficiency strings
power = formatWatts(convertKwToW(raw_power))
if battery_present == "Y":
if battery_charge_status == "Discharging":
combinedPower = formatWatts(convertKwToW(raw_power) + convertKwToW(battery_power))
else:
combinedPower = formatWatts(convertKwToW(raw_power))
energy = formatWatts(raw_energy, "h")
if system_wattage > 0:
efficiency = "%.2f" % raw_efficiency + " Wh/W"
# Formulate PV output string
if battery_present == "Y":
if raw_energy == 0 and convertKwToW(raw_power) == 0 and battery_power == 0:
toolbar_output = "— Wh"
elif convertKwToW(raw_power) == 0 and battery_power == 0:
toolbar_output = energy
else:
toolbar_output = energy + " @ " + combinedPower
else:
if raw_energy == 0 and convertKwToW(raw_power) == 0:
toolbar_output = "— Wh"
elif convertKwToW(raw_power) == 0:
toolbar_output = energy
else:
toolbar_output = energy + " @ " + power
# Battery Icon
if battery_present == "Y":
if battery_charge_status == "Charging":
if battery_charge_level > 25:
battery_icon_prefix = "++" + battery_icon
elif battery_charge_level <= 25:
battery_icon_prefix = "++" + low_battery_icon
else:
battery_icon_prefix = ""
elif battery_charge_status == "Discharging":
if battery_charge_level > 25:
battery_icon_prefix = "--" + battery_icon
elif battery_charge_level <= 25:
battery_icon_prefix = "--" + low_battery_icon
else:
battery_icon_prefix = ""
else:
if battery_charge_level > 25:
battery_icon_prefix = battery_icon
elif battery_charge_level <= 25:
battery_icon_prefix = low_battery_icon
else:
battery_icon_prefix = ""
# Icon
if convertKwToW(raw_power) == 0 and asleep_icon:
icon_prefix = asleep_icon + " "
elif convertKwToW(raw_power) > 0 and awake_icon:
icon_prefix = awake_icon + " "
else:
icon_prefix = ""
# Print the data
if battery_present == "Y":
print((battery_icon_prefix + str(battery_charge_level) + "% " + icon_prefix + toolbar_output + "|font='SF Compact Text Regular'|size=" + font_size))
else:
print((icon_prefix + toolbar_output + "|font='SF Compact Text Regular'"))
print("---")
print("⚡ " + "Self Consumption @ " + (formatWatts(convertKwToW(inverter_load)) + " |href=https://monitoring.solaredge.com") + "|size=" + font_size)
if check_import_export(inverter_connections_state) == "IMPORTING":
print("🔌 " + "Importing from Grid @ " + (formatWatts(convertKwToW(inverter_grid_load)) + "|href=https://monitoring.solaredge.com") + "|size=" + font_size)
elif check_import_export(inverter_connections_state) == "EXPORTING":
print("🔌 " + "Exporting to Grid @ " + (formatWatts(convertKwToW(inverter_grid_load)) + "|href=https://monitoring.solaredge.com") + "|size=" + font_size)
elif inverter_grid_load == 0 and check_import_export(inverter_connections_state) == "EXPORTING" or check_import_export(inverter_connections_state) == "IMPORTING" :
print("🔌 " + "Grid Idle @ " + (formatWatts(convertKwToW(inverter_grid_load)) + "|href=https://monitoring.solaredge.com") + "|size=" + font_size)
if system_wattage > 0:
print("---")
print((efficiency + " efficiency |href=https://monitoring.solaredge.com") + "| size=12")
print("---")
print((energy_mtd + " this month |href=https://monitoring.solaredge.com") + "|size=" + font_size)
print((energy_ytd + " this year |href=https://monitoring.solaredge.com") + "|size=" + font_size)
# If YTD and lifetime energy are within 1 kWh, consider them equal and
# suppress the total energy data from the dropdown menu
if raw_energy_total - raw_energy_ytd > 1000:
print((energy_total + " lifetime |href=https://monitoring.solaredge.com") + "|size=" + font_size)
if show_env_benefit == "Y":
print("---")
print(("🌲 Trees Planted: " + str(round(treesPlanted, 2)) + "|href=https://monitoring.solaredge.com |size=" + font_size))
print(("🌍 CO₂ Saved: " + str(round(co2_saved, 2)) + display_units + "|href=https://monitoring.solaredge.com |size=" + font_size))
print(("💧 SO₂ Saved: " + str(round(so2_saved, 2)) + display_units + "|href=https://monitoring.solaredge.com |size=" + font_size))
print(("🚗 NOX Saved: " + str(round(nox_saved, 2)) + display_units + "|href=https://monitoring.solaredge.com |size=" + font_size))
print("---")
print((jsonOverview['overview']['lastUpdateTime'] + "|size=" + font_size))