Solcast Global Solar Power Forecast Integration

Looks good but why all header values are N/A?

I fixed the N/A issues, apparently it was because those entities were filtered out in the database recorder. I added them in the “include”.

  include:
    entities:
    - sensor.solcast_forecast_today
    - sensor.solcast_forecast_remaining_today
    - sensor.solcast_forecast_tomorrow
    - sensor.solcast_api_last_polled
  exclude:
    domains:
    - automation
    - device_tracker        
    - sun
    - zone
    entities:
    - sensor.local_ip
    - sensor.date
    - sensor.time
    entity_globs:
    - sensor.solcast*
1 Like

Hi, nice was the reason for my problem too :slight_smile:
One more question:


grafik
Time where screens where taken is 19:10
Do you also have the difference between the Last update time shown and the real last update time of the sensor if you click on it?

A couple of improvements to archive the most accurate remaining solar forecast actually (this minute):

Card:

type: custom:apexcharts-card
header:
  show: true
  standard_format: true
  show_states: true
  colorize_states: true
apex_config:
  chart:
    height: 220px
  tooltip:
    enabled: true
    shared: true
    followCursor: true
graph_span: 4d
now:
  show: false
  color: grey
  label: Now
span:
  start: day
  offset: '-1day'
all_series_config:
  type: area
  opacity: 0.3
  stroke_width: 1
series:
  - entity: sensor.battery_state_of_capacity
    name: Battery
    float_precision: 0
    type: line
    color: '#f06292'
    opacity: 0.6
    yaxis_id: capacity
    extend_to: now
    show:
      legend_value: true
      in_header: false
    group_by:
      func: last
      duration: 5m
  - entity: sensor.house_consumption_power
    name: House Power
    float_precision: 3
    type: line
    color: MediumAquaMarine
    opacity: 0.8
    yaxis_id: kWh
    unit: kW
    transform: return x/1000;
    extend_to: now
    show:
      legend_value: true
      in_header: false
    group_by:
      func: avg
      duration: 5m
  - entity: sensor.inverter_input_power
    name: Solar Power
    float_precision: 3
    color: '#ff9800'
    yaxis_id: kWh
    unit: kW
    transform: return x/1000;
    extend_to: now
    show:
      legend_value: true
      in_header: false
    group_by:
      func: avg
      duration: 5m
  - entity: sensor.solcast_forecast_today
    name: Solar Forecast (D1)
    extend_to: false
    color: grey
    opacity: 0.3
    stroke_width: 0
    yaxis_id: kWh
    show:
      legend_value: false
      in_header: false
    data_generator: |
      return entity.attributes.forecast.map((entry) => {
            return [new Date(entry.period_start), entry.pv_estimate];
          });
  - entity: sensor.solcast_forecast_tomorrow
    name: Solar Forecast (D2)
    float_precision: 3
    extend_to: false
    color: grey
    opacity: 0.3
    stroke_width: 0
    yaxis_id: kWh
    show:
      legend_value: false
      in_header: false
    data_generator: |
      return entity.attributes.forecast.map((entry) => {
            return [new Date(entry.period_start), entry.pv_estimate];
          });
  - entity: sensor.solcast_forecast_d3
    name: Solar Forecast (D3)
    float_precision: 3
    extend_to: false
    color: grey
    opacity: 0.3
    stroke_width: 0
    yaxis_id: kWh
    show:
      legend_value: false
      in_header: false
    data_generator: |
      return entity.attributes.forecast.map((entry) => {
            return [new Date(entry.period_start), entry.pv_estimate];
          });
  - entity: sensor.solcast_forecast_today
    yaxis_id: header_only
    name: Solar Forecast (D1)
    color: grey
    show:
      legend_value: true
      in_header: true
      in_chart: false
  - entity: sensor.solcast_forecast_remaining_today_every_minute
    yaxis_id: header_only
    name: Forecast (D1 Remaining)
    color: grey
    show:
      legend_value: true
      in_header: true
      in_chart: false
  - entity: sensor.solcast_forecast_tomorrow
    yaxis_id: header_only
    name: Forecast (D2)
    color: grey
    show:
      legend_value: true
      in_header: true
      in_chart: false
  - entity: sensor.solcast_forecast_d3
    yaxis_id: header_only
    name: Solar Forecast (D3)
    color: grey
    show:
      legend_value: true
      in_header: true
      in_chart: false
  - entity: sensor.solcast_api_last_polled
    yaxis_id: header_only
    name: Forecast (Last Update)
    color: grey
    unit: ' min.'
    transform: return ((Date.now()) - (new Date(x).getTime())) / 60 / 60 / 24
    show:
      legend_value: true
      in_header: true
      in_chart: false
