Error: UndefinedError: 'str object' has no attribute 'pv_estimate'

Hi,

I want to use the sensor sensor.solcast_pv_forecast_forecast_today of the solcast integration to switch on my boiler at the most perfect time (using solar energy) :

state_class: total
01a5-a421-b8ea-86e2: 50.7099
estimate-01a5-a421-b8ea-86e2: 50.7099
estimate10-01a5-a421-b8ea-86e2: 36.0587
estimate90-01a5-a421-b8ea-86e2: 50.9801
estimate: 50.7099
estimate10: 36.0587
estimate90: 50.9801
dayname: Saturday
dataCorrect: true
detailedForecast:
  - period_start: "2025-03-29T00:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T00:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T01:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T01:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T02:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T02:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T03:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T03:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T04:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T04:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T05:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T05:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T06:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T06:30:00+01:00"
    pv_estimate: 0.041
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T07:00:00+01:00"
    pv_estimate: 0.181
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T07:30:00+01:00"
    pv_estimate: 0.806
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T08:00:00+01:00"
    pv_estimate: 1.7331
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T08:30:00+01:00"
    pv_estimate: 2.6946
    pv_estimate10: 2.4683
    pv_estimate90: 2.6946
  - period_start: "2025-03-29T09:00:00+01:00"
    pv_estimate: 3.5805
    pv_estimate10: 3.5672
    pv_estimate90: 3.5805
  - period_start: "2025-03-29T09:30:00+01:00"
    pv_estimate: 4.4041
    pv_estimate10: 3.846
    pv_estimate90: 4.4041
  - period_start: "2025-03-29T10:00:00+01:00"
    pv_estimate: 5.1745
    pv_estimate10: 4.9705
    pv_estimate90: 5.1745
  - period_start: "2025-03-29T10:30:00+01:00"
    pv_estimate: 5.7289
    pv_estimate10: 4.5828
    pv_estimate90: 5.7994
  - period_start: "2025-03-29T11:00:00+01:00"
    pv_estimate: 6.0873
    pv_estimate10: 4.3277
    pv_estimate90: 6.186
  - period_start: "2025-03-29T11:30:00+01:00"
    pv_estimate: 5.6245
    pv_estimate10: 3.7872
    pv_estimate90: 5.6916
  - period_start: "2025-03-29T12:00:00+01:00"
    pv_estimate: 5.4651
    pv_estimate10: 3.8016
    pv_estimate90: 5.6668
  - period_start: "2025-03-29T12:30:00+01:00"
    pv_estimate: 6.3516
    pv_estimate10: 4.4035
    pv_estimate90: 6.6996
  - period_start: "2025-03-29T13:00:00+01:00"
    pv_estimate: 6.7974
    pv_estimate10: 4.6978
    pv_estimate90: 7.1772
  - period_start: "2025-03-29T13:30:00+01:00"
    pv_estimate: 6.8296
    pv_estimate10: 4.654
    pv_estimate90: 7.0957
  - period_start: "2025-03-29T14:00:00+01:00"
    pv_estimate: 6.7452
    pv_estimate10: 4.5965
    pv_estimate90: 6.9176
  - period_start: "2025-03-29T14:30:00+01:00"
    pv_estimate: 6.4362
    pv_estimate10: 4.3989
    pv_estimate90: 6.5771
  - period_start: "2025-03-29T15:00:00+01:00"
    pv_estimate: 6.0156
    pv_estimate10: 4.1055
    pv_estimate90: 6.1572
  - period_start: "2025-03-29T15:30:00+01:00"
    pv_estimate: 5.4543
    pv_estimate10: 3.7162
    pv_estimate90: 5.6452
  - period_start: "2025-03-29T16:00:00+01:00"
    pv_estimate: 4.7551
    pv_estimate10: 3.2456
    pv_estimate90: 4.9884
  - period_start: "2025-03-29T16:30:00+01:00"
    pv_estimate: 3.933
    pv_estimate10: 2.6768
    pv_estimate90: 4.1708
  - period_start: "2025-03-29T17:00:00+01:00"
    pv_estimate: 3.06
    pv_estimate10: 2.0343
    pv_estimate90: 3.2911
  - period_start: "2025-03-29T17:30:00+01:00"
    pv_estimate: 2.1006
    pv_estimate10: 1.3617
    pv_estimate90: 2.3154
  - period_start: "2025-03-29T18:00:00+01:00"
    pv_estimate: 1.1516
    pv_estimate10: 0.7101
    pv_estimate90: 1.35
  - period_start: "2025-03-29T18:30:00+01:00"
    pv_estimate: 0.2641
    pv_estimate10: 0.1603
    pv_estimate90: 0.3724
  - period_start: "2025-03-29T19:00:00+01:00"
    pv_estimate: 0.0049
    pv_estimate10: 0.0049
    pv_estimate90: 0.0049
  - period_start: "2025-03-29T19:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T20:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T20:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T21:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T21:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T22:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T22:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T23:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T23:30:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
