Help with Template sensor w/ action: variables and handling errors

I have the following template sensor that fetches an hourly forecast and returns the max temperature for the next few hours as its state:

- triggers:
    - trigger: time_pattern
      hours: 05

  action:
    - action: weather.get_forecasts
      data:
        type: hourly
      target:
        entity_id: weather.forecast_home
      response_variable: response

  sensor:
    - name: "Max Morning Temp"
      unique_id: max_morning_temp
      device_class: temperature
      unit_of_measurement: "°F"

      state: >-
        {{
          response['weather.forecast_home'].forecast[1:6]
            |selectattr('datetime', 'match', utcnow().strftime("%Y-%m-%d"))
            |map(attribute='temperature')
            |list
            |max
        }}

      attributes:
        future_temps: >-
          {% set ns = namespace(stuff=[]) -%}
          {% set temp_list =
            response['weather.forecast_home'].forecast[1:6]
              |selectattr('datetime', 'match', utcnow().strftime("%Y-%m-%d"))
          -%}
          {% for item in temp_list -%}
            {%
              set ns.stuff = ns.stuff + [
                (item.datetime|as_datetime|as_local).strftime("%H:%M") ~ '=' ~
                item.temperature ~ '°F'
              ]
            %}
          {% endfor -%}
          {{ ns.stuff|list|join(', ') }}

I have three questions:

First, I’m repeating the same filter in both state: and the attributes: sections. Is there a way to filter response into a variable and use that instead?

What I’d like to do is something like:

  variables:
    future_temps: >-
      {{
          response['weather.forecast_home'].forecast[1:6]
            |selectattr('datetime', 'match', utcnow().strftime("%Y-%m-%d"))
      }}

but response is not available in that block.

Second, I’m wondering how to make it more robust, for example if the action fails. That one filter:

|selectattr('datetime', 'match', utcnow().strftime("%Y-%m-%d"))

is a bit of a sanity check to make sure I’m looking at today’s dates in the response data. But what if the action (api call) fails? Or if the data is not what is expected? Should there be an availability: that checks everything?

Note that if I change the key .forecast to .x_forecast (an invalid key) I get an excetpion in the logs and the state becomes unavailable.

But if the match filter returns an empty list I get the following and the state is NOT updated.

2025-05-24 12:04:00.086 WARNING (MainThread) [homeassistant.helpers.template] Template variable warning: No aggregated item, sequence was empty. when rendering '{{
  response['weather.forecast_home'].forecast[1:6]
    |selectattr('datetime', 'match', utcnow().strftime("foo%Y-%m-%d"))
    |map(attribute='temperature')
    |list
    |max
}}'

Is there a way to add a default()?

Finally, is there any way to make testing faster? What I’m doing is changing my time_pattern to:

- triggers:
    - trigger: time_pattern
      minutes: /1

And then reloading the template YAML. But, then I have to wait for the minute to come around. Would be nice if I could trigger it on a reload of the template YAML.

Update: Oddly, this works in a debug template but not in the template sensor.
In the sensor if I use selectattr('foo') then it throws an exception in the logs but does NOT set the state to unavailable.

      availability: >-
        {{
          response['weather.forecast_home'].forecast[1:6]
            |selectattr('datetime', 'match', utcnow().strftime("%Y-%m-%d"))
            |selectattr('temperature')
            |list
            |count > 0
        }}

Update 2: Oh, needed the defined in the test:

      availability: >-
        {{
          response['weather.forecast_home'].forecast[1:6]
            |selectattr('datetime', 'match', utcnow().strftime("%Y-%m-%d"))
            |selectattr('emperature','defined')
            |list
            |count > 0
        }}

In a debug template it will return False with the dict key changed. Is that a bug?

Thanks,

Do it in the action block.

Also, if you’re just going to join the future values like that, the namespace isn’t necessary. You can just do string manipulation in each loop.


- triggers:
    - trigger: time_pattern
      hours: 5
  action:
    - action: weather.get_forecasts
      data:
        type: hourly
      target:
        entity_id: weather.forecast_home
      response_variable: response
    - variables:
        forecasts: |
          {{ response['weather.forecast_home'].forecast[1:6] 
          | selectattr('datetime', 'match', now().date()|string)|list }}
  sensor:
    - name: "Max Morning Temp"
      unique_id: max_morning_temp
      device_class: temperature
      unit_of_measurement: "°F"
      state: >-
       {{ forecasts | selectattr('temperature', 'defined')
       | map(attribute='temperature') | list | max }}
      attributes:
        future_temps: >-
          {% for item in forecasts -%}
            {{ item.datetime.split('T')[1][:5]~'='~item.temperature ~ '°F' }}{{ ', ' if not loop.last }}
          {%- endfor -%}
      availability: "{{ has_value('weather.forecast_home') }}"

1 Like

Do you have an example? I only see a way to set response_variable in the action.

Thank you. Exactly what I was struggling with. I was looking at configuration variables and was interpreting variables as a separate top-level key, not as a separate action. Now I see what you were saying.

I’m curious why this is used for availability.

Is it that if that has a value then the forecast is available? I was assuming weather.get_forecasts was an API call. Is it just data stored in the integration?

Thanks again. I really appreciate the help.

The availability template is checked before the action is fired. The function has_value() checks that the entity’s state isn’t unknown or unavailable. This prevents your template sensor from getting stuck in an error state in those cases.

It’s pulling the data from the integration. The integration controls when API calls are made… for weather provider integrations it’s usually at restart and at the top of each hour.