diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index 2e9f8cd77..ecf0566dc 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -2225,7 +2225,7 @@ def press_and_poll_button(self, entity_id): self.base.record_status(f"Warn: Inverter {self.id} Trying to press {entity_id} didn't complete") return False - def rest_readData(self, api="readData"): + def rest_readData(self, api="readData", retry=True): """ Get inverter status @@ -2244,9 +2244,13 @@ def rest_readData(self, api="readData"): if "Control" in json: return json else: - self.base.log("WARN: Inverter {} read bad REST data from {} - REST will be disabled".format(self.id, url)) - self.base.record_status("Inverter {} read bad REST data from {} - REST will be disabled".format(self.id, url), had_errors=True) - return None + if retry: + # If this is the first call in error then try to re-read the data + return self.rest_runAll() + else: + self.base.log("WARN: Inverter {} read bad REST data from {} - REST will be disabled".format(self.id, url)) + self.base.record_status("Inverter {} read bad REST data from {} - REST will be disabled".format(self.id, url), had_errors=True) + return None else: self.base.log("WARN: Inverter {} unable to read REST data from {} - REST will be disabled".format(self.id, url)) self.base.record_status("Inverter {} unable to read REST data from {} - REST will be disabled".format(self.id, url), had_errors=True) @@ -2256,7 +2260,7 @@ def rest_runAll(self, old_data=None): """ Updated and get inverter status """ - new_data = self.rest_readData(api="runAll") + new_data = self.rest_readData(api="runAll", retry=False) if new_data: return new_data else: @@ -5210,8 +5214,18 @@ def basic_rates(self, info, rtype, prev=None): self.record_status("Bad date {} provided in energy rates".format(this_rate["date"]), had_errors=True) continue - rate = this_rate.get("rate", 0.0) + # Support increment to existing rates (for override) + if "rate" in this_rate: + rate = this_rate.get("rate", 0.0) + rate_increment = False + elif "rate_increment" in this_rate: + rate = this_rate.get("rate_increment", 0.0) + rate_increment = True + + # Resolve any sensor links rate = self.resolve_arg("rate", rate, 0.0) + + # Ensure the end result is a float try: rate = float(rate) except ValueError: @@ -5223,7 +5237,11 @@ def basic_rates(self, info, rtype, prev=None): start_minutes = max(self.minutes_to_time(start, midnight), 0) end_minutes = min(self.minutes_to_time(end, midnight), 24 * 60 - 1) - self.log("Adding rate {} => {} to {} @ {} date {}".format(this_rate, self.time_abs_str(start_minutes), self.time_abs_str(end_minutes), rate, date)) + self.log( + "Adding rate {} => {} to {} @ {} date {} increment {}".format( + this_rate, self.time_abs_str(start_minutes), self.time_abs_str(end_minutes), rate, date, rate_increment + ) + ) # Make end > start if end_minutes <= start_minutes: @@ -5238,14 +5256,18 @@ def basic_rates(self, info, rtype, prev=None): # Store rates against range if end_minutes >= 0 and start_minutes < max_minute: for minute in range(start_minutes, end_minutes): + minute_mod = minute % max_minute if (not date) or (minute >= 0 and minute < max_minute): - rates[minute % max_minute] = rate + if rate_increment: + rates[minute_mod] = rates.get(minute % max_minute, 0.0) + rate + else: + rates[minute_mod] = rate if load_scaling is not None: - self.load_scaling_dynamic[minute % max_minute] = load_scaling + self.load_scaling_dynamic[minute_mod] = load_scaling if not date and not prev: - rates[(minute % max_minute) + max_minute] = rate + rates[minute_mod + max_minute] = rate if load_scaling is not None: - self.load_scaling_dynamic[(minute % max_minute) + max_minute] = load_scaling + self.load_scaling_dynamic[minute_mod + max_minute] = load_scaling return rates diff --git a/docs/energy-rates.md b/docs/energy-rates.md index 87be876bd..382894e16 100644 --- a/docs/energy-rates.md +++ b/docs/energy-rates.md @@ -200,8 +200,25 @@ rates_import_override: Would say that during a 1 hour period at 5:30-6:30pm on 21st of Jan set the import rate to 150p and assume our load will be 80% of normal (20% lower). -**date** is in the date format of "YYYY-MM-DD" e.g. "2023-09-09", **start** and **end** in "HH:MM:SS" time format e.g. "12:30:00", and **rate** in pence. -**load_scaling** is a factor, where 1.0 would be no change, 0.8 is 80% of nominal. +You can also make relative adjustments to your energy rates, e.g. if you want to avoid exporting during peak periods to improve your energy +saving session results you could make a relative adjustment your export rates using **rate_increment**. +The reason not to just set **rate** is then when an energy saving session is active you do not want to ignore the higher export take. + +In this example we subtract 10p from our export rate during the period that saving sessions normally fall within and thus steer Predbat away from +force exporting during that time. The saving session will still work correctly as a 10p adjustment on rates >100p will have little/no impact. + +```yaml +rates_export_override: + - start: '17:00:00' + end: '19:00:00' + rate_increment: -10 +``` + +You can also use rate_increment with load_scaling, e.g. a rate_increment of 0 can be used to just apply load scaling to certain defined periods. + +- **date** is in the date format of "YYYY-MM-DD" e.g. "2023-09-09", **start** and **end** in "HH:MM:SS" time format e.g. "12:30:00", and **rate** in pence. +- **load_scaling** is a factor, where 1.0 would be no change, 0.8 is 80% of nominal. +- **rate_increment** is the number of pence to add to the reported energy rates during this period ## Rate offsets