detailedHourly:
  - period_start: "2025-03-29T00:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T01:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T02:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T03:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T04:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T05:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T06:00:00+01:00"
    pv_estimate: 0.0205
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T07:00:00+01:00"
    pv_estimate: 0.4935
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T08:00:00+01:00"
    pv_estimate: 2.2138
    pv_estimate10: 1.2342
    pv_estimate90: 1.3473
  - period_start: "2025-03-29T09:00:00+01:00"
    pv_estimate: 3.9923
    pv_estimate10: 3.7066
    pv_estimate90: 3.9923
  - period_start: "2025-03-29T10:00:00+01:00"
    pv_estimate: 5.4517
    pv_estimate10: 4.7767
    pv_estimate90: 5.487
  - period_start: "2025-03-29T11:00:00+01:00"
    pv_estimate: 5.8559
    pv_estimate10: 4.0575
    pv_estimate90: 5.9388
  - period_start: "2025-03-29T12:00:00+01:00"
    pv_estimate: 5.9084
    pv_estimate10: 4.1025
    pv_estimate90: 6.1832
  - period_start: "2025-03-29T13:00:00+01:00"
    pv_estimate: 6.8135
    pv_estimate10: 4.6759
    pv_estimate90: 7.1364
  - period_start: "2025-03-29T14:00:00+01:00"
    pv_estimate: 6.5907
    pv_estimate10: 4.4977
    pv_estimate90: 6.7473
  - period_start: "2025-03-29T15:00:00+01:00"
    pv_estimate: 5.7349
    pv_estimate10: 3.9108
    pv_estimate90: 5.9012
  - period_start: "2025-03-29T16:00:00+01:00"
    pv_estimate: 4.344
    pv_estimate10: 2.9612
    pv_estimate90: 4.5796
  - period_start: "2025-03-29T17:00:00+01:00"
    pv_estimate: 2.5803
    pv_estimate10: 1.698
    pv_estimate90: 2.8033
  - period_start: "2025-03-29T18:00:00+01:00"
    pv_estimate: 0.7078
    pv_estimate10: 0.4352
    pv_estimate90: 0.8612
  - period_start: "2025-03-29T19:00:00+01:00"
    pv_estimate: 0.0024
    pv_estimate10: 0.0024
    pv_estimate90: 0.0024
  - period_start: "2025-03-29T20:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T21:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T22:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
  - period_start: "2025-03-29T23:00:00+01:00"
    pv_estimate: 0
    pv_estimate10: 0
    pv_estimate90: 0
unit_of_measurement: kWh
attribution: Data retrieved from Solcast
device_class: energy
friendly_name: Solcast PV Forecast Forecast Today

With the help of ChatGPT I wrote this automation :

alias: Bepaal boiler inschakeltijd op basis van zonneproductie
description: ""
triggers:
  - event: sunrise
    trigger: sun
