Stopwatch with start/stop/resume, lap and reset

Of course, if you have several automations related to the same application, like in this case, you can use id’s to use a single automation for all the triggers and actions:

description: "Clothes dryer stopwatch automation"
mode: single
trigger:
  - platform: state
    entity_id:
      - binary_sensor.clothes_dryer
    from: "off"
    to: "on"
    id: start
  - platform: state
    entity_id:
      - binary_sensor.anticrease_cycle
    from: "off"
    to: "on"
    id: anti-crease_cycle
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id: start
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.start_stopwatch
      - conditions:
          - condition: trigger
            id: anti-crease_cycle
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
            entity_id: input_boolean.lap_stopwatch

@miguelpucela Thank you very much - I should be able to put something together with a bit of trial and error and get the state generating the relevant time periods, so that I can display them in my dashboard while the machine is running. Looks like I will need a couple of additional sensors than what I have already set up but it sounds like it’s going to work. Once I’ve put it together and tested, I’ll post back here in case the code is helpful for anyone wanting to use the stopwatch in a similar way :slight_smile:

1 Like

It’ll be great if you come back and share your code/final setup.

Just wanted to share an alternate solution. I wanted a timer for the office showing how stale the current pot of coffee is. I basically just needed a label showing the elapsed time. It would be reset automatically with code when a fresh pot of coffee was ready, but I also wanted to be able to start/stop/reset manually just in case. This is how I ended up doing it, crude but works for me.

  1. Create a helper of type number and call it “stopwatch”. I set max value to 1440 (24 hours)
  2. Create an automation called “increment stopwatch”
    • trigger: time pattern, minutes field set to /1 (trigger once per minute)
    • action: Call Service - Input number: Increment, select stopwatch as entity

3a. If you only want to display the current timer on a dashboard, you can now just create a basic sensor card, add the stopwatch entity and you have a timer on your dashboard. Then just use whatever automation you want to reset the timer by using Call Service: input_number.set_value, and set it to zero.

Done! If you want an interactive control you can tap to start/stop/reset read on.

3b. I personally wanted to format into HH:MM plus give it some interactivity, and already had the HACS button-card frontend integration. So I created a manual card and put in the code below.

type: custom:button-card
entity: input_number.stopwatch
show_icon: false
show_name: false
show_state: false
show_label: true
label: |
  [[[ 
    function minutesToTime(mins) {
      let hours = Math.floor((mins % (60 * 60)) / 60);
      let minutes = Math.floor(mins % 60);

      return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
    }

    let minutes = parseInt(entity.state);

    return minutesToTime(minutes);
  ]]]
tap_action:
  action: call-service
  service: >
    [[[ return states['automation.stopwatch_increment'].state === 'on' ?
    'automation.turn_off' : 'automation.turn_on'; ]]]
  service_data:
    entity_id: automation.stopwatch_increment
hold_action:
  action: call-service
  service: input_number.set_value
  service_data:
    entity_id: input_number.stopwatch
    value: 0

Now I have a timer showing hours and minutes. Clicking/Tapping it will stop/start (enable/disable the automation) and holding down will set the timer number helper to zero.

NOTE: This will puke all over the Home Assistant Logbook so if you need it for troubleshooting anything else in HA you need to filter out these events. I have found no way of changing the logging level for an individual automation, if anyone knows how do let me know.

1 Like

Is there anyone that have done a very simple version of the stopwatch were you just push a button (ZigBee) to start it and when you push the button again you stop the stopwatch. Minutes and seconds just. I was also planing that it could be read up on a media player with the time.
My daughter is training math and she is clocking here self and always asking me to take the time and I was thinking that HA could do that and I also get a log on it…

You can set precission as you want, as explained here:

You can create automations with the button press as trigger and the stopwatch to start/stop/reset depending on the precious state and the button pressings. Similar to these:

Hi! Thank you for sharing your code. It is great! I am trying to time the ON time for my garden pump. I would create two timers - one would be reset every 24h and the other would never be reset.

Since I will not need the tic tac timer and the lap timer, I was trying to remove that part of the code. I am relatively new to this and I am not sure if I did it correctly. I actually do not even need any buttons since the start, pause and reset of the stopwatch would be handled by simple automations.

