Datetime attribute is used as string and cannot be compared with datetime object

Hello dear community,

I have a question about the handling of attributes of entities that I am not getting anywhere with. Maybe someone can help me?

I have various weather entities, some of which have long-term forecasts. I would like to select a certain time range from the forecast, which is stored as an attribute in the entity. To make my request easier to understand, I will use “weather.homeassistant_hourly” as an example.

As you know, the attributes of weather.homeassistant_hourly look like this:

temperature: 13.9
humidity: 83
pressure: 1023.8
wind_bearing: 207
wind_speed: 5.4
forecast: 
- condition: sunny
  precipitation: 0
  temperature: 16.4
  datetime: '2022-06-10T07:00:00+00:00'
  wind_bearing: 202.6
  wind_speed: 5.8
- condition: sunny
  precipitation: 0
  temperature: 18.8
  datetime: '2022-06-10T08:00:00+00:00'
  wind_bearing: 209.3
  wind_speed: 6.8
...

I know that the following template can be used to read out the various attributes under “forecast”:

{{ state_attr('weather.homeassistant_hourly', 'forecast') 
| map(attribute='datetime')
| list
}}

This results in a list

[
  "2022-06-10T07:00:00+00:00",
  "2022-06-10T08:00:00+00:00",
...
]

But I want to select a certain time range. Unfortunately, it is not possible for me to make a date comparison with the datetime attribute. The only possibility I can think of is to narrow it down using:

| selectattr('datetime', 'search', 'STRING') 
| rejectattr('datetime', 'search', 'STRING') 

However, this is very inelegant and quickly becomes spaghetti code when using the forecast for several days.
Is there a way to recycle the entity string under “datetime” as a datetime? I would like to be able to execute something like the following, for example:

{{ state_attr('weather.homeassistant_hourly', 'forecast') 
| selectattr('datetime', '>', now()) 
| map(attribute='datetime')
| list
}}

This results in the error message

TypeError: '>' not supported between instances of 'str' and 'datetime.datetime'

| selectattr('datetime', '>', utcnow().isoformat())

Great, that was much easier than I thought. @petro Thank you very much!

Okay… Now I run into the next problem. When I try to set a time in the future or past, I end up with a new error message: TypeError: can only concatenate str (not "datetime.timedelta") to str

I have tried the following:

{{ state_attr('weather.homeassistant', 'forecast') 
| selectattr('datetime', '<', utcnow().isoformat() + timedelta(hours=5) )
| map(attribute='datetime')
| list
}}

add the timedelta before formatting it

(utcnow() + timedelta(hours=5)).isoformat()
1 Like

Great! This has made numerous sensors much easier for me.

It seems I’m encountering a variation of this issue.

 {{ result['weather.mafra'].forecast | map(attribute='datetime') | list }} 

When I list the variable results of the action weather.get_forecasts, I receive:

 [datetime.datetime(2024, 4, 26, 18, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 26, 19, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 26, 20, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 26, 21, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 26, 22, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 26, 23, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 27, 0, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 27, 1, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 27, 2, 0, tzinfo=datetime.timezone.utc),
  datetime.datetime(2024, 4, 27, 3, 0, tzinfo=datetime.timezone.utc)]

The datetime attribute is a Python datetime.datetime object,

Yes, it is… did you have a question or need help with something related to that?

Yes, no luck trying to find out the max temperature of today:

 {% set midnight = (today_at() + timedelta(days=1)).isoformat() %}
 {{ result['weather.mafra'].forecast | selectattr('datetime', 'lt', midnight) | map(attribute='temperature') | list | max }}

based on How to get max temperature for today from forecast? - #22 by petro

Somehow the selectattr is unable to be applied to the python datetime object.

service: weather.get_forecasts output:

I have implemented a workaround to datetime selectattr issue. Slice the number of hours until the end of the day:

trigger:
  - platform: state
    entity_id: weather.mafra
  - platform: homeassistant
    event: start
  - platform: event
    event_type: event_template_reloaded
action:
  - service: weather.get_forecasts
    data:
      type: hourly
    target:
      entity_id: weather.mafra
    response_variable: result
sensor:
  - name: Today Max Temperature Forecast
    unique_id: ed777a98-c192-4e99-af2e-621c17c026f5
    device_class: temperature
    unit_of_measurement: °C 
    state: >
        {% set hours_eod = ((today_at() + timedelta(days=1)) - now()).total_seconds() // 3600 | int %}
        {{ result['weather.mafra'].forecast[:int(hours_eod)] | map(attribute='temperature') | list | max }}

The following works for me (only the part under “Sensor”):

sensor:
  - name: "property_temperature_forecast_0d_maximum"
    unique_id: "property_temperature_forecast_0d_maximum"
    unit_of_measurement: "°C"
    state: >-
      {{
        response[entity_id].forecast 
        | selectattr('datetime', '>=', now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0).isoformat() )
        | selectattr('datetime', '<=', (now().replace(hour=23).replace(minute=59).replace(second=59).replace(microsecond=999999) + timedelta(days=0) ).isoformat() )
        | map(attribute='temperature') 
        | list
        | max
        | float(0)
        | round(1)
      }}

Tip

Whenever you find yourself modifying a datetime object like this:

now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0).isoformat()

consider using today_at() instead of now(). If you don’t pass a time string to it then today_at() defaults to using 00:00:00.

Therefore the following produces the same result as the longer version that uses now() with replace.

today_at().isoformat()