conditions: []
actions:
  - variables:
      forecast: >-
        {{ state_attr('sensor.solcast_pv_forecast_forecast_today',
        'detailedHourly') | default([], true) }}
      sorted_forecast: "{{ forecast | sort(attribute='period_start') }}"
      energy_needed: 6
      accumulated_energy: 0
  - if:
      - condition: template
        value_template: "{{ sorted_forecast | length > 0 }}"
    then:
      - variables:
          best_start_time: >
            {% set energy_sum = 0 %} {% set start_time = none %} {% for entry in
            sorted_forecast %}
              {% if start_time is none %}
                {% set start_time = entry.period_start %}
              {% endif %}
              {% set energy_sum = energy_sum + entry.pv_estimate %}
              {% if energy_sum >= energy_needed %}
                {% break %}
              {% endif %}
            {% endfor %} {{ start_time | default('12:00:00', true) }}
      - target:
          entity_id: input_datetime.tijdstip_boiler_starttijd
        data:
          time: "{{ best_start_time[11:16] }}"
        action: input_datetime.set_datetime
mode: single

Unfortunately I get an error :

I tried this in the developer tools :

{% set sorted_forecast = state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedHourly') | default([], true)  %}
{% set energy_needed = 6 %}

{% set energy_sum = 0 %}
{% set start_time = none %} 
{% for entry in sorted_forecast %}

  ===> {{ entry.period_start}} - {{ entry.pv_estimate }} - {{ energy_sum }}
  
  {% if start_time is none %}
    {% set start_time = entry.period_start %}
  {% endif %}
  
  {% set energy_sum = energy_sum + entry.pv_estimate|float(0) %}
  
  {% if energy_sum >= energy_needed %}
    {% break %}
  {% endif %}
{% endfor %} 
{{ start_time | default('10:00:00', true) }}

I energy_sum is not changing and the result is ‘10:00’.

Can somebody help out of this ?

use a namespace for working in the for-loop

{% set ns = namespace(energy_sum=0) %}
{% for ..}
{% set ns.energy_sum = ns.energy_sum + .. %}
{% endfor %}

Your issue is that you’re iterating the keys in the dictionary, not the keys and items.

for key, entry in sorted_forecast.items()

This works in the developer tools and gives me the right result (9:00) :

{% set sorted_forecast = state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedHourly') | default([], true)  %}
{% set energy_needed = 6 %}

{% set ns = namespace(energy_sum = 0) %}
{% set ns2 = namespace(start_time = none) %}
{% for entry in sorted_forecast %}
  {% if ns2.start_time is none %}
    {% set ns2.start_time = entry.period_start %}
  {% endif %}
  {% set ns.energy_sum = ns.energy_sum + entry.pv_estimate|float(0) %}
  
  {% if ns.energy_sum >= energy_needed %}
    {% set ns2.start_time = entry.period_start %}
    {% break %}
  {% endif %}
{% endfor %}
{{ ns2.start_time | as_timestamp | timestamp_custom('%H:%M:%S',True) }}

But not in the automation :

alias: Bepaal boiler inschakeltijd op basis van zonneproductie
description: ""
triggers:
  - event: sunrise
    trigger: sun
conditions: []
actions:
  - variables:
      forecast: >-
        {{ state_attr('sensor.solcast_pv_forecast_forecast_today',
        'detailedHourly') | default([], true) }}
      sorted_forecast: "{{ forecast | sort(attribute='period_start') }}"
      energy_needed: 6
      accumulated_energy: 0
  - if:
      - condition: template
        value_template: "{{ sorted_forecast | length > 0 }}"
    then:
      - variables:
          best_start_time: >
            {% set ns = namespace(energy_sum = 0) %} {% set ns2 =
            namespace(start_time = none) %} {% for entry in sorted_forecast %}
              {% if ns2.start_time is none %}
                {% set ns2.start_time = entry.period_start %}
              {% endif %}
              {% set ns.energy_sum = ns.energy_sum + entry.pv_estimate|float(0) %}
              {% if ns.energy_sum >= energy_needed %}
                {% set ns2.start_time = entry.period_start %}
                {% break %}
              {% endif %}
            {% endfor %} {{ ns2.start_time | as_timestamp |
            timestamp_custom('%H:%M:%S',True) }}
      - target:
          entity_id: input_datetime.tijdstip_boiler_starttijd
        data:
          time: "{{ best_start_time }}"
        action: input_datetime.set_datetime

@petro
This gives me that error :

You won’t be able to test it in the template editor unless you take the actual response from the weather call in the actions tab.

EDIT: I just noticed you aren’t using a service call but an attribute. You likely can’t assign it as a sorted variable if attributes have datetime objects in them because they do not serialize.

I’m almost there…

I changed it like that :

alias: Bepaal boiler inschakeltijd op basis van zonneproductie
description: ""
triggers:
  - event: sunrise
    trigger: sun
conditions: []
actions:
  - action: input_datetime.set_datetime
    metadata: {}
    data:
      datetime: >
        {% set sorted_forecast =
        state_attr('sensor.solcast_pv_forecast_forecast_today',
        'detailedHourly') | default([], true)  %} {% set energy_needed = 6 %}

        {% set ns = namespace(energy_sum = 0) %} {% set ns2 =
        namespace(start_time = none) %} {% for entry in sorted_forecast %}
          {% if ns2.start_time is none %}
            {% set ns2.start_time = entry.period_start %}
          {% endif %}
          {% set ns.energy_sum = ns.energy_sum + entry.pv_estimate|float(0) %}
          
          {% if ns.energy_sum >= energy_needed %}
            {% set ns2.start_time = entry.period_start %}
            {% break %}
          {% endif %}
        {% endfor %} "{{ as_timestamp(ns2.start_time|replace('+01:00','')) |
        timestamp_custom('%Y-%m-%d %H:%M:%S') }}"
    target:
      entity_id: input_datetime.tijdstip_boiler_starttijd

