Template Jinja2: Filter datetime values

Hey community,

I’m currently struggeling with a template sensor, which I’ve created.

The goal is to create a sensor, which contains the earliest alarm of the day. This sensor is used in several automations. In the past, I used this template sensor with a list of alarms provided by an Philips Somneo device and two smartphones. It worked great. But now I’ve added my Galaxy Watch. This next_alarm sensor of the watch works most of the time, but sometimes it responses with times in the past and sometimes with an alarm at noon (I have no clue, where this is comming from).

So I’m trying now to exclude timestamps, which are:

  • In the past
  • Not in the preferred time range between 4 and 9 am.

My current template:

{% set activeAlarms = [states('sensor.wecker_mark_next_alarm')] %}
{% if states('person.mark') == 'home' -%}
  {% set activeAlarms = activeAlarms + [states('sensor.marks_galaxy_next_alarm'), states('sensor.marks_galaxy_watch7_next_alarm')] %}
{%- endif %}
{% if states('person.nicole') == 'home' -%}
  {% set activeAlarms = activeAlarms + [states('sensor.nicoles_pixel_next_alarm')] %}
{%- endif %}
{## Just for testing: Adding two invalid alarms ##}
{% set activeAlarms = activeAlarms + [today_at('12:00') | string] %}
{% set activeAlarms = activeAlarms + [(today_at('07:00') - timedelta(days = 1)) | string] %}
{## End of testing ##}

{## ToDo: Remove alarms in the past + alarms which are not between 4 and 9 am ##}

{{ activeAlarms | sort | first }}

{## For Debugging ##}
{{ activeAlarms }}

Output:

2025-04-16 07:00:00+02:00


['2025-04-18T05:00:00+00:00', 'unavailable', '2025-04-17T10:00:00+00:00', '2025-04-21T03:45:00+00:00', '2025-04-17 12:00:00+02:00', '2025-04-16 07:00:00+02:00']

Do you have any idea how to resolve that? I played around with the reject filter, but that seemed to not work with timestamps.

Thanks in advance!

BR

  • Reject list values that are unavailable or unknown.
  • Convert datetime strings into datetime objects.
  • Convert datetime objects to your local time zone.
  • Reject all datetime objects earlier than today at 04:00.
  • Reject all datetime objects later than today at 09:00.
  • Convert the datetime objects to Unix timestamps.
  • Convert the Unix timestamps to datetime strings (in your preferred format).
  • Sort the list and report its first item.
  • If the result is an empty list, the first filter will fail so supply a default value (an empty string or whatever you prefer).
{{ activeAlarms
  | reject('in', ['unavailable', 'unknown'])
  | map('as_datetime')
  | map('as_local')
  | reject('lt', today_at('04:00'))
  | reject('gt', today_at('09:00'))
  | map('as_timestamp')
  | map('timestamp_custom', '%Y-%m-%d %H:%M:%S')
  | sort | first | default('') }}
2 Likes

Hey,

thanks for your solution! That works!

Is there a documentation for the pipe filters? I don’t find the official website very helpful.

Maybe one other question, is there a way to reject timestamps, that are not in the preferred time range in general? So not related to “today”?

The current issue is, that the template is removing alarms, which are starting the next morning. This is no issue for the automations, but I like to show the next starting alarm in the UI, and currently it’s not showing the alarms of the next morning, since rejects are based on “today”.

You’re welcome!

Please consider marking my post above with the Solution tag. It will automatically place a check-mark next to the topic’s title which indicates to others that the topic has been solved. This helps other users find answers to similar questions.

It’s the primary reference for the Jinja2 language.

Home Assistant’s Templating documentation doesn’t attempt to duplicate it but rather show Home Assistant’s implementation of Jinja2 (such as additional filters and tests).

Having said that, the most practical templating examples will be found in this forum.

You can offset a datetime using timedelta, for example this is 09:00 tomorrow.

{{ today_at('09:00') + timedelta(days=1) }}

If I understood your requirements correctly, you want to select datetimes between 04:00 and 09:00 today and between the same two hours tomorrow. That’s two different time ranges. Is this correct?

Hello,

I have now tried a bit more and unfortunately have not come up with a satisfactory solution. I would prefer it if the next alarm clock was always displayed, even if it is not for another 3 days.
I’ve been watching it for a few days now, it seems that the alarm clock on the Galaxy Watch 7 is always either correct or it shows 12 noon. So I have now helped myself with another IF loop:

{% set activeAlarms = [states('sensor.wecker_mark_next_alarm')] %}
{% if states('person.mark') == 'home' -%}
  {% set activeAlarms = activeAlarms + [states('sensor.marks_galaxy_next_alarm')] %}
  {% if (states('sensor.marks_galaxy_watch7_next_alarm') | as_datetime | as_local != today_at('12:00') | as_datetime | as_local) -%}
    {% set activeAlarms = activeAlarms + [states('sensor.marks_galaxy_watch7_next_alarm')] %}
  {%- endif %}
{%- endif %}
{% if states('person.nicole') == 'home' -%}
  {% set activeAlarms = activeAlarms + [states('sensor.nicoles_pixel_next_alarm')] %}
{%- endif %}
{{ activeAlarms
  | reject('in', ['unavailable', 'unknown'])
  | sort | first | default('')
}}

I will now observe this for a few more days to see if it works.

Another thing that was a bit strange: The template you provided me above (with the rejects) works wonderfully in the developer tools, but strangely enough not as a template sensor. In the template editor in the developer tools the next alarm clock is displayed, in the template sensor it remains empty. I can’t explain what the problem is.

If the template works correctly in the Template Editor but not when used in a Template Sensor then the common source of the problem is usually the Template Sensor’s configuration.

I recommend changing your logic.

{% set t = now() %}
{% set ns = namespace(items=['sensor.wecker_mark_next_alarm']) %}
{% if is_state('person.mark', 'home') -%}
  {% set ns.items = ns.items + ['sensor.marks_galaxy_next_alarm'] %}
  {% if states('sensor.marks_galaxy_watch7_next_alarm') | as_datetime(t) | as_local not in [today_at('12:00'), t] %}
    {% set ns.items = ns.items + ['sensor.marks_galaxy_watch7_next_alarm'] %}
  {% endif %}
{% endif %}
{% if is_state('person.nicole', 'home') -%}
  {% set ns.items = ns.items + ['sensor.nicoles_pixel_next_alarm'] %}
{%- endif %}
{% set items = ns.items | map("states") | select('has_value') | map('as_datetime') | map('as_local') | sort %}
{% set today = items | reject('<', today_at('04:00')) | reject('>', today_at('09:00')) | first | default %}
{{ today if today else items | reject('<', today_at() + timedelta(days=1)) | first | default(today_at() - timedelta(days=1)) }}

It will default to midnight yesterday if no alarms are set so you should never get a false alarm. Otherwise it will chose the next alarm within the time window or the next alarm beyond midnight tomorrow that is closest.

There is an issue in this line:

AttributeError: 'NoneType' object has no attribute 'tzinfo'

Do I maybe have to extract the state first? Because currently only the ID of the entity is stored in the array, not the state.

Forgot a | map("states") after ns.items

The mapping with “has_value” is currently removing all entries from the array. Do you have any idea why that happens?

Items before: {{ ns.items | map("states") | list }}
{% set items = ns.items | map("states") | select('has_value') | map('as_datetime') | map('as_local') | sort %}
Items after: {{ items }}

Items before: ['2025-04-25T05:00:00+00:00', 'unavailable', '2025-04-28T04:40:00+00:00']

Items after: []

Sorry, reverse has value and states in the order