Skip to content

Commit

Permalink
don't turn back inverter on unless turned off by BMS
Browse files Browse the repository at this point in the history
Only allow DVCC to turn an inverter on, if it was previously turned off by
DVCC. If inverter was soft-turned off manually, leave it off.

victronenergy/venus#736
  • Loading branch information
izak committed Jun 2, 2021
1 parent c6126b8 commit bc42bbb
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 8 deletions.
38 changes: 30 additions & 8 deletions delegates/dvcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
VEDIRECT_FIRMWARE_REQUIRED = 0x129
VECAN_FIRMWARE_REQUIRED = 0x10200 # 1.02, 24-bit version

class Flags(object):
InverterSwitchedOff = 1 # inverter was switched off when discharge was disabled

# This is a place to account for some BMS quirks where we may have to ignore
# the BMS value and substitute our own.

Expand Down Expand Up @@ -244,7 +247,8 @@ def update_values(self):

class Inverter(object):
""" Encapsulates an inverter object. """
def __init__(self, monitor, service):
def __init__(self, dvcc, monitor, service):
self.dvcc = dvcc
self.monitor = monitor
self.service = service

Expand All @@ -259,17 +263,24 @@ def mode(self, m):
def set_maxdischargecurrent(self, limit):
""" For plain inverters, a limit of zero turns it off, and a non-zero
limit turns it back on. """
m = 2 if limit > 0 else 4 # 2 == invert-only, 4 == off
if self.mode != m:
self.mode = m
if limit == 0 and self.mode != 4:
self.dvcc.flags |= Flags.InverterSwitchedOff
self.mode = 4 # off
elif limit > 0 and self.mode != 2:
# If the inverter was turned off by us, switch it back on
# This avoids turning the inverter on if it was previously
# soft-switched off by something other than DVCC.
if self.dvcc.flags & Flags.InverterSwitchedOff:
self.dvcc.flags &= ~Flags.InverterSwitchedOff
self.mode = 2 # inverter only

class InverterCharger(SolarCharger, Inverter):
""" Encapsulates an inverter/charger object, currently the inverter RS,
which has a solar input and can charge the battery like a solar
charger, but is also an inverter.
"""
def __init__(self, monitor, service):
super(InverterCharger, self).__init__(monitor, service)
SolarCharger.__init__(self, monitor, service)

@property
def has_externalcontrol_support(self):
Expand All @@ -294,7 +305,8 @@ def set_maxdischargecurrent(self, limit):

class InverterSubsystem(object):
""" Encapsulate collection of inverters. """
def __init__(self, monitor):
def __init__(self, dvcc, monitor):
self.dvcc = dvcc
self.monitor = monitor
self._inverters = {}

Expand All @@ -303,7 +315,7 @@ def _add_inverter(self, ob):
return ob

def add_inverter(self, service):
return self._add_inverter(Inverter(self.monitor, service))
return self._add_inverter(Inverter(self.dvcc, self.monitor, service))

def remove_inverter(self, service):
del self._inverters[service]
Expand Down Expand Up @@ -756,20 +768,22 @@ def get_input(self):
'/Link/NetworkMode']),
('com.victronenergy.settings', [
'/Settings/CGwacs/OvervoltageFeedIn',
'/Settings/Dvcc/Flags',
'/Settings/Services/Bol'])]

def get_settings(self):
return [
('maxchargecurrent', '/Settings/SystemSetup/MaxChargeCurrent', -1, -1, 10000),
('maxchargevoltage', '/Settings/SystemSetup/MaxChargeVoltage', 0.0, 0.0, 80.0),
('dvcc_flags', '/Settings/Dvcc/Flags', 0, 0, 0),
('bol', '/Settings/Services/Bol', 0, 0, 7)
]

def set_sources(self, dbusmonitor, settings, dbusservice):
SystemCalcDelegate.set_sources(self, dbusmonitor, settings, dbusservice)
self._batterysystem = BatterySubsystem(dbusmonitor)
self._solarsystem = SolarChargerSubsystem(dbusmonitor)
self._inverters = InverterSubsystem(dbusmonitor)
self._inverters = InverterSubsystem(self, dbusmonitor)
self._multi = Multi(dbusmonitor, dbusservice)

self._dbusservice.add_path('/Control/SolarChargeVoltage', value=0)
Expand Down Expand Up @@ -848,6 +862,14 @@ def has_dvcc(self):
v = self._settings['bol']
return bool(v & 1)

@property
def flags(self):
return self._settings['dvcc_flags']

@flags.setter
def flags(self, v):
self._settings['dvcc_flags'] = int(v)

@property
def bms(self):
bmses = sorted(self._batterysystem.bmses,
Expand Down
50 changes: 50 additions & 0 deletions tests/hub_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1755,6 +1755,7 @@ def test_inverter_rs_discharge_current(self):
'/Link/DischargeCurrent': 0.0}})

def test_inverter_switchoff_bms(self):
from delegates.dvcc import Dvcc
# Add inverter
self._add_device('com.victronenergy.inverter.ttyO1', {
'/Ac/Out/L1/P': 2000,
Expand Down Expand Up @@ -1791,9 +1792,58 @@ def test_inverter_switchoff_bms(self):
self._check_external_values({
'com.victronenergy.inverter.ttyO1': {
'/Mode': 4}}) # OFF
self.assertTrue(Dvcc.instance.flags & 1) # InverterSwitchedOff flag set

self._monitor.set_value('com.victronenergy.battery.ttyO2', '/Info/MaxDischargeCurrent', 100.0)
self._update_values(interval=3000)
self._check_external_values({
'com.victronenergy.inverter.ttyO1': {
'/Mode': 2}}) # InvertOnly

self.assertFalse(Dvcc.instance.flags & 1) # InverterSwitchedOff flag unset

def test_inverter_switchoff_bms2(self):
# Leave inverter off and don't turn it back on if it wasn't turned off
# by us.
# Add inverter
self._add_device('com.victronenergy.inverter.ttyO1', {
'/Ac/Out/L1/P': 2000,
'/Ac/Out/L1/V': 234.2,
'/Dc/0/Voltage': 53.1,
'/Dc/0/Current': 40.0,
'/DeviceInstance': 278,
'/Soc': 53.2,
'/State': 9,
'/Mode': 4, # Off
'/IsInverterCharger': 0, # Dumb inverter
'/Link/NetworkMode': 0,
'/Link/ChargeVoltage': None,
'/Link/ChargeCurrent': None,
'/Link/DischargeCurrent': None,
'/Settings/ChargeCurrentLimit': 100},
product_name='Inverter RS', connection='VE.Direct')

# Battery
self._add_device('com.victronenergy.battery.ttyO2',
product_name='battery',
values={
'/Dc/0/Voltage': 53.2,
'/Dc/0/Current': 80.0,
'/Dc/0/Power': 4000,
'/Soc': 43.2,
'/DeviceInstance': 512,
'/Info/BatteryLowVoltage': 47,
'/Info/MaxChargeCurrent': 100,
'/Info/MaxChargeVoltage': 58.2,
'/Info/MaxDischargeCurrent': 0.0})

self._update_values(interval=3000)
self._check_external_values({
'com.victronenergy.inverter.ttyO1': {
'/Mode': 4}}) # OFF

self._monitor.set_value('com.victronenergy.battery.ttyO2', '/Info/MaxDischargeCurrent', 100.0)
self._update_values(interval=3000)
self._check_external_values({
'com.victronenergy.inverter.ttyO1': {
'/Mode': 4}}) # Remains OFF

0 comments on commit bc42bbb

Please sign in to comment.