Helper Timer remaining not counting down

Hi,

When I create a timer via a helper and start it, the remaining-var value doesn’t count down. This causes the value in the type: custom:button-card label to not count down.
Is this a bug, or is there another field I can display in the label?

Installation method Home Assistant Core
Core 2025.10.2
Frontend 20251001.2

duration: "0:10:00"
editable: true
finishes_at: "2025-10-14T19:47:02+00:00"
remaining: "0:10:00"
friendly_name: test_timer

This is by design. The UI uses finishes_at to show the countdown on the screen.

Thanks for the quick response.
Then the naming isn’t clear. I also read on Git that someone had asked this question.

Do you know if there’s an easy way to see the timer count down in the button (label)?
Without having it calculated into a value via a script and an input_text?

There’s no easy way, dashboards in general do not handle something updating frequently. There are typically dedicated workers that handle time and time updates in UI frameworks to keep the overhead low.

I’ve now cobbled this together.
The timer now counts down in the button :slight_smile:

Automation:

alias: Update Air Extraction 2Go every second
description: Update Air Extraction 2Go
triggers:
  - seconds: /1
    trigger: time_pattern
conditions:
  - condition: state
    entity_id: timer.afzuiging_timer
    state: active
actions:
  - target:
      entity_id: input_text.airextraction2go
    data:
      value: >
        {% set timer = states.timer.afzuiging_timer %} {% if timer.state
        == 'active' %}
          {% set finish = as_datetime(timer.attributes.finishes_at) %}
          {% set diff = (finish - now()).total_seconds() %}
          {% if diff > 0 %}
            {{ '%02d:%02d:%02d' | format((diff // 3600) | int, ((diff % 3600) // 60) | int, (diff % 60) | int) }}
          {% else %}
            00:00:00
          {% endif %}
        {% else %}
          Inactief
        {% endif %}
    action: input_text.set_value
mode: restart




Button:

    - type: custom:button-card
      show_label: true
      name: Airextraction
      label: |
        [[[
          return states['input_text.airextraction2go'].state;
        ]]]
      show_state: false




Helper:
input_text.airextraction2go

Yep, and now you’re adding 86400 pieces of text to your database every day instead of just using a card that properly displays the countdown through normal methods.

What is the normal method like then?

When I had made everything in bash, before I was persuaded to switch to Home-Assistant, I did not have to write 86400 entries per day into the database for a couter in HTML5.

How can I make a counter visible in Home-Assistant when it has started and is visibly counting down. So that I know the counter is running and can see how long it has left?

Is there a reason you can’t just put the timer entity in the ui? That behaves exactly how you’d expect. Otherwise you can take the finishes_at attribute and put it in a template sensors state and make that a timestamp entity. That will also update properly.

The issue is that you’re using a card, custom button card, that does not support the feature you’re looking for. And that’s causing your problems.

Never thought about it, so that’s possible?
Do you have a small example for that?

You’d use this template in a template sensor

{{ state_attr('timer.afzuiging_timer', 'finishes_at') }}

with an availabilty template

{{ state_attr('timer.afzuiging_timer', 'finishes_at') is not none }}

and choose timestamp as the device class

What’s a template sensor? Do I need to edit: configuration.yaml ?

If I add this card I see the countdown in realtime :slight_smile:
image

type: tile
entity: timer.water_afzuiging_timer
features_position: bottom
vertical: false
grid_options:
  rows: 1

It’s a helper, you can make it in the ui

Not in label but you can have it displayed as state (timer needs to be set as button’s entity). You can set the position of various fields in the button (e.g., state/name/label) and do other things like change colors or override the state display when the timer is idle.

type: custom:button-card
entity: timer.test
show_state: true
show_icon: false
state:
  - value: idle
    state_display: n/a
  - operator: default
    styles:
      state:
        - color: green
name: "Time remaining:"
styles:
  grid:
    - justify-content: start
    - grid-template-areas: '"n s"'
    - grid-template-columns: repeat(2,min-content)
  name:
    - margin: 0px 8px
tap_action:
  action: perform-action
  perform_action: "[[[ return entity.state == 'idle' ? 'timer.start' : 'timer.cancel' ]]]"
  target:
    entity_id: "[[[ return entity.entity_id ]]]"

obraz

Thanks everyone for the input :+1:

I wasn’t entirely sure about the “tap_action:” part. I wanted to reset the timer (when it’s running) and turn off the extractor fan relay.

image

image


    - type: custom:button-card
      entity: timer.extraction_timer
      show_state: true
      show_icon: true
      show_name: true
      icon: mdi:fan
      state_display: |
        [[[
          const timer = states['timer.extraction_timer'];
          const e = 'switch.extraction_relais_switch_home_assistant';
          if (timer.state === 'idle') {
            return states[e].state === 'on' ? 'On' : 'Off';
          } else {
            return '';
          }
        ]]]
      state:
        - operator: template
          value: >
            [[[ return states['timer.extraction_timer'].state !== 'idle';
            ]]]
          icon: mdi:air-conditioner
          color: orange
        - operator: template
          value: >-
            [[[ return
            states['switch.extraction_relais_switch_home_assistant'].state
            === 'on' ]]]
          styles:
            icon:
              - color: red
        - operator: default
          styles:
            icon:
              - color: green
      name: Airextraction
      styles:
        card:
          - height: 80px
          - width: 90px
          - background-color: rgb(33,38,40)
          - border: 1px solid rgb(80,80,80)
          - border-radius: 10%
          - font-family: sans-serif
          - padding: 2%
          - margin: "-6px 0px 0px -6px"
          - color: rgb( 170, 183, 184 )
          - font-size: 14px
        grid:
          - justify-content: start
          - grid-template-areas: "I n s"
      tap_action:
        action: fire-dom-event
        browser_mod:
          service: |
            [[[ 
              const e = 'switch.extraction_relais_switch_home_assistant';
              const on = states[e].state === 'on';
              hass.callService('switch', on ? 'turn_off' : 'turn_on', { entity_id: e });
              hass.callService('timer', 'finish', { entity_id: 'timer.extraction_timer' });
            ]]]

Nice! The latest versions of custom buttons (I think 5+) have a special action type for javascript actions, so it’s better to use that.
You can also make the timer only reset when the switching action is successful. hass.callService() is actually asynchronous and you can make use of that.

tap_action:
  action: javascript
  javascript: |
    [[[
      const e = 'switch.dummy';
      const on = states[e].state === 'on';
      hass.callService('switch', on ? 'turn_off' : 'turn_on', { entity_id: e })
      .then(_ => {
        if (on)
          hass.callService('timer', 'finish', { entity_id: entity.entity_id })
      }).catch(error => {
        /* do something in case of an error; it will show up in logs anyway */
      });
    ]]]