Sorting template output for Markdown Card

Hello to All,
Longtime Lurker here
I have problem
I search and closest I find is - Template: how to sort by attribute and treat as int - but no answer given

Reading Jinja 2 documention is confusing and I try many options, none seem to work for me
(sorry if I miss something obvious)

I have set up different events, each has an input_datetime (date only) and I do corresponding sensor to give me countdown to that event (examples of entities) : -

input_datetime:
  cal_date_aniv:
    name: Aniversary Next
    has_date: true
    icon: mdi:heart

sensor:
  - platform: template
    sensors:
      cal_cd_aniv:
        icon_template: "{{ 'mdi:heart' if is_state('binary_sensor.cal_alrt_aniv', 'on') else 'mdi:heart-outline' }}"
        value_template: >
          {% set today = as_timestamp(states('sensor.date')) %}
          {% set next_date = as_timestamp(states('input_datetime.cal_date_aniv')) %}
          {% set delta = ((next_date - today) / 86400) | int(0) %}
          {{ delta }}
        friendly_name: Aniversary Countdown

The above gives me a number eg 23 and that’s okay but I could change output to {{ ('%03d' % delta) }} if that helps as easier to sort as text so ‘023’
I have card to show events and countdowns but card very big and most important - Not Sorted so you have to scan list (I have other sensors for next event and what that event is but very difficult to see the 2nd next event etc.)
So the makdown card allows more compact output but needs sorting (text or integer - don’t care)
Current markdown is : -

      ### Event List
      - type: markdown
        title: Event Countdown Short
        content: |
          {%- for item in states.sensor if 'cal_cd_' in item.entity_id %}
          {{ ('%03d' % item.state | int(0) ~ '   -   ' ~ item.name) | replace(' Countdown', '') -}}
          {% endfor %}

This produces : -
image
The " Countdown" appears in ‘some’ friendly names due to context but is not consistent

I also played around with {{ ('% 5d' % item.state | int(0) ~ ' - ' ~ item.name) | replace(' Countdown', '') -}} Which gives pleasing results in template editor but is completely ignored in markdown card

So is it possible to sort this output into a readable list ?
Sorry if I not explain it clearly

Take a look at this post from @123, I think it will get you there

Copy-paste this into a Markdown card and experiment with it:

type: markdown
content: |
  |Days||Name|
  |----:|--|:----|
  {% for item in states.sensor | selectattr('object_id', 'match', 'cal_cd_') | sort(attribute='state') -%}
  |{{item.state}}||{{item.name}}|
  {% endfor %}

The caveat is that this performs a string sort so the order of the numeric results might not be exactly what you want; let me know how it looks.

Hi,
Thanks for response but that was the topic that inspired this Markdown, I tried many options but not found good solution.
Cryptic

Hi 123,
As the state comes back text then when sorted order comes out as : -

113 blarb
17 blarb
175 blarb
182 blarb

Now I realise that Outputting the sensors as {{ ('%03d' % item.state | int(0) ~ ' - ' ~ item.name) | replace(' Countdown', '') -}} will allow this sort to work but if a number sort would work it be better readable.
Thanks for your response anyway

Edit: if I do the %03d I suppose I could replace the '0’s in the output with nbsp or something but then 30 would come out as 3 ???

That’s what I suspected would happen because an entity’s state value is a string. When you sort numeric strings you don’t get the same ordering as when you sort numbers.

In the linked example, the sorting is performed on the entity’s attribute which is not limited to storing values as a string but can be other types like datetime, int, float, boolean, etc. That makes it more flexible for sorting purposes.

Sorry, what example ?
Can I add an attribute to the sensor with the same value but stored as int ? and then sort on that ?

The linked example in cyn’s post above (the one that you said served as inspiration for using a Markdown card).

Yes, you can enhance your Template Sensor so that it also stores the number of days as an integer value in an attribute. If the new attribute’s name is days then the template can be changed to this:

type: markdown
content: |
  |Days||Name|
  |----:|--|:----|
  {% for item in states.sensor | selectattr('object_id', 'match', 'cal_cd_') | sort(attribute='attributes.days') -%}
  |{{item.state}}||{{item.name}}|
  {% endfor %}

If you are interested, you can simplify the template used to compute the number of days remaining until the event’s date. You can use either of these two templates (your choice):

{{ ((state_attr('input_datetime.cal_date_aniv','timestamp') - now().timestamp()) / 86400) | int(0) }}
{{ (states('input_datetime.cal_date_aniv') | as_datetime | as_local - now()).days }}

Ah Ha !
No - I think I got that
The bad bit for me was adding the attribute, so I’m away to experiment it.
I went to : - Template - Home Assistant
and : -


(Sorry, don’t know how to point at specific part of page half down, same below)
And : -

So I’ll change : -

sensor:
  - platform: template
    sensors:
      cal_cd_aniv:
        icon_template: "{{ 'mdi:heart' if is_state('binary_sensor.cal_alrt_aniv', 'on') else 'mdi:heart-outline' }}"
        value_template: >
          {% set today = as_timestamp(states('sensor.date')) %}
          {% set next_date = as_timestamp(states('input_datetime.cal_date_aniv')) %}
          {% set delta = ((next_date - today) / 86400) | int(0) %}
          {{ delta }}
        friendly_name: Aniversary Countdown

To : -

