Jinja2 loop not working?

I am trying to develop a binary sensor that:

  • Searches through an array containing dates (sample to follow)
  • Checks whether each date is today but in the future

Currently the jinja2 for loop iterates through the elements but the ‘flag’ variable never switches to ‘True’.

Please see sample below from the sensor I am trying to parse:

planned_dispatches: [{'start':'2023-11-29T01:30:00.000000+00:00','end':'2023-11-29T02:30:00.000000'}]
completed_dispatches: []
data_last_retrieved: "2023-11-28T22:53:03.198352+00:00"
last_evaluated: "2023-11-28T22:53:03.198984+00:00"
icon: mdi:power-plug-battery
friendly_name: Octopus intelligent dispatching

And here is the jinja2 code I am using, which is not setting the variable/namespace cs_left_today.value to True:

{% set planned_dispatches=state_attr('binary_sensor.octopus_energy_a_14bee0dd_intelligent_dispatching','planned_dispatches') %}
{% set cs_left_today = namespace(value=False) %}
{% if planned_dispatches %}
  {% set now_ts=as_timestamp(now())|int %}
  {% set tomorrow_ts=as_timestamp(now().replace(hour=23, minute=59, second=59))|int+1 %}
  {% for pd in planned_dispatches %}
    {% if pd is set %}
      {% set test_ts=as_timestamp(pd['start'])|int %}
      {% if test_ts >= now_ts and test_ts < tomorrow_ts %}
        {% set cs_left_today.value = True %}
      {% endif %}
    {% endif %}
  {% endfor %}
{% endif %}
{{ cs_left_today.value }}

Can anybody give me a hand with this please?

Use {% if pd is iterable %}

iterable (a value that can be iterated over such as a list , set , string , or generator),

Type Checking

1 Like

Off-topic for the question itself, but this can be done without a loop, if I’ve understood the request correctly. This returns true if there is a start timestamp in that attribute’s list that is in the future and in the current day:

{{ state_attr('binary_sensor.octopus_energy_a_14bee0dd_intelligent_dispatching','planned_dispatches')
   | map(attribute='start')
   | select('>', now().isoformat())
   | select('<', (now()+timedelta(days=1)).isoformat()[:10])
   | list | count > 0 }}

Relies on the time format in the attribute matching the isoformat, which it does on my system in the template editor.

2 Likes

Thanks - I’ll tweak that!

Your jinja2 skill level is way above mine. I will give that a try and it might just be the solution. But I would still like to get the loop working, it’s bugging me.

@pedolsky solved your problem above. This currently returns true with the start at 21:30 today:

{% set planned_dispatches=[{'start':'2023-11-29T21:30:00.000000+00:00','end':'2023-11-29T02:30:00.000000'}] %}
{% set cs_left_today = namespace(value=False) %}
{% if planned_dispatches %}
  {% set now_ts=as_timestamp(now())|int %}
  {% set tomorrow_ts=as_timestamp(now().replace(hour=23, minute=59, second=59))|int+1 %}
  {% for pd in planned_dispatches %}
    {% if pd is iterable %}
      {% set test_ts=as_timestamp(pd['start'])|int %}
      {% if test_ts >= now_ts and test_ts < tomorrow_ts %}
        {% set cs_left_today.value = True %}
      {% endif %}
    {% endif %}
  {% endfor %}
{% endif %}
{{ cs_left_today.value }}
1 Like

I would use datetime instead of timestamps

{% set planned_dispatches=[{'start':'2023-11-29T21:30:00.000000+00:00','end':'2023-11-29T02:30:00.000000'}] %}
{% set cs_left_today = namespace(value=False) %}
{% if planned_dispatches %}
  {% set now_dt = now() %}
  {% set tomorrow_dt = today_at() + timedelta(days=1) %}
  {% for pd in planned_dispatches %}
    {% if pd is mapping %}
      {% set test_dt = as_datetime(pd['start']) %}
      {% if now_dt < test_dt <= tomorrow_dt %}
        {% set cs_left_today.value = True %}
      {% endif %}
    {% endif %}
  {% endfor %}
{% endif %}
{{ cs_left_today.value }}
1 Like

Just keep in mind that your end time doesn’t have a timezone, which will throw off all calculations you use. Especially if you go with datetimes over timestamps (which I also recommend). So when you start incorporating end in other calc’s, you’ll need a timezone.

1 Like

Thanks - I don’t mind playing with timestamps and ensuring that everything is a timestamp seemed feasible, but I agree this solution is more elegant. I will incorporate and report back.

Thanks, though I am not sure I fully understand what you mean.

I can’t do much about the array/list I’m given in the sensor, I believe they have a timezone (+00:00).

Are you suggesting that I ensure the timezones are set for the datetimes I use for comparison - today/now/tomorrow, etc?

The value for end in your example doesn’t have a timezone, so if you also want to start using that, you will get errors because it can’t compare a datetime with a timezone to a datetime which isn’t timezone aware

1 Like

This has a timezone +00:00

This does not

If you use end with | as_datetime, you will not be able to use it in calculations because the code won’t know what timezone the time is in.

1 Like

gotcha - it could have been a copy+paste/retyping error. that said, I can’t control it, but I will check next time the sensor has values.

You can always add the timezone to the string if you know what it is.

1 Like

Just wanted to close this off by saying that I got the sensors working. The main error was bad type checking. The main improvement was using datetime instead of timestamp.

Thanks all!