@@ -33,6 +33,7 @@ def __init__(self):
33
33
self .database_handler = DatabaseHandler ("power_buy" )
34
34
35
35
self .next_price_minimum = None
36
+ self .average_power_consumption = None
36
37
# This is a dict which saves the values of a certain operations such as the upcoming energy rates, the
37
38
# expected power harvested by the sun or the expected power usage
38
39
# 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:
123
124
current_state_of_charge = self .inverter .get_state_of_charge ()
124
125
self .log .info (f"The battery is currently is at { current_state_of_charge } " )
125
126
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 } " )
128
129
129
130
self .log .info (f"The battery shall be at least at { self .target_min_soc } at all times" )
130
131
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:
133
134
self .sun_forecast_handler .calculate_min_and_max_of_soc_in_timeframe (
134
135
timestamp_now ,
135
136
self .next_price_minimum .timestamp ,
136
- average_power_consumption ,
137
+ self . average_power_consumption ,
137
138
current_state_of_charge ,
138
139
self .next_price_minimum .has_to_be_rechecked ,
139
140
self ._get_solar_data (timestamp_now , self .next_price_minimum .timestamp ),
@@ -150,7 +151,7 @@ def _do_iteration(self) -> None:
150
151
"next price minimum" : self .next_price_minimum ,
151
152
"next price minimum has to be rechecked" : self .next_price_minimum .has_to_be_rechecked ,
152
153
"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 ,
154
155
"target min soc" : self .target_min_soc ,
155
156
"target max soc" : self .target_max_soc ,
156
157
"minimum of soc until next price minimum" : minimum_of_soc_until_next_price_minimum ,
@@ -159,39 +160,29 @@ def _do_iteration(self) -> None:
159
160
self .log .debug (f"Summary of energy values: { summary_of_energy_vales } " )
160
161
161
162
self .coordinate_charging (
162
- timestamp_now ,
163
163
current_state_of_charge ,
164
- average_power_consumption ,
165
164
current_energy_rate ,
166
165
minimum_of_soc_until_next_price_minimum ,
167
- maximum_of_soc_until_next_price_minimum ,
168
166
)
169
167
170
168
self .iteration_cache = {}
171
169
172
170
def coordinate_charging (
173
171
self ,
174
- timestamp_now : datetime ,
175
172
current_state_of_charge : StateOfCharge ,
176
- average_power_consumption : Power ,
177
173
current_energy_rate : EnergyRate ,
178
174
minimum_of_soc_until_next_price_minimum : StateOfCharge ,
179
- maximum_of_soc_until_next_price_minimum : StateOfCharge ,
180
175
) -> None :
181
176
"""
182
177
Coordinates the charging process based on the current state of charge, power consumption, energy rate,
183
178
calculated min and max state of charges and targets for state of charge.
184
179
Determines whether the next price minimum is reachable and initiates corresponding charging strategies.
185
180
186
181
Args:
187
- timestamp_now (datetime): The current datetime.
188
182
current_state_of_charge (StateOfCharge): The current level of charge in the battery.
189
- average_power_consumption (Power): The average rate of power consumption.
190
183
current_energy_rate (EnergyRate): The current cost of energy, influencing charging decisions.
191
184
minimum_of_soc_until_next_price_minimum (StateOfCharge): The calculated minimum SOC in the timespan to the
192
185
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.
195
186
"""
196
187
if minimum_of_soc_until_next_price_minimum < self .target_min_soc :
197
188
self .log .info (
@@ -200,39 +191,28 @@ def coordinate_charging(
200
191
f"{ self .target_min_soc } "
201
192
f"--> Checking whether the next price minimum can be reached even by charging to { self .target_max_soc } "
202
193
)
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 ()
207
196
return
208
197
209
198
self ._coordinate_charging_next_price_minimum_is_reachable (
210
199
current_state_of_charge ,
211
200
current_energy_rate ,
212
201
minimum_of_soc_until_next_price_minimum ,
213
- maximum_of_soc_until_next_price_minimum ,
214
202
)
215
203
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 :
221
205
"""
222
206
Determines whether the next price minimum can be reached by fully charging the battery.
223
207
224
- Args:
225
- timestamp_now (datetime): The current datetime.
226
- average_power_consumption (Power): The average rate of power consumption.
227
-
228
208
Returns:
229
209
bool: Whether it is possible to reach the next price minimum by charging to the target maximum
230
210
"""
231
211
minimum_of_soc_until_next_price_minimum , _ = (
232
212
self .sun_forecast_handler .calculate_min_and_max_of_soc_in_timeframe (
233
- timestamp_now ,
213
+ TimeHandler . get_time () ,
234
214
self .next_price_minimum .timestamp ,
235
- average_power_consumption ,
215
+ self . average_power_consumption ,
236
216
self .target_max_soc ,
237
217
self .next_price_minimum .has_to_be_rechecked ,
238
218
self ._get_solar_data (),
@@ -251,14 +231,11 @@ def _is_next_price_minimum_reachable_by_charging_the_battery_fully(
251
231
)
252
232
return False
253
233
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 :
255
235
"""
256
236
Handles the coordination of battery charging when the upcoming price minimum cannot be reached.
257
237
258
238
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.
262
239
"""
263
240
energy_rate_after_price_drops_over_average , energy_rate_before_price_rises_over_average = (
264
241
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
278
255
self .log .info ("Waking up to determine the optimal charging time around the price spike" )
279
256
if energy_rate_after_price_drops_over_average < energy_rate_before_price_rises_over_average :
280
257
self ._coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_after_spike_cheaper_than_before (
281
- average_power_consumption ,
282
258
energy_rate_after_price_drops_over_average ,
283
259
energy_rate_before_price_rises_over_average ,
284
260
)
@@ -306,7 +282,6 @@ def _get_energy_rates_before_and_after_price_spike(
306
282
307
283
def _coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_after_spike_cheaper_than_before (
308
284
self ,
309
- average_power_consumption : Power ,
310
285
energy_rate_after_price_drops_after_average : EnergyRate ,
311
286
energy_rate_before_price_rises_over_average : EnergyRate ,
312
287
) -> None :
@@ -322,7 +297,7 @@ def _coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_af
322
297
self .sun_forecast_handler .calculate_min_and_max_of_soc_in_timeframe (
323
298
energy_rate_before_price_rises_over_average .timestamp ,
324
299
energy_rate_after_price_drops_after_average .timestamp ,
325
- average_power_consumption ,
300
+ self . average_power_consumption ,
326
301
current_state_of_charge ,
327
302
self .next_price_minimum .has_to_be_rechecked ,
328
303
self ._get_solar_data (),
@@ -345,7 +320,7 @@ def _coordinate_charging_when_next_price_minimum_is_unreachable_second_charge_af
345
320
self .sun_forecast_handler .calculate_min_and_max_of_soc_in_timeframe (
346
321
energy_rate_after_price_drops_after_average .timestamp , # = now
347
322
self .next_price_minimum .timestamp ,
348
- average_power_consumption ,
323
+ self . average_power_consumption ,
349
324
current_state_of_charge ,
350
325
self .next_price_minimum .has_to_be_rechecked ,
351
326
self ._get_solar_data (),
@@ -357,7 +332,6 @@ def _coordinate_charging_next_price_minimum_is_reachable(
357
332
current_state_of_charge : StateOfCharge ,
358
333
current_energy_rate : EnergyRate ,
359
334
minimum_of_soc_until_next_price_minimum : StateOfCharge ,
360
- maximum_of_soc_until_next_price_minimum : StateOfCharge ,
361
335
) -> None :
362
336
"""
363
337
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(
372
346
current_energy_rate (EnergyRate): The current cost of energy, influencing charging decisions.
373
347
minimum_of_soc_until_next_price_minimum (StateOfCharge): The calculated minimum SOC in the timespan to the
374
348
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.
377
349
"""
378
350
if current_energy_rate >= self .next_price_minimum :
379
351
charging_target_soc = (
@@ -386,7 +358,8 @@ def _coordinate_charging_next_price_minimum_is_reachable(
386
358
else :
387
359
charging_target_soc = (
388
360
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 ,
390
363
)
391
364
)
392
365
@@ -425,19 +398,34 @@ def _calculate_target_soc_next_price_minimum_is_reachable_and_current_minimum_is
425
398
self ,
426
399
current_energy_rate : EnergyRate ,
427
400
current_state_of_charge : StateOfCharge ,
428
- maximum_of_soc_until_next_price_minimum : StateOfCharge ,
429
401
) -> StateOfCharge :
430
402
self .log .info (
431
403
f"The price of the upcoming minimum ({ self .next_price_minimum .rate } ct/kWh) is higher than the one of "
432
404
f"the current minimum ({ current_energy_rate .rate } ct/kWh) "
433
405
"--> Will charge as much as possible without wasting any energy of the sun"
434
406
)
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
+ )
435
422
self .log .debug (
436
423
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 } )"
439
427
)
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
441
429
442
430
def _charge_inverter (self , target_state_of_charge : StateOfCharge ) -> None :
443
431
"""
0 commit comments