Skip to content

Commit cc1d008

Browse files
authored
Solaredge Add Active Bat Control (#3109)
* Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py
1 parent 2db231d commit cc1d008

1 file changed

Lines changed: 77 additions & 54 deletions

File tree

  • packages/modules/devices/solaredge/solaredge

packages/modules/devices/solaredge/solaredge/bat.py

Lines changed: 77 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
import pymodbus
99

1010

11-
from control import data
12-
13-
1411
from modules.common import modbus
1512
from modules.common.abstract_device import AbstractBat
1613
from modules.common.component_state import BatState
@@ -24,11 +21,12 @@
2421
log = logging.getLogger(__name__)
2522

2623
FLOAT32_UNSUPPORTED = -0xffffff00000000000000000000000000
27-
MAX_DISCHARGE_LIMIT = 5000
28-
DEFAULT_CONTROL_MODE = 1 # Control Mode Max Eigenverbrauch
29-
REMOTE_CONTROL_MODE = 4 # Control Mode Remotesteuerung
30-
DEFAULT_COMMAND_MODE = 0 # Command Mode ohne Steuerung
31-
ACTIVE_COMMAND_MODE = 7 # Command Mode Max Eigenverbrauch bei Steuerung
24+
MAX_CHARGEDISCHARGE_LIMIT = 5000
25+
CONTROL_MODE_MSC = 1 # Storage Control Mode Maximize Self Consumption
26+
CONTROL_MODE_REMOTE = 4 # Control Mode Remotesteuerung
27+
REMOTE_CONTROL_COMMAND_MODE_DEFAULT = 0 # Default RC Command Mode ohne Steuerung
28+
REMOTE_CONTROL_COMMAND_MODE_CHARGE = 3 # RC Command Mode Charge from PV+AC
29+
REMOTE_CONTROL_COMMAND_MODE_MSC = 7 # RC Command Mode Maximize Self Consumtion
3230

3331

3432
class KwargsDict(TypedDict):
@@ -45,9 +43,10 @@ class SolaredgeBat(AbstractBat):
4543
"Battery2InstantaneousPower": (0xe274, ModbusDataType.FLOAT_32,),
4644
"StorageControlMode": (0xe004, ModbusDataType.UINT_16,),
4745
"StorageBackupReserved": (0xe008, ModbusDataType.FLOAT_32,),
48-
"StorageChargeDischargeDefaultMode": (0xe00a, ModbusDataType.UINT_16,),
46+
"RemoteControlCommandModeDefault": (0xe00a, ModbusDataType.UINT_16,),
4947
"RemoteControlCommandMode": (0xe00d, ModbusDataType.UINT_16,),
50-
"RemoteControlCommandDischargeLimit": (0xe010, ModbusDataType.FLOAT_32,),
48+
"RemoteControlChargeLimit": (0xe00e, ModbusDataType.FLOAT_32,),
49+
"RemoteControlDischargeLimit": (0xe010, ModbusDataType.FLOAT_32,),
5150
}
5251

5352
def __init__(self, component_config: SolaredgeBatSetup, **kwargs: Any) -> None:
@@ -61,7 +60,7 @@ def initialize(self) -> None:
6160
self.store = get_bat_value_store(self.component_config.id)
6261
self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))
6362
self.min_soc = 13
64-
self.StorageControlMode_Read = DEFAULT_CONTROL_MODE
63+
self.StorageControlMode_Read = CONTROL_MODE_MSC # Default Control Mode Set to MSC if not Read
6564
self.last_mode = 'undefined'
6665

6766
def update(self) -> None:
@@ -124,47 +123,33 @@ def set_power_limit(self, power_limit: Optional[int]) -> None:
124123
# Use 1 as fallback if battery_index is not set
125124
battery_index = getattr(self.component_config.configuration, "battery_index", 1)
126125

