Improvement to sensor template with for loop

Good Morning,

I’ve created a trigger template to set a sensor for the max temperature during the day based on attributes from the met office entity. This works fine but I can’t help but think there is a neater way of writing the template which sets the sensor state.

The met office entity is (partial):

My template is:

template:
  - trigger:
      - platform: time_pattern
        # This will update every morning at 06:05
        hours: 06
        minutes: 05
    sensor:
      # Loop the forecasts and if the datetime is less than today at 21:00 get the max temp for those results
      - name: "Max Temp"
        state: >-
          {%- set ns = namespace(temp=0) %} 
          {% for forecast in states.weather.met_office_hunslet_3_hourly.attributes.forecast -%}
            {% if as_timestamp(forecast.datetime) < as_timestamp(today_at("21:00")) %}
              {% if forecast.temperature > ns.temp | int %}
                {%- set ns.temp = forecast.temperature %} 
              {% endif -%}
            {% endif -%}
          {%- endfor %}
          {{ ns.temp }}
        unit_of_measurement: "C"

I’m thinking the state template section could be improved to not use a for loop - this doesn’t work but I was thinking something along the lines:

{{ as_timestamp(state_attr("weather.met_office_hunslet_3_hourly", "forecast") | map(attribute="datetime")) | select("lessthan", as_timestamp(today_at("21:00")) | somehow_get_the_max_temp }}

Maybe it’s not possible, maybe it doesn’t need to be changed but my original solution feels messy.

Thanks,
Chris

Since you’re taking this “snapshot” at the same time everyday, the number of forecasts should be the same every time… so you can use slice to sub divide your list of forecasts so that only the ones you are interested in are in the first grouping, then get the max of those. You will likely need to play with the value of “slice”…

{{ state_attr('weather.met_office_hunslet_3_hourly', 'forecast') | map(attribute='temperature') | slice(4)| list | first | max  }}

@Didgeridrew that wont work because it’s utc compared to now. You’d need to compare in the correct tz.

This is one that compares utc formats to eachother.

{% set forcast = state_attr('weather.met_office_hunslet_3_hourly', 'forecast') | selectattr('datetime', '>=', utcnow().isoformat()) | list | first | default(none) %}
{{ forcast.temp if forcast is not none else none }}

I tested this with my local (hourly) weather and it correctly reported today’s highest temperature (prior to 21:00, inclusively).

{{ state_attr('weather.met_office_hunslet_3_hourly', 'forecast') 
  | selectattr('datetime', '<=', today_at('21:00'))
  | map(attribute='temperature')
  | max }}

I don’t really understand your argument here.

Also, I get an incorrect value when using your template:


Note: The sample in the screenshot was taken at 10am local time, so forecasts are for 09-00:00. If the sensor was triggered at 6:05, the forecasts should be for 06-21:00.

I tried that method first, but it throws a TypeError:

TypeError: '<=' not supported between instances of 'str' and 'datetime.datetime'"

That’s curious. I’m using a vanilla weather entity and its datetime attribute is understood to contain a datetime object.

Screenshot from 2021-11-26 10-17-29

It looks like ‘datetime’ from the NWS is coming through as a string…

Screen Shot 2021-11-26 at 10.21.27 AM

In the second screenshot of my previous post, the value of datetime is also a string. However, the difference is that it’s UTC whereas yours is local time. I think that difference may determine how Home Assistant interprets the value’s type (although I don’t know why because both are convertible to datetime). It’s very clear from your screenshot that the value’s type is preserved as string and not converted to datetime.

FWIW, I’m testing this with the Environment Canada integration.

Scratch that you aren’t using today at, however the slice would need to be adjusted to the correct number in the list based on the tz offset.

Edit: on mobile and it’s being stupid autocorrecting every world. I’ll update when I’m at a computer because I’m sick of autocorrect.

I installed the NWS integration with some random location in the NorthEast and can confirm that its datetime values are not handled as datetime objects. Comparison of the two integrations:

Also the template i provided just gets you the temperature After the supplied time. Changing it to max would be removing the |first and summing the attribute.

For your NWS integration, this should work:

{{ state_attr('weather.kmrb_hourly', 'forecast') 
  | selectattr('datetime', '<=', today_at('21:00').isoformat())
  | map(attribute='temperature')
  | max }}

The isoformat() method produces a string (with a T between date and time) thereby allowing for a comparison of two strings.

The topic’s author won’t need to use isoformat() because the Met Office integration reports datetime as UTC.

2 Likes

Am I understanding that this is a bug in the template helpers that they do not recognize valid isoformatted strings that are not in UTC? Or is it a bug that NWS does not provide it in that format? This should be put as an issue on GitHub either way.

I don’t think there’s any bug. 123 was expecting a datetime object, but the attribute was an isoformat datetime string. That is unless datetime is expected to be a datetime object, then NWS is incorrectly outputting a datetime isoformat string instead of datetime object.

It seems fishy to me that it works when the datetime string is in UTC but not otherwise.

Well it’s because the one attribute has the object vrs the string. Jinja can’t handle both together, it’s one or the other. If all forecast attributes are supposed to be the same across all weather domains, then it’s a bug. But when deserializing the data after a restart, it might be keeping these as strings. IIRC that’s expected behavior. I don’t know if the weather domain serializes and deserializes the states attributes on shutdown/restart in the database.

The datetime for the forecast is supposed to be a string according to https://developers.home-ohassistant.io/docs/core/entity/weather/#forecast.

Something here doesn’t work as expected, I’m trying to understand which piece it is in case I need to fix NWS integration.

Then Environment Canada is doing it incorrectly by keeping them datetime objects instead of datetime strings.

To compare results, I picked Met.no (because it doesn’t need an API key) and it reports in UTC as well:

OP is using met too. Possibly 2 bugs then: non-local for met, and datetimes objs in Env canada.