Skip to content

Commit

Permalink
Minor fixes on compare charts and updates
Browse files Browse the repository at this point in the history
  • Loading branch information
springfall2008 authored Feb 7, 2025
1 parent 15413d4 commit 1085ab6
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 54 deletions.
85 changes: 53 additions & 32 deletions apps/predbat/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
# 2. Allow change to start/end comparison points e.g. tomorrow or today
# 3. Consider Octopus API key to access current tariff info and switch links


class Compare:
def __init__(self, my_predbat):
self.pb = my_predbat
Expand All @@ -42,6 +41,7 @@ def fetch_config(self, tariff):
self.pb.fetch_config_options()

def fetch_rates(self, tariff, rate_import_base, rate_export_base):

pb = self.pb

# Reset threshold to automatic
Expand All @@ -62,16 +62,16 @@ def fetch_rates(self, tariff, rate_import_base, rate_export_base):
if entity_id:
pb.rate_import = pb.fetch_octopus_rates(entity_id, adjust_key="is_intelligent_adjusted")
else:
self.log("Warn: Compare tariff {} bad Octopus entity id {}".format(tariff.get("id", ""), entity_id))
self.log("Warn: Compare tariff {} bad Octopus entity id {}".format(tariff.get('id', ''), entity_id))
elif "metric_energidataservice_import" in tariff:
# Octopus import rates
entity_id = pb.resolve_arg("metric_energidataservice_import", tariff["metric_energidataservice_import"])
if entity_id:
pb.rate_import = pb.fetch_energidataservice_rates(entity_id, adjust_key="is_intelligent_adjusted")
else:
self.log("Warn: Compare tariff {} bad Energidata entity id {}".format(tariff.get("id", ""), entity_id))
elif "rates_import" in tariff:
pb.rate_import = pb.basic_rates(tariff["rates_import"], "rates_import")
self.log("Warn: Compare tariff {} bad Energidata entity id {}".format(tariff.get('id', ''), entity_id))
elif 'rates_import' in tariff:
pb.rate_import = pb.basic_rates(tariff['rates_import'], "rates_import")
else:
self.log("Using existing rate import data")

Expand All @@ -82,33 +82,33 @@ def fetch_rates(self, tariff, rate_import_base, rate_export_base):
# Octopus export rates
entity_id = pb.resolve_arg("metric_octopus_export", tariff["metric_octopus_export"])
if entity_id:
pb.rate_export = pb.fetch_octopus_rates(entity_id)
pb.rate_export= pb.fetch_octopus_rates(entity_id)
else:
self.log("Warn: Compare tariff {} bad Octopus entity id {}".format(tariff.get("id", ""), entity_id))
self.log("Warn: Compare tariff {} bad Octopus entity id {}".format(tariff.get('id', ''), entity_id))
elif "metric_energidataservice_export" in tariff:
# Octopus import rates
entity_id = pb.resolve_arg("metric_energidataservice_export", tariff["metric_energidataservice_export"])
if entity_id:
pb.rate_export = pb.fetch_energidataservice_rates(entity_id, adjust_key="is_intelligent_adjusted")
else:
self.log("Warn: Compare tariff {} bad Energidata entity id {}".format(tariff.get("id", ""), entity_id))
elif "rates_export" in tariff:
pb.rate_export = pb.basic_rates(tariff["rates_export"], "rates_export")
self.log("Warn: Compare tariff {} bad Energidata entity id {}".format(tariff.get('id', ''), entity_id))
elif 'rates_export' in tariff:
pb.rate_export = pb.basic_rates(tariff['rates_export'], "rates_export")
else:
self.log("Using existing rate export data")

if pb.rate_import:
pb.rate_scan(pb.rate_import, print=False)
pb.rate_import, pb.rate_import_replicated = pb.rate_replicate(pb.rate_import, pb.io_adjusted, is_import=True)
if "rates_import_override" in tariff:
if 'rates_import_override' in tariff:
pb.rate_import = pb.basic_rates(tariff["rates_import_override"], "rates_import_override", pb.rate_import, pb.rate_import_replicated)
pb.rate_scan(pb.rate_import, print=True)

# Replicate and scan export rates
if pb.rate_export:
pb.rate_scan_export(pb.rate_export, print=False)
pb.rate_export, pb.rate_export_replicated = pb.rate_replicate(pb.rate_export, is_import=False)
if "rates_export_override" in tariff:
if 'rates_export_override' in tariff:
pb.rate_export = pb.basic_rates(tariff["rates_export_override"], "rates_export_override", pb.rate_export, pb.rate_export_replicated)
pb.rate_scan_export(pb.rate_export, print=True)

