Mercurial > ~darius > hgwebdir.cgi > epro
comparison eprodbus.py @ 9:446cfe74827b
Add program to report epro status to DBus for Venus tools.
Move sqlite3 logging to here as well
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sun, 05 Dec 2021 16:19:31 +1030 |
parents | |
children | 08b61687b75f |
comparison
equal
deleted
inserted
replaced
8:9c0435a617db | 9:446cfe74827b |
---|---|
1 #!/usr/bin/env python | |
2 | |
3 # Read enerdrive ePro packets from serial port and update DBus with them | |
4 # Also logs to an sqlite3 DB every 60 seconds | |
5 | |
6 import datetime | |
7 from dbus.mainloop.glib import DBusGMainLoop | |
8 import epro | |
9 import gobject | |
10 import logging | |
11 import os | |
12 import serial | |
13 import signal | |
14 import sqlite3 | |
15 import sys | |
16 | |
17 sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'velib_python')) | |
18 from vedbus import VeDbusService | |
19 | |
20 logging.basicConfig(format = '%(asctime)s %(message)s', level = logging.DEBUG) | |
21 logging.info(__file__ + " is starting up") | |
22 | |
23 port = 'ttyepro' | |
24 servicename = 'com.victronenergy.battery.' + port | |
25 instance = 0 | |
26 | |
27 class eProUpdater: | |
28 def __init__(self, dbusservice, s, dbh): | |
29 self.log_queue = [] | |
30 self.dbusservice = dbusservice | |
31 self.p = epro.Processor() | |
32 self.s = s | |
33 self.dbh = dbh | |
34 gobject.io_add_watch(s.fileno(), gobject.IO_IN, self.read_serial) | |
35 gobject.timeout_add(60000, self.log_epro) | |
36 | |
37 def read_serial(self, fd, userdata): | |
38 try: | |
39 data = self.s.read(1024) | |
40 except Exception as e: | |
41 logging.error('Failed to read from serial port: %s', str(e)) | |
42 return False | |
43 | |
44 logging.debug('Read %d bytes from serial port', len(data)) | |
45 self.p.process(data) | |
46 | |
47 while len(self.p.packets) > 0: | |
48 # Process oldest packets first | |
49 p = self.p.packets.pop(0) | |
50 self.log_queue.append(p) | |
51 logging.debug('%s', str(p)) | |
52 if type(p) == epro.StateOfCharge: | |
53 self.dbusservice['/Soc'] = p.soc | |
54 elif type(p) == epro.MainVoltage: | |
55 self.dbusservice['/Dc/0/Voltage'] = p.volts | |
56 elif type(p) == epro.BatteryCurrent: | |
57 self.dbusservice['/Dc/0/Current'] = p.amps | |
58 elif type(p) == epro.AmpHours: | |
59 self.dbusservice['/ConsumedAmphours'] = p.amphrs | |
60 elif type(p) == epro.TimeRemaining: | |
61 # ePro reports in minutes, Venus expects seconds | |
62 self.dbusservice['/TimeToGo'] = p.time * 60 | |
63 return True | |
64 | |
65 def log_epro(self): | |
66 logging.debug('Logging epro data') | |
67 # Check we have all the packets we need in the queue | |
68 msgtypes = set([x.msgtype for x in self.log_queue]) | |
69 wantedtypes = set([ | |
70 epro.MainVoltage.MSGTYPE, | |
71 epro.AmpHours.MSGTYPE, | |
72 epro.BatteryCurrent.MSGTYPE, | |
73 epro.StateOfCharge.MSGTYPE, | |
74 epro.TimeRemaining.MSGTYPE, | |
75 epro.BatteryTemperature.MSGTYPE, | |
76 epro.MonitorStatus.MSGTYPE, | |
77 epro.AuxVoltage.MSGTYPE, | |
78 ]) | |
79 if msgtypes < wantedtypes: | |
80 logging.debug('Didn\'t get all packet types required to log') | |
81 return | |
82 | |
83 row = {} | |
84 usedtypes = set() | |
85 while len(self.log_queue) > 0: | |
86 pkt = self.log_queue.pop() # Read latest packets first | |
87 if pkt.msgtype == epro.MainVoltage.MSGTYPE: | |
88 row['main_voltage'] = pkt.volts | |
89 elif pkt.msgtype == epro.AmpHours.MSGTYPE: | |
90 row['amp_hours'] = pkt.amphrs | |
91 elif pkt.msgtype == epro.BatteryCurrent.MSGTYPE: | |
92 row['battery_curr'] = pkt.amps | |
93 elif pkt.msgtype == epro.StateOfCharge.MSGTYPE: | |
94 row['state_of_charge'] = pkt.soc | |
95 elif pkt.msgtype == epro.TimeRemaining.MSGTYPE: | |
96 row['time_remaining'] = pkt.time | |
97 elif pkt.msgtype == epro.BatteryTemperature.MSGTYPE: | |
98 row['battery_temp'] = pkt.temp | |
99 elif pkt.msgtype == epro.MonitorStatus.MSGTYPE: | |
100 row['auto_sync_volts'] = pkt.autosyncvolt | |
101 row['auto_sync_curr'] = pkt.autosyncamp | |
102 row['e501'] = pkt.e501compat | |
103 row['alarm_test'] = pkt.alarmtst | |
104 row['light'] = pkt.backlight | |
105 row['display_test'] = pkt.disptst | |
106 row['temp_sensor'] = pkt.tempsense | |
107 row['aux_hv'] = pkt.auxhv | |
108 row['aux_lv'] = pkt.auxlv | |
109 row['installer_lock'] = pkt.lock | |
110 row['main_hv'] = pkt.mainhv | |
111 row['main_lv'] = pkt.mainlv | |
112 row['low_battery'] = pkt.lowbatalarm | |
113 row['battery_flat'] = pkt.batflat | |
114 row['battery_full'] = pkt.batfull | |
115 row['battery_charged'] = pkt.charged | |
116 row['no_sync'] = pkt.nosync | |
117 row['monitor_reset'] = pkt.monreset | |
118 elif pkt.msgtype == epro.AuxVoltage.MSGTYPE: | |
119 row['aux_voltage'] = pkt.volts | |
120 | |
121 usedtypes.add(pkt.msgtype) | |
122 if usedtypes >= wantedtypes: | |
123 self.log_queue = [] | |
124 break | |
125 | |
126 logging.info('Got all packets, logging') | |
127 cur = self.dbh.cursor() | |
128 row['tstamp'] = int(datetime.datetime.now().strftime('%s')) | |
129 cur.execute('INSERT INTO eprolog VALUES (:tstamp, :main_voltage, :aux_voltage, :battery_curr, :amp_hours, :state_of_charge, :time_remaining, :battery_temp, :auto_sync_volts, :auto_sync_curr, :e501, :alarm_test, :light, :display_test, :temp_sensor, :aux_hv, :aux_lv, :installer_lock, :main_hv, :main_lv, :low_battery, :battery_flat, :battery_full, :battery_charged, :no_sync, :monitor_reset)', row) | |
130 self.dbh.commit() | |
131 | |
132 def doexit(): | |
133 sys.exit(1) | |
134 | |
135 def main(): | |
136 # Add signal handler to exit, otherwise we have to press ctrl-c twice to quit | |
137 signal.signal(signal.SIGINT, doexit) | |
138 | |
139 DBusGMainLoop(set_as_default = True) | |
140 | |
141 dbusservice = VeDbusService(servicename) | |
142 dbusservice.add_path('/Connected', value = True) | |
143 dbusservice.add_path('/ProductName', value = 'Enerdrive ePro') | |
144 dbusservice.add_path('/Mgmt/Connection', value = '/dev/' + port) | |
145 dbusservice.add_path('/DeviceInstance', value = instance) | |
146 dbusservice.add_path('/ProductId', value = 'unknown') | |
147 dbusservice.add_path('/Dc/0/Voltage', value = None) | |
148 dbusservice.add_path('/Dc/0/Current', value = None) | |
149 dbusservice.add_path('/Soc', value = None) | |
150 dbusservice.add_path('/TimeToGo', value = None) | |
151 dbusservice.add_path('/ConsumedAmphours', value = None) | |
152 | |
153 s = serial.Serial('/dev/' + port, 2400, parity = 'E') | |
154 s.timeout = 0.1 | |
155 | |
156 dbh = sqlite3.connect('/home/root/vanlogger/log.db') | |
157 | |
158 updater = eProUpdater(dbusservice, s, dbh) | |
159 | |
160 logging.info('Starting main loop') | |
161 mainloop = gobject.MainLoop() | |
162 mainloop.run() | |
163 | |
164 if __name__ == '__main__': | |
165 main() |