diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index 77a7a6e1..edf1f478 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -945,7 +945,7 @@ def update_status(self, minutes_now, quiet=False): if not quiet: self.base.log( - "Inverter {} SOC: {} kw {} % Current charge rate {} w Current discharge rate {} w Current power {} w Current voltage{}".format( + "Inverter {} SOC: {} kw {} % Current charge rate {} w Current discharge rate {} w Current power {} w Current voltage {}".format( self.id, self.base.dp2(self.soc_kw), self.soc_percent, @@ -5872,7 +5872,7 @@ def publish_discharge_limit(self, discharge_window, discharge_limits, best): }, ) - def publish_charge_limit(self, charge_limit, charge_window, charge_limit_percent, best): + def publish_charge_limit(self, charge_limit, charge_window, charge_limit_percent, best=False, soc={}): """ Create entity to chart charge limit """ @@ -5890,6 +5890,12 @@ def publish_charge_limit(self, charge_limit, charge_window, charge_limit_percent else: soc_perc = 0 soc_kw = 0 + + # Convert % of charge freeze to current SOC number + if self.set_charge_freeze and (soc_perc == self.reserve_percent): + offset = int((minute - self.minutes_now) / 5) * 5 + soc_kw = soc.get(offset, soc_kw) + if prev_perc != soc_perc: charge_limit_time[stamp] = soc_perc charge_limit_time_kw[stamp] = soc_kw @@ -6369,6 +6375,7 @@ def optimise_charge_limit( best_soc_min = 0 best_soc_min_minute = 0 best_metric = 9999999 + on_metric = 9999999 best_cost = 0 prev_soc = self.soc_max + 1 prev_metric = 9999999 @@ -6495,7 +6502,7 @@ def optimise_charge_limit( # to try to avoid constant small changes to SOC target if not all_n and (window_n == self.in_charge_window(charge_window, self.minutes_now)): try_percent = self.calc_percent_limit(try_soc) - compare_with = max(self.current_charge_limit, self.reserve_current_percent) + compare_with = max(self.current_charge_limit, self.reserve_percent) if abs(compare_with - try_percent) <= 2: metric -= max(0.5, self.metric_min_improvement) @@ -6531,7 +6538,7 @@ def optimise_charge_limit( # Only select the lower SOC if it makes a notable improvement has defined by min_improvement (divided in M windows) # and it doesn't fall below the soc_keep threshold - if (metric + self.metric_min_improvement) <= best_metric: + if ((metric + self.metric_min_improvement) <= on_metric) and (metric <= best_metric): best_metric = metric best_soc = try_soc best_cost = cost @@ -6539,6 +6546,10 @@ def optimise_charge_limit( best_soc_min_minute = soc_min_minute best_keep = metric_keep + # Default on metric + if on_metric == 9999999: + on_metric = metric + prev_soc = try_soc prev_metric = metric first_window = False @@ -6585,6 +6596,7 @@ def optimise_discharge( """ best_discharge = False best_metric = 9999999 + off_metric = 9999999 best_cost = 0 best_soc_min = 0 best_soc_min_minute = 0 @@ -6680,8 +6692,6 @@ def optimise_discharge( dwindow = self.discharge_window[0] if self.minutes_now >= pwindow["start"] and self.minutes_now < pwindow["end"]: if (self.minutes_now >= dwindow["start"] and self.minutes_now < dwindow["end"]) or (dwindow["end"] == pwindow["start"]): - if self.debug_enable: - self.log("Sim: Discharge window {} - weighting as it falls within currently configured discharge slot (or continues from one)".format(window_n)) metric -= max(0.5, self.metric_min_improvement_discharge) if self.debug_enable: @@ -6706,13 +6716,12 @@ def optimise_discharge( ) ) - window_size = window["end"] - start + window_size = try_discharge_window[window_n]["end"] - start window_key = str(int(this_discharge_limit)) + "_" + str(window_size) window_results[window_key] = self.dp2(metric) - # Only select the lower SOC if it makes a notable improvement has defined by min_improvement (divided in M windows) - # and it doesn't fall below the soc_keep threshold - if (metric + self.metric_min_improvement_discharge) <= best_metric: + # Only select a discharge if it makes a notable improvement has defined by min_improvement (divided in M windows) + if ((metric + self.metric_min_improvement_discharge) <= off_metric) and (metric <= best_metric): best_metric = metric best_discharge = this_discharge_limit best_cost = cost @@ -6722,6 +6731,10 @@ def optimise_discharge( best_size = window_size best_keep = metric_keep + # Store the metric for discharge off + if off_metric == 9999999: + off_metric = metric + if not all_n: self.log( "Try optimising discharge window(s) {}: {} - {} price {} selected {}% size {} was {}% results {}".format( @@ -8054,7 +8067,7 @@ def calculate_plan(self, recompute=True): # Publish charge and discharge window best self.charge_limit_percent_best = self.calc_percent_limit(self.charge_limit_best) - self.publish_charge_limit(self.charge_limit_best, self.charge_window_best, self.charge_limit_percent_best, best=True) + self.publish_charge_limit(self.charge_limit_best, self.charge_window_best, self.charge_limit_percent_best, best=True, soc=self.predict_soc_best) self.publish_discharge_limit(self.discharge_window_best, self.discharge_limits_best, best=True) # HTML data @@ -8820,6 +8833,7 @@ def fetch_inverter_data(self): self.soc_kw = 0.0 self.soc_max = 0.0 self.reserve = 0.0 + self.reserve_percent = 0.0 self.reserve_current = 0.0 self.reserve_current_percent = 0.0 self.battery_rate_max_charge = 0.0 @@ -8866,6 +8880,8 @@ def fetch_inverter_data(self): # Remove extra decimals self.soc_max = self.dp2(self.soc_max) self.soc_kw = self.dp2(self.soc_kw) + self.reserve = self.dp2(self.reserve) + self.reserve_percent = int(self.reserve / self.soc_max * 100.0 + 0.5) self.reserve_current = self.dp2(self.reserve_current) self.reserve_current_percent = int(self.reserve_current / self.soc_max * 100.0 + 0.5)