127-
try:
128-
power_limit_mode = data.data.bat_all_data.data.config.power_limit_mode
129-
except AttributeError:
130-
log.warning("power_limit_mode not found, assuming 'no_limit'")
131-
power_limit_mode = 'no_limit'
132-
133-
if power_limit_mode == 'no_limit' and self.last_mode != 'limited':
134-
"""
135-
Keine Speichersteuerung, andere Steuerungen zulassen (SolarEdge One, ioBroker, Node-Red etc.).
136-
Falls andere Steuerungen vorhanden sind, sollten diese nicht beeinflusst werden,
137-
daher erfolgt im Modus "Immer" der Speichersteuerung keine Steuerung.
138-
"""
139-
return
140-
141-
if power_limit is None:
142-
# Keine Ladung mit Speichersteuerung.
143-
if self.last_mode == 'limited':
144-
# Steuerung deaktivieren.
126+
if power_limit is None: # No Bat Control should be used.
127+
if self.last_mode in ('discharge-mode', 'charge-mode'):
128+
# Disable Bat Control
145129
log.debug(f"Speicher{battery_index}:Keine Steuerung gefordert, Steuerung deaktivieren.")
146130
values_to_write = {
147-
"RemoteControlCommandDischargeLimit": MAX_DISCHARGE_LIMIT,
148-
"StorageChargeDischargeDefaultMode": DEFAULT_COMMAND_MODE,
149-
"RemoteControlCommandMode": DEFAULT_COMMAND_MODE,
131+
"RemoteControlChargeLimit": MAX_CHARGEDISCHARGE_LIMIT,
132+
"RemoteControlDischargeLimit": MAX_CHARGEDISCHARGE_LIMIT,
133+
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
134+
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
150135
"StorageControlMode": self.StorageControlMode_Read,
151136
}
152137
self._write_registers(values_to_write, unit)
153138
self.last_mode = None
154139
else:
155140
return
156141

157-
elif abs(power_limit) >= 0:
142+
elif power_limit <= 0: # Limit Discharge Mode should be used.
158143
"""
159-
Ladung mit Speichersteuerung.
160-
SolarEdge entlaedt den Speicher immer nur bis zur SoC-Reserve.
161-
Steuerung beenden, wenn der SoC vom Speicher die SoC-Reserve unterschreitet.
144+
SolarEdge discharges the battery only to SoC-Reserve.
145+
Disable Remote Control if SoC of battery is lower than SoC-Reserve.
162146
"""
163147
registers_to_read = [
164148
f"Battery{battery_index}StateOfEnergy",
165149
"StorageControlMode",
166150
"StorageBackupReserved",
167-
"RemoteControlCommandDischargeLimit",
151+
"RemoteControlCommandMode",
152+
"RemoteControlDischargeLimit",
168153
]
169154
try:
170155
values = self._read_registers(registers_to_read, unit)
@@ -177,44 +162,82 @@ def set_power_limit(self, power_limit: Optional[int]) -> None:
177162
log.warning(f"Speicher{battery_index}: Invalid SoC: {soc}")
178163
soc_reserve = max(int(self.min_soc + 2), int(values["StorageBackupReserved"]))
179164
log.debug(f"SoC-Reserve Speicher{battery_index}: {int(soc_reserve)}%.")
180-
discharge_limit = int(values["RemoteControlCommandDischargeLimit"])
165+
discharge_limit = int(values["RemoteControlDischargeLimit"])
181166

182-
if values["StorageControlMode"] == REMOTE_CONTROL_MODE: # Speichersteuerung ist aktiv.
167+
if (values["StorageControlMode"] == CONTROL_MODE_REMOTE and
168+
values["RemoteControlCommandMode"] == REMOTE_CONTROL_COMMAND_MODE_MSC):
169+
# RC Discharge Mode active.
183170
if soc_reserve > soc:
184-
# Speichersteuerung erst deaktivieren, wenn SoC-Reserve unterschritten wird.
185-
# Darf wegen 2 Speichern nicht bereits bei SoC-Reserve deaktiviert werden!
171+
# Disable Remote Control if SOC is lower than SOC-RESERVE.
172+
# toDo: Problem with 2 batteries is unsolved.
186173
log.debug(f"Speicher{battery_index}: Steuerung deaktivieren. SoC-Reserve unterschritten")
187174
values_to_write = {
188-
"RemoteControlCommandDischargeLimit": MAX_DISCHARGE_LIMIT,
189-
"StorageChargeDischargeDefaultMode": DEFAULT_COMMAND_MODE,
190-
"RemoteControlCommandMode": DEFAULT_COMMAND_MODE,
175+
"RemoteControlDischargeLimit": MAX_CHARGEDISCHARGE_LIMIT,
176+
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
177+
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
191178
"StorageControlMode": self.StorageControlMode_Read,
192179
}
193180
self._write_registers(values_to_write, unit)
194181
self.last_mode = None
195182