Expand All @@ -129,7 +129,7 @@ def fetch_rates(self, tariff, rate_import_base, rate_export_base):
pb.low_rates, lowest, highest = pb.rate_scan_window(pb.rate_import, 5, pb.rate_import_cost_threshold, False)
# Update threshold automatically
if pb.rate_low_threshold == 0 and highest >= pb.rate_min:
pb.rate_import_cost_threshold = highest
pb.rate_import_cost_threshold = highest

# Compare to see if rates changes
for minute in range(pb.minutes_now, pb.forecast_minutes + pb.minutes_now):
Expand All @@ -152,15 +152,22 @@ def run_scenario(self, end_record):
my_predbat.calculate_plan(recompute=True, debug_mode=False, publish=False)

cost, import_kwh_battery, import_kwh_house, export_kwh, soc_min, soc, soc_min_minute, battery_cycle, metric_keep, final_iboost, final_carbon_g = my_predbat.run_prediction(
my_predbat.charge_limit_best, my_predbat.charge_window_best, my_predbat.export_window_best, my_predbat.export_limits_best, False, end_record=end_record, save="compare"
my_predbat.charge_limit_best,
my_predbat.charge_window_best,
my_predbat.export_window_best,
my_predbat.export_limits_best,
False,
end_record=end_record,
save="compare"
)
cost10, import_kwh_battery10, import_kwh_house10, export_kwh10, soc_min10, soc10, soc_min_minute10, battery_cycle10, metric_keep10, final_iboost10, final_carbon_g10 = my_predbat.run_prediction(
my_predbat.charge_limit_best,
my_predbat.charge_window_best,
my_predbat.export_window_best,
my_predbat.export_limits_best,
True,
end_record=end_record,
my_predbat.charge_limit_best,
my_predbat.charge_window_best,
my_predbat.export_window_best,
my_predbat.export_limits_best,
True,
end_record=end_record,

)
# Work out value of the battery at the start end end of the period to allow for the change in SOC
metric_start, battery_value_start = my_predbat.compute_metric(end_record, my_predbat.soc_kw, my_predbat.soc_kw, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
Expand Down Expand Up @@ -188,8 +195,8 @@ def run_scenario(self, end_record):
"metric_keep10": dp2(metric_keep10),
"final_iboost": dp2(final_iboost),
"final_iboost10": dp2(final_iboost10),
"final_carbon_g": dp2(final_carbon_g),
"final_carbon_g10": dp2(final_carbon_g10),
"final_carbon_g": dp0(final_carbon_g),
"final_carbon_g10": dp0(final_carbon_g10),
"battery_value_start": dp2(battery_value_start),
"battery_value_end": dp2(battery_value_end),
"metric_real": dp2(metric_end),
Expand All @@ -204,6 +211,7 @@ def run_scenario(self, end_record):
return result_data

def run_single(self, tariff, rate_import_base, rate_export_base, end_record, debug=False, fetch_sensor=True):

"""
Compare a single energy tariff with the current settings and report results
"""
Expand All @@ -227,13 +235,14 @@ def run_single(self, tariff, rate_import_base, rate_export_base, end_record, deb

self.log("Running scenario for tariff: {}".format(name))
result_data = self.run_scenario(end_record)
result_data["existing_tariff"] = existing_tariff
result_data['existing_tariff'] = existing_tariff
self.log("Scenario complete for tariff: {} cost {} metric {}".format(name, result_data["cost"], result_data["metric"]))
if debug:
with open("compare_{}.html".format(tariff_id), "w") as f:
f.write(result_data["html"])
f.write(result_data['html'])
return result_data


def select_best(self, compare_list, results):
"""
Recommend the best tariff
Expand Down Expand Up @@ -271,9 +280,9 @@ def load_yaml(self):
try:
data = yaml.safe_load(f)
if data:
self.comparisons = data.get("comparisons", {})
self.comparisons = data.get("comparisons", {})
except yaml.YAMLError as exc:
self.log("Error loading comparisons: {}".format(exc))
self.log("Error loading comparisons: {}".format(exc))

if self.comparisons:
compare_list = self.pb.get_arg("compare_list", [])
Expand Down Expand Up @@ -301,7 +310,7 @@ def publish_data(self):
if result:
cost = result.get("cost", 0)
name = result.get("name", "")
attributes = {
attributes={
"friendly_name": "Compare " + name,
"state_class": "measurement",
"unit_of_measurement": "p",
Expand All @@ -310,21 +319,32 @@ def publish_data(self):
for item in result:
value = result[item]
if item != "html":
attributes[item] = value
attributes[item] = value

entity_id = self.prefix + ".compare_tariff_" + tariff_id
self.dashboard_item(
entity_id,
state=cost,
attributes=attributes,
)
)
result["entity_id"] = entity_id

def publish_only(self):
"""
Update HA sensors only
"""
compare_list = self.pb.get_arg("compare_list", [])
if not compare_list:
return

self.select_best(compare_list, self.comparisons)
self.publish_data()

def run_all(self, debug=False, fetch_sensor=True):
"""
Compare a comparison in prices across multiple energy tariffs and report results
take care not to destroy the state of the system for the primary settings
"""
"""
compare_list = self.pb.get_arg("compare_list", [])
if not compare_list:
return
Expand All @@ -342,8 +362,8 @@ def run_all(self, debug=False, fetch_sensor=True):
save_manual_freeze_export_times = my_predbat.manual_freeze_export_times
save_manual_demand_times = my_predbat.manual_demand_times
save_manual_all_times = my_predbat.manual_all_times

# Change to a fixed 48 hour plan
# Change to a fixed 48 hour plan
my_predbat.forecast_plan_hours = 48
my_predbat.forecast_minutes = my_predbat.forecast_plan_hours * 60
my_predbat.forecast_days = my_predbat.forecast_plan_hours / 24
Expand Down Expand Up @@ -384,3 +404,4 @@ def run_all(self, debug=False, fetch_sensor=True):
my_predbat.manual_freeze_export_times = save_manual_freeze_export_times
my_predbat.manual_demand_times = save_manual_demand_times
my_predbat.manual_all_times = save_manual_all_times

8 changes: 5 additions & 3 deletions apps/predbat/predbat.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
from alertfeed import Alertfeed
from compare import Compare


class PredBat(hass.Hass, Octopus, Energidataservice, Solcast, GECloud, Alertfeed, Fetch, Plan, Execute, Output, UserInterface):
"""
The battery prediction class itself
Expand Down Expand Up @@ -520,7 +519,6 @@ def reset(self):
self.alerts = []
self.alert_active_keep = {}
self.alert_cache = {}

self.config_root = "./"
for root in CONFIG_ROOTS:
if os.path.exists(root):
Expand Down Expand Up @@ -767,10 +765,13 @@ def update_pred(self, scheduled=True):
self.expose_config("active", False)
self.save_current_config()

# Compare tariffs either when triggered or daily at midnight
if ((scheduled and self.minutes_now < RUN_EVERY) or self.compare_tariffs) and self.comparison:
# Compare tariffs either when triggered or daily at midnight
self.comparison.run_all()
self.compare_tariffs = False
else:
# Otherwise just update HA sensors to prevent then expiring
self.comparison.publish_only()

async def async_download_predbat_version(self, version):
"""
Expand Down Expand Up @@ -837,6 +838,7 @@ def initialize(self):

try:
self.reset()
self.update_time(print=False)
self.log("Starting HA interface")
try:
self.ha_interface = HAInterface(self)
Expand Down
35 changes: 16 additions & 19 deletions apps/predbat/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

TIME_FORMAT_DAILY = "%Y-%m-%d"


class WebInterface:
def __init__(self, base) -> None:
self.abort = False
Expand Down Expand Up @@ -104,15 +103,15 @@ def history_update(self):
self.cost_yesterday_hist = self.history_attribute(self.base.get_history_wrapper(self.base.prefix + ".cost_yesterday", 28), daily=True, offset_days=-1)
self.cost_yesterday_car_hist = self.history_attribute(self.base.get_history_wrapper(self.base.prefix + ".cost_yesterday_car", 28), daily=True, offset_days=-1)

compare_list = self.base.get_arg("compare_list", [])
compare_list = self.base.get_arg('compare_list', [])
for item in compare_list:
id = item.get("id", None)
if id and self.base.comparison:
if id and self.base.comparison:
self.compare_hist[id] = {}
result = self.base.comparison.get_comparison(id)
if result:
self.compare_hist[id]["cost"] = self.history_attribute(self.base.get_history_wrapper(result["entity_id"], 28), daily=True)
self.compare_hist[id]["metric"] = self.history_attribute(self.base.get_history_wrapper(result["entity_id"], 2), state_key="metric", attributes=True, daily=True)
self.compare_hist[id]["metric"] = self.history_attribute(self.base.get_history_wrapper(result["entity_id"], 28), state_key="metric", attributes=True, daily=True)

async def start(self):
# Start the web server on port 5052
Expand Down Expand Up @@ -325,7 +324,7 @@ def get_chart_series(self, name, results, chart_type, color):
text += " }\n"
return text

def render_chart(self, series_data, yaxis_name, chart_name, now_str, tagname="chart", daily_chart=True):
def render_chart(self, series_data, yaxis_name, chart_name, now_str, tagname='chart', daily_chart=True):
"""
Render a chart
"""
Expand Down Expand Up @@ -386,7 +385,7 @@ def render_chart(self, series_data, yaxis_name, chart_name, now_str, tagname="ch
start: 'day'
},
"""

text += " series: [\n"
first = True
opacity = []
Expand Down Expand Up @@ -830,7 +829,7 @@ async def html_charts(self, request):
args = request.query
chart = args.get("chart", "Battery")
self.default_page = "./charts?chart={}".format(chart)
text = self.get_header("Predbat Charts", refresh=60 * 5)
text = self.get_header("Predbat Charts", refresh=60*5)
text += "<body>\n"
text += "<h2>{} Chart</h2>\n".format(chart)
text += '- <a href="./charts?chart=Battery">Battery</a> '
Expand All @@ -851,7 +850,7 @@ async def html_apps(self, request):
Render apps.yaml as an HTML page
"""
self.default_page = "./apps"
text = self.get_header("Predbat Apps.yaml", refresh=60 * 5)
text = self.get_header("Predbat Apps.yaml", refresh=60*5)
text += "<body>\n"
text += "<a href='./debug_apps'>apps.yaml</a><br>\n"
text += "<table>\n"
Expand Down Expand Up @@ -957,7 +956,7 @@ async def html_compare(self, request):
"""
self.default_page = "./compare"

text = self.get_header("Predbat Compare", refresh=5 * 60)
text = self.get_header("Predbat Compare", refresh=5*60)

text += "<body>\n"
text += '<form class="form-inline" action="./compare" method="post" enctype="multipart/form-data" id="compareform">\n'
Expand All @@ -972,9 +971,9 @@ async def html_compare(self, request):
text += "</form>"

text += "<table>\n"
text += "<tr><th>ID</th><th>Name</th><th>Date</th><th>Metric</th><th>Cost</th><th>Cost 10%</th><th>Export</th><th>Import</th><th>Final SOC</th><th>Iboost</th><th>Carbon</th><th>Result</th>\n"
text += "<tr><th>ID</th><th>Name</th><th>Date</th><th>True cost</th><th>Cost</th><th>Cost 10%</th><th>Export</th><th>Import</th><th>Final SOC</th><th>Iboost</th><th>Carbon</th><th>Result</th>\n"

compare_list = self.base.get_arg("compare_list", [])
compare_list = self.base.get_arg('compare_list', [])

for compare in compare_list:
name = compare.get("name", "")
Expand All @@ -997,13 +996,11 @@ async def html_compare(self, request):
best = result.get("best", False)
existing_tariff = result.get("existing_tariff", False)

selected = "<td bgcolor=#FFaaaa>Best<td>" if best else "<td>&nbsp;</td>"
selected = '<td bgcolor=#FFaaaa>Best<td>' if best else "<td>&nbsp;</td>"
if existing_tariff:
selected += "<td bgcolor=#aaFFaa>Existing<td>"
selected += '<td bgcolor=#aaFFaa>Existing<td>'

text += "<tr><td><a href='#heading-{}'>{}</a></td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td>{}\n".format(
id, id, name, date, metric, cost, cost10, export, imported, soc, final_iboost, final_carbon_g, selected
)
text += "<tr><td><a href='#heading-{}'>{}</a></td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td>{}\n".format(id, id, name, date, metric, cost, cost10, export, imported, soc, final_iboost, final_carbon_g, selected)

text += "</table>\n"

Expand All @@ -1013,9 +1010,9 @@ async def html_compare(self, request):
for compare in compare_list:
name = compare.get("name", "")
id = compare.get("id", "")
series_data.append({"name": name, "data": self.compare_hist.get(id, {}).get("metric", {}), "chart_type": "bar"})
series_data.append({"name": "Actual", "data": self.cost_yesterday_hist, "chart_type": "bar"})
series_data.append({"name": "Car actual", "data": self.cost_yesterday_car_hist, "chart_type": "bar"})
series_data.append({"name" : name, "data" : self.compare_hist.get(id, {}).get("metric", {}), "chart_type": "bar"})
series_data.append({"name" : "Actual", "data" : self.cost_yesterday_hist, "chart_type": "line"})
series_data.append({"name" : "Car actual", "data" : self.cost_yesterday_car_hist, "chart_type": "line"})

now_str = self.base.now_utc.strftime(TIME_FORMAT)

Expand Down

0 comments on commit 1085ab6

Please sign in to comment.