Does the below make sense? I am asking because when I start the timer, I do not see the status updating. It only shows me the current stopwatch time when I pause it.

input_boolean:
  start_stopwatch:
    # It triggers stopwatch to start/stop(pause)
    name: Start/Stop Stopwatch
#    initial: off
  reset_stopwatch:
    # It triggers stopwatch to reset
    name: Reset
#    initial: off
  tictac_stopwatch:

template:
  - trigger:
    # Stopwatch sensor with Start, Stop/Pause, Reset and Lap features. Hundreds of second precission
    - platform: state
      entity_id: input_boolean.start_stopwatch
      from: "off"
      to: "on"
    - platform: state
      entity_id: input_boolean.start_stopwatch
      from: "on"
      to: "off"
    - platform: state
      entity_id: input_boolean.reset_stopwatch
      from: "off"
      to: "on"

    sensor:
      - name: "Stopwatch"
        state: >-
          {% if is_state('input_boolean.reset_stopwatch','on') %}
              {{ '00:00:00' }}
          {% elif  is_state('input_boolean.start_stopwatch','off') and is_state('input_boolean.lap_stopwatch','off') %}
              {% set value = as_timestamp(now()) - state_attr('sensor.stopwatch','initial_time') + state_attr('sensor.stopwatch','elapsed_time') %}
              {{ value|float|timestamp_custom("%H:%M:%S", False) + '.' + ((value|float*100)%100)|round(0)|string }}
          {% elif  is_state_attr('sensor.stopwatch','running','on') %}
              {% set value = as_timestamp(now()) - state_attr('sensor.stopwatch','initial_time') + state_attr('sensor.stopwatch','elapsed_time') %}
              {{ value|float|timestamp_custom("%H:%M:%S", False) |string }}
          {% else %}
              {{ states('sensor.stopwatch') }}
          {% endif %}
        icon: mdi:timer
        attributes:
          initial_time: >-
            {% if is_state('input_boolean.start_stopwatch', 'on') and is_state_attr('sensor.stopwatch','running','off') %}
              {{ as_timestamp(now()) }}
            {% else %}
              {{ state_attr('sensor.stopwatch','initial_time') }}
            {% endif %}
          elapsed_time: >-
            {% if is_state('input_boolean.reset_stopwatch','on') %}
              {{ 0 }}
            {% elif is_state('input_boolean.start_stopwatch','off') and is_state('input_boolean.lap_stopwatch','off') %}
              {{ as_timestamp(now()) - state_attr('sensor.stopwatch','initial_time') + state_attr('sensor.stopwatch','elapsed_time') }}
            {% else %}
              {{ state_attr('sensor.stopwatch','elapsed_time') }}
            {% endif %}
          running: >-
            {{ states('input_boolean.start_stopwatch') }}
          laps: >-
            {% if is_state('input_boolean.reset_stopwatch','on') %}
              {{[]}}
            {% elif is_state('input_boolean.lap_stopwatch','on') and is_state_attr('sensor.stopwatch','running','on') %}
              {% set data = namespace(laps=state_attr('sensor.stopwatch','laps')) %}
              {% set value = as_timestamp(now()) - state_attr('sensor.stopwatch','initial_time') + state_attr('sensor.stopwatch','elapsed_time') %}
              {% set data.laps = (data.laps + [value|float|timestamp_custom("%H:%M:%S", False) + '.' + ((value|float*100)%100)|round(0)|string]) %}
              {{ data.laps }}
            {% else %}
              {{ state_attr('sensor.stopwatch','laps')}}
            {% endif %}  

  # Start/Stop(Pause) button
  - button:
    - unique_id: 'start_stop_stopwatch'
      name: >-
        {% if is_state('input_boolean.start_stopwatch','off') %}
          {% if is_state('sensor.stopwatch','00:00:00') %}
            Start
          {% else %}
            Resume
          {% endif %}
        {% else %}
          Stop/Pause
        {% endif %}
      icon: >-
        {% if states('input_boolean.start_stopwatch') == 'off' %}
          mdi:play-circle-outline
        {% else %}
          mdi:stop-circle-outline
        {% endif %}
      press:
        service: input_boolean.toggle
        target:
          entity_id: input_boolean.start_stopwatch