196183
elif discharge_limit not in range(int(abs(power_limit)) - 10, int(abs(power_limit)) + 10):
197-
# Limit nur bei Abweichung von mehr als 10W, um Konflikte bei 2 Speichern zu verhindern.
184+
# Limit only if difference is more than 10W, needed with more than 1 battery.
198185
log.debug(f"Discharge-Limit Speicher{battery_index}: {int(abs(power_limit))}W.")
199186
values_to_write = {
200-
"RemoteControlCommandDischargeLimit": int(min(abs(power_limit), MAX_DISCHARGE_LIMIT))
187+
"RemoteControlDischargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
201188
}
202189
self._write_registers(values_to_write, unit)
203-
self.last_mode = 'limited'
190+
self.last_mode = 'discharge-mode'
204191

205-
else: # Speichersteuerung ist inaktiv.
192+
else: # Remote Control not active.
206193
if soc_reserve < soc:
207-
# Speichersteuerung nur aktivieren, wenn SoC ueber SoC-Reserve.
194+
# Enable Remote Control if SoC above SoC-Reserve.
208195
log.debug(f"Discharge-Limit aktivieren, Speicher{battery_index}: {int(abs(power_limit))}W.")
209196
self.StorageControlMode_Read = values["StorageControlMode"]
210197
values_to_write = {
211-
"StorageControlMode": REMOTE_CONTROL_MODE,
212-
"StorageChargeDischargeDefaultMode": ACTIVE_COMMAND_MODE,
213-
"RemoteControlCommandMode": ACTIVE_COMMAND_MODE,
214-
"RemoteControlCommandDischargeLimit": int(min(abs(power_limit), MAX_DISCHARGE_LIMIT))
198+
"StorageControlMode": CONTROL_MODE_REMOTE,
199+
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_MSC,
200+
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_MSC,
201+
"RemoteControlDischargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
215202
}
216203
self._write_registers(values_to_write, unit)
217-
self.last_mode = 'limited'
204+
self.last_mode = 'discharge-mode'
205+
206+
elif power_limit > 0: # Charge Mode should be used
207+
registers_to_read = [
208+
"StorageControlMode",
209+
"RemoteControlCommandMode",
210+
"RemoteControlChargeLimit",
211+
]
212+
try:
213+
values = self._read_registers(registers_to_read, unit)
214+
except pymodbus.exceptions.ModbusException as e:
215+
log.error(f"Failed to read registers: {e}")
216+
self.fault_state.error(f"Modbus read error: {e}")
217+
return
218+
219+
if (values["StorageControlMode"] == CONTROL_MODE_REMOTE and
220+
values["RemoteControlCommandMode"] == REMOTE_CONTROL_COMMAND_MODE_CHARGE):
221+
# Remote Control Charge Mode active.
222+
log.debug(
223+
f"Ladung Speicher.{battery_index}: {int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))}W.")
224+
values_to_write = {
225+
"RemoteControlChargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
226+
}
227+
self._write_registers(values_to_write, unit)
228+
self.last_mode = 'charge-mode'
229+
230+
else: # Remote Control Charge Mode inactive.
231+
log.debug(f"Aktivierung Laden Speicher{battery_index}: {int(abs(power_limit))}W.")
232+
self.StorageControlMode_Read = values["StorageControlMode"]
233+
values_to_write = {
234+
"StorageControlMode": CONTROL_MODE_REMOTE,
235+
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_CHARGE,
236+
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_CHARGE,
237+
"RemoteControlChargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
238+
}
239+
self._write_registers(values_to_write, unit)
240+
self.last_mode = 'charge-mode'
218241

219242
def _read_registers(self, register_names: list, unit: int) -> Dict[str, Union[int, float]]:
220243
values = {}

0 commit comments

Comments
 (0)