3 from suntime import Sun
10 parser = argparse.ArgumentParser()
11 parser.add_argument("-s",
15 help="Print Status Information",
17 parser.add_argument("-f",
21 help="Force retrieval of Power value",
23 args = parser.parse_args()
26 def isDaylight(lat, lon, toleranceSeconds):
29 sunriseTimestamp = int(sun.get_local_sunrise_time().timestamp())
30 sunsetTimestamp = int(sun.get_local_sunset_time().timestamp())
31 nowTimestamp = int(time.time())
33 if ((sunriseTimestamp + toleranceSeconds) < nowTimestamp) and (
34 (sunsetTimestamp - toleranceSeconds) > nowTimestamp):
40 # Query all relevant System Information in one Session
41 def retrieveData(userName, password, stationId):
42 with requests.Session() as s:
44 r = s.post('https://www.envertecportal.com/apiaccount/login',
52 'https://www.envertecportal.com/ApiStations/getStationInfo',
54 'stationId': stationId
56 timeout=(20, 60)).json()
57 power = r['Data']['Power']
59 # ignores paging for now, inputs are website defaults
61 'https://www.envertecportal.com/ApiInverters/QueryTerminalReal',
65 'orderBy': 'GATEWAYSN',
66 'whereCondition': f"{{\"STATIONID\":\"{stationId}\"}}"
68 timeout=(10, 60)).json()
69 powerDetails = r['Data']['QueryResults']
71 r = s.post('https://www.envertecportal.com/apiAccount/Logout',
74 # connect timeouts occur so frequently since the portal relaunch,
75 # ignore them for the time beeing completely
76 except requests.exceptions.ConnectTimeout as eTimeout:
78 except requests.exceptions.RequestException as e:
82 return float(power), powerDetails
85 # use our stateFile to determine if we have a state change
86 # used to decide if we print something - thus generate a mail - later on
87 def stateCheck(newState, stateFile):
89 with open(stateFile, "r") as f:
91 except FileNotFoundError:
94 if newState == oldState:
98 with open(stateFile, "w") as f:
103 def panelPrint(currentPower):
104 print(f"Total Power: {currentPower[0]}")
105 for panel in currentPower[1]:
106 print(f"{panel['SNALIAS']}: {panel['POWER']} - {panel['SITETIME']}")
109 # read configuration file
110 conf = configparser.ConfigParser()
111 conf.read('portalmonitor.ini')
113 # retrieve current power value as reported by envertecportal
114 if isDaylight(conf['config'].getfloat('lat'), conf['config'].getfloat('lon'),
115 conf['config'].getint('toleranceSeconds')) or args.force:
116 currentPower = retrieveData(conf['config']['userName'],
117 conf['config']['password'],
118 conf['config']['stationId'])
121 panelPrint(currentPower)
124 if currentPower[0] == 0:
125 if stateCheck('FAILED', conf['config']['stateFile']):
126 print('Error: Power dropped to 0 but we should have daylight!')
127 panelPrint(currentPower)
130 if stateCheck('OK', conf['config']['stateFile']):
132 panelPrint(currentPower)