I use my Solar forecast from SolCast PV to automate my SolaX inverter and battery settings. Basically, my system has three modes: Full Sun, Partial Sun, or No Sun. I have then created three Scenes that reflect the settings I want in these three scenarios, based upon tomorrows Solar forecast.
The issue I have, is that the Solar forecast isn’t always accurate and I end up overriding the scene based upon a local weather forecast that is more right than wrong for my area. This is sometimes a good idea and sometime I should have just it left alone!
So I decided to create an automation that reviews the accuracy between the Forecast Solar and Actual Solar for the past 7 days, and creates a % Error Correction value for ongoing estimates. I have to admit, my coding ability has, shall we say, degraded since I moved into a management role, and then retired! ![]()
So I thought that I would combine this challenge with my other intellectual project, which is getting to know ChatGPT, Google Gemini, and Claude in detail and what the strengths and weaknesses are of each. ![]()
Google Gemini couldn’t generate YAML code that Home Assistant could understand, nothing even close. ChatGPT, however was on a different level and was very fast.
Basically, I have used/created these entities and helpers.
sensor.solcast_pv_forecast_forecast_today: Today’s solar forecast (kWh).sensor.brenchley_today_s_solar_energy: Today’s actual solar generation (kWh).sensor.solcast_pv_forecast_forecast_tomorrow: Tomorrow’s forecast (kWh).input_text.forecast_error_history: Stores the last 7 daily % errors.input_number.adjusted_solar_forecast: Stores the final corrected forecast.
(YAML code available, if anyone wants further detail.)
Then, I’ve (ChatGPT!) created this automation:
alias: Adjust Tomorrow's Solar Forecast With Learning
description: >
Adjusts tomorrow’s solar forecast using a 7-day rolling average of the %
difference between actual and forecasted solar. Caps each day's contribution
to ±10% to avoid distortion.
trigger:
- platform: time
at: "20:00:00" # Run every evening at 8 PM
variables:
# Retrieve today's forecasted solar generation (in kWh)
forecast_today: "{{ states('sensor.solcast_pv_forecast_forecast_today') | float(0) }}"
# Retrieve today's actual measured solar generation (in kWh)
actual_today: "{{ states('sensor.brenchley_today_s_solar_energy') | float(0) }}"
# Retrieve tomorrow's forecasted solar generation (in kWh)
forecast_tomorrow: "{{ states('sensor.solcast_pv_forecast_forecast_tomorrow') | float(0) }}"
# Calculate today's percentage error between forecast and actual
# Capped at ±10% to reduce influence of outliers
percent_error_today: |-
{% if forecast_today > 0 %}
{% set raw_error = (actual_today - forecast_today) / forecast_today * 100 %}
{% if raw_error > 10 %}
10
{% elif raw_error < -10 %}
-10
{% else %}
{{ raw_error }}
{% endif %}
{% else %}
0 # Avoid divide-by-zero or meaningless result
{% endif %}
# Get previous percentage errors from text helper
history_raw: "{{ states('input_text.forecast_error_history') }}"
# Parse historical values into float list, filtering out invalid entries
history_list: >-
{% set raw = history_raw.split(',') if history_raw else [] %}
{{ raw | select('match', '^-?[0-9.]+$') | map('float') | list }}
# Add today's error and trim the list to retain only the last 7 entries
updated_list: >-
{% set new_list = (history_list + [percent_error_today | float])[-7:] %}
{{ new_list }}
# Calculate average percentage error over the stored days
error_avg: "{{ updated_list | sum / updated_list | length }}"
# Apply the averaged % error to tomorrow's forecast to produce an adjusted value
adjusted_forecast: "{{ forecast_tomorrow * (1 + error_avg / 100) }}"
action:
# Update the forecast error history helper with the new 7-day list
- service: input_text.set_value
target:
entity_id: input_text.forecast_error_history
data:
value: "{{ updated_list | map('round', 2) | join(',') }}"
# Save the adjusted forecast value into a number helper for downstream use
- service: input_number.set_value
target:
entity_id: input_number.adjusted_solar_forecast
data:
value: "{{ adjusted_forecast | round(2) }}"
mode: single # Prevent overlapping runs
It all appears to work, although I’m only on day2!!!
EDIT: I’ve amended the code after asking ChatGPT to add comments to aid code maintenance.