Mercurial > ~darius > hgwebdir.cgi > pw2log
comparison pw2log.py @ 2:393a879db9eb
Update to latest tesla_powerwall code changes.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Tue, 09 Feb 2021 21:30:06 +1030 |
parents | 7edf54ec37f2 |
children | ec3c8b63d8e7 |
comparison
equal
deleted
inserted
replaced
1:7edf54ec37f2 | 2:393a879db9eb |
---|---|
7 import logging | 7 import logging |
8 from logging.handlers import RotatingFileHandler | 8 from logging.handlers import RotatingFileHandler |
9 import psycopg2 | 9 import psycopg2 |
10 import requests | 10 import requests |
11 import sys | 11 import sys |
12 import tesla_powerwall # https://github.com/jrester/tesla_powerwall | 12 # https://github.com/jrester/tesla_powerwall |
13 from tesla_powerwall import APIError, Powerwall | |
13 import time | 14 import time |
14 | 15 |
15 # Standard in 3.7.. | 16 # Standard in 3.7.. |
16 class NullContextManager(object): | 17 class NullContextManager(object): |
17 def __init__(self, dummy_resource=None): | 18 def __init__(self, dummy_resource=None): |
43 sys.exit(1) | 44 sys.exit(1) |
44 | 45 |
45 if not cp.has_section('pw'): | 46 if not cp.has_section('pw'): |
46 print('Config file missing pw section', file = sys.stderr) | 47 print('Config file missing pw section', file = sys.stderr) |
47 sys.exit(1) | 48 sys.exit(1) |
48 if not cp.has_option('pw', 'ip'): | 49 for opt in ('ip', 'username', 'password'): |
49 print('pw section missing ip parameter', file = sys.stderr) | 50 if not cp.has_option('pw', opt): |
50 sys.exit(1) | 51 print('pw section missing %s parameter' % (opt,), file = sys.stderr) |
52 sys.exit(1) | |
51 | 53 |
52 if cp.has_option('pw2log', 'logfile'): | 54 if cp.has_option('pw2log', 'logfile'): |
53 logfile = cp.get('pw2log', 'logfile') | 55 logfile = cp.get('pw2log', 'logfile') |
54 else: | 56 else: |
55 logfile = None | 57 logfile = None |
85 | 87 |
86 try: | 88 try: |
87 with ctx: | 89 with ctx: |
88 logger.critical('Starting') | 90 logger.critical('Starting') |
89 try: | 91 try: |
90 collectdata(cp.get('pw', 'ip'), cp.get('db', 'dsn'), cp.getint('db', 'logtime')) | 92 collectdata(cp.get('pw', 'ip'), cp.get('pw', 'username'), cp.get('pw', 'password'), cp.get('db', 'dsn'), cp.getint('db', 'logtime')) |
91 except Exception as e: | 93 except Exception as e: |
92 logger.critical('Unable to collect data: ' + str(e)) | 94 logger.critical('Unable to collect data: ' + str(e)) |
93 except Exception as e: | 95 except Exception as e: |
94 logger.critical('Unable to enter daemon context: ' + str(e)) | 96 logger.critical('Unable to enter daemon context: ' + str(e)) |
95 | 97 |
96 def collectdata(pwip, dsn, logtime): | 98 def collectdata(pwip, username, password, dsn, logtime): |
97 dbh = psycopg2.connect(dsn) | 99 dbh = psycopg2.connect(dsn) |
98 cur = dbh.cursor() | 100 cur = dbh.cursor() |
99 | 101 pw = None |
100 pw = tesla_powerwall.PowerWall(pwip) | |
101 | 102 |
102 while True: | 103 while True: |
103 try: | 104 try: |
104 # As per.. https://github.com/vloschiavo/powerwall2 | 105 # As per.. https://github.com/vloschiavo/powerwall2 |
105 # | | Load | Grid | Battery | Solar | | 106 # | | Load | Grid | Battery | Solar | |
106 # |==========+==============+===================+======================+==================| | 107 # |==========+==============+===================+======================+==================| |
107 # | Positive | Supply house | Drawing from grid | Drawing from battery | Solar generation | | 108 # | Positive | Supply house | Drawing from grid | Drawing from battery | Solar generation | |
108 # | Negative | n/a | Feeding grid | Charging battery | n/a | | 109 # | Negative | n/a | Feeding grid | Charging battery | n/a | |
109 # | 110 # |
110 grid = pw.grid | 111 if not pw: |
111 load = pw.load | 112 first = True |
112 battery = pw.battery | 113 pw = Powerwall(pwip) |
113 solar = pw.solar | 114 pw.login(username, password) |
114 charge = pw.charge | 115 pw.detect_and_pin_version() |
115 except requests.ConnectionError as e: | 116 meters = pw.get_meters() |
117 grid_volts = meters.site.avarage_voltage | |
118 grid_freq = meters.site.avarage_voltage | |
119 grid_power = meters.site.instant_power | |
120 load_power = meters.load.instant_power | |
121 battery_power = meters.battery.instant_power | |
122 solar_power = meters.solar.instant_power | |
123 charge = pw.get_charge() | |
124 except APIError as e: | |
125 pw = None | |
116 logger.error('Error communicating with Powerwall: ' + str(e)) | 126 logger.error('Error communicating with Powerwall: ' + str(e)) |
117 time.sleep(300) | 127 time.sleep(300) |
118 continue | 128 continue |
119 try: | 129 try: |
120 cur.execute('INSERT INTO pw2 (date, grid_voltage, grid_freq, grid_power, load_power, battery_power, battery_charge, solar_power) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)', | 130 cur.execute('INSERT INTO pw2 (date, grid_voltage, grid_freq, grid_power, load_power, battery_power, battery_charge, solar_power) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)', |
121 (datetime.datetime.now(), grid.instant_average_voltage, grid.frequency, grid.instant_power, load.instant_power, battery.instant_power, charge, solar.instant_power)) | 131 (datetime.datetime.now(), grid_volts, grid_freq, grid_power, load_power, battery_power, charge, solar_power)) |
122 dbh.commit() | 132 dbh.commit() |
123 except psycopg2.OperationalError as e: | 133 except psycopg2.OperationalError as e: |
124 logger.error('Reconnecting after database error:' + str(e)) | 134 logger.error('Reconnecting after database error:' + str(e)) |
125 time.sleep(60) | 135 time.sleep(60) |
126 dbh = psycopg2.connect(dsn) | 136 dbh = psycopg2.connect(dsn) |
127 cur = dbh.cursor() | 137 cur = dbh.cursor() |
128 continue | 138 continue |
139 if first: | |
140 logger.error('Logged OK') | |
141 first = False | |
129 | 142 |
130 time.sleep(logtime) | 143 time.sleep(logtime) |
131 | 144 |
132 def createdb(dbh): | 145 def createdb(dbh): |
133 cur = dbh.cursor() | 146 cur = dbh.cursor() |