Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 48 additions & 94 deletions packages/modules/devices/solaredge/solaredge/bat.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
CONTROL_MODE_REMOTE = 4 # Control Mode Remotesteuerung
REMOTE_CONTROL_COMMAND_MODE_DEFAULT = 0 # Default RC Command Mode ohne Steuerung
REMOTE_CONTROL_COMMAND_MODE_CHARGE = 3 # RC Command Mode Charge from PV+AC
REMOTE_CONTROL_COMMAND_MODE_MSC = 7 # RC Command Mode Maximize Self Consumtion
REMOTE_CONTROL_COMMAND_MODE_MSC = 7 # RC Command Mode Maximize Self Consumtion used for Limit Discharge


class KwargsDict(TypedDict):
Expand Down Expand Up @@ -59,9 +59,6 @@ def initialize(self) -> None:
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher")
self.store = get_bat_value_store(self.component_config.id)
self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))
self.min_soc = 13
self.StorageControlMode_Read = CONTROL_MODE_MSC # Default Control Mode Set to MSC if not Read
self.last_mode = 'undefined'

def update(self) -> None:
self.store.set(self.read_state())
Expand Down Expand Up @@ -109,9 +106,6 @@ def get_values(self) -> Tuple[float, float]:
power = 0
if soc == FLOAT32_UNSUPPORTED or not 0 <= soc <= 100:
log.warning(f"Invalid SoC Speicher{battery_index}: {soc}")
else:
self.min_soc = min(int(soc), int(self.min_soc))
log.debug(f"Min-SoC Speicher{battery_index}: {int(self.min_soc)}%.")

return power, soc

Expand All @@ -123,121 +117,81 @@ def set_power_limit(self, power_limit: Optional[int]) -> None:
# Use 1 as fallback if battery_index is not set
battery_index = getattr(self.component_config.configuration, "battery_index", 1)

registers_to_read = [
"StorageControlMode",
"RemoteControlCommandMode",
"RemoteControlChargeLimit",
"RemoteControlDischargeLimit",
]
try:
values = self._read_registers(registers_to_read, unit)
except pymodbus.exceptions.ModbusException as e:
log.error(f"Failed to read registers: {e}")
self.fault_state.error(f"Modbus read error: {e}")
return

if power_limit is None: # No Bat Control should be used.
if self.last_mode in ('discharge-mode', 'charge-mode'):
if values["StorageControlMode"] == CONTROL_MODE_MSC:
log.debug(f"Speicher{battery_index}:Keine Steuerung gefordert, bereits deaktiviert.")
else:
# Disable Bat Control
log.debug(f"Speicher{battery_index}:Keine Steuerung gefordert, Steuerung deaktivieren.")
values_to_write = {
"RemoteControlChargeLimit": MAX_CHARGEDISCHARGE_LIMIT,
"RemoteControlDischargeLimit": MAX_CHARGEDISCHARGE_LIMIT,
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
"StorageControlMode": self.StorageControlMode_Read,
"StorageControlMode": CONTROL_MODE_MSC,
}
self._write_registers(values_to_write, unit)
self.last_mode = None
else:
return
log.debug(f"Speicher{battery_index}:Keine Steuerung gefordert, Steuerung deaktiviert.")

elif power_limit <= 0: # Limit Discharge Mode should be used.
"""
SolarEdge discharges the battery only to SoC-Reserve.
Disable Remote Control if SoC of battery is lower than SoC-Reserve.
"""
registers_to_read = [
f"Battery{battery_index}StateOfEnergy",
"StorageControlMode",
"StorageBackupReserved",
"RemoteControlCommandMode",
"RemoteControlDischargeLimit",
]
try:
values = self._read_registers(registers_to_read, unit)
except pymodbus.exceptions.ModbusException as e:
log.error(f"Failed to read registers: {e}")
self.fault_state.error(f"Modbus read error: {e}")
return
soc = values[f"Battery{battery_index}StateOfEnergy"]
if soc == FLOAT32_UNSUPPORTED or not 0 <= soc <= 100:
log.warning(f"Speicher{battery_index}: Invalid SoC: {soc}")
soc_reserve = max(int(self.min_soc + 2), int(values["StorageBackupReserved"]))
log.debug(f"SoC-Reserve Speicher{battery_index}: {int(soc_reserve)}%.")
discharge_limit = int(values["RemoteControlDischargeLimit"])

