From 86f61ac7dd1e69f8475c15cdc612652b919f8332 Mon Sep 17 00:00:00 2001
From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com>
Date: Sun, 4 Feb 2024 13:51:34 +0000
Subject: [PATCH 1/5] Add symbols in the plan for different rate sources
---
apps/predbat/predbat.py | 76 +++++++++++++++++++++++++++++++----------
1 file changed, 58 insertions(+), 18 deletions(-)
diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py
index 3aceb34ca..d6a572e59 100644
--- a/apps/predbat/predbat.py
+++ b/apps/predbat/predbat.py
@@ -4370,7 +4370,7 @@ def find_charge_window_optimised(self, charge_windows):
for window_n in range(len(charge_windows)):
for minute in range(charge_windows[window_n]["start"], charge_windows[window_n]["end"], PREDICT_STEP):
charge_window_optimised[minute] = window_n
- return charge_window_optimised
+ return charge_window_optimised
def run_prediction(self, charge_limit, charge_window, discharge_window, discharge_limits, load_minutes_step, pv_forecast_minute_step, end_record, save=None, step=PREDICT_STEP):
"""
@@ -4471,6 +4471,7 @@ def run_prediction(self, charge_limit, charge_window, discharge_window, discharg
if self.set_reserve_enable:
reserve_expected = max(charge_limit_n, self.reserve)
+
# Add in standing charge, only for the final plan when we save the results
if (minute_absolute % (24 * 60)) < step and (save in ["best", "base", "base10", "best10"]):
metric += self.metric_standing_charge
@@ -5441,6 +5442,7 @@ def rate_replicate(self, rates, rate_io={}, is_import=True, is_gas=False):
# Add 48 extra hours to make sure the whole cycle repeats another day
while minute < (self.forecast_minutes + 48 * 60):
if minute not in rates:
+ adjust_type = "copy"
# Take 24-hours previous if missing rate
if (minute >= 24 * 60) and ((minute - 24 * 60) in rates):
minute_mod = minute - 24 * 60
@@ -5466,6 +5468,7 @@ def rate_replicate(self, rates, rate_io={}, is_import=True, is_gas=False):
):
prev_rate = rate_offset
rate_offset = rate_offset - self.future_energy_rates_import[minute_mod] + self.future_energy_rates_import[minute]
+ adjust_type = "future"
elif (
(not is_import)
and (not is_gas)
@@ -5474,18 +5477,22 @@ def rate_replicate(self, rates, rate_io={}, is_import=True, is_gas=False):
and (minute_mod in self.future_energy_rates_export)
):
rate_offset = max(rate_offset - self.future_energy_rates_export[minute_mod] + self.future_energy_rates_export[minute], 0)
+ adjust_type = "future"
elif is_import:
rate_offset = rate_offset + self.metric_future_rate_offset_import
+ if self.metric_future_rate_offset_import:
+ adjust_type = "offset"
elif (not is_import) and (not is_gas):
rate_offset = max(rate_offset + self.metric_future_rate_offset_export, 0)
+ if self.metric_future_rate_offset_export:
+ adjust_type = "offset"
adjusted_rates[minute] = True
rates[minute] = rate_offset
- replicated_rates[minute] = True
+ replicated_rates[minute] = adjust_type
else:
rate_last = rates[minute]
- replicated_rates[minute] = False
minute += 1
return rates, replicated_rates
@@ -5553,7 +5560,7 @@ def find_charge_window(self, rates, minute, threshold_rate, find_high):
rate_low_average = self.dp2(rate_low_average / rate_low_count)
return rate_low_start, rate_low_end, rate_low_average
- def basic_rates(self, info, rtype, prev=None):
+ def basic_rates(self, info, rtype, prev=None, rate_replicate={}):
"""
Work out the energy rates based on user supplied time periods
works on a 24-hour period only and then gets replicated later for future days
@@ -5655,8 +5662,10 @@ def basic_rates(self, info, rtype, prev=None):
while minute_index < max_minute:
if rate_increment:
rates[minute_index] = rates.get(minute_index, 0.0) + rate
+ rate_replicate[minute_index] = "increment"
else:
rates[minute_index] = rate
+ rate_replicate[minute_index] = "user"
if load_scaling is not None:
self.load_scaling_dynamic[minute_index] = load_scaling
if date or not prev:
@@ -5782,7 +5791,7 @@ def add_now_to_octopus_slot(self, octopus_slots, now_utc):
self.log("Car is charging now - added new IO slot {}".format(slot))
return octopus_slots
- def load_saving_slot(self, octopus_saving_slots, export=False):
+ def load_saving_slot(self, octopus_saving_slots, export=False, rate_replicate={}):
"""
Load octopus saving session slot
"""
@@ -5819,6 +5828,7 @@ def load_saving_slot(self, octopus_saving_slots, export=False):
else:
self.rate_import[minute] += rate
self.load_scaling_dynamic[minute] = self.load_scaling_saving
+ rate_replicate[minute] = "saving"
def load_octopus_slots(self, octopus_slots):
"""
@@ -6438,6 +6448,32 @@ def publish_rates_import(self):
attributes={"friendly_name": "Next+1 low rate cost", "state_class": "measurement", "unit_of_measurement": "p", "icon": "mdi:currency-usd"},
)
+ def adjust_symbol(self, adjust_type):
+ """
+ Returns an HTML symbol based on the adjust rate type.
+
+ Parameters:
+ - adjust_type (str): The type of adjustment.
+
+ Returns:
+ - symbol (str): The symbol corresponding to the adjust_type.
+ """
+ symbol = ""
+ if adjust_type:
+ if adjust_type == "offset":
+ symbol = "? ⅆ"
+ elif adjust_type == "future":
+ symbol = "? ⚖"
+ elif adjust_type == "user":
+ symbol = "="
+ elif adjust_type == "increment":
+ symbol = "±"
+ elif adjust_type == "saving":
+ symbol = "$"
+ else:
+ symbol = "?"
+ return symbol
+
def publish_html_plan(self, pv_forecast_minute_step, load_minutes_step, end_record):
"""
Publish the current plan in HTML format
@@ -6630,10 +6666,12 @@ def publish_html_plan(self, pv_forecast_minute_step, load_minutes_step, end_reco
state += " ⅎ"
# Import and export rates -> to string
- if self.rate_import_replicated.get(minute, False):
- rate_str_import = "%02.02f ?" % (rate_value_import)
+ adjust_type = self.rate_import_replicated.get(minute, None)
+ adjust_symbol = self.adjust_symbol(adjust_type)
+ if adjust_symbol:
+ rate_str_import = "%02.02f %s" % (rate_value_import, adjust_symbol)
else:
- rate_str_import = "%02.02f" % rate_value_import
+ rate_str_import = "%02.02f" % (rate_value_import)
if plan_debug:
rate_str_import += " (%02.02f)" % (rate_value_import / self.battery_loss / self.inverter_loss + self.metric_battery_cycle)
@@ -6641,10 +6679,12 @@ def publish_html_plan(self, pv_forecast_minute_step, load_minutes_step, end_reco
if charge_window_n >= 0:
rate_str_import = "" + rate_str_import + ""
- if self.rate_export_replicated.get(minute, False):
- rate_str_export = "%02.02f ?" % (rate_value_export)
+ adjust_type = self.rate_export_replicated.get(minute, None)
+ adjust_symbol = self.adjust_symbol(adjust_type)
+ if adjust_symbol:
+ rate_str_export = "%02.02f %s" % (rate_value_export, adjust_symbol)
else:
- rate_str_export = "%2.02f" % (rate_value_export)
+ rate_str_export = "%02.02f" % (rate_value_export)
if plan_debug:
rate_str_export += " (%02.02f)" % (rate_value_export * self.battery_loss_discharge * self.inverter_loss - self.metric_battery_cycle)
@@ -10323,9 +10363,9 @@ def fetch_sensor_data(self):
self.rate_import = self.rate_scan(self.rate_import, print=False)
self.rate_import, self.rate_import_replicated = self.rate_replicate(self.rate_import, self.io_adjusted, is_import=True)
self.rate_import = self.rate_add_io_slots(self.rate_import, self.octopus_slots)
- self.load_saving_slot(octopus_saving_slots, export=False)
+ self.load_saving_slot(octopus_saving_slots, export=False, rate_replicate=self.rate_import_replicated)
if "rates_import_override" in self.args:
- self.rate_import = self.basic_rates(self.get_arg("rates_import_override", [], indirect=False), "import", self.rate_import)
+ self.rate_import = self.basic_rates(self.get_arg("rates_import_override", [], indirect=False), "import", self.rate_import, self.rate_import_replicated)
self.rate_import = self.rate_scan(self.rate_import, print=True)
else:
self.log("Warning: No import rate data provided")
@@ -10337,9 +10377,9 @@ def fetch_sensor_data(self):
self.rate_export, self.rate_export_replicated = self.rate_replicate(self.rate_export, is_import=False)
# For export tariff only load the saving session if enabled
if self.rate_export_max > 0:
- self.load_saving_slot(octopus_saving_slots, export=True)
+ self.load_saving_slot(octopus_saving_slots, export=True, rate_replicate=self.rate_export_replicated)
if "rates_export_override" in self.args:
- self.rate_export = self.basic_rates(self.get_arg("rates_export_override", [], indirect=False), "export", self.rate_export)
+ self.rate_export = self.basic_rates(self.get_arg("rates_export_override", [], indirect=False), "export", self.rate_export, self.rate_export_replicated)
self.rate_export = self.rate_scan_export(self.rate_export, print=True)
else:
self.log("Warning: No export rate data provided")
@@ -11058,9 +11098,9 @@ async def select_event(self, event, data, kwargs):
None
Description:
- This method is used to handle Home Assistant input select updates.
- It extracts the necessary information from the data and performs different actions based on the selected option.
- The actions include calling update service, saving and restoring settings, performing manual selection, and exposing configuration.
+ This method is used to handle Home Assistant input select updates.
+ It extracts the necessary information from the data and performs different actions based on the selected option.
+ The actions include calling update service, saving and restoring settings, performing manual selection, and exposing configuration.
After performing the actions, it triggers an update by setting update_pending flag to True and plan_valid flag to False.
"""
service_data = data.get("service_data", {})
From d9bddede64f1eb96e15f542db4cb9e2d8371631b Mon Sep 17 00:00:00 2001
From: "pre-commit-ci-lite[bot]"
<117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Date: Sun, 4 Feb 2024 13:52:57 +0000
Subject: [PATCH 2/5] [pre-commit.ci lite] apply automatic fixes
---
apps/predbat/predbat.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py
index d6a572e59..4b0efa9b9 100644
--- a/apps/predbat/predbat.py
+++ b/apps/predbat/predbat.py
@@ -4370,7 +4370,7 @@ def find_charge_window_optimised(self, charge_windows):
for window_n in range(len(charge_windows)):
for minute in range(charge_windows[window_n]["start"], charge_windows[window_n]["end"], PREDICT_STEP):
charge_window_optimised[minute] = window_n
- return charge_window_optimised
+ return charge_window_optimised
def run_prediction(self, charge_limit, charge_window, discharge_window, discharge_limits, load_minutes_step, pv_forecast_minute_step, end_record, save=None, step=PREDICT_STEP):
"""
@@ -4471,7 +4471,6 @@ def run_prediction(self, charge_limit, charge_window, discharge_window, discharg
if self.set_reserve_enable:
reserve_expected = max(charge_limit_n, self.reserve)
-
# Add in standing charge, only for the final plan when we save the results
if (minute_absolute % (24 * 60)) < step and (save in ["best", "base", "base10", "best10"]):
metric += self.metric_standing_charge
@@ -11098,9 +11097,9 @@ async def select_event(self, event, data, kwargs):
None
Description:
- This method is used to handle Home Assistant input select updates.
- It extracts the necessary information from the data and performs different actions based on the selected option.
- The actions include calling update service, saving and restoring settings, performing manual selection, and exposing configuration.
+ This method is used to handle Home Assistant input select updates.
+ It extracts the necessary information from the data and performs different actions based on the selected option.
+ The actions include calling update service, saving and restoring settings, performing manual selection, and exposing configuration.
After performing the actions, it triggers an update by setting update_pending flag to True and plan_valid flag to False.
"""
service_data = data.get("service_data", {})
From c7e85a11ac216a7caef20acdd9fad8a442f6f28c Mon Sep 17 00:00:00 2001
From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com>
Date: Sun, 4 Feb 2024 14:00:58 +0000
Subject: [PATCH 3/5] Update predbat-plan-card.md
---
docs/predbat-plan-card.md | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/docs/predbat-plan-card.md b/docs/predbat-plan-card.md
index 4944b9344..3f3c7d011 100644
--- a/docs/predbat-plan-card.md
+++ b/docs/predbat-plan-card.md
@@ -43,6 +43,22 @@ For every half hour period (slot) that Predbat has planned for (the *forecast_ho
- The forecast cost for the half hour slot
- A running total cost
+Rate symbols (import and export):
+- ? ⅆ - Rate that has been modified based on **input_number.predbat_metric_future_rate_offset_import** or **input_number.predbat_metric_future_rate_offset_export**
+- ? ⚖ - Rate that has been estimated using future rate estimation data (e.g. Nordpool)
+- = - Rate that has been overriden by the users apps.yaml
+- ± - Rate that has been adjusted with a rate offset in the users apps.yaml
+- $ - Rate that has been adjusted for an Octopus Saving session
+- ? - Rate that has not yet been defined and the previous days data was used instead
+Battery SOC symbols:
+- → - Current SOC expected to remain level
+- ↘ - Current SOC expected to fall
+- ↗ - Current SOC expected to rise
+Cost symbols:
+- → - Current cost expected to remain level
+- ↘ - Current cost expected to fall (due to export)
+- ↗ - Current cost expected to rise (due to import or standing charge)
+
Explaining each column in the Predbat plan in more detail:
- **Time** - Predbat plans your home, solar and battery load in 30 minute slots, on the :00 and :30 minutes past each hour.
From 473c4e3329fc5384bbc045ae0ce1c13c9b4258f7 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci-lite[bot]"
<117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Date: Sun, 4 Feb 2024 14:01:49 +0000
Subject: [PATCH 4/5] [pre-commit.ci lite] apply automatic fixes
---
docs/predbat-plan-card.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/predbat-plan-card.md b/docs/predbat-plan-card.md
index 3f3c7d011..03d003917 100644
--- a/docs/predbat-plan-card.md
+++ b/docs/predbat-plan-card.md
@@ -44,6 +44,7 @@ For every half hour period (slot) that Predbat has planned for (the *forecast_ho
- A running total cost
Rate symbols (import and export):
+
- ? ⅆ - Rate that has been modified based on **input_number.predbat_metric_future_rate_offset_import** or **input_number.predbat_metric_future_rate_offset_export**
- ? ⚖ - Rate that has been estimated using future rate estimation data (e.g. Nordpool)
- = - Rate that has been overriden by the users apps.yaml
From 02c8142e6c0f507097bd39d1da760f31ecadc522 Mon Sep 17 00:00:00 2001
From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com>
Date: Sun, 4 Feb 2024 14:03:17 +0000
Subject: [PATCH 5/5] Update predbat-plan-card.md
---
docs/predbat-plan-card.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/predbat-plan-card.md b/docs/predbat-plan-card.md
index 03d003917..222de87ba 100644
--- a/docs/predbat-plan-card.md
+++ b/docs/predbat-plan-card.md
@@ -47,7 +47,7 @@ Rate symbols (import and export):
- ? ⅆ - Rate that has been modified based on **input_number.predbat_metric_future_rate_offset_import** or **input_number.predbat_metric_future_rate_offset_export**
- ? ⚖ - Rate that has been estimated using future rate estimation data (e.g. Nordpool)
-- = - Rate that has been overriden by the users apps.yaml
+- = - Rate that has been overridden by the users apps.yaml
- ± - Rate that has been adjusted with a rate offset in the users apps.yaml
- $ - Rate that has been adjusted for an Octopus Saving session
- ? - Rate that has not yet been defined and the previous days data was used instead