88import pymodbus
99
1010
11- from control import data
12-
13-
1411from modules .common import modbus
1512from modules .common .abstract_device import AbstractBat
1613from modules .common .component_state import BatState
2421log = logging .getLogger (__name__ )
2522
2623FLOAT32_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
3432class 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