sensor:
  - platform: template
    sensors:
      cal_cd_aniv:
        icon_template: "{{ 'mdi:heart' if is_state('binary_sensor.cal_alrt_aniv', 'on') else 'mdi:heart-outline' }}"
        value_template: >
          {% set today = as_timestamp(states('sensor.date')) %}
          {% set next_date = as_timestamp(states('input_datetime.cal_date_aniv')) %}
          {% set delta = ((next_date - today) / 86400) | int(0) %}
          {{ delta }}
        attributes:
          integer: >
            {% set today = as_timestamp(states('sensor.date')) %}
            {% set next_date = as_timestamp(states('input_datetime.cal_date_aniv')) %}
            {% set delta = ((next_date - today) / 86400) | int(0) %}
            {{ delta }}
        friendly_name: Aniversary Countdown

You last point about simplify sensor template - yours is based on now() which update every minute, the one I use only updates 1 per day

I experiment and report back
Many Many Thanks

This version eliminates the use of now().

{{ ((state_attr('input_datetime.cal_date_aniv','timestamp') - states('sensor.date') | as_timestamp) / 86400) | int(0) }}

It helps to reduce overall complexity:

sensor:
  - platform: template
    sensors:
      cal_cd_aniv:
        icon_template: "{{ 'mdi:heart' if is_state('binary_sensor.cal_alrt_aniv', 'on') else 'mdi:heart-outline' }}"
        value_template: >
          {{ ((state_attr('input_datetime.cal_date_aniv','timestamp') - states('sensor.date') | as_timestamp) / 86400) | int(0) }}
        attribute_templates:
          days: >
            {{ ((state_attr('input_datetime.cal_date_aniv','timestamp') - states('sensor.date') | as_timestamp) / 86400) | int(0) }}
        friendly_name: Aniversary Countdown

Plus Markdown card’s template, for completeness:

type: markdown
content: |
  |Days||Name|
  |----:|--|:----|
  {% for item in states.sensor | selectattr('object_id', 'match', 'cal_cd_') | sort(attribute='attributes.days') -%}
  |{{item.state}}||{{item.name}}|
  {% endfor %}
1 Like

So when I do edit for sensor I think like I do icon and use own value : -

sensor:
  - platform: template
    sensors:
      cal_cd_xmas:
        icon_template: "{{ 'mdi:pine-tree-box' if is_state('binary_sensor.bs_cal_alrt_xmas', 'on') else 'mdi:pine-tree' }}"
        value_template: >
          {% set today = as_timestamp(states('sensor.date')) %}
          {% set next_date = as_timestamp(states('input_datetime.cal_date_xmas')) %}
          {% set delta = ((next_date - today) / 86400) | int(0) %}
          {{ delta }}
        attributes:
          integer: "{{ states('sensor.cal_cd_xmas') | int(0) }}"
        friendly_name: Christmas Countdown

But Config check not like it
I also try : -

        attributes:
          integer: "{{ states('sensor.s_cal_cd_xmas') | int }}"

        attribute:
          integer: "{{ states('sensor.s_cal_cd_xmas') | int }}"

        attribute_templates:
          integer:
            attribute: template
            integer: "{{ states('sensor.s_cal_cd_xmas') | int }}"

I do not know correct format it look for.
Docs have been cut down to make smaller at sacrifice of clarity
:cry:

You’re using the legacy style of defining a Template Sensor, not the modern style. In the legacy style, the option is called attribute_templates whereas in modern style it’s attributes. Look at the example I posted above.

I had a play and it works

Fantastic

I must have missed your edit to the solution post, I see that you included the necessary - sorry

The final version I went with was : -

      cal_cd_aniv:
        icon_template: "{{ 'mdi:heart' if is_state('binary_sensor.cal_alrt_aniv', 'on') else 'mdi:heart-outline' }}"
        value_template: >
          {% set today = as_timestamp(states('sensor.date')) %}
          {% set next_date = as_timestamp(states('input_datetime._cal_date_aniv')) %}
          {% set delta = ((next_date - today) / 86400) | int(0) %}
          {{ delta }}
        attribute_templates:
          days: >
            {{ states('sensor.cal_cd_aniv') | int(0) }}
        friendly_name: Aniversary Countdown

      ### Event List
      - type: markdown
        title: Event Countdown
        content: |
          |**Days**||**Event**|
          |----:|--|:----|
          {% for item in states.sensor | selectattr('object_id', 'match', 'cal_cd_') | sort(attribute='attributes.days') -%}
          |{{item.state}}||{{item.name | replace(' Countdown', '')}}|
          {% endfor %}

          'Other' Event : {{ states('input_text.cal_title_other') }}

Following above -

  1. I have a sensor that is ‘spare’ it has text to lable it - this is what the dynamic key at bottom displays. (I will later adjust template to give text in that case ‘sensor.cal_cd_other’ )
  2. Will also go through and adjust sensors as you gave me
  3. The Tab effect is very good - I tried just to remove the headings and keep tabs but then whole effect ruined. So went back
  4. It is still VERY GOOD
    :grinning: :grinning: :grinning:

Many, Many, Many, Many Thanks

Cryptic

Post Script Edit :

          |**Days**||**Evemt**|
          |----:|--|:----|
          {% for item in states.sensor | selectattr('object_id', 'match', 'cal_cd_') | sort(attribute='attributes.days') -%}
          |{{item.state}}||{{ states('input_text.cal_title_other') if 'Other' in item.name else (item.name | replace(' Countdown', '')) }}|
          {% endfor %}

This got rid of the need for 'Key’