What is this solar forecast you are talking about?
I just signed up with solcast.com.au
Allows 10 API pull requests a day for free.
See if I can work this into automation somehow.
What is this solar forecast you are talking about?
I just signed up with solcast.com.au
Allows 10 API pull requests a day for free.
See if I can work this into automation somehow.
Time for me to give something back! See https://community.home-assistant.io/t/rest-api-command-platform-fails-on-post-to-external-url-solcast/. It’s a long thread, but comes to some very good conclusions. Here’s what I ended up with:
In my rest.yaml config file:
###########################
# Get Solcast Forecast Data
###########################
- resource: !secret solcast_forecast_resource
#resource: https://api.solcast.com.au/rooftop_sites/RESOURCE_ID/forecasts?format=json&api_key=API_KEY&hours=72
scan_interval: '00:30:00' # RATE LIMIT!
sensor:
- name: "Solcast Forecast Data"
force_update: true
value_template: "{{ value_json.forecasts[0].pv_estimate|round(2) }}"
unit_of_measurement: "kW"
device_class: power
json_attributes:
- forecasts
- name: "Solcast Forecast 10"
force_update: true
value_template: "{{ value_json.forecasts[0].pv_estimate10|round(2) }}"
- name: "Solcast Forecast 90"
force_update: true
value_template: "{{ value_json.forecasts[0].pv_estimate90|round(2) }}"
In my templates.yaml config file:
sensor:
##################
# Solcast sensors
##################
- name: "Solcast Forecast Energy Today"
unique_id: solcast_forecast_energy_today
unit_of_measurement: "kWh"
device_class: energy
state: >
{% set ns = namespace (fc_today = 0) %}
{% for forecast in state_attr('sensor.solcast_forecast_data', 'forecasts')|default('variable is not defined') %}
{% set daydiff = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).date() - as_local(utcnow()).date() %}
{% if daydiff.days == 0 %}
{% set ns.fc_today = ns.fc_today + (forecast.pv_estimate/2)|float %}
{%- endif %}
{%- endfor %}
{{ ns.fc_today|round(2) }}
- name: "Solcast Forecast Energy Tomorrow"
unique_id: solcast_forecast_energy_tomorrow
unit_of_measurement: "kWh"
device_class: energy
state: >
{% set ns = namespace (fc_tommorrow = 0) %}
{% for forecast in state_attr('sensor.solcast_forecast_data', 'forecasts')|default('variable is not defined') %}
{% set daydiff = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).date() - as_local(utcnow()).date() %}
{% if daydiff.days == 1 %}
{% set ns.fc_tommorrow = ns.fc_tommorrow + (forecast.pv_estimate/2)|float %}
{%- endif %}
{%- endfor %}
{{ ns.fc_tommorrow|round(2) }}
- name: "Solcast Forecast Peak Power Today"
unique_id: solcast_forecast_peak_power_today
unit_of_measurement: "kW"
device_class: power
state_class: measurement
state: >
{% set ns = namespace (fc_today_max = 0) %}
{% for forecast in state_attr('sensor.solcast_forecast_data', 'forecasts')|default('variable is not defined') %}
{% set daydiff = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).date() - as_local(utcnow()).date() %}
{% if daydiff.days == 0 %}
{% if ns.fc_today_max < forecast.pv_estimate|float %}
{% set ns.fc_today_max = forecast.pv_estimate|float %}
{%- endif %}
{%- endif %}
{%- endfor %}
{{ ns.fc_today_max|round(2) }}
- name: "Solcast Forecast Peak Power Tomorrow"
unique_id: solcast_forecast_peak_power_tomorrow
unit_of_measurement: "kW"
device_class: power
state_class: measurement
state: >
{% set ns = namespace (fc_tomorrow_max = 0) %}
{% for forecast in state_attr('sensor.solcast_forecast_data', 'forecasts')|default('variable is not defined') %}
{% set daydiff = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).date() - as_local(utcnow()).date() %}
{% if daydiff.days == 1 %}
{% if ns.fc_tomorrow_max < forecast.pv_estimate|float %}
{% set ns.fc_tomorrow_max = forecast.pv_estimate|float %}
{%- endif %}
{%- endif %}
{%- endfor %}
{{ ns.fc_tomorrow_max|round(2) }}
- name: "Solcast Forecast Peak Time Today"
unique_id: solcast_forecast_peak_time_today
state: >
{% set ns = namespace (fc_today_max = 0, fc_today_max_time = 0) %}
{% for forecast in state_attr('sensor.solcast_forecast_data', 'forecasts')|default('variable is not defined') %}
{% set daydiff = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).date() - as_local(utcnow()).date() %}
{% if daydiff.days == 0 %}
{% if ns.fc_today_max < forecast.pv_estimate|float %}
{% set ns.fc_today_max = forecast.pv_estimate|float %}
{% set ns.fc_today_max_time = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).time() %}
{%- endif %}
{%- endif %}
{%- endfor %}
{{ ns.fc_today_max_time }}
- name: "Solcast Forecast Peak Time Tomorrow"
unique_id: solcast_forecast_peak_time_tomorrow
state: >
{% set ns = namespace (fc_tomorrow_max = 0, fc_tomorrow_max_time = 0) %}
{% for forecast in state_attr('sensor.solcast_forecast_data', 'forecasts')|default('variable is not defined') %}
{% set daydiff = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).date() - as_local(utcnow()).date() %}
{% if daydiff.days == 1 %}
{% if ns.fc_tomorrow_max < forecast.pv_estimate|float %}
{% set ns.fc_tomorrow_max = forecast.pv_estimate|float %}
{% set ns.fc_tomorrow_max_time = as_local(strptime(forecast.period_end, '%Y-%m-%dT%H:%M:%S.%f0Z').replace(tzinfo=utcnow().tzinfo)).time() %}
{%- endif %}
{%- endif %}
{%- endfor %}
{{ ns.fc_tomorrow_max_time }}
- name: "Solcast Forecast Power Next Hour"
unique_id: solcast_forecast_power_next_hour
state: >
{{ ((state_attr('sensor.solcast_forecast_data', 'forecasts')[0].pv_estimate|default('variable is not defined') + state_attr('sensor.solcast_forecast_data', 'forecasts')[1].pv_estimate|default('variable is not defined'))/2)|round(2) }}
unit_of_measurement: 'kW'
device_class: power
- name: "Solcast Forecast Power Next 12 Hours"
unique_id: solcast_forecast_power_next_12_hours
state: >
{% set ns = namespace (fc_next12 = 0) %}
{% for i in range(0, 23) %}
{% set ns.fc_next12 = ns.fc_next12 + states.sensor.solcast_forecast_data.attributes['forecasts'][i]['pv_estimate']|float %}
{%- endfor %}
{{ ns.fc_next12|round(2) }}
unit_of_measurement: 'kW'
device_class: power
- name: "Solcast Forecast Power Next 24 Hours"
unique_id: solcast_forecast_power_next_24_hours
state: >
{% set ns = namespace (fc_next24 = 0) %}
{% for i in range(0, 47) %}
{% set ns.fc_next24 = ns.fc_next24 + states.sensor.solcast_forecast_data.attributes['forecasts'][i]['pv_estimate']|float %}
{%- endfor %}
{{ ns.fc_next24|round(2) }}
unit_of_measurement: 'kW'
device_class: power
- name: "Solcast Forecast Energy Current Hour"
unique_id: solcast_forecast_energy_current_hour
state: >
{% set ns = namespace (fc_energy_current = 0) %}
{% for i in range(0, 1) %}
{% set ns.fc_energy_current = ns.fc_energy_current + (states.sensor.solcast_forecast_data.attributes['forecasts'][i]['pv_estimate']/2)|float %}
{%- endfor %}
{{ ns.fc_energy_current|round(2) }}
unit_of_measurement: 'kWh'
device_class: energy
- name: "Solcast Forecast Energy Next Hour"
unique_id: solcast_forecast_energy_next_hour
state: >
{% set ns = namespace (fc_energy_next = 0) %}
{% for i in range(2, 3) %}
{% set ns.fc_energy_next = ns.fc_energy_next + (states.sensor.solcast_forecast_data.attributes['forecasts'][i]['pv_estimate']/2)|float %}
{%- endfor %}
{{ ns.fc_energy_next|round(2) }}
unit_of_measurement: 'kWh'
device_class: energy
Then an apex-charts-card:
graph_span: 48h
span:
start: day
offset: '-0h'
header:
show: true
title: Solar Production vs. forecast
show_states: true
now:
show: true
label: now
apex_config:
legend:
show: false
series:
- entity: sensor.envoy_current_power_production
stroke_width: 2
show:
extremas: false
color: '#32CD32'
name: Actual
unit: W
fill_raw: last
extend_to_end: false
group_by:
func: avg
duration: 30min
- entity: sensor.solcast_forecast_data
stroke_width: 2
show:
extremas: false
color: '#3498DB'
transform: return x * 1000;
name: Forecast
unit: W
fill_raw: last
extend_to_end: false
- entity: sensor.solcast_forecast_10
stroke_width: 2
show:
extremas: false
in_header: false
color: '#797D7F'
transform: return x * 1000;
name: Forecast 10
unit: W
fill_raw: last
extend_to_end: false
opacity: 0.4
- entity: sensor.solcast_forecast_90
stroke_width: 2
show:
extremas: false
in_header: false
color: '#797D7F'
transform: return x * 1000;
name: Forecast 90
unit: W
fill_raw: last
extend_to_end: false
opacity: 0.4
- entity: sensor.solcast_forecast_data
stroke_width: 2
show:
extremas: false
in_header: false
color: '#E74C3C'
type: line
extend_to_end: false
unit: W
data_generator: |
return entity.attributes.forecasts.map((entry) => {
return [new Date(entry.period_end), entry.pv_estimate*1000];
});
- entity: sensor.solcast_forecast_data
stroke_width: 2
show:
extremas: false
in_header: false
color: '#797D7F'
type: line
extend_to_end: false
unit: W
opacity: 0.4
data_generator: |
return entity.attributes.forecasts.map((entry) => {
return [new Date(entry.period_end), entry.pv_estimate10*1000];
});
- entity: sensor.solcast_forecast_data
stroke_width: 2
show:
extremas: false
in_header: false
color: '#797D7F'
type: line
extend_to_end: false
unit: W
opacity: 0.4
data_generator: |
return entity.attributes.forecasts.map((entry) => {
return [new Date(entry.period_end), entry.pv_estimate90*1000];
});
sensor.solcast_forecast_energy_tomorrow always gives you tomorrow’s forecast.
This is not my own work, just an amalgamation of that of the people over on that thread. But it seems to work beautifully. Hope it saves you some time, after all the time you’ve saved me!
Ok, so this, this I can work into automations if I feel compelled to do.
Very cool. Thanks.
This is much better than trying to automate a pool pump based on ‘sunny’ or other various descriptions.
Ok, you’ve completely lost me! There’s an integration that does all this, rather than adding all that code above, gleaned from the other thread? And is that dotted line the forecast line? How does that make it into the HA Energy component? I couldn’t find the integration using HACS or normal HA integrations. How is it added?
Yes, I started by looking at the various weather descriptions, but they’re very coarse.
Apparently so.
The included instructions are terrible.
Here is how I did it.
Not sure if you have the HACS add on yet.
I already do, so this is how once you have it.
Open up HACS
Tap on integrations
Tap the 3 dots and then custom repositories
Paste this into the repo field and press add https://github.com/oziee/ha-solcast-solar
Then go to configure / integrations / add new and search for solcast and paste in the api key and the site id
Then in configure /energy you can add forecast
I guess the integration must mimic how the official forecast.solar integration (which doesn’t support Australia) works, because my energy configuration looks like this:
There seems to be an open issue with too many API calls. Has it been running well for you so far? The one thing that’s missing is the 10/90% probability stats by the looks of it, but that’s no big deal.
I don’t understand why the thread I mentioned, which is very current, doesn’t mention this integration!
Would you mind posting the yaml for your dashboard? Always keen to learn how people put these things together…
It updates once an hour and takes a break from 7pm to 5am. I just checked my solcast dashboard.
Daily Usage Limit
50 requests
Daily Usage Consumed
26 requests
The consumed usage does not reset until midnight UTC
It’s currently 8pm utc, I think it will make it another 4 hours before counter resets.
The description of the add on says
Modified from the great works of
Appears to combine elements form those 3 GitHub repos
Ok, great. I’ll give it a go. It will be nice to have the forecast integrated with the Energy component. Thanks again.
I just did the sums.
There are 5 sensors and I am assuming each sensor consumes 1 api call.
(7pm) 19 - 5 (5am) = 14 active hours.
14 * 5 calls per hour = 70 requests per day.
if I change the slider to 2 hours per request, then it halves to 7 requests per day * 5 sensors = 35 requests per day.
Given the free accounts are limited to 50 per day, I am going with 2 hours on the slider.
Not anymore. All my graphs are now in UTC time instead of +11hours
I don’t think so. If you look at the REST call in the stuff I posted above, I think one api call gets all the data in one go. The other thread goes into a lot of detail on the data formats. If you look at Issues on the repo, there is more discussion on the frequency too.
Hmmm - which graphs exactly?
appears to be a bug in home assistant.
The graphs show fine in iOS app.
while when accessing using web browser, the graphs are in UTC.
What graphs? all graphs. even the history tab is all in utc via web browser.
Same graph when viewed on my mobile via the iOS app which formats perfectly.
Every time you post something, I see something interesting! What do you have on your 1H, 12H, 24H tabs?
The same graphs with the same elements, just with a different history length (hours to show) for better clarity.
Always learning…
I’ve been playing with the solcast integration. In the Energy graphs you included above, you showed one which was presumably for tomorrow, with a nice sloping graph of the forecast. Any idea how this is produced from the integration’s sensors, which don’t seem to provide enough data to do that?
Pretty much have to learn python and pick apart the repo.
Either that or ask oziee (Greg) · GitHub directly
Yeh. Must be feeding the data directly to the Energy platform, and not creating sensors for it all (which is what the other method does). Oh well, moot point until the integration is behaving a little better. Hope it gets there.