This is not correct, but I don’t know what it should be :

“{{ as_timestamp(ns2.start_time|replace(‘+01:00’,‘’)) |
timestamp_custom(‘%Y-%m-%d %H:%M:%S’) }}”

Problem with converting I guess…

This works fine (exact the same, but hard-coded):

  - action: input_datetime.set_datetime
    metadata: {}
    data:
      datetime: "2025-03-29 09:00:00"
    target:
      entity_id: input_datetime.tijdstip_boiler_starttijd

Found it, no quotes :smirk:

How can I solve this ?

This is my last version, works fine except that the table is indeed not sorted :angry:

action: input_datetime.set_datetime
metadata: {}
data:
  datetime: >
    {% set forecast =
    state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedHourly') |
    default([], true)  %}
    {% set sorted_forecast =  forecast | sort(attribute='period_start') %}
    {% set energy_needed = 6 %}

    {% set ns = namespace(energy_sum = 0) %} {% set ns2 = namespace(start_time =
    none) %} {% for entry in sorted_forecast %}
      {% if ns2.start_time is none %}
        {% set ns2.start_time = entry.period_start %}
      {% endif %}
      {% set ns.energy_sum = ns.energy_sum + entry.pv_estimate|float(0) %}
      
      {% if ns.energy_sum >= energy_needed %}
        {% set ns2.start_time = entry.period_start %}
        {% break %}
      {% endif %}
    {% endfor %} {{ as_timestamp(ns2.start_time|replace('+01:00','')) |
    timestamp_custom('%Y-%m-%d %H:%M:%S') }}
target:
  entity_id: input_datetime.tijdstip_boiler_starttijd

What do you mean … in your first post you showentity attributes where all seem to be sorted…
And if I put the 13:00 values first in detaildHours then it still comes up with 09:00

Sorry, still not awake :wink:

I want to sort on pv_estimate, so obviously I have to mention that attribute…

{% set sorted_forecast =  forecast | sort(attribute='pv_estimate') %}

One thing : how to sort descending ?

Edit
Found it :

{% set sorted_forecast =  forecast | sort(attribute='pv_estimate', reverse=true) %}