automation:

  - id: reset_stopwatch
    alias: "Reset Stopwatch"
    description: "It reset input_booleans when input_boolean.reset_stopwatch is set to on"
    trigger:
      - platform: state
        entity_id: input_boolean.reset_stopwatch
        from: "off"
        to: "on"
    action:
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.start_stopwatch
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.tictac_stopwatch
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.reset_stopwatch
    mode: single

You have to include all the tictac stuff, which is in charge of updating the status.

So, you should keep:

  • input_boolean.tictac_stopwatch including the name TicTac.
  • Trigger wtih input_boolean.tictac_stopwatch in sensor.Stopwatch.
  • Complete code of sensor.tictac_stopwatch.
  • Complete automation.tic_tac_stopwatch.

I hope this helps.

Thank you for your reply. I realised that you mentioned this in the description or in a reply to someone a bit too late - sorry!

Very good, this is exactly what I was looking for!
The code is easy to understand and new instances of the StopWatch can be created with one click using “Search and Replace”.
Cool implementation, thanks!

1 Like

Great to hear your feedback.

Thank you for trying!

Hi! I am trying to create a second stopwatch. I followed your instructions and almost everything else. What does not work is just the visual part of when you toggle the start/stop button.

The code refers to the button.start entity. When is this one created? I need a separate one for the second timer. When I use the “input_boolean.start_stopwatch2” instead, the button works but the icon toggling does not. I suppose this is a very basic question but I do not know how to solve this.

So I am wondering when and how the button.start entity is created, why this one is used even though it is not referred to in the stopwatch.yaml and for resetting the code refers to “input_boolean.reset_stopwatch” and how I can create a new one for the stopwatch2, stopwatch3 and so on. Do I need to add a helper and an automation manually or is there an easier way to do it when creating a new stopwatch?

I would really appreciate your help. Thank you!

Hello,

I’m looking at the entities created and you’re right, I create a button with unique_id start_stop_stopwatch, but the name of the button afterwards is button.start which is one of the names given to the button depending it’s state, but afterwards changing the name really changes its friendly_name, but not its name.

I had to assign a unique_id for the button to work, as you can see in this thread:
https://community.home-assistant.io/t/template-button/443821/6

Can you try to change the name of the button of the second stopwatch and test if this works? (Now I cannot test it).

Try something like this:

 - button:
    - unique_id: 'start_stop_stopwatch2'
      name: >-
        {% if is_state('input_boolean.start_stopwatch2','off') %}
          {% if is_state('sensor.stopwatch2','00:00:00') %}
            Start2
          {% else %}
            Resume2
          {% endif %}
        {% else %}
          Stop/Pause2
        {% endif %}
      icon: >-
        {% if states('input_boolean.start_stopwatch2') == 'off' %}
          mdi:play-circle-outline
        {% else %}
          mdi:stop-circle-outline
        {% endif %}
      press:
        service: input_boolean.toggle
        target:
          entity_id: input_boolean.start_stopwatch2

Or maybe you can remove the name part of the button code and check the name given to the new button.

I hope this helps.

I’ve asked the question about template buttons in Configuration:

https://community.home-assistant.io/t/template-button-name/586900

Hi, thank you for your reply!

I changed the name of the button from the first time I started as you suggested above. I changed all the entity names to stopwatch2 at the end.
It is a bit strange because I can see a button.resume entity, not button.start as you wrote. At the same time, when I wrote my previous post, I am pretty sure that I could see a button.start entity since that’s why I wrote. What is stranger is that there is no button.start_stop_stopwatch2 or as a matter of fact, there are no other button entities related to the stopwatch at all. Just one.

In the code for the card, in the section for the button I have:

    cards:
      - show_name: true
        show_icon: true
        type: button
        tap_action:
          action: toggle
        entity: input_boolean.start_stopwatch2
        name: Start/Stop daily stopwatch
        show_state: false

