1212import traceback
1313from datetime import datetime , timedelta
1414from multiprocessing import Pool , cpu_count
15- from config import PREDICT_STEP , TIME_FORMAT
16- from utils import calc_percent_limit , dp0 , dp1 , dp2 , dp3 , dp4 , remove_intersecting_windows , calc_percent_limit
15+ from config import PREDICT_STEP , TIME_FORMAT , MINUTE_WATT
16+ from utils import calc_percent_limit , dp0 , dp1 , dp2 , dp3 , dp4 , remove_intersecting_windows , calc_percent_limit , in_car_slot
1717from prediction import Prediction , wrapped_run_prediction_single , wrapped_run_prediction_charge , wrapped_run_prediction_charge_min_max , wrapped_run_prediction_export , wrapped_run_prediction_charge_min_max
1818
1919"""
@@ -36,6 +36,43 @@ def get(self):
3636
3737
3838class Plan :
39+ def dynamic_load (self ):
40+ # Last period load analysis
41+ self .load_last_status = "baseline"
42+ if self .load_last_period > self .battery_rate_max_discharge * MINUTE_WATT / 1000 :
43+ self .load_last_status = "high"
44+ elif (self .load_last_period < self .battery_rate_max_discharge * 0.9 * MINUTE_WATT / 1000 ) and (self .load_last_period < self .car_charging_threshold * 0.9 ):
45+ # Check if the load is less than car charging threshold
46+ self .load_last_status = "low"
47+ else :
48+ self .load_last_status = "baseline"
49+
50+ self .dynamic_load_baseline = {}
51+ if self .metric_dynamic_load_adjust :
52+ minutes_now = self .minutes_now
53+ minutes_end_slot = int ((self .minutes_now + 30 ) / 30 ) * 30
54+ # When dynamic load is enabled we try can do two things
55+ # 1. Increase the load prediction in the current 30 minute period to match the actual load (if its low)
56+ # 2. If its low and car charging is predicted then cancel off that car slot
57+ if self .load_last_status == "low" :
58+ for car_n in range (0 , self .num_cars ):
59+ for slot in self .car_charging_slots [car_n ]:
60+ if (slot ["start" ] < minutes_now ) and (slot ["start" ] < minutes_end_slot ) and (slot ["end" ] > minutes_now ):
61+ # If the slot is within the current 30 minute period
62+ self .log ("Dynamic load adjust is cancelling car {} slot {}-{} due to low load" .format (car_n + 1 , slot ["start" ], slot ["end" ]))
63+ slot ["kwh" ] = 0
64+ if self .load_last_status == "high" :
65+ have_printed = False
66+ for minute_absolute in range (minutes_now , minutes_end_slot , PREDICT_STEP ):
67+ car_load = sum (in_car_slot (minute_absolute , self .num_cars , self .car_charging_slots ))
68+ load_last_period = self .load_last_period / 60 * PREDICT_STEP
69+ load_last_period = max (load_last_period - car_load , 0 )
70+ if load_last_period > 0 :
71+ if not have_printed :
72+ self .log ("Dynamic load adjust is setting load minimum {:.2f}kW at {}" .format (load_last_period , self .time_abs_str (minute_absolute )))
73+ have_printed = True
74+ self .dynamic_load_baseline [minute_absolute ] = load_last_period
75+
3976 def find_price_levels (
4077 self ,
4178 price_set ,
@@ -791,6 +828,7 @@ def calculate_plan(self, recompute=True, debug_mode=False, publish=True):
791828 load_scaling_dynamic = self .load_scaling_dynamic ,
792829 cloud_factor = self .metric_load_divergence ,
793830 load_adjust = self .manual_load_adjust ,
831+ load_baseline = self .dynamic_load_baseline ,
794832 )
795833 load_minutes_step10 = self .step_data_history (
796834 self .load_minutes ,
@@ -803,6 +841,7 @@ def calculate_plan(self, recompute=True, debug_mode=False, publish=True):
803841 load_scaling_dynamic = self .load_scaling_dynamic ,
804842 cloud_factor = min (self .metric_load_divergence + 0.5 , 1.0 ) if self .metric_load_divergence else None ,
805843 load_adjust = self .manual_load_adjust ,
844+ load_baseline = self .dynamic_load_baseline ,
806845 )
807846 pv_forecast_minute_step = self .step_data_history (self .pv_forecast_minute , self .minutes_now , forward = True , cloud_factor = self .metric_cloud_coverage )
808847 pv_forecast_minute10_step = self .step_data_history (self .pv_forecast_minute10 , self .minutes_now , forward = True , cloud_factor = min (self .metric_cloud_coverage + 0.2 , 1.0 ) if self .metric_cloud_coverage else None , flip = True )
@@ -3306,7 +3345,7 @@ def run_prediction(self, charge_limit, charge_window, export_window, export_limi
33063345 self .dashboard_item (
33073346 self .prefix + ".export_energy_h0" ,
33083347 state = dp3 (export_kwh_h0 ),
3309- attributes = {"friendly_name" : "Current export kWh " , "state_class" : "measurement" , "unit_of_measurement" : "kWh" , "icon" : "mdi:transmission-tower-export" },
3348+ attributes = {"friendly_name" : "Current export" , "state_class" : "measurement" , "unit_of_measurement" : "kWh" , "icon" : "mdi:transmission-tower-export" },
33103349 )
33113350 self .dashboard_item (
33123351 self .prefix + ".load_energy" ,
@@ -3323,7 +3362,12 @@ def run_prediction(self, charge_limit, charge_window, export_window, export_limi
33233362 self .dashboard_item (
33243363 self .prefix + ".load_energy_h0" ,
33253364 state = dp3 (load_kwh_h0 ),
3326- attributes = {"friendly_name" : "Current load kWh" , "state_class" : "measurement" , "unit_of_measurement" : "kWh" , "icon" : "mdi:home-lightning-bolt" },
3365+ attributes = {"friendly_name" : "Current load" , "state_class" : "measurement" , "unit_of_measurement" : "kWh" , "icon" : "mdi:home-lightning-bolt" },
3366+ )
3367+ self .dashboard_item (
3368+ self .prefix + ".load_energy_last_period" ,
3369+ state = dp3 (self .load_last_period ),
3370+ attributes = {"friendly_name" : "Last period load" , "state_class" : "measurement" , "unit_of_measurement" : "kW" , "icon" : "mdi:home-lightning-bolt" , "status" : self .load_last_status },
33273371 )
33283372 self .dashboard_item (
33293373 self .prefix + ".pv_energy" ,
@@ -3333,7 +3377,7 @@ def run_prediction(self, charge_limit, charge_window, export_window, export_limi
33333377 self .dashboard_item (
33343378 self .prefix + ".pv_energy_h0" ,
33353379 state = dp3 (pv_kwh_h0 ),
3336- attributes = {"friendly_name" : "Current PV kWh " , "state_class" : "measurement" , "unit_of_measurement" : "kWh" , "icon" : "mdi:solar-power" },
3380+ attributes = {"friendly_name" : "Current PV" , "state_class" : "measurement" , "unit_of_measurement" : "kWh" , "icon" : "mdi:solar-power" },
33373381 )
33383382 self .dashboard_item (
33393383 self .prefix + ".import_energy" ,
0 commit comments