yaxis:
  - id: capacity
    show: true
    opposite: true
    decimals: 0
    max: 100
    min: 0
    apex_config:
      tickAmount: 5
  - id: kWh
    show: true
    min: 0
    apex_config:
      tickAmount: 5
  - id: header_only
    show: false

Template sensor:

#
# Estimation of actual remaining Solcast Forecast (Every Minute)
#
# - Attributes for expected_battery_charge_end_time
# - Attributes for expected_battery_charge_end_energy
#
template:
  - trigger:
      - platform: state
        entity_id: sensor.solcast_forecast_today
        not_from:
          - "unknown"
          - "unavailable"
          - "none"
        not_to:
          - "unknown"
          - "unavailable"
          - "none"
      - platform: time_pattern
        minutes: "/1"
    sensor:
      - name: solcast_forecast_remaining_today_every_minute
        unique_id: solcast_forecast_remaining_today_every_minute
        #friendly_name: "Solcast Forecast Remaining Today (Every Minute)"
        state_class: "total"
        device_class: energy
        unit_of_measurement: "kWh"
        icon: mdi:solar-power
        state: >-
          {%- set forecast_today = state_attr('sensor.solcast_forecast_today', 'detailedForecast') %}

          {%- set var = namespace(total = 0) %}
          {%- set ts_now = ((as_timestamp(now())/1800)|round(0,'floor')|int * 1800) %}

          {%- if now().minute < 30 %}
            {%- set var.ts_actual_period = today_at(now().hour ~ ':00').timestamp() %}
            {%- set var.remaining_minutes_actual_period = 30 - now().minute %}
          {% else %}
            {%- set var.ts_actual_period = today_at(now().hour ~ ':30').timestamp() %}
            {%- set var.remaining_minutes_actual_period = 60 - now().minute %}
          {%- endif %}

          {%- for record in forecast_today %}
            {%- set ts = as_timestamp(record.period_start) %}      
            {%- if ts == var.ts_actual_period %}
              {%- set var.total = var.total + (record.pv_estimate / 30 * var.remaining_minutes_actual_period) %}
            {%- endif %}
            {%- if ts > ts_now %}
              {%- set var.total = var.total + record.pv_estimate %}
            {%- endif %}
          {%- endfor %}
          {{ var.total }}
        attributes:
          expected_battery_charge_end_time: "14:00"
          expected_battery_charge_end_energy: >-
            {%- set forecast_today = state_attr('sensor.solcast_forecast_today', 'detailedForecast') %}
            {%- set ts_expected_charge_end_time = today_at(this.attributes.expected_battery_charge_end_time).timestamp() %}

            {%- set var = namespace(total = 0) %}
            {%- set ts_now = ((as_timestamp(now())/1800)|round(0,'floor')|int * 1800) %}

            {%- if now().minute < 30 %}
              {%- set var.ts_actual_period = today_at(now().hour ~ ':00').timestamp() %}
              {%- set var.remaining_minutes_actual_period = 30 - now().minute %}
            {% else %}
              {%- set var.ts_actual_period = today_at(now().hour ~ ':30').timestamp() %}
              {%- set var.remaining_minutes_actual_period = 60 - now().minute %}
            {%- endif %}

            {%- for record in forecast_today %}
              {%- set ts = as_timestamp(record.period_start) %}      
              {%- if ts == var.ts_actual_period and ts < ts_expected_charge_end_time %}
                {%- set var.total = var.total + (record.pv_estimate / 30 * var.remaining_minutes_actual_period) %}
              {%- endif %}
              {%- if ts > ts_now and ts < ts_expected_charge_end_time %}
                {%- set var.total = var.total + record.pv_estimate %}
              {%- endif %}
            {%- endfor %}
            {{ var.total }}
        availability: >-
          {{ (states('sensor.solcast_forecast_today')|is_number) }}

With the advanced attributes within the sensor you can define:
expected_battery_charge_end_time: 14:00

expected_battery_charge_end_energy attribute will calculate every minute the remaining solar forecast until 14:00.

With this hack it is possible to control your battery charging in a most efficient and reliable way as you can see in the graph for yesterday morning.

10 Likes

New to HA and been trying to integrate solcast. My main issue is the reported solar array capacity is a lot higher in HA than in reality (roof east 26.81 vs 5.95 kwh, roof west 11.25 vs 3.4 kwh) .Atributes look correct and identical to solcast. Where are those figures coming from? I have deleted solcast intregration several times but it’s not making any difference. What am I doing wrong?