As I wrote before, the functionality is there after I changed the code to: input_boolean.start_stopwatch2 but the icon does not change with the state. Or to be precise it does but from mdi:stop-circle-outline to a circled tick icon or however I should call it, instead of changing from mdi:play-circle-outline to mdi:stop-circle-outline.
The only difference between the stopwatch and stopwatch2 code for the card is that the original one refers to button.start while the second one refers to input_boolean.start_stopwatch2.
When I change the button.start or now in my case button.resume to input_boolean.start_stopwatch (without the “2”) the first stopwatch also switches from the correct icons to the wrong ones.

Same behaviour here…

What I don’t know is whether it’s due to any change in latest versions of HA…

Let’s wait for an answer in the other thread.

Hello krkr,

I’ve created a new card using Mushroom integration (you can install it through HACS). You won’t need the button section of the stopwatch code, so you can safely remove and create several stopwatches without problems (I hope).

Here it is the card code:

type: vertical-stack
cards:
  - type: entity
    entity: sensor.stopwatch
  - type: horizontal-stack
    cards:
      - type: custom:mushroom-template-card
        primary: |-
          {% if is_state('input_boolean.start_stopwatch','off') %}
            {% if is_state('sensor.stopwatch','00:00:00') %}
              Start
            {% else %}
              Resume
            {% endif %}
          {% else %}
            Pause/Stop
          {% endif %}
        secondary: ''
        icon: |-
          {% if is_state('input_boolean.start_stopwatch','off') %}
            {% if is_state('sensor.stopwatch','00:00:00') %}
              mdi:play-circle-outline
            {% else %}
              mdi:pause-circle-outline
            {% endif %}
          {% else %}
            mdi:stop-circle-outline
          {% endif %}
        entity: input_boolean.start_stopwatch
        icon_color: |
          {% if is_state('input_boolean.start_stopwatch','off') %}
            {% if is_state('sensor.stopwatch','00:00:00') %}
              green
            {% else %}
              orange
            {% endif %}
          {% else %}
              red
          {% endif %}
        hold_action:
          action: none
        double_tap_action:
          action: none
      - type: custom:mushroom-template-card
        primary: Lap
        secondary: ''
        icon: mdi:camera-outline
        entity: input_boolean.lap_stopwatch
        icon_color: |-
          {% if is_state('input_boolean.start_stopwatch','off') %}
            grey
          {% else %}
            blue
          {% endif %}
        hold_action:
          action: none
        double_tap_action:
          action: none
      - type: custom:mushroom-template-card
        primary: Reset
        secondary: ''
        icon: mdi:close-circle-outline
        entity: input_boolean.reset_stopwatch
        icon_color: |-
          {% if is_state('input_boolean.start_stopwatch','off') %}
            {% if is_state('sensor.stopwatch','00:00:00') %}
              grey
            {% else %}
              red
            {% endif %}
          {% else %}
            red
          {% endif %}
        hold_action:
          action: none
        double_tap_action:
          action: none
  - type: horizontal-stack
    cards:
      - type: markdown
        content: |-
          **Laps:**
          {% for i in range(state_attr('sensor.stopwatch','laps')|length) %}
            {{ (i+1)|string + ': ' + state_attr('sensor.stopwatch','laps')[i]}}
          {% endfor %}
      - type: gauge
        entity: sensor.template_tictac_stopwatch
        min: 0
        max: 1
        needle: true
1 Like

Hi! I am sorry for such a late reply. I just tested it and it works perfectly. Thank you so much! :slight_smile:

1 Like

That’s great!

Hi @miguelpucela,
hope you might be able to help me.
when i start the stop watch via input_boolean.start_stopwatch the tic tac starts. but the stop watch only moves from Unknown to Unavailable?

if i template

{{states('sensor.stopwatch')}}

all i get is unavailable. not any number what so ever. perhaps i have made a mistake in setup?

your code was pasted under a package like this:

homeassistant:
  packages:
    stop_watch:
      input_boolean:
        start_stopwatch:
          # It triggers stopwatch to start/stop(pause)
          name: Start/Stop Stopwatch
        #    initial: off
        reset_stopwatch:
          # It triggers stopwatch to reset
....................................

etc.

any chance you could help me?