At a loss what you are trying to acomplish but apparently you are happy. :slight_smile: I thought you wanted the ‘time’ when the total generated energy hit 6

Good call, I had to do some modifications :

action: input_datetime.set_datetime
metadata: {}
data:
  datetime: >
    {% set forecast = state_attr('sensor.solcast_pv_forecast_forecast_today',
    'detailedHourly') | default([], true)  %} {% set sorted_forecast =  forecast
    | sort(attribute='pv_estimate', reverse=true) %} {% set energy_needed = 12 %}

    {% set ns = namespace(energy_sum = 0) %} {% set ns2 = namespace(start_time =
    none) %} {% for entry in sorted_forecast %}
      {% if ns2.start_time is none %}
        {% set ns2.start_time = entry.period_start %}
      {% else %}
        {% if as_timestamp(entry.period_start) < as_timestamp(ns2.start_time) %}
          {% set ns2.start_time = entry.period_start %}
        {% endif %}
      {% endif %}
      {% set ns.energy_sum = ns.energy_sum + entry.pv_estimate|float(0) %}
      
      {% if ns.energy_sum >= energy_needed %}
        {% break %}
      {% endif %}
    {% endfor %}

    {% if ns2.start_time is not none %}
      {{ as_timestamp(ns2.start_time|replace('+01:00','')) |timestamp_custom('%Y-%m-%d %H:%M:%S') }}
    {% else %}
      {{ as_timestamp(today_at('10:00:00')) |timestamp_custom('%Y-%m-%d %H:%M:%S') }}
    {% endif %}    
target:
  entity_id: input_datetime.tijdstip_boiler_starttijd

It gives me the correct time when to switch on my boiler (just for testing I increased the needed energy to 9 kWh).

Let’s hope my logic is correct…

I am not sure … this logic would not work for me as I donot store any energy…so for me the moment to start a boiler would be when the pv_estimate > 6 …not the sum

EDIT…and that would be a lot easier to extract

This will be in my logic too… the hours with the most production come first (that’s why I sort). If the first one is more than 6 kWh, than this will be the hour to start.

But your sorting result may be unwanted (?) quite a lot. if the largest return is at say 14:00 and the first time it hits 6 is at 9:00 then your boiler will start at 14:00 or vv.

Anyhow for me this works

{{ (state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedHourly') 
|selectattr('pv_estimate', 'gt', 6)|list|first).period_start }}

You are right, I changed my logic :grinning:

In my case my boiler consumes 3kW en needs 2 hours of charge (so 6kWh).

I now take the 2 hours with the most solar energy production. Normally I take as starting hour the one with the most solar energy, except when the 2nd largest is earlier (and with one hour difference).

{% set forecast = state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedHourly') | default([], true)  %}
{% set sorted_forecast = forecast | sort(attribute='pv_estimate', reverse=true) %}

{% set ns = namespace(start_time = none) %}

{% for entry in sorted_forecast %}
  
  ==> {{ entry.period_start }} - {{ entry.pv_estimate }}
  
  {% if ns.start_time is none %}
    {% set ns.start_time = entry.period_start %}
  {% else %}
    {% if as_timestamp(entry.period_start) < as_timestamp(ns.start_time) %}
      {% if (as_timestamp(entry.period_start) - as_timestamp(ns.start_time))|abs == 3600 %}
        {% set ns.start_time = entry.period_start %}
      {% endif %}
    {% endif %}
    {% break %}
  {% endif %}
  
{% endfor %}

{% if ns.start_time is not none %}
  {{ as_timestamp(ns.start_time|replace('+01:00','')) |timestamp_custom('%Y-%m-%d %H:%M:%S') }}
{% else %}
  {{ as_timestamp(today_at('10:00:00')) |timestamp_custom('%Y-%m-%d %H:%M:%S') }}
{% endif %}    

PS
Your solution will not work if there are no hours with at least 6 kWh available

Not for you :slight_smile: as you need multiple hours