Compare commits

...

6 commits

3 changed files with 74 additions and 81 deletions

View file

@ -38,7 +38,7 @@ I try to integrate every datapoint from Batriums BMS to fill Victrons registers.
- Ah, kWh charged and discharged is calcualated by script (see above) - Ah, kWh charged and discharged is calcualated by script (see above)
- Capacity of battery must be entered into script batrium.py (see above) - Capacity of battery must be entered into script batrium.py (see above)
- dbus script is started by rc.local which gets executed last, so all mppts and multiplus' showing temporarily "BMS missing" and need to be cleared after (re-)boot of Venus OS - dbus script is started by rc.local which gets executed last, so all mppts and multiplus' showing temporarily "BMS missing" and need to be cleared after (re-)boot of Venus OS
- include calculation of State Of Health, based on Ah discharge and SoC State after full charge, becuase Batrium does not have - include calculation of State Of Health, based on Ah discharge and SoC State after full charge, because Batrium does not have
### Victrons datapoints ### Victrons datapoints
This script will emulate one of Victrons own BMS, therefore are many registers to fill, but I dont know how to fill all. This script will emulate one of Victrons own BMS, therefore are many registers to fill, but I dont know how to fill all.

View file

@ -15,10 +15,10 @@ VERSION = '1.0'
class BatriumBattery(can.Listener): class BatriumBattery(can.Listener):
def __init__(self, connection): def __init__(self, connection):
##Capacity of battery need to set here, because Batrium not transmitting
self.capacity = 300 self.capacity = 300
self.maxChargeVoltage = 0 self.maxChargeVoltage = 0
self.numberOfModules = 0 self.numberOfModules = 0
self.chargeComplete = 0
self.soc = 0 self.soc = 0
self.voltage = 0 self.voltage = 0
self.current = 0 self.current = 0
@ -28,10 +28,10 @@ class BatriumBattery(can.Listener):
self.minCellTemperature = 0 self.minCellTemperature = 0
self.minCellTemperatureId = 0 self.minCellTemperatureId = 0
self.cellVoltages_min = dict() self.cellVoltages_min = dict()
self.cellVoltages_max = dict() #self.cellVoltages_max = dict()
self.cellTemperatures = dict() #self.cellTemperatures = dict()
self.cellBypassTemperatures = dict() #self.cellBypassTemperatures = dict()
self.cellBypassPWM = dict() #self.cellBypassPWM = dict()
self.maxCellVoltage = 0 self.maxCellVoltage = 0
self.maxCellVoltageId = 0 self.maxCellVoltageId = 0
self.minCellVoltage = 0 self.minCellVoltage = 0
@ -39,17 +39,15 @@ class BatriumBattery(can.Listener):
self.maxChargeCurrent = 0 self.maxChargeCurrent = 0
self.maxDischargeCurrent = 0 self.maxDischargeCurrent = 0
self.updated = -1 self.updated = -1
self.cyclicModeTask = None
self.TimeToEmpty = 0 self.TimeToEmpty = 0
self.TimeToFull = 0 self.TimeToFull = 0
self.AhToEmpty = 0 #self.AhToEmpty = 0
self.AhToFull = 0 #self.AhToFull = 0
self.NumberInBypass = 0 self.NumberInBypass = 0
self.alarm_high_temperature = 0 self.alarm_high_temperature = 0
self.alarm_high_voltage = 0 self.alarm_high_voltage = 0
self.alarm_low_temperature = 0 self.alarm_low_temperature = 0
self.alarm_low_voltage = 0 self.alarm_low_voltage = 0
self.alarm_low_soc = 0
self.alarm_high_charge_current = 0 self.alarm_high_charge_current = 0
self.alarm_high_discharge_current = 0 self.alarm_high_discharge_current = 0
@ -83,10 +81,10 @@ class BatriumBattery(can.Listener):
self.current = round((struct.unpack('>f', bytearray([msg.data[7], msg.data[6], msg.data[5], msg.data[4]])))[0] * 0.001, 2) self.current = round((struct.unpack('>f', bytearray([msg.data[7], msg.data[6], msg.data[5], msg.data[4]])))[0] * 0.001, 2)
self.temperature = msg.data[1] - 40 self.temperature = msg.data[1] - 40
elif msg.arbitration_id == 0x00111900: #elif msg.arbitration_id == 0x00111900:
logging.debug("Ah 0x00111900 received") #logging.debug("Ah 0x00111900 received")
# message never arrived, batrium fw bug? # message never arrived, batrium fw bug? contacted Batrium I hope they fix it too
#self.AhToFull = round((struct.unpack('>f', bytearray([msg.data[3], msg.data[2], msg.data[1], msg.data[0]])))[0] * 0.001, 2) #self.AhToFull = round((struct.unpack('>f', bytearray([msg.data[3], msg.data[2], msg.data[1], msg.data[0]])))[0] * 0.001, 2)
#self.AhToEmpty = round((struct.unpack('>f', bytearray([msg.data[7], msg.data[6], msg.data[5], msg.data[4]])))[0] * 0.001, 2) #self.AhToEmpty = round((struct.unpack('>f', bytearray([msg.data[7], msg.data[6], msg.data[5], msg.data[4]])))[0] * 0.001, 2)
@ -150,21 +148,15 @@ class BatriumBattery(can.Listener):
elif ((msg.arbitration_id >= 0x001D1101) and (msg.arbitration_id <= 0x001D11F9)): elif ((msg.arbitration_id >= 0x001D1101) and (msg.arbitration_id <= 0x001D11F9)):
cell_id = msg.arbitration_id & 0xff cell_id = msg.arbitration_id & 0xff
#logging.debug("Cell ID: %d, Voltage_min: %2.2f, Voltage_max: %2.2f, Temperature: %d, Bypass_Temperature: %d, BypassPWM: %d", cell_id, ((msg.data[1] * 256) + msg.data[0]) * 0.001, ((msg.data[3] * 256) + msg.data[2]) * 0.001, msg.data[4] - 40, msg.data[5] - 40, msg.data[6]) logging.debug("Cell ID: %d, Voltage_min: %2.2f, Voltage_max: %2.2f, Temperature: %d, Bypass_Temperature: %d, BypassPWM: %d", cell_id, ((msg.data[1] * 256) + msg.data[0]) * 0.001, ((msg.data[3] * 256) + msg.data[2]) * 0.001, msg.data[4] - 40, msg.data[5] - 40, msg.data[6])
self.cellVoltages_min[cell_id] = ((msg.data[1] * 256) + msg.data[0]) * 0.001 self.cellVoltages_min[cell_id] = ((msg.data[1] * 256) + msg.data[0]) * 0.001
self.cellVoltages_max[cell_id] = ((msg.data[3] * 256) + msg.data[2]) * 0.001 #self.cellVoltages_max[cell_id] = ((msg.data[3] * 256) + msg.data[2]) * 0.001
self.cellTemperatures[cell_id] = msg.data[4] - 40 #self.cellTemperatures[cell_id] = msg.data[4] - 40
self.cellBypassTemperatures[cell_id] = msg.data[5] - 40 #self.cellBypassTemperatures[cell_id] = msg.data[5] - 40
self.cellBypassPWM[cell_id] = msg.data[6] #self.cellBypassPWM[cell_id] = msg.data[6]
self.numberOfModules = len(self.cellVoltages_min.keys()) self.numberOfModules = len(self.cellVoltages_min.keys())
def prnt(self): ## All code below is to simply run it from the commandline for debugging purposes ##
print("SOC: %2d, I: %2.2fA, U: %2.2fV, T:%2.1fC" % (self.soc, self.current, self.voltage, self.maxCellTemperature))
print("MinCellVolt: %1.2f, MaxCellVolt: %1.2f CellVoltDelta: %1.2f" % (self.minCellVoltage, self.maxCellVoltage, self.maxCellVoltage-self.minCellVoltage))
print("MaxChargeCurrent (CCL): %4.1f A MaxChargeVoltage (CVL): %2.2f V" % (self.maxChargeCurrent, self.maxChargeVoltage))
print("Number of modules found: %1.0f" % (self.numberOfModules))
# === All code below is to simply run it from the commandline for debugging purposes ===
def main(): def main():
logging.basicConfig( format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', logging.basicConfig( format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S', datefmt='%Y-%m-%d %H:%M:%S',
@ -172,23 +164,20 @@ def main():
stream=sys.stdout, stream=sys.stdout,
) )
logging.info('Starting dbus_batrium_native listener') logging.info('Starting dbus-batrium-native listener')
#logger = can.Logger('logfile.asc') #logger = can.Logger('logfile.asc') # CAN raw data logging
bat = BatriumBattery(connection='can1') bat = BatriumBattery(connection='can1')
listeners = [ listeners = [
#logger, # Regular Listener object #logger, # CAN raw data logging
bat bat
] ]
notifier = can.Notifier(bat._ci, listeners) notifier = can.Notifier(bat._ci, listeners)
for msg in bat._ci:
if msg.arbitration_id == 0x00140100:
bat.prnt()
# Clean-up # Shutdown
notifier.stop() notifier.stop()
bat._ci.shutdown() bat._ci.shutdown()

