Skip to content

Commit c5b2ff8

Browse files
Changes to allow the prediction graphs to run longer but the values to stop at the end of the period
1 parent 52886a9 commit c5b2ff8

File tree

2 files changed

+36
-22
lines changed

2 files changed

+36
-22
lines changed

apps.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ pred_bat:
3131
metric_export: 4
3232
metric_min_improvement: 5
3333
set_soc_enable: True
34-
set_soc_minutes: 90
34+
set_soc_minutes: 60
3535
set_soc_notify: True
3636
run_every: 5

predbat.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,15 @@ def minutes_since_yesterday(self, now):
6363
def dp2(self, value):
6464
return math.ceil(value*100)/100
6565

66-
def run_prediction(self, now, charge_limit, load_minutes, pv_forecast_minute, save, save_best, short):
66+
def run_prediction(self, now, charge_limit, load_minutes, pv_forecast_minute, save, save_best):
6767

6868
six_days = 24*60*(self.days_previous - 1)
6969

7070
# Offset by 6 (configurable) days to get to last week
7171
load_yesterday = load_minutes[self.difference_minutes + six_days]
7272
load_yesterday_now = load_minutes[24*60 + six_days]
73-
self.log("Minutes since yesterday " + str(self.difference_minutes) + " load past day " + str(load_yesterday) + " load past day now " + str(load_yesterday_now))
7473

