Here’s how I use the awesome bubble card to create incremental timer control.
Click on the +10 min
button and it will start a timer with 10 minutes or add ten minutes if the timer is already running. When the timer starts, the fan turns on. When the timer finishes, the fan turns off. When the timer is running, you see the red cancel timer button and the remaining time card below (as shown). Keep reading to find out why it’s so ugly.
I tell my users (family) to think of the timer as an automatic off. If you no longer want an automatic off, cancel the timer with the red button. The fan will stay on. (I use a confirm popup to make sure they remember that. YMMV)
Clicking on the fan icon toggles the fan on/off. When the fan turns off, the timer is cancelled.
Notes:
-
Everything is a switch. Two simple automations monitor all timers. When a timer starts or finishes it turns on or off a switch of the same name. (
timer.my_fan
controlsswitch.my_fan
). If I want to automate a light or anything else, I simply create a template switch. A third automation cancels the timer when the switch is off. I use this system flawlessly on over fifty timers. -
The decluttering card is a great way to create the bubble card once and use it many times. I’m including it, but you could easily remove it.
-
Home Assistant timers are weird. First even though timers have a
remaining
attribute, it does does not accurately show the remaining time on an active timer unless it is paused. Some people create entities and count them down with automations. Or, you can calculate the remaining time by subtracting the current time from thefinishes_at
attribute. -
Some cards calculate the remaining time for you. The ones I’m aware of are:
- the entities card (but not the entity card).
- the custom button card IIRC
- a heading badge which is my ugly workaround that uses visibility to show when the timer is running
Have fun.
YAML to call decluttering card and heading card. Adjust timer label and duration here. Add your specific switch and timer. My timer icon is: mdi:timer-off-outline
- type: custom:decluttering-card
template: bubble-bump
variables:
- onoff-entity: switch.gym_fan # '[[onoff-entity]]'
- timer-entity: timer.gym_fan # '[[timer-entity]]'
- duration-name: "+10 min" # '[[duration-name]]'
- duration-seconds: 600 # '[[duration-seconds]]'
# this does work but throws lots of VS Code errors
- type: heading
badges:
- type: entity
entity: timer.gym_fan
heading: Remaining time
heading_style: subtitle
visibility:
- condition: state
entity: timer.gym_fan
state: active
Automation to cancel specific timers. Add your specific switches.
- alias: "Cancel Timer"
id: cancel_timers
mode: parallel
max: 25
triggers:
- trigger: state
to: "off"
entity_id:
- switch.gym_fan
# add additional switches here
# I create copies of this animation and include about ten switches in each
actions:
- action: timer.cancel
target:
entity_id: "timer.{{ trigger.to_state.entity_id.split('.')[-1] }}"
Everything below here requires no customization.
YAML for decluttering card. Feel free to choose better colors.
bubble-bump:
card:
type: custom:bubble-card
card_type: button
entity: '[[onoff-entity]]'
tap_action:
action: toggle
button_action:
tap_action:
action: none
show_state: false
show_icon: true
sub_button:
- entity: '[[timer-entity]]'
show_background: false
visibility:
- condition: state
entity: '[[timer-entity]]'
state: "active"
tap_action:
action: call-service
service: timer.cancel
target:
entity_id: '[[timer-entity]]'
confirmation:
text: Switch will stay on.
- name: '[[duration-name]]'
show_name: true
show_icon: false
tap_action:
action: call-service
service: script.start_or_increment_timer
data:
timer_entity_id: '[[timer-entity]]'
duration_seconds: '[[duration-seconds]]'
styles: |
.bubble-icon {
color: ${state === 'on' ? 'var(--rju-state-on)' : ''} !important;
}
.bubble-button-background {
opacity: 1 !important;
background-color: #3d6c89 !important;
}
.bubble-sub-button-icon {
--mdc-icon-size: 36px !important;
}
.bubble-sub-button-1 {
color:rgb(141, 7, 29) !important;
}
.bubble-sub-button-2 {
background-color: #2f8e2f !important;
}
Script to start_or_increment_timer. No customization needed.
start_or_increment_timer:
alias: Start or Increment Timer
sequence:
- variables:
timer_entity_id: "{{ timer_entity_id }}"
duration_seconds_int: "{{ duration_seconds | int }}"
# Get finishes_at datetime and calculate remaining time in seconds
remaining_time_seconds: >
{% set f = state_attr(timer_entity_id, 'finishes_at') %}
{% if f is none %}
0
{% else %}
{% set diff = (as_datetime(f) - now()).total_seconds() %}
{% if diff < 0 %}
0 # If the calculated difference is negative, set remaining time to 0
{% else %}
{{ diff | int }}
{% endif %}
{% endif %}
# Ensure that remaining_time_seconds is treated as an int
remaining_time_seconds_int: "{{ remaining_time_seconds | int }}"
new_duration_seconds: "{{ remaining_time_seconds_int + duration_seconds_int }}" # Increment timer
- action: timer.start
data:
entity_id: '{{ timer_entity_id }}'
duration: "{{ new_duration_seconds }}"
Automation when any timer finishes. No customization needed.
- alias: "Timer Finished to Switch"
id: timer_finished_to_switch
mode: parallel
max: 25
triggers:
- trigger: event
event_type: timer.finished
actions:
- action: switch.turn_off
data: {}
target:
entity_id: "switch.{{ trigger.event.data.entity_id.split('.')[-1] }}"
Automation when any timer starts. No customization needed.
- alias: "Timer Started to Switch"
id: timer_started_to_switch
mode: parallel
max: 25
triggers:
- trigger: event
event_type: timer.started
conditions: []
actions:
- action: switch.turn_on
data: {}
target:
entity_id: "switch.{{ trigger.event.data.entity_id.split('.')[-1] }}"