if (values["StorageControlMode"] == CONTROL_MODE_REMOTE and
values["RemoteControlCommandMode"] == REMOTE_CONTROL_COMMAND_MODE_MSC):
# RC Discharge Mode active.
if soc_reserve > soc:
# Disable Remote Control if SOC is lower than SOC-RESERVE.
# toDo: Problem with 2 batteries is unsolved.
log.debug(f"Speicher{battery_index}: Steuerung deaktivieren. SoC-Reserve unterschritten")
values_to_write = {
"RemoteControlDischargeLimit": MAX_CHARGEDISCHARGE_LIMIT,
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_DEFAULT,
"StorageControlMode": self.StorageControlMode_Read,
}
self._write_registers(values_to_write, unit)
self.last_mode = None

elif discharge_limit not in range(int(abs(power_limit)) - 10, int(abs(power_limit)) + 10):
# Limit only if difference is more than 10W, needed with more than 1 battery.
log.debug(f"Discharge-Limit Speicher{battery_index}: {int(abs(power_limit))}W.")
values_to_write = {
"RemoteControlDischargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
}
self._write_registers(values_to_write, unit)
self.last_mode = 'discharge-mode'

else: # Remote Control not active.
if soc_reserve < soc:
# Enable Remote Control if SoC above SoC-Reserve.
log.debug(f"Discharge-Limit aktivieren, Speicher{battery_index}: {int(abs(power_limit))}W.")
self.StorageControlMode_Read = values["StorageControlMode"]
# Remote Control and Discharge Mode already active.
discharge_limit = int(values["RemoteControlDischargeLimit"])
if discharge_limit not in range(int(abs(power_limit)) - 10, int(abs(power_limit)) + 10):
# Send Limit only if difference is more than 10W, needed with more than 1 battery.
values_to_write = {
"StorageControlMode": CONTROL_MODE_REMOTE,
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_MSC,
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_MSC,
"RemoteControlDischargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
}
self._write_registers(values_to_write, unit)
self.last_mode = 'discharge-mode'

elif power_limit > 0: # Charge Mode should be used
registers_to_read = [
"StorageControlMode",
"RemoteControlCommandMode",
"RemoteControlChargeLimit",
]
try:
values = self._read_registers(registers_to_read, unit)
except pymodbus.exceptions.ModbusException as e:
log.error(f"Failed to read registers: {e}")
self.fault_state.error(f"Modbus read error: {e}")
return

if (values["StorageControlMode"] == CONTROL_MODE_REMOTE and
values["RemoteControlCommandMode"] == REMOTE_CONTROL_COMMAND_MODE_CHARGE):
# Remote Control Charge Mode active.
log.debug(
f"Ladung Speicher.{battery_index}: {int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))}W.")
log.debug(f"Entlade-Limit Speicher{battery_index}: {int(abs(power_limit))}W.")
else:
log.debug(f"Entlade-Limit Speicher{battery_index}: Abweichung unter +/- 10W.")
else: # Enable Remote Control and Discharge Mode.
values_to_write = {
"RemoteControlChargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
"StorageControlMode": CONTROL_MODE_REMOTE,
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_MSC,
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_MSC,
"RemoteControlDischargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
}
self._write_registers(values_to_write, unit)
self.last_mode = 'charge-mode'
log.debug(f"Entlade-Limit aktiviert, Speicher{battery_index}: {int(abs(power_limit))}W.")

else: # Remote Control Charge Mode inactive.
log.debug(f"Aktivierung Laden Speicher{battery_index}: {int(abs(power_limit))}W.")
self.StorageControlMode_Read = values["StorageControlMode"]
elif power_limit > 0: # Charge Mode should be used
if (values["StorageControlMode"] == CONTROL_MODE_REMOTE and
values["RemoteControlCommandMode"] == REMOTE_CONTROL_COMMAND_MODE_CHARGE):
# Remote Control and Charge Mode already active.
charge_limit = int(values["RemoteControlChargeLimit"])
if charge_limit not in range(int(abs(power_limit)) - 10, int(abs(power_limit)) + 10):
# Send Limit only if difference is more than 10W.
values_to_write = {
"RemoteControlChargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
}
self._write_registers(values_to_write, unit)
log.debug(f"Ladung Speicher{battery_index}: {int(abs(power_limit))}W.")
else:
log.debug(f"Ladung Speicher{battery_index}: Abweichung unter +/- 10W.")
else: # Enable Remote Control and Charge Mode.
values_to_write = {
"StorageControlMode": CONTROL_MODE_REMOTE,
"RemoteControlCommandModeDefault": REMOTE_CONTROL_COMMAND_MODE_CHARGE,
"RemoteControlCommandMode": REMOTE_CONTROL_COMMAND_MODE_CHARGE,
"RemoteControlChargeLimit": int(min(abs(power_limit), MAX_CHARGEDISCHARGE_LIMIT))
}
self._write_registers(values_to_write, unit)
self.last_mode = 'charge-mode'
log.debug(f"Aktivierung Ladung Speicher{battery_index}: {int(abs(power_limit))}W.")

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