7574
forecast_minutes = self.forecast_hours * 60
76-
77-
# For the SOC calculation we need to stop at the second charge window to avoid confusing multiple days out
78-
if short:
79-
forecast_minutes = min(forecast_minutes, self.charge_start_time_minutes + 24*60 - self.minutes_now)
80-
8175
predict_soc = {}
8276
predict_soc_time = {}
8377
minute = 0
@@ -88,9 +82,19 @@ def run_prediction(self, now, charge_limit, load_minutes, pv_forecast_minute, sa
8882
import_kwh_house = 0
8983
import_kwh_battery = 0
9084

85+
# For the SOC calculation we need to stop at the second charge window to avoid confusing multiple days out
86+
end_record = min(forecast_minutes, self.charge_start_time_minutes + 24*60 - self.minutes_now)
87+
record = True
88+
89+
self.log("Minutes since yesterday " + str(self.difference_minutes) + " load past day " + str(load_yesterday) + " load past day now " + str(load_yesterday_now) + " end record " + str(end_record))
90+
9191
# Simulate each forward minute
9292
while minute < forecast_minutes:
9393

94+
# Outside the recording window?
95+
if minute >= end_record:
96+
record = False
97+
9498
minute_yesterday = 24 * 60 - minute + six_days
9599
# Average previous load over 10 minutes due to sampling accuracy
96100
load_yesterday = (load_minutes[minute_yesterday] - load_minutes[minute_yesterday + 10]) / 10.0
@@ -121,8 +125,9 @@ def run_prediction(self, now, charge_limit, load_minutes, pv_forecast_minute, sa
121125

122126
# Apply battery loss to computed charging energy
123127
# For now we ignore PV in this as it's probably not a major factor when mains charging is enabled
124-
import_kwh += max(0, soc - old_soc - pv_now) / self.battery_loss
125-
import_kwh_battery += max(0, soc - old_soc - pv_now) / self.battery_loss
128+
if record:
129+
import_kwh += max(0, soc - old_soc - pv_now) / self.battery_loss
130+
import_kwh_battery += max(0, soc - old_soc - pv_now) / self.battery_loss
126131

127132
if self.debug_enable and minute % 60 == 0:
128133
self.log("Hour %s battery charging target soc %s" % (minute/60, charge_limit))
@@ -135,18 +140,21 @@ def run_prediction(self, now, charge_limit, load_minutes, pv_forecast_minute, sa
135140

136141
if diff > self.discharge_rate:
137142
soc -= self.discharge_rate
138-
import_kwh += (diff - self.discharge_rate)
139-
import_kwh_house += (diff - self.discharge_rate)
143+
if record:
144+
import_kwh += (diff - self.discharge_rate)
145+
import_kwh_house += (diff - self.discharge_rate)
140146
else:
141147
soc -= diff
142148

143149
if soc < self.reserve:
144-
import_kwh += self.reserve - soc
145-
import_kwh_house += self.reserve - soc
150+
if record:
151+
import_kwh += self.reserve - soc
152+
import_kwh_house += self.reserve - soc
146153
soc = self.reserve
147154

148155
if soc > self.soc_max:
149-
export_kwh += soc - self.soc_max
156+
if record:
157+
export_kwh += soc - self.soc_max
150158
soc = self.soc_max
151159

152160
if self.debug_enable and minute % 60 == 0:
@@ -158,10 +166,15 @@ def run_prediction(self, now, charge_limit, load_minutes, pv_forecast_minute, sa
158166
if minute % 10 == 0:
159167
predict_soc_time[str(minute_timestamp)] = self.dp2(soc)
160168

161-
# Store the worst caste
162-
if soc <= self.reserve:
169+
# Store the number of minutes until the battery runs out
170+
if record and soc <= self.reserve:
163171
if minute_left > minute:
164172
minute_left = minute
173+
174+
# Record final soc
175+
if record:
176+
final_soc = soc
177+
165178
minute += 1
166179

167180
#self.log("load yesterday " + str(load_minutes))
@@ -175,17 +188,18 @@ def run_prediction(self, now, charge_limit, load_minutes, pv_forecast_minute, sa
175188

176189
if save:
177190
self.set_state("predbat.battery_hours_left", state=self.dp2(hours_left), attributes = {'friendly_name' : 'Battery Hours left', 'state_class': 'measurement', 'unit_of_measurement': 'hours', 'step' : 0.5})
178-
self.set_state("predbat.soc_kw", state=self.dp2(soc), attributes = {'results' : predict_soc_time, 'friendly_name' : 'Battery SOC kwh', 'state_class': 'measurement', 'unit_of_measurement': 'kwh', 'step' : 0.5})
191+
self.set_state("predbat.soc_kw", state=self.dp2(final_soc), attributes = {'results' : predict_soc_time, 'friendly_name' : 'Battery SOC kwh', 'state_class': 'measurement', 'unit_of_measurement': 'kwh', 'step' : 0.5})
179192
self.set_state("predbat.export_energy", state=self.dp2(export_kwh), attributes = {'friendly_name' : 'Predicted exports', 'state_class': 'measurement', 'unit_of_measurement': 'kwh'})
180193
self.set_state("predbat.import_energy", state=self.dp2(import_kwh), attributes = {'friendly_name' : 'Predicted imports', 'state_class': 'measurement', 'unit_of_measurement': 'kwh'})
181194
self.set_state("predbat.import_energy_battery", state=self.dp2(import_kwh_battery), attributes = {'friendly_name' : 'Predicted import to battery', 'state_class': 'measurement', 'unit_of_measurement': 'kwh'})
182195
self.set_state("predbat.import_energy_house", state=self.dp2(import_kwh_house), attributes = {'friendly_name' : 'Predicted import to house', 'state_class': 'measurement', 'unit_of_measurement': 'kwh'})
183196
self.log("Battery has " + str(hours_left) + " hours left - now at " + str(self.soc_kw))
184197
self.set_state("predbat.metric", state=self.dp2(metric), attributes = {'friendly_name' : 'Predicted metric (cost)', 'state_class': 'measurement', 'unit_of_measurement': 'p'})
198+
self.set_state("predbat.duration", state=self.dp2(end_record/60), attributes = {'friendly_name' : 'Predicted duration', 'state_class': 'measurement', 'unit_of_measurement': 'hours'})
185199

186200
if save_best:
187201
self.log('Saving best data with charge_limit %s' % charge_limit)
188-
self.set_state("predbat.soc_kw_best", state=self.dp2(soc), attributes = {'results' : predict_soc_time, 'friendly_name' : 'Battery SOC kwh best', 'state_class': 'measurement', 'unit_of_measurement': 'kwh', 'step' : 0.5})
202+
self.set_state("predbat.soc_kw_best", state=self.dp2(final_soc), attributes = {'results' : predict_soc_time, 'friendly_name' : 'Battery SOC kwh best', 'state_class': 'measurement', 'unit_of_measurement': 'kwh', 'step' : 0.5})
189203
self.set_state("predbat.best_charge_limit_kw", state=self.dp2(charge_limit), attributes = {'friendly_name' : 'Predicted charge limit kwh best', 'state_class': 'measurement', 'unit_of_measurement': 'kwh'})
190204
self.set_state("predbat.best_charge_limit", state=charge_limit_percent, attributes = {'friendly_name' : 'Predicted charge limit best', 'state_class': 'measurement', 'unit_of_measurement': '%'})
191205
self.set_state("predbat.best_export_energy", state=self.dp2(export_kwh), attributes = {'friendly_name' : 'Predicted exports best', 'state_class': 'measurement', 'unit_of_measurement': 'kwh'})
@@ -292,7 +306,7 @@ def update_pred(self):
292306
while try_soc > self.reserve:
293307
was_debug = self.debug_enable
294308
self.debug_enable = False
295-
metric, charge_limit_percent, import_kwh_battery, import_kwh_house, export_kwh = self.run_prediction(now, try_soc, load_minutes, pv_forecast_minute, False, False, True)
309+
metric, charge_limit_percent, import_kwh_battery, import_kwh_house, export_kwh = self.run_prediction(now, try_soc, load_minutes, pv_forecast_minute, False, False)
296310
self.debug_true = was_debug
297311
if self.debug_enable:
298312
self.log("Trying soc %s gives import battery %s house %s export %s metric %s (%s + %s - %s)" %
@@ -311,7 +325,7 @@ def update_pred(self):
311325
best_soc = max(self.best_soc_min, best_soc)
312326
best_soc = min(best_soc, self.soc_max)
313327
self.log("Best soc calculated at %s (margin added %s and min %s) with metric %s" % (best_soc, self.best_soc_margin, self.best_soc_min, best_metric))
314-
best_metric, charge_limit_percent, import_kwh_battery, import_kwh_house, export_kwh = self.run_prediction(now, best_soc, load_minutes, pv_forecast_minute, False, True, True)
328+
best_metric, charge_limit_percent, import_kwh_battery, import_kwh_house, export_kwh = self.run_prediction(now, best_soc, load_minutes, pv_forecast_minute, False, True)
315329
self.log("Best soc %s gives import battery %s house %s export %s metric %s (%s + %s - %s)" %
316330
(best_soc, import_kwh_battery, import_kwh_house, export_kwh, metric,
317331
import_kwh_house*self.metric_house, import_kwh_battery*self.metric_battery, export_kwh*self.metric_export))
@@ -324,7 +338,7 @@ def update_pred(self):
324338
self.log("Not setting charging SOC as we are not within the window (now %s target set_soc_minutes %s charge start time %s" % (self.minutes_now,self.set_soc_minutes, self.charge_start_time_minutes))
325339

326340
# Simulate current settings
327-
metric, charge_limit_percent, import_kwh_battery, import_kwh_house, export_kwh = self.run_prediction(now, self.charge_limit, load_minutes, pv_forecast_minute, True, False, False)
341+
metric, charge_limit_percent, import_kwh_battery, import_kwh_house, export_kwh = self.run_prediction(now, self.charge_limit, load_minutes, pv_forecast_minute, True, False)
328342

329343
def initialize(self):
330344
self.log("Startup")

0 commit comments

Comments
 (0)