Hey @miguelpucela!
Thanks for your great work on this! It really helps me understand some core concepts. I am trying to create a shot-timer for my espresso machine. I have the pump state of my machine on, and I’d love to have a timer for how long the pump has been on. Unfortunately in my modification it seems the sensor attributes elapsed_time and initial_time fail to initialize, and I’m having trouble to understand why. They always seem to be None.
Here’s my implementation:
template:
- trigger:
# Stopwatch sensor with Start, Stop/Pause, Reset and Lap features. Hundreds of second precission
- platform: state
entity_id: binary_sensor.mara_x_pump
from: "0"
to: "1"
- platform: state
entity_id: binary_sensor.mara_x_pump
from: "1"
to: "0"
- platform: state
entity_id: input_boolean.tictac_stopwatch
sensor:
- name: "Mara X Shot Timer"
#id: "mara_x_shot_timer"
state: >-
{% if is_state('binary_sensor.mara_x_pump','on') %}
{{ '00:00:00' }}
{% elif is_state('binary_sensor.mara_x_pump','unavailable') %}
{{ '00:00:00' }}
{% elif is_state_attr('sensor.mara_x_shot_timer', 'running', 'on') and state_attr('sensor.mara_x_shot_timer','initial_time') != None and state_attr('sensor.mara_x_shot_timer','elapsed_time') != None %}
{% set value = as_timestamp(now()) - state_attr('sensor.mara_x_shot_timer','initial_time') + state_attr('sensor.mara_x_shot_timer','elapsed_time') %}
{{ value|float|timestamp_custom("%H:%M:%S", False) + '.' + ((value|float*100)%100)|round(0) |string }}
{% elif is_state('sensor.mara_x_shot_timer', 'unknown') or is_state('sensor.mara_x_shot_timer', 'unavailable') %}
{{ '00:00:00' }}
{% else %}
{{ states('sensor.mara_x_shot_timer') }}
{% endif %}
icon: mdi:timer
attributes:
initial_time: >-
{% if is_state('binary_sensor.mara_x_pump','on') and is_state_attr('sensor.mara_x_shot_timer','running','off') %}
{{ as_timestamp(now()) }}
{% else %}
{{ state_attr('sensor.mara_x_shot_timer','initial_time') }}
{% endif %}
elapsed_time: >-
{% if is_state('binary_sensor.mara_x_pump','on') and is_state_attr('sensor.mara_x_shot_timer','running','on') %}
{{ as_timestamp(now()) - state_attr('sensor.mara_x_shot_timer','initial_time') + state_attr('sensor.mara_x_shot_timer','elapsed_time') }}
{% else %}
{{ state_attr('sensor.mara_x_shot_timer','elapsed_time') }}
{% endif %}
running: >-
{% if is_state('binary_sensor.mara_x_pump','unavailable') %}
{{ 'off' }}
{% else %}
{{ states('binary_sensor.mara_x_pump') }}
{% endif %}
I removed both timer reset and lap functions, as really I only want to know for how long the pump has been running, and I only want it to reset when the pump goes from off to on, and only stop when it goes from on to off. But the latter part sounds trivial, as of now i am mainly interested why my state attributes don’t properly initialize, as they would always be none
Ah right that makes sense. At the beginning I had the impression that the binary sensor would be 0 or 1 and later realized it’s the same as on off. It seems it did not change anything on the behavior unfortunately. setting value by accessing initial_time and elapsed_time results in TypeError: unsupported operand type(s) for -: 'float' and 'NoneType'
The problem is that in the original code the reset/stop function sets to 0 the elapsed time and the value of the sensor. In your case as you don’t have the reset/stop function, none of these two things happen, resulting in not reseting the values of the attributes.
You could set a default value when evaluating them. For instance:
elapsed_time: >-
{% if is_state('binary_sensor.mara_x_pump','on') and is_state_attr('sensor.mara_x_shot_timer','running','on') %}
{{ as_timestamp(now()) - state_attr('sensor.mara_x_shot_timer','initial_time') + state_attr('sensor.mara_x_shot_timer','elapsed_time')|float(0) }}
...
The default value is the code:
|float(0).
With this, you’ll avoid the TypeErrors.
The problem even if you correct this is that in your code if the pump is on, the stopwatch will always have the value 00:00:00. You have to think when and how do you want to stop or reset the stopwatch.
One final comment: if your stopwatch is not intended to start->pause->continue … and you only want a start->final stop behaviour, you can avoid and remove the elapsed time feature.
First of all you have to set up packages folder if you haven’t done before. To do this, follow this link.
After that, in you packages folder, create a yaml file, for instance stopwatch.yaml with the code in the first codebox of the first post in this thread.
The final step is to create a lovelace (frontend) for the stopwatch with one of the alternative codes I posted in the first post of this thread.
If you have problems or need more help, let me know.
@miguelpucela Thanks a lot for this one. I was planning to set up something similar to stay informed of the time the dishwasher is running and then I’ve found this one.
I still have an issue in the code of the UI-cards you’ve provided (object of type ‘NoneType’ has no len()), but this is not important for me, because I only want to have the time shown in an own card and this works nicely. I put it into a conditional card, so it is only visible when the machine is running and this works fine.
For my purposes, I guess I’ll simplify the code and remove e.g. the laps and so on, but this might come true, once I have vacations
I changed as you wrote by also replacing sensor.mara_x_shot_timer to my sensor.stopwatch but it doesn’t work. always the fabric error. can you help me? Thank you
and thank you for your work!
My usecase is funny, I would track lap timings for my kids using a sensor. Now, I can link the sensor to the start/stop trigger, and I just get the timing of the first lap, of course.
What I would like to do:
Start of the race: timer starts, triggered by sensor. this works.
First lap: sensor stops timer and it starts again from 0.0.0 Laptime 1 is showed as Lap 1
Second lap: sensor stops timer and it starts again from 0.0.0 Laptime 2 is showed as Lap 2
If anybody wants to show in the laps the time between laps instead of the elapsed time from start, you have to add another attribute with last lap value and slightly modify lap attribute as follows:
attributes:
...
last_lap: >-
{% if is_state('input_boolean.start_stopwatch', 'on') and is_state_attr('sensor.stopwatch','running','off') %}
{{ 0 }}
{% elif is_state('input_boolean.lap_stopwatch','on') and 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 }}
{% else %}
{{ state_attr('sensor.stopwatch','last_lap') }}
{% endif %}
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') - state_attr('sensor.stopwatch','last_lap') %}
{% 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 %}
Miguel thank you for your work.
Couple of questions please.
I tried to change the time pattern to millisecond for the TicToc but frontend doesn’t change, the time only shows after stop is pressed. Is it possible to have the refresh in 1/10 or 1/100 of a second? (I’m not using the gauge)
If very new at code, is there a way to turn off the stopwatch when it reaches a max time?
Thanks
Thank you so much, I was so close, after creating a new attribute I did not modify lap attribute. Now I just need to adjust the sensor on the “finish” line to start the stopwatch on the start and to trigger the lap_stopwatch on every pass. I think I can do that with an automation btw.
Then I will create a second dashboard and will think how to store the best lap for each kid to track their “best lap”.
I understand you want to change the precission to 1/10 or 1/100 while running? I think it has only sense to show that precission when you stop the stopwatch, because HA frontend won’t refresh so fast.
You can do this with an automation with the state as trigger. If you need a hint on that, come back and I’ll try to help you.
Thanks, thought it might be a refresh issue.
That was the nudge I needed. The stop time ends up at 30.020, guessing that is the just the delay of the boolean action.
Blockquote - id: timeout_stopwatch
alias: “Timeout Stopwatch”
description: “Stopd stopwatch after 30 sec”
trigger:
- platform: state
entity_id: input_boolean.start_stopwatch
from: “off”
to: “on”
action:
- delay: ‘00:00:30’
- service: input_boolean.turn_off
target:
entity_id: input_boolean.start_stopwatch
mode: single
Thank you for creating this! I’m always amazed at the number of people who can’t see a need for this functionality.
My use case is this: I have a couple of water bowls in the house for my dogs. They have light sensors underneath to keep track of when the water is changed. This stopwatch will make it easy to monitor how much time has passed since the last water change. This is much better than the timer I have been using.
How do I create automations based on stopwatch? I’d love to make a simple “When button is pressed, start stopwatch” for example. How can I do that with the help of your stopwatch?