WIP kitchen timer card
this weekend I worked on a kitchen timer card for my kitchen dashboard, this is still a work in progress. I wanted to put it up to see if anyone had any thoughts
there are 3 sections,
the 1st is the time input that I got from this post (thanks @Mattias_Persson and @ParalaX)
this is where how you can enter a time for the timer to run in hours and minutes, the last enter time is saved, and tapping the button will start the timer
the 2ed sections, is the countdown, if a timer is running this is displayed that shows how long the timer has remaining, and can be tapped to stop a running timer, a lot of work went into drawing the outline around the circle as the timer ticks down the circle outline will fill in.
The last section is just 4 buttons that can be used to start common timers with one click, this section can be shown by swiping
setup
you will need an input date time and a timer Entity, along with the following script that is used to start the timer with the input from the card
script
kitchen_timer:
alias: calculates the time given for the timer and then starts the kitchen timer,
fields:
time_input:
description: Entity id of the date time input
example: input_datetime.kitchen_timer
timer:
description: Entity id of the timer to set
example: timer.kitchen_timer
sequence:
- service: timer.start
data:
duration: '{{ states(time_input) }}'
target:
entity_id: '{{ timer }}'
mode: single
code
use
#################################################
# #
# Timer #
# #
#################################################
- type: grid
title: Timer ↔
view_layout:
grid-area: timer
columns: 1
cards: !include decks/kitchen_timer_deck.yaml
kitchen_timer_deck.yaml
#################################################
# #
# kitchen timer deck #
# #
#################################################
- type: custom:swipe-card
start_card: 1
parameters:
roundLengths: true
effect: coverflow
speed: 650
spaceBetween: 20
threshold: 7
coverflowEffect:
rotate: 80
depth: 300
cards:
# page 1
- type: horizontal-stack
cards:
# show input if timer is idle, else show countdown
- type: conditional
conditions:
- entity: timer.kitchen_timer
state: idle
card:
#################################################
# #
# Input #
# #
#################################################
type: custom:button-card
name: >
[[[ return ' '; ]]]
state_display: Kitchen Timer
template:
- base
custom_fields:
time:
card:
type: custom:time-picker-card
entity: input_datetime.kitchen_timer
hide:
name: true
card_mod:
style:
.: |
.time-picker-content {
justify-content: space-evenly !important;
padding-right: 1%;
}
.time-separator {
color: rgba(255, 255, 255, 0.3);
}
.time-picker-row {
display: block !important;
padding: 0 !important;
overflow: hidden !important;
}
.time-input {
border-radius: 10px;
}
:host {
--ha-card-border-width: 0px;
--time-picker-elements-background-color: rgba(0, 0, 0, 0.15);
--time-picker-icon-color: rgba(255, 255, 255, 0.4);
--time-picker-text-color: rgba(255, 255, 255, 0.6);
--time-picker-control-padding: 6px;
}
time-unit:
$: |
.time-input {
border-radius: 0.4vw;
}
.time-unit {
padding: 0 !important;
}
styles:
custom_fields:
time:
- position: absolute
- width: 100%
- height: 80%
- clip-path: inset(0 round var(--custom-button-card-border-radius))
- left: 0
- top: 15%
tap_action:
action: call-service
service: script.kitchen_timer
service_data:
time_input: input_datetime.kitchen_timer
timer: timer.kitchen_timer
- type: conditional
conditions:
- entity: timer.kitchen_timer
state_not: idle
card:
#################################################
# #
# Countdown #
# #
#################################################
type: custom:button-card
entity: timer.kitchen_timer
template:
- base
tap_action:
action: call-service
service: timer.cancel
service_data:
entity_id: timer.kitchen_timer
custom_fields:
countdown: >
[[[
setTimeout(() => {
let elt = this.shadowRoot,
circle_stroke = elt.getElementById('circle_stroke'),
r = 22.1,
c = r * 2 * Math.PI,
now = new Date().getTime(),
endDate = new Date(entity.attributes.finishes_at),
remaining = entity.attributes.remaining.split(':'),
duration = entity.attributes.duration.split(':'),
startDate = new Date(endDate.getTime() - (remaining[0]*3600+remaining[1]*60)*1000),
percent = ((now - startDate) / (endDate - startDate)) * 100;
circle_stroke.style.strokeDashoffset = c - percent / 100 * c;
circle_stroke.style.strokeWidth = 'var(--c-stroke-width-dragging)';
}, 0);
let r = 22.1,
c = r * 2 * Math.PI,
state = true,
input = variables.circle_input || ' ';
return `
<svg viewBox="0 0 50 50">
<style>
circle {
transform: rotate(-90deg);
transform-origin: 50% 50%;
stroke-dasharray: ${c};
stroke-dashoffset: ${typeof input === 'number' && c - input / 100 * c};
stroke-width: var(--c-stroke-width);
}
#circle_stroke{
stroke: ${'var(--c-stroke-color-on)' };
fill: ${'var(--c-fill-color-on)'};
}
#circle_bg {
stroke: ${'var(--c-stroke-color-off)'};
fill: ${'var(--c-fill-color-off)'};
}
text {
font-size: var(--c-font-size);
font-weight: var(--c-font-weight);
letter-spacing: var(--c-letter-spacing);
fill: var(--c-font-color);
}
tspan {
font-size: var(--c-unit-font-size);
}
#circle_value, tspan {
text-anchor: middle;
dominant-baseline: central;
}
</style>
<circle id="circle_stroke" cx="25" cy="25" r="${r}"/>
<circle id="circle_bg" cx="25" cy="25" r="${r}"/>
</svg> `;
]]]
styles:
state:
- position: relative
- line-height: 0px
- overflow: visible
- top: -2.5em
- margin: auto
- display: initial
- --button-card-font-size: 4vw
card:
- --c-stroke-color-on: '#b0b0b0'
- --c-stroke-color-off: none
- --c-fill-color-on: none
- --c-fill-color-off: rgba(255,255,255,0.04)
- --c-stroke-width: 2.3
- --c-stroke-width-dragging: 1
- --c-font-color: '#97989c'
- --c-font-size: 14px
- --c-unit-font-size: 10.5px
- --c-font-weight: 700
- --c-letter-spacing: -0.02rem
custom_fields:
countdown:
- position: absolute
- width: 100%
- height: 100%
- left: 0
- top: -8%
- display: initial
- opacity: 1
- justify-self: end
# page 2
- type: grid
columns: 2
cards:
- type: custom:button-card
entity: binary_sensor.template_living_room_tv_source_plex
template:
- base
name: 5 min
show_state: false
custom_fields:
icon: <ha-icon icon="mdi:clock-time-two-outline"></ha-icon>
tap_action:
action: call-service
service: timer.start
service_data:
duration: "00:05:00"
entity_id: timer.kitchen_timer
- type: custom:button-card
entity: binary_sensor.template_living_room_tv_source_youtube
template:
- base
name: 25 min
show_state: false
custom_fields:
icon: <ha-icon icon="mdi:clock-time-five-outline"></ha-icon>
tap_action:
action: call-service
service: timer.start
service_data:
duration: "00:25:00"
entity_id: timer.kitchen_timer
- type: custom:button-card
entity: binary_sensor.template_living_room_tv_source_netflix
template:
- base
name: 45 min
show_state: false
custom_fields:
icon: <ha-icon icon="mdi:clock-time-eight-outline"></ha-icon>
tap_action:
action: call-service
service: timer.start
service_data:
duration: "00:45:00"
entity_id: timer.kitchen_timer
- type: custom:button-card
entity: binary_sensor.template_living_room_tv_source_funimation
template:
- base
name: 1 hour
show_state: false
custom_fields:
icon: <ha-icon icon="mdi:clock-time-twelve-outline"></ha-icon>
tap_action:
action: call-service
service: timer.start
service_data:
duration: "01:00:00"
entity_id: timer.kitchen_timer