Skip to content

Commit 1e7c794

Browse files
committed
Calculate max soc if the next price minimum is reachable and higher than the current one depending on the next sunset
1 parent 7fa9ae3 commit 1e7c794

File tree

1 file changed

+34
-46
lines changed

1 file changed

+34
-46
lines changed

source/inverter_charge_controller.py

+34-46
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def __init__(self):
3333
self.database_handler = DatabaseHandler("power_buy")
3434

3535
self.next_price_minimum = None
36+
self.average_power_consumption = None
3637
# This is a dict which saves the values of a certain operations such as the upcoming energy rates, the
3738
# expected power harvested by the sun or the expected power usage
3839
# This way if one of the requests to an external system fails (e.g. no Internet access) the prior requests don't
@@ -123,8 +124,8 @@ def _do_iteration(self) -> None:
123124
current_state_of_charge = self.inverter.get_state_of_charge()
124125
self.log.info(f"The battery is currently is at {current_state_of_charge}")
125126

126-
average_power_consumption = self._get_average_power_consumption()
127-
self.log.info(f"The average power consumption is {average_power_consumption}")
127+
self.average_power_consumption = self._get_average_power_consumption()
128+
self.log.info(f"The average power consumption is {self.average_power_consumption}")
128129

129130
self.log.info(f"The battery shall be at least at {self.target_min_soc} at all times")
130131
self.log.info(f"The battery shall be at most be charged up to {self.target_max_soc}")
@@ -133,7 +134,7 @@ def _do_iteration(self) -> None:
133134
self.sun_forecast_handler.calculate_min_and_max_of_soc_in_timeframe(
134135
timestamp_now,
135136
self.next_price_minimum.timestamp,
136-
average_power_consumption,
137+
self.average_power_consumption,
137138
current_state_of_charge,
138139
self.next_price_minimum.has_to_be_rechecked,
139140
self._get_solar_data(timestamp_now, self.next_price_minimum.timestamp),
@@ -150,7 +151,7 @@ def _do_iteration(self) -> None:
150151
"next price minimum": self.next_price_minimum,
151152
"next price minimum has to be rechecked": self.next_price_minimum.has_to_be_rechecked,
152153
"current state of charge": current_state_of_charge,
153-
"average power consumption": average_power_consumption.watts,
154+
"average power consumption": self.average_power_consumption.watts,
154155
"target min soc": self.target_min_soc,
155156
"target max soc": self.target_max_soc,
156157
"minimum of soc until next price minimum": minimum_of_soc_until_next_price_minimum,
@@ -159,39 +160,29 @@ def _do_iteration(self) -> None:
159160
self.log.debug(f"Summary of energy values: {summary_of_energy_vales}")
160161

161162
self.coordinate_charging(
162-
timestamp_now,
163163
current_state_of_charge,
164-
average_power_consumption,
165164
current_energy_rate,
166165
minimum_of_soc_until_next_price_minimum,
167-
maximum_of_soc_until_next_price_minimum,
168166
)
169167

170168
self.iteration_cache = {}
171169

172170
def coordinate_charging(
173171
self,
174-
timestamp_now: datetime,
175172
current_state_of_charge: StateOfCharge,
176-
average_power_consumption: Power,
177173
current_energy_rate: EnergyRate,
178174
minimum_of_soc_until_next_price_minimum: StateOfCharge,
179-
maximum_of_soc_until_next_price_minimum: StateOfCharge,
180175
) -> None:
181176
"""
182177
Coordinates the charging process based on the current state of charge, power consumption, energy rate,
183178
calculated min and max state of charges and targets for state of charge.
184179
Determines whether the next price minimum is reachable and initiates corresponding charging strategies.
185180
186181
Args:
187-
timestamp_now (datetime): The current datetime.
188182
current_state_of_charge (StateOfCharge): The current level of charge in the battery.
189-
average_power_consumption (Power): The average rate of power consumption.
190183
current_energy_rate (EnergyRate): The current cost of energy, influencing charging decisions.
191184
minimum_of_soc_until_next_price_minimum (StateOfCharge): The calculated minimum SOC in the timespan to the
192185
next price minimum.
193-
maximum_of_soc_until_next_price_minimum (StateOfCharge): The calculated maximum SOC in the timespan to the
194-
next price minimum.
195186
"""
196187
if minimum_of_soc_until_next_price_minimum < self.target_min_soc:
197188
self.log.info(
@@ -200,39 +191,28 @@ def coordinate_charging(
200191
f"{self.target_min_soc} "
201192
f"--> Checking whether the next price minimum can be reached even by charging to {self.target_max_soc}"
202193
)
203-
if not self._is_next_price_minimum_reachable_by_charging_the_battery_fully(
204-
timestamp_now, average_power_consumption
205-
):
206-
self._coordinate_charging_when_next_price_minimum_is_unreachable(average_power_consumption)
194+
if not self._is_next_price_minimum_reachable_by_charging_the_battery_fully():
195+
self._coordinate_charging_when_next_price_minimum_is_unreachable()
207196
return
208197

209198
self._coordinate_charging_next_price_minimum_is_reachable(
210199
current_state_of_charge,
211200
current_energy_rate,
212201
minimum_of_soc_until_next_price_minimum,
213-
maximum_of_soc_until_next_price_minimum,
214202
)
215203

216-
def _is_next_price_minimum_reachable_by_charging_the_battery_fully(
217-
self,
218-
timestamp_now: datetime,
219-
average_power_consumption: Power,
220-
) -> bool:
204+
def _is_next_price_minimum_reachable_by_charging_the_battery_fully(self) -> bool:
221205
"""
222206
Determines whether the next price minimum can be reached by fully charging the battery.
223207
224-
Args:
225-
timestamp_now (datetime): The current datetime.
226-
average_power_consumption (Power): The average rate of power consumption.
227-
228208
Returns:
229209
bool: Whether it is possible to reach the next price minimum by charging to the target maximum
230210
"""
231211
minimum_of_soc_until_next_price_minimum, _ = (
232212
self.sun_forecast_handler.calculate_min_and_max_of_soc_in_timeframe(
233-
timestamp_now,
213+
TimeHandler.get_time(),
234214
self.next_price_minimum.timestamp,
235-
average_power_consumption,
215+
self.average_power_consumption,
236216
self.target_max_soc,
237217
self.next_price_minimum.has_to_be_rechecked,
238218
self._get_solar_data(),
@@ -251,14 +231,11 @@ def _is_next_price_minimum_reachable_by_charging_the_battery_fully(
251231
)
252232
return False
253233

254-
def _coordinate_charging_when_next_price_minimum_is_unreachable(self, average_power_consumption: Power) -> None:
234+
def _coordinate_charging_when_next_price_minimum_is_unreachable(self) -> None:
255235
"""
256236
Handles the coordination of battery charging when the upcoming price minimum cannot be reached.
257237
258238
This function utilizes the upcoming energy rates to determine the most efficient charging strategy.
259-
260-
Args:
261-
average_power_consumption: The average rate of power consumption during the charging period.
262239
"""
263240
energy_rate_after_price_drops_over_average, energy_rate_before_price_rises_over_average = (
264241
self._get_energy_rates_before_and_after_price_spike()
@@ -278,7 +255,6 @@ def _coordinate_charging_when_next_price_minimum_is_unreachable(self, average_po
278255
self.log.info("Waking up to determine the optimal charging time around the price spike")
279256
if energy_rate_after_price_drops_over_average < energy_rate_before_price_rises_over_average:
280257
self._coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_after_spike_cheaper_than_before(
281-
average_power_consumption,
282258
energy_rate_after_price_drops_over_average,
283259
energy_rate_before_price_rises_over_average,
284260
)
@@ -306,7 +282,6 @@ def _get_energy_rates_before_and_after_price_spike(
306282

307283
def _coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_after_spike_cheaper_than_before(
308284
self,
309-
average_power_consumption: Power,
310285
energy_rate_after_price_drops_after_average: EnergyRate,
311286
energy_rate_before_price_rises_over_average: EnergyRate,
312287
) -> None:
@@ -322,7 +297,7 @@ def _coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_af
322297
self.sun_forecast_handler.calculate_min_and_max_of_soc_in_timeframe(
323298
energy_rate_before_price_rises_over_average.timestamp,
324299
energy_rate_after_price_drops_after_average.timestamp,
325-
average_power_consumption,
300+
self.average_power_consumption,
326301
current_state_of_charge,
327302
self.next_price_minimum.has_to_be_rechecked,
328303
self._get_solar_data(),
@@ -345,7 +320,7 @@ def _coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_af
345320
self.sun_forecast_handler.calculate_min_and_max_of_soc_in_timeframe(
346321
energy_rate_after_price_drops_after_average.timestamp, # = now
347322
self.next_price_minimum.timestamp,
348-
average_power_consumption,
323+
self.average_power_consumption,
349324
current_state_of_charge,
350325
self.next_price_minimum.has_to_be_rechecked,
351326
self._get_solar_data(),
@@ -357,7 +332,6 @@ def _coordinate_charging_next_price_minimum_is_reachable(
357332
current_state_of_charge: StateOfCharge,
358333
current_energy_rate: EnergyRate,
359334
minimum_of_soc_until_next_price_minimum: StateOfCharge,
360-
maximum_of_soc_until_next_price_minimum: StateOfCharge,
361335
) -> None:
362336
"""
363337
Determines and coordinates the target state of charge for charging the battery, either to reach the next
@@ -372,8 +346,6 @@ def _coordinate_charging_next_price_minimum_is_reachable(
372346
current_energy_rate (EnergyRate): The current cost of energy, influencing charging decisions.
373347
minimum_of_soc_until_next_price_minimum (StateOfCharge): The calculated minimum SOC in the timespan to the
374348
next price minimum.
375-
maximum_of_soc_until_next_price_minimum (StateOfCharge): The calculated maximum SOC in the timespan to the
376-
next price minimum.
377349
"""
378350
if current_energy_rate >= self.next_price_minimum:
379351
charging_target_soc = (
@@ -386,7 +358,8 @@ def _coordinate_charging_next_price_minimum_is_reachable(
386358
else:
387359
charging_target_soc = (
388360
self._calculate_target_soc_next_price_minimum_is_reachable_and_current_minimum_is_lower_than_next_one(
389-
current_energy_rate, current_state_of_charge, maximum_of_soc_until_next_price_minimum
361+
current_energy_rate,
362+
current_state_of_charge,
390363
)
391364
)
392365

@@ -425,19 +398,34 @@ def _calculate_target_soc_next_price_minimum_is_reachable_and_current_minimum_is
425398
self,
426399
current_energy_rate: EnergyRate,
427400
current_state_of_charge: StateOfCharge,
428-
maximum_of_soc_until_next_price_minimum: StateOfCharge,
429401
) -> StateOfCharge:
430402
self.log.info(
431403
f"The price of the upcoming minimum ({self.next_price_minimum.rate} ct/kWh) is higher than the one of "
432404
f"the current minimum ({current_energy_rate.rate} ct/kWh) "
433405
"--> Will charge as much as possible without wasting any energy of the sun"
434406
)
407+
upcoming_sunset_time = self.sun_forecast_handler.get_upcoming_sunset_time()
408+
timeframe_end = max(self.next_price_minimum.timestamp, upcoming_sunset_time)
409+
minimum_comes_last = self.next_price_minimum.timestamp == timeframe_end
410+
self.log.debug(
411+
f"The timeframe end is {timeframe_end} (next price minimum: {self.next_price_minimum.timestamp}, "
412+
f"sunset: {upcoming_sunset_time})"
413+
)
414+
_, maximum_of_soc_until_timeframe_end = self.sun_forecast_handler.calculate_min_and_max_of_soc_in_timeframe(
415+
TimeHandler.get_time(),
416+
timeframe_end,
417+
self.average_power_consumption,
418+
current_state_of_charge,
419+
self.next_price_minimum.has_to_be_rechecked,
420+
self._get_solar_data(),
421+
)
435422
self.log.debug(
436423
f"Formula for calculating the target state of charge: current ({current_state_of_charge}) + "
437-
f"target maximum state of charge ({self.target_max_soc}) - "
438-
f"maximum state of charge until next price minimum ({maximum_of_soc_until_next_price_minimum})"
424+
f"target maximum state of charge ({self.target_max_soc}) - maximum state of charge until the "
425+
f"{'next price minimum' if minimum_comes_last else 'upcoming sunset'} "
426+
f"({maximum_of_soc_until_timeframe_end})"
439427
)
440-
return current_state_of_charge + self.target_max_soc - maximum_of_soc_until_next_price_minimum
428+
return current_state_of_charge + self.target_max_soc - maximum_of_soc_until_timeframe_end
441429

442430
def _charge_inverter(self, target_state_of_charge: StateOfCharge) -> None:
443431
"""

0 commit comments

Comments
 (0)