Display time difference in custom:button-card

Hi everyone,

i want to display the time until my dishwasher is finished in a nice way.
For that I display a gif as “background-image” in a button card and the progress in percent over the grid system.
Now I want to display the time until it is finished. For that the dishwasher reports a Datetime when it is approximately finished which get’s nicely presented by a entites card in the form of “19 minutes remaining” or something like this.
Currently the button-card is just displaying the State of the finish time entity (I have replaced it with a temporary input_datetime so the dishwasher doesn’t need to run all the time for testing):

Capture

The Code:

type: custom:button-card
styles:
  card:
    - border-radius: 10%
    - height: 250px
  custom_fields:
    progress:
      - text-align: left
    duration:
      - text-align: left
  grid:
    - grid-template-areas: '"." "progress" "duration"'
    - grid-template-columns: 100%
extra_styles: |
  .button-card-main {
    background-image: url("https://laughingsquid.com/wp-content/uploads/2021/04/Dishwasher-Interior-View-Dispensing-Soap.gif")
  }
custom_fields:
  progress: >
    [[[ return '<span style="color: white; font-size: 40px; margin-left: 10px">'
    + states['sensor.dishwasher_program_progress'].state + '%</span>' ]]]
  duration: |
    [[[ 
      return '<span style="color: white; font-size: 40px; margin-left: 10px">'
    + states['input_datetime.dt_test'].state +'</span>' 
    ]]]

Of course I could write a JS function in the “duration” custom field to calculate the difference between now() and the finishing time, extracting hours/minutes/seconds and then compile a string which gets displayed.
My thought is: Could you reuse the Home Assistant function that does the conversion and is the “custom_fields” section of the button-card really the right location for this “big” JS function?

I think you are asking how to display the time left.
Here are two examples of how I am utilizing time. Below is the completion time of a print which is reported to HA in seconds left. The second is a reader friendly time left.
You can ignore the availability template in this case.
The “Intentionally Blank” covers prints completed otherwise it would always display the current time if completed since it would be 0 seconds from now.

        prusa_connect_time_done:
          friendly_name: "Estimated Completion Time"
          value_template: >-
            {% if states.sensor.prusa_connect.attributes["time_est"] is defined %}
              {{ (as_timestamp(now()) + states('sensor.prusa_connect_time_est') | float) | timestamp_custom('%H:%M')}}
            {% else %}
              {# Intentionally Blank #}
            {% endif %}
          icon_template: mdi:clock
          availability_template: '{{ not is_state("sensor.prusa_connect", "unavailable") }}'
        prusa_connect_time_countdown:
          friendly_name: "Estimated Completion Countdown"
          value_template: >-
            {% set etime = states.sensor.prusa_connect.attributes["time_est"] | int %}
            {% set seconds = etime % 60 %}
            {% set minutes = ((etime % 3600) / 60) | int %}
            {% set hours = ((etime % 86400) / 3600) | int %}
            {% set days = (etime / 86400) | int %}
            {% if days > 0 %}{{ days }}d {% endif %}{% if hours > 0 %}{{ hours }}h {% endif %}{% if minutes > 0 %}{{ minutes }}m{% endif %}
          icon_template: mdi:timer-sand
          availability_template: '{{ not is_state("sensor.prusa_connect", "unavailable") }}'