View file

@ -23,13 +23,13 @@ from argparse import ArgumentParser
from batrium import * from batrium import *
# our own packages # Victron energy libraries
sys.path.insert(1, os.path.join(os.path.dirname(__file__), '/opt/victronenergy/dbus-systemcalc-py/ext/velib_python')) sys.path.insert(1, os.path.join(os.path.dirname(__file__), '/opt/victronenergy/dbus-systemcalc-py/ext/velib_python'))
from vedbus import VeDbusService from vedbus import VeDbusService
from ve_utils import exit_on_error from ve_utils import exit_on_error
from settingsdevice import SettingsDevice from settingsdevice import SettingsDevice
VERSION = '1.0' VERSION = '1.5'
def handle_changed_setting(setting, oldvalue, newvalue): def handle_changed_setting(setting, oldvalue, newvalue):
logging.debug('setting changed, setting: %s, old: %s, new: %s' % (setting, oldvalue, newvalue)) logging.debug('setting changed, setting: %s, old: %s, new: %s' % (setting, oldvalue, newvalue))
@ -53,7 +53,7 @@ class DbusBatteryService:
except: except:
exit exit
# Create the management objects, as specified in the ccgx dbus-api document # Create the management objects, as specified in the Venus OS dbus-api document https://github.com/victronenergy/venus/wiki/dbus-api
self._dbusservice.add_path('/Mgmt/ProcessName', __file__) self._dbusservice.add_path('/Mgmt/ProcessName', __file__)
self._dbusservice.add_path('/Mgmt/ProcessVersion', VERSION + ' running on Python ' + platform.python_version()) self._dbusservice.add_path('/Mgmt/ProcessVersion', VERSION + ' running on Python ' + platform.python_version())
self._dbusservice.add_path('/Mgmt/Connection', connection) self._dbusservice.add_path('/Mgmt/Connection', connection)
@ -66,7 +66,7 @@ class DbusBatteryService:
self._dbusservice.add_path('/HardwareVersion', 'unknown') self._dbusservice.add_path('/HardwareVersion', 'unknown')
self._dbusservice.add_path('/Connected', 0) self._dbusservice.add_path('/Connected', 0)
# Create battery specific objects # Create battery specific objects
self._dbusservice.add_path('/Status', None, writeable=True) self._dbusservice.add_path('/Status', 0, writeable=False)
self._dbusservice.add_path('/Dc/0/Temperature', None, writeable=True) self._dbusservice.add_path('/Dc/0/Temperature', None, writeable=True)
self._dbusservice.add_path('/Info/BatteryLowVoltage', None, writeable=True) self._dbusservice.add_path('/Info/BatteryLowVoltage', None, writeable=True)
self._dbusservice.add_path('/Alarms/CellImbalance', None, writeable=True) self._dbusservice.add_path('/Alarms/CellImbalance', None, writeable=True)
@ -104,7 +104,6 @@ class DbusBatteryService:
}, },
eventCallback=handle_changed_setting) eventCallback=handle_changed_setting)
self._summeditems = { self._summeditems = {
'/System/MaxCellVoltage': {'gettext': '%.3F V'}, '/System/MaxCellVoltage': {'gettext': '%.3F V'},
'/System/MinCellVoltage': {'gettext': '%.3F V'}, '/System/MinCellVoltage': {'gettext': '%.3F V'},
@ -131,24 +130,27 @@ class DbusBatteryService:
for path in self._summeditems.keys(): for path in self._summeditems.keys():
self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext)
## Load settings from Venus OS config xml ##
self._dbusservice['/History/AverageDischarge'] = self._settings['AvgDischarge'] self._dbusservice['/History/AverageDischarge'] = self._settings['AvgDischarge']
self._dbusservice['/History/TotalAhDrawn'] = self._settings['TotalAhDrawn'] self._dbusservice['/History/TotalAhDrawn'] = self._settings['TotalAhDrawn']
self._dbusservice.add_path('/History/TimeSinceLastFullCharge', 0) self._dbusservice.add_path('/History/TimeSinceLastFullCharge', 0)
self._dbusservice.add_path('/History/MinCellVoltage', self._settings['MinCellVoltage']) self._dbusservice.add_path('/History/MinCellVoltage', self._settings['MinCellVoltage'])
self._dbusservice.add_path('/History/MaxCellVoltage', self._settings['MaxCellVoltage']) self._dbusservice.add_path('/History/MaxCellVoltage', self._settings['MaxCellVoltage'])
## Set variables I will use later to standard values ##
self._dbusservice['/Soh'] = 100 self._dbusservice['/Soh'] = 100
self._dbusservice['/Status'] = 0
self._dbusservice['/ConsumedAmphours'] = 0 self._dbusservice['/ConsumedAmphours'] = 0
## Set CCL to 1 amp temporarily, it will overwritte immediately after first message, suppresing BMS missing after script crash ##
self._dbusservice['/Info/MaxChargeCurrent'] = 1 self._dbusservice['/Info/MaxChargeCurrent'] = 1
## Set SOC according to Batrium as init values, if Cerbo or script is restarted at 100% SOC
logging.info("History cell voltage min: %.3f, max: %.3f, totalAhDrawn: %d", self._settings['MinCellVoltage'], self._settings['MaxCellVoltage'], self._settings['TotalAhDrawn'])
if self._bat.soc <= 99: if self._bat.soc <= 99:
self._dbusservice['/Soc'] = self._bat.soc self._dbusservice['/Soc'] = self._bat.soc
else: else:
self._dbusservice['/Soc'] = 100 self._dbusservice['/Soc'] = 100
## Log values written to history/XML ##
logging.debug("History cell voltage min: %.3f, max: %.3f, totalAhDrawn: %d", self._settings['MinCellVoltage'], self._settings['MaxCellVoltage'], self._settings['TotalAhDrawn'])
GLib.timeout_add( self._settings['interval'], exit_on_error, self._update) GLib.timeout_add( self._settings['interval'], exit_on_error, self._update)
def _gettext(self, path, value): def _gettext(self, path, value):
@ -159,7 +161,7 @@ class DbusBatteryService:
def __del__(self): def __del__(self):
self._safe_history() self._safe_history()
logging.info('Stopping dbus_Batrium') logging.info('Stopping dbus-batrium-native integration')
def _safe_history(self): def _safe_history(self):
logging.debug('Saving history to localsettings') logging.debug('Saving history to localsettings')
@ -170,22 +172,31 @@ class DbusBatteryService:
self._settings['TargetChargeVoltage'] = self._dbusservice['/Info/MaxChargeVoltage'] self._settings['TargetChargeVoltage'] = self._dbusservice['/Info/MaxChargeVoltage']
def _daily_stats(self): def _daily_stats(self):
## Update daily statistics and reset energy counters
if (self._dbusservice['/History/DischargedEnergy'] == 0): return if (self._dbusservice['/History/DischargedEnergy'] == 0): return
logging.info("Updating stats, SOC: %d, Discharged: %.2f, Charged: %.2f ",self._bat.soc, self._dbusservice['/History/DischargedEnergy'], self._dbusservice['/History/ChargedEnergy']) logging.debug("Updating stats, SOC: %d, Discharged: %.2f, Charged: %.2f ",self._bat.soc, self._dbusservice['/History/DischargedEnergy'], self._dbusservice['/History/ChargedEnergy'])
self._dbusservice['/History/AverageDischarge'] = (6*self._dbusservice['/History/AverageDischarge'] + self._dbusservice['/History/DischargedEnergy'])/7 #rolling week self._dbusservice['/History/AverageDischarge'] = (6*self._dbusservice['/History/AverageDischarge'] + self._dbusservice['/History/DischargedEnergy'])/7 #rolling week
self._dbusservice['/History/ChargedEnergy'] = 0 self._dbusservice['/History/ChargedEnergy'] = 0
self.ChargedEnergy = 0 self.ChargedEnergy = 0
self._dbusservice['/History/DischargedEnergy'] = 0 self._dbusservice['/History/DischargedEnergy'] = 0
self.DischargedEnergy = 0 self.DischargedEnergy = 0
dt = datetime.now() - datetime.fromtimestamp( float(self._settings['TimeLastFull']) )
self.dailyResetDone = datetime.now().day self.dailyResetDone = datetime.now().day
def _update(self): def _update(self):
#logging.debug("Update CAN %d, Time Now: %d", self._bat.updated, self.lastUpdated)
time = datetime.now() time = datetime.now()
self.lastUpdated = time.timestamp() self.lastUpdated = time.timestamp()
## Monitor communication ##
if (self._bat.updated != -1 and self.lastUpdated == 0) or ((self.lastUpdated - self._bat.updated) < 10):
self._dbusservice['/Connected'] = 1
else:
self._dbusservice['/Connected'] = 0
## Call daily statistics and reset energy counters function
if datetime.now().hour == 6 and datetime.now().minute == 0 and datetime.now().day != self.dailyResetDone :
self._daily_stats()
## Alarms ##
if self._bat.alarm_high_temperature: if self._bat.alarm_high_temperature:
self._dbusservice['/Alarms/HighTemperature'] = 2 self._dbusservice['/Alarms/HighTemperature'] = 2
else: else:
@ -210,78 +221,73 @@ class DbusBatteryService:
self._dbusservice['/Alarms/HighDischargeCurrent'] = 2 self._dbusservice['/Alarms/HighDischargeCurrent'] = 2
else: else:
self._dbusservice['/Alarms/HighDischargeCurrent'] = 0 self._dbusservice['/Alarms/HighDischargeCurrent'] = 0
if (self._bat.updated != -1 and self.lastUpdated == 0) or ((self.lastUpdated - self._bat.updated) < 10):
self._dbusservice['/Connected'] = 1
else:
self._dbusservice['/Connected'] = 0
## State of Charge, Batrium couting SoC above 100, stay at 99 till all cells are balanced, then set timestamp and SoC to 100 ##
if self._bat.soc <= 99: if self._bat.soc <= 99:
self._dbusservice['/Soc'] = self._bat.soc self._dbusservice['/Soc'] = self._bat.soc
# calculate time since last full charge
dt = datetime.now() - datetime.fromtimestamp( float(self._settings['TimeLastFull']) ) dt = datetime.now() - datetime.fromtimestamp( float(self._settings['TimeLastFull']) )
self._dbusservice['/History/TimeSinceLastFullCharge'] = (dt.seconds + dt.days * 24 * 3600) self._dbusservice['/History/TimeSinceLastFullCharge'] = (dt.seconds + dt.days * 24 * 3600)
elif (self._bat.soc > 99) and (self.cell_balanced): elif (self._bat.soc > 99) and (self.cell_balanced):
self._dbusservice['/Soc'] = 100 self._dbusservice['/Soc'] = 100
self._dbusservice['/ConsumedAmphours'] = 0 self._dbusservice['/ConsumedAmphours'] = 0
# set timestamp last full charge self._settings['TimeLastFull'] = time()
#if datetime.fromtimestamp(time()).day != datetime.fromtimestamp(float(self._settings['TimeLastFull'])).day:
# self._settings['TimeLastFull'] = time()
## Cells are balancing or not ##
if self._bat.NumberInBypass != 0: if self._bat.NumberInBypass != 0:
self._dbusservice['/Balancing'] = 1 self._dbusservice['/Balancing'] = 1
else: else:
self._dbusservice['/Balancing'] = 0 self._dbusservice['/Balancing'] = 0
self._dbusservice['/InstalledCapacity'] = self._bat.capacity ## Write Batrium values direct to Venus OS registers ##
self._dbusservice['/Capacity'] = self._bat.capacity
self._dbusservice['/Dc/0/Current'] = self._bat.current self._dbusservice['/Dc/0/Current'] = self._bat.current
self._dbusservice['/Dc/0/Voltage'] = self._bat.voltage self._dbusservice['/Dc/0/Voltage'] = self._bat.voltage
power = self._bat.voltage * self._bat.current power = self._bat.voltage * self._bat.current
self._dbusservice['/Dc/0/Power'] = power self._dbusservice['/Dc/0/Power'] = power
self._dbusservice['/Dc/0/Temperature'] = self._bat.maxCellTemperature self._dbusservice['/Dc/0/Temperature'] = self._bat.maxCellTemperature
self._dbusservice['/Info/MaxChargeCurrent'] = self._bat.maxChargeCurrent self._dbusservice['/Info/MaxChargeCurrent'] = self._bat.maxChargeCurrent
self._dbusservice['/Info/MaxDischargeCurrent'] = self._bat.maxDischargeCurrent self._dbusservice['/Info/MaxDischargeCurrent'] = self._bat.maxDischargeCurrent
self._dbusservice['/System/NrOfModulesOnline'] = self._bat.numberOfModules
self._dbusservice['/System/NrOfBatteriesBalancing'] = self._bat.NumberInBypass
self._dbusservice['/System/BatteriesSeries'] = self._bat.numberOfModules
self._dbusservice['/InstalledCapacity'] = self._bat.capacity
self._dbusservice['/Capacity'] = self._bat.capacity
## Set Charge Voltage Limit according to Batrium, if charging is disabled, Batrium sent 0.0V. Stay at last CVL and save it to Venus OS config xml, to workaround if restarted with charge off ##
if self._bat.maxChargeVoltage != 0: if self._bat.maxChargeVoltage != 0:
self._dbusservice['/Info/MaxChargeVoltage'] = self._bat.maxChargeVoltage self._dbusservice['/Info/MaxChargeVoltage'] = self._bat.maxChargeVoltage
self.maxChargeVoltage = self._bat.maxChargeVoltage self.maxChargeVoltage = self._bat.maxChargeVoltage
# Workaround lower charge voltage a bit, because Batrium sends TCL 0V if charge is disabled # If all cells are balanced, lower CVL 0.1V to spare cells mons
elif self.maxChargeVoltage != 0: elif self.maxChargeVoltage != 0:
self._dbusservice['/Info/MaxChargeVoltage'] = self.maxChargeVoltage - 0.1 self._dbusservice['/Info/MaxChargeVoltage'] = self.maxChargeVoltage - 0.1
elif self.maxChargeVoltage == 0 and self._bat.maxChargeVoltage == 0: elif self.maxChargeVoltage == 0 and self._bat.maxChargeVoltage == 0:
self._dbusservice['/Info/MaxChargeVoltage'] = self._settings['TargetChargeVoltage'] self._dbusservice['/Info/MaxChargeVoltage'] = self._settings['TargetChargeVoltage']
self._dbusservice['/System/NrOfModulesOnline'] = self._bat.numberOfModules ## Calculation every minute ##
self._dbusservice['/System/NrOfBatteriesBalancing'] = self._bat.NumberInBypass
self._dbusservice['/System/BatteriesSeries'] = self._bat.numberOfModules
#update energy statistics daily at 6:00,
if datetime.now().hour == 6 and datetime.now().minute == 0 and datetime.now().day != self.dailyResetDone :
self._daily_stats()
now = datetime.now().time() now = datetime.now().time()
if now.minute != self.minUpdateDone: if now.minute != self.minUpdateDone:
self.minUpdateDone = now.minute self.minUpdateDone = now.minute
## Display min and max cell temperatures every minute, because of better readability
self._dbusservice['/System/MinCellTemperature'] = self._bat.minCellTemperature self._dbusservice['/System/MinCellTemperature'] = self._bat.minCellTemperature
self._dbusservice['/System/MaxCellTemperature'] = self._bat.maxCellTemperature self._dbusservice['/System/MaxCellTemperature'] = self._bat.maxCellTemperature
self._dbusservice['/System/MinTemperatureCellId'] = self._bat.minCellTemperatureId self._dbusservice['/System/MinTemperatureCellId'] = self._bat.minCellTemperatureId
self._dbusservice['/System/MaxTemperatureCellId'] = self._bat.maxCellTemperatureId self._dbusservice['/System/MaxTemperatureCellId'] = self._bat.maxCellTemperatureId
## Same for cell voltages and saving to history
self._dbusservice['/System/MaxCellVoltage'] = self._bat.maxCellVoltage self._dbusservice['/System/MaxCellVoltage'] = self._bat.maxCellVoltage
if (self._bat.maxCellVoltage > self._dbusservice['/History/MaxCellVoltage'] ): if (self._bat.maxCellVoltage > self._dbusservice['/History/MaxCellVoltage'] ):
self._dbusservice['/History/MaxCellVoltage'] = self._bat.maxCellVoltage self._dbusservice['/History/MaxCellVoltage'] = self._bat.maxCellVoltage
logging.info("New maximum cell voltage: %f", self._bat.maxCellVoltage) logging.debug("New maximum cell voltage: %f", self._bat.maxCellVoltage)
self._dbusservice['/System/MinCellVoltage'] = self._bat.minCellVoltage self._dbusservice['/System/MinCellVoltage'] = self._bat.minCellVoltage
if (0 < self._bat.minCellVoltage < self._dbusservice['/History/MinCellVoltage'] ): if (0 < self._bat.minCellVoltage < self._dbusservice['/History/MinCellVoltage'] ):
self._dbusservice['/History/MinCellVoltage'] = self._bat.minCellVoltage self._dbusservice['/History/MinCellVoltage'] = self._bat.minCellVoltage
logging.info("New minimum cell voltage: %f", self._bat.minCellVoltage) logging.debug("New minimum cell voltage: %f", self._bat.minCellVoltage)
self._dbusservice['/System/MaxVoltageCellId'] = self._bat.maxCellVoltageId self._dbusservice['/System/MaxVoltageCellId'] = self._bat.maxCellVoltageId
self._dbusservice['/System/MinVoltageCellId'] = self._bat.minCellVoltageId self._dbusservice['/System/MinVoltageCellId'] = self._bat.minCellVoltageId
# Time remaining to full and to empty, show None if under 1 amp and under 5 days left for nicer looking readings
if self._bat.current > 1: if self._bat.current > 1:
#charging #charging
if self._bat.TimeToFull < 432000: if self._bat.TimeToFull < 432000:
@ -299,16 +305,19 @@ class DbusBatteryService:
else : else :
self._dbusservice['/TimeToGo'] = None self._dbusservice['/TimeToGo'] = None
## Check if all cells are balancing
if self._bat.NumberInBypass == self._bat.numberOfModules: if self._bat.NumberInBypass == self._bat.numberOfModules:
self.cell_balanced = True self.cell_balanced = True
elif self._bat.NumberInBypass == 0: elif self._bat.NumberInBypass == 0:
self.cell_balanced = False self.cell_balanced = False
## Call history save function to write history items to Venus OS config xml
self._safe_history() self._safe_history()
## Calculation every second ##
if now.second != self.secUpdateDone: if now.second != self.secUpdateDone:
self.secUpdateDone = now.second self.secUpdateDone = now.second
#Workaround because of missing messages of Batrium BMS # Calculated charged and discharged energy and save it to history, because Batrium not sending
if power > 0: if power > 0:
self.ChargedEnergy += (power / 3600.0) / 1000 self.ChargedEnergy += (power / 3600.0) / 1000
if power < 0: if power < 0:
@ -319,9 +328,9 @@ class DbusBatteryService:
return True return True
def main(): def main():
parser = ArgumentParser(description='dbus_batrium', add_help=True) ## Cli parser to set interface and for debbuging purposes
parser.add_argument('-d', '--debug', help='enable debug logging', parser = ArgumentParser(description='dbus-batrium-native integration', add_help=True)
action='store_true') parser.add_argument('-d', '--debug', help='enable debug logging', action='store_true')
parser.add_argument('-i', '--interface', help='CAN interface') parser.add_argument('-i', '--interface', help='CAN interface')
parser.add_argument('-p', '--print', help='print only') parser.add_argument('-p', '--print', help='print only')
@ -339,17 +348,12 @@ def main():
logging.info('No CAN interface specified, using default can0') logging.info('No CAN interface specified, using default can0')
args.interface = 'can0' args.interface = 'can0'
logging.info('Starting dbus_batrium %s on %s ' % logging.info('Starting dbus-batrium-native integration %s on %s ' % (VERSION, args.interface))
(VERSION, args.interface))
logging.debug('Setup main loop')
from dbus.mainloop.glib import DBusGMainLoop from dbus.mainloop.glib import DBusGMainLoop
# Have a mainloop, so we can send/receive asynchronous calls to and from dbus # Have a mainloop, so we can send/receive asynchronous calls to and from dbus
DBusGMainLoop(set_as_default=True) DBusGMainLoop(set_as_default=True)
logging.debug('Setup dbus service')
battery_output = DbusBatteryService( battery_output = DbusBatteryService(
servicename='com.victronenergy.battery', servicename='com.victronenergy.battery',
connection = args.interface, connection = args.interface,