ccaa2fbc7506ead42cb6f93c1f63e8cb267ad8a0
[sven/scripts.git] / home / portalmonitor.py
1 #!/usr/bin/env python3
2
3 from suntime import Sun
4 import argparse
5 import configparser
6 import requests
7 import sys
8 import time
9
10 parser = argparse.ArgumentParser()
11 parser.add_argument("-s",
12                   "--status",
13                   action="store_true",
14                   dest="printStatus",
15                   help="Print Status Information",
16                   default=False)
17 parser.add_argument("-f",
18                   "--force",
19                   action="store_true",
20                   dest="force",
21                   help="Force retrieval of Power value",
22                   default=False)
23 args = parser.parse_args()
24
25
26 def isDaylight(lat, lon, toleranceSeconds):
27     daylight = False
28     sun = Sun(lat, lon)
29     sunriseTimestamp = int(sun.get_local_sunrise_time().timestamp())
30     sunsetTimestamp = int(sun.get_local_sunset_time().timestamp())
31     nowTimestamp = int(time.time())
32
33     if ((sunriseTimestamp + toleranceSeconds) < nowTimestamp) and (
34         (sunsetTimestamp - toleranceSeconds) > nowTimestamp):
35         daylight = True
36
37     return daylight
38
39
40 # Query all relevant System Information in one Session
41 def retrieveData(userName, password, stationId):
42     with requests.Session() as s:
43         try:
44             r = s.post('https://www.envertecportal.com/apiaccount/login',
45                        data={
46                            'userName': userName,
47                            'pwd': password
48                        },
49                        timeout=(20, 40))
50
51             r = s.post(
52                 'https://www.envertecportal.com/ApiStations/getStationInfo',
53                 data={
54                     'stationId': stationId
55                 },
56                 timeout=(20, 60)).json()
57             power = r['Data']['Power']
58
59             # ignores paging for now, inputs are website defaults
60             r = s.post(
61                 'https://www.envertecportal.com/ApiInverters/QueryTerminalReal',
62                 data={
63                     'page': 1,
64                     'perPage': 20,
65                     'orderBy': 'GATEWAYSN',
66                     'whereCondition': f"{{\"STATIONID\":\"{stationId}\"}}"
67                 },
68                 timeout=(10, 60)).json()
69             powerDetails = r['Data']['QueryResults']
70
71             r = s.post('https://www.envertecportal.com/apiAccount/Logout',
72                        timeout=(20, 40))
73
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:
77             sys.exit(1)
78         except requests.exceptions.RequestException as e:
79             print(e)
80             raise SystemExit(e)
81
82     return float(power), powerDetails
83
84
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):
88     try:
89         with open(stateFile, "r") as f:
90             oldState = f.read()
91     except FileNotFoundError:
92         oldState = "NULL"
93
94     if newState == oldState:
95         change = False
96     else:
97         change = True
98         with open(stateFile, "w") as f:
99             f.write(newState)
100
101     return change
102
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']}")
107
108
109 # read configuration file
110 conf = configparser.ConfigParser()
111 conf.read('portalmonitor.ini')
112
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'])
119
120     if args.printStatus:
121         panelPrint(currentPower)
122         sys.exit(0)
123
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)
128         sys.exit(1)
129     else:
130         if stateCheck('OK', conf['config']['stateFile']):
131             print('Resolved')
132             panelPrint(currentPower)
133         sys.exit(0)