The integration is HA is telling you what to expect to be produced from your Solar array today based on the Solcast Forecast, so the 26.81kWh is the forecast production today from your 5.95kW array (that’s if I’m reading your setup correctly from the attributes)

Your Roof East array capacity is measured in kW not kWh. 5.95 kW is the theoretical power that can be generated at a single point in time.
Solcast is giving you an estimated forecast of 26.81 kWh (Energy) given todays expected conditions.
Energy kWh is measured as Power kW over Time.

That makes a lot more sense now and thanks for the correction (KWh vs KW).

1 Like

Great card. Thanks for sharing. Just so I understand correctly expected_battery_charge_end_energy is the expected solar available to charge the battery up until the charge end time?

Yes - you are right.

expected_battery_charge_end
Parameter A: time => in my example 14:00 local time
Parameter B: energy => remaining estimated solar energy until the charge end time

Hey all,
Im trying to make use of Solcast for my PV string. It has 8x535 Longi panels on Deye inverter (total 4250w)
So i put my location to the api, enter my real inclanation of 15 degree (flat roof mount)
my azimuth is 200 angle southwest
and have some strange data, in the morning i have a little of production and Solcast shows great production, in the evening i have more than predicted. For me it looks like to have it shifted a few hours ahead that forecast.
I tried making some changes of abnormal settings (changes to inclanation 0 to 45, azimuth -160 +160, with effitiency adjustments) and i just dont get the forecast nearly acceptable

Hello I’m having trouble getting the Solcast prediction to work.
I installed ‘Solcast Solar’ via HACS. I got a Solcast API key from the Solcast website. Unfortunately I can’t make a query. 0 is displayed for all entities.
Can you help me? What information do you still need?
My API Key (somewhat blurred)
https://api.solcast.com.au/rooftop_sites/xxxx-xxxx-xxxx-xxxx/estimated_actuals?format=json

Dieser Fehler wurde von einer benutzerdefinierten Integration verursacht

Logger: custom_components.solcast_solar.solcastapi
Source: custom_components/solcast_solar/solcastapi.py:110
Integration: Solcast PV Forecast (documentation, issues)
First occurred: 17:23:55 (1 occurrences)
Last logged: 17:23:55

SOLCAST - sites_data http status Error 404 - Gathering rooftop sites data.
Dieser Fehler wurde von einer benutzerdefinierten Integration verursacht

Logger: custom_components.solcast_solar.solcastapi
Source: custom_components/solcast_solar/solcastapi.py:121
Integration: Solcast PV Forecast (documentation, issues)
First occurred: 17:23:55 (1 occurrences)
Last logged: 17:23:55

SOLCAST - sites_data sites_data error: Traceback (most recent call last): File "/config/custom_components/solcast_solar/solcastapi.py", line 113, in sites_data raise Exception(f"SOLCAST - HTTP sites_data error: Solcast Error gathering rooftop sites data.") Exception: SOLCAST - HTTP sites_data error: Solcast Error gathering rooftop sites data.

Did you get an email from them confirming your account is active? Did you check your log for errors?

have you used up all 10 of your limit calls already… if so just wait till UTC midnight and it will reset and work

make use to get your API key to use in the integration from here
https://toolkit.solcast.com.au/account/api-key

Same for me. Never had an issue until this morning, and same issue for now.

Same here from this morning, this is in my logs:

SOLCAST - sites_data sites_data error: Traceback (most recent call last): File “/config/custom_components/solcast_solar/solcastapi.py”, line 93, in sites_data resp_json = await resp.json(content_type=None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File “/usr/src/homeassistant/homeassistant/helpers/aiohttp_client.py”, line 73, in json return await super().json(*args, loads=loads, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.11/site-packages/aiohttp/client_reqrep.py”, line 1120, in json return loads(stripped.decode(encoding)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ orjson.JSONDecodeError: unexpected character: line 1 column 1 (char 0)

Yep, same here:

SOLCAST - sites_data sites_data error: Traceback (most recent call last): File "/config/custom_components/solcast_solar/solcastapi.py", line 93, in sites_data resp_json = await resp.json(content_type=None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/helpers/aiohttp_client.py", line 73, in json return await super().json(*args, loads=loads, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/aiohttp/client_reqrep.py", line 1120, in json return loads(stripped.decode(encoding)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ orjson.JSONDecodeError: unexpected character: line 1 column 1 (char 0)

and I am sure I have not used my 10 api calls (I use service to check for updates 5 time a day)

There’s an issue open for it on Github

yep - I opened it :slight_smile:


:+1: