I have a dedicated device bedside (an old iphone) that I have been using as an alarm clock, and I was looking to upgrade / integrate that functionality with home assistant. Specifically, I wanted:
- A dimmable display with a clock
- a few shortcut buttons (ie, turn of house)
- Alarm clock functionality: display next alarm, off
- Recurring alarms (ie: Mondays, Tuesdays)
- Ability to modify a day of recurring alarms without turning off the alarm (eg, no alarm on Wednesday, June 19th – it’s a holiday, without having to remember to switch alarm back on)
- Ability to quickly add alarms
- fade in music
Using a combination of front end, automations, templates and scripts, I put together my new alarm clock:
This uses the following add ons: Custom Button Card, Browser Mod.
It also makes use of the local calendar, so you have the ability to add recurring alarms, filter for holidays, delete individual alarms without turning off all future alarms. I don’t believe there is a way to delete calendar entries or add recurring entries via services (if I am wrong, please let me know!!) so you need to do that directly from the calendar, but you can add a calendar entry right from the page.
If anyone is interested, the front end, template, and script codes are below. As a caveat, I am not a programmer, and I borrowed heavily from various threads here and elsewhere. This is more a compilation of others’ work…
Front-end:
- title: clock
theme: amoled
path: clock
icon: mdi:clock
badges: []
type: custom:ha-dashboard
cards:
- type: vertical-stack
cards:
- type: custom:button-card
color_type: label-card
color: rgb(0, 0, 0)
name: MBR Alarm Clock
styles:
card:
- height: 25px
- font-weight: bold
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
name:
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
- type: horizontal-stack
cards:
- show_name: true
show_icon: true
type: custom:button-card
color_type: label-card
color: rgb(0,0,0)
styles:
name:
- font-size: 12px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
icon:
- height: 25px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
card:
- height: 65px
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
entity: ""
name: Other rooms
icon: mdi:bullseye
tap_action:
action: navigate
navigation_path: navigate
icon_height: 20px
- type: custom:button-card
styles:
name:
- font-size: 12px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
icon:
- height: 25px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
card:
- height: 65px
- font-size: 14px
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
color_type: label-card
color: rgb(0,0,0)
entity: scene.attic_enclosure_device_001_bedtime
name: House off
icon: mdi:theme-light-dark
tap_action:
action: call-service
service: script.house_off
- type: custom:button-card
styles:
name:
- font-size: 12px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
icon:
- height: 25px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
card:
- height: 65px
- font-size: 14px
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
color_type: label-card
color: rgb(0,0,0)
name: Kids Home
icon: mdi:bed-clock
tap_action:
action: call-service
service: script.house_off_kids_home
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
icon:
- height: 25px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
card:
- height: 65px
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
color_type: label-card
color: rgb(0,0,0)
entity: ""
name: Quick Actions
icon: mdi:gesture-tap
tap_action:
action: fire-dom-event
browser_mod:
service: browser_mod.popup
data:
title: " "
content:
type: vertical-stack
cards: *quick
- type: conditional
conditions:
- state: "on"
entity: binary_sensor.alarm_status
card:
type: custom:button-card
color_type: label-card
color: rgb(191, 8, 8)
name: Open doors / Alarm Zones
styles:
card:
- height: 25px
- font-weight: bold
name:
- font-size: 12px
- color: rgb(125,125,125)
- type: entity-filter
entities: *alarm
state_filter:
- "on"
- open
show_empty: false
card:
type: glance
state_color: true
- type: custom:button-card
color_type: label-card
color: rgb(0,0,0)
variables:
show_name: false
show_icon: false
show_state: true
entity: sensor.time
styles:
state:
- font-size: 5.0em
- color: >
[[[
return "rgb(0, 18,"+states['sensor.rgb2'].state + ")"
]]]
card:
- height: 150px
- type: custom:button-card
color_type: label-card
color: rgb(0,0,0)
name: >
[[[return "Next Alarm: " + states['calendar.alarm_mbr'].attributes.message + ", " + helpers.formatTimeWeekday(states['calendar.alarm_mbr'].attributes.start_time) ]]]
styles:
card:
- height: 25px
- font-weight: bold
name:
- color: >
[[[
return "rgb(7,"+states['sensor.rgb'].state + ", 20)"
]]]
icon:
- height: 25px
- color: >
[[[
return "rgb(7,"+states['sensor.rgb'].state + ", 20)"
]]]
- type: conditional
conditions:
- state: "playing"
entity: media_player.roam
card:
type: custom:button-card
color_type: label-card
color: rgb(138, 7, 50)
name: Alarm off
styles:
card:
- height: 75px
- font-weight: bold
tap_action:
action: call-service
service: script.mbr_pause_alarm
- square: false
columns: 3
type: grid
cards:
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
icon:
- height: 25px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
card:
- height: 65px
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
color_type: label-card
color: rgb(0,0,0)
entity: ""
name: Add Alarm
icon: mdi:alarm
tap_action:
action: navigate
navigation_path: clock2
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
icon:
- height: 25px
card:
- height: 65px
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
color_type: label-card
color: rgb(0,0,0)
entity: ""
name: " "
icon: " "
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
icon:
- height: 25px
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
card:
- height: 65px
- border: >
[[[
return "solid 1px rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
color_type: label-card
color: rgb(0,0,0)
entity: ""
name: Add recurring alarm
icon: mdi:calendar
tap_action:
action: navigate
navigation_path: clock3
- type: 'custom:button-card'
styles:
card:
- height: 100px
- border-radius: 15px
- font-size: 16px
- padding-bottom: 0px
- padding-top: 0px
name:
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
label:
- color: >
[[[
return "rgb("+states['sensor.rgb'].state + "," + states['sensor.rgb'].state + "," +states['sensor.rgb'].state +")"
]]]
grid:
- grid-template-areas: '"n" "slider" "l"'
- grid-template-columns: 1fr
- grid-template-rows: min-content fr min-content min-content
slider:
custom_fields:
slider:
card:
full_row: true
type: 'custom:slider-entity-row'
entity: input_number.alarm_dim
colorize: true
hide_state: true
name: Dimmer
entity: input_number.alarm_dim
show_icon: false
- title: set alarm
theme: slate
path: clock2
icon: mdi:clock
badges: []
type: custom:ha-dashboard
cards:
- type: vertical-stack
cards:
- type: custom:button-card
color_type: label-card
color: rgb(45, 56, 61)
name: Add Alarm (one time)
styles:
card:
- height: 25px
- font-weight: bold
- type: horizontal-stack
cards:
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 15px
- color: red
icon:
- height: 30px
- color: red
entity: ""
name: Cancel / Go Back
icon: mdi:alpha-x-circle-outline
tap_action:
action: navigate
navigation_path: /lovelace-yaml/clock
- type: conditional
conditions:
- state: "on"
entity: binary_sensor.alarm_status
card:
type: custom:button-card
color_type: label-card
color: rgb(191, 8, 8)
name: Open doors / Alarm Zones
styles:
card:
- height: 25px
- font-weight: bold
- type: entity-filter
entities: *alarm
state_filter:
- "on"
- open
show_empty: false
card:
type: glance
state_color: true
- type: entities
title:
entities:
- entity: input_datetime.new_alarm
name: "Enter time for alarm:"
icon: false
- square: false
columns: 3
type: grid
cards:
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
- color: green
icon:
- height: 25px
- color: green
card:
- height: 65px
entity: ""
name: Add Alarm
icon: mdi:alarm
tap_action:
action: call-service
service: script.set_new_alarm
service_data:
summary: New Alarm
start: input_datetime.new_alarm
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
icon:
- height: 25px
card:
- height: 65px
entity: ""
name: " "
icon: " "
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
icon:
- height: 25px
card:
- height: 65px
entity: ""
name: Recurring options
icon: mdi:calendar
tap_action:
action: navigate
navigation_path: clock3
- title: set recurring alarm
theme: slate
path: clock3
icon: mdi:clock
badges: []
type: custom:ha-dashboard
cards:
- type: vertical-stack
cards:
- type: custom:button-card
color_type: label-card
color: rgb(45, 56, 61)
name: MBR Alarm Clock
styles:
card:
- height: 25px
- font-weight: bold
- type: horizontal-stack
cards: *navigate1
- type: conditional
conditions:
- state: "on"
entity: binary_sensor.alarm_status
card:
type: custom:button-card
color_type: label-card
color: rgb(191, 8, 8)
name: Open doors / Alarm Zones
styles:
card:
- height: 25px
- font-weight: bold
- type: entity-filter
entities: *alarm
state_filter:
- "on"
- open
show_empty: false
card:
type: glance
state_color: true
- type: entities
title: Add Alarm
entities:
- input_datetime.new_alarm
- type: horizontal-stack
cards:
- entity: input_boolean.alarm_mon
name: Mon
template: day_button
type: custom:button-card
- entity: input_boolean.alarm_tue
name: Tue
template: day_button
type: custom:button-card
- entity: input_boolean.alarm_wed
name: Wed
template: day_button
type: custom:button-card
- entity: input_boolean.alarm_thu
name: Thu
template: day_button
type: custom:button-card
- entity: input_boolean.alarm_fri
name: Fri
template: day_button
type: custom:button-card
- entity: input_boolean.alarm_sat
name: Sat
template: day_button
type: custom:button-card
- entity: input_boolean.alarm_sun
name: Sun
template: day_button
type: custom:button-card
- square: false
columns: 3
type: grid
cards:
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
icon:
- height: 25px
card:
- height: 65px
entity: ""
name: Add Alarm
icon: mdi:alarm
tap_action:
action: call-service
service: script.set_new_alarm
service_data:
summary: New Alarm
start: input_datetime.new_alarm
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
icon:
- height: 25px
card:
- height: 65px
entity: ""
name: " "
icon: " "
- show_name: true
show_icon: true
type: custom:button-card
styles:
name:
- font-size: 12px
icon:
- height: 25px
card:
- height: 65px
entity: ""
name: Recurring options
icon: mdi:calendar
tap_action:
action: navigate
navigation_path: clock3
A create a sensor for time. I wanted to use an existing card, but I also wanted the ability to dim the clock, and I could only do that using a custom button card. The sensor code for sensor.time:
{% set x = states('sensor.time') %}
{{now().strftime("%-I:%M %p")}}
The alarm automation:
alias: mbr alarm
description: ""
trigger:
- platform: state
entity_id:
- calendar.alarm_mbr
to: "on"
condition: []
action:
- service: script.test_alarm
metadata: {}
data: {}
mode: single
Then script.test_alarm:
alias: bedroom alarm
sequence:
- service: media_player.volume_set
metadata: {}
data:
volume_level: 0.01
target:
entity_id: media_player.roam
- service: media_player.play_media
target:
entity_id:
- media_player.roam
data:
media_content_id: https://open.spotify.com/playlist/1LS91m0a848S93H5gBsgPM
media_content_type: playlist
- service: script.fade_the_volume_of_a_media_player
metadata: {}
data: {}
- delay:
hours: 0
minutes: 15
seconds: 0
milliseconds: 0
- service: media_player.media_pause
metadata: {}
data: {}
target:
entity_id: media_player.roam
mode: single
icon: mdi:alarm
description: ""
script.fade_the_volume_of_a_media_player:
alias: Fade the volume of a media player
mode: restart
variables:
target_player: media_player.roam
target_volume: 0.33
curve: linear
start_volume: 0.01
fade_duration: 30
fade_step_timeout: 500
start_timestamp: "{{ as_timestamp(now()) }}"
fade_volume_diff: "{{ (target_volume - start_volume) | float(0) }}"
fade_duration_cutoff: "{{ fade_duration - 0.2 }}"
sequence:
- alias: Set the media player volume in incremental steps for the fade duration.
repeat:
sequence:
- alias: >-
Set next volume step. Value based on time progress of fade,
start/end volume and algorithm.
service: media_player.volume_set
data_template:
entity_id: "{{ target_player }}"
volume_level: >-
{%- set relative_fade_pos = (as_timestamp(now()) -
start_timestamp) / fade_duration %} {%- if curve == 'logarithmic'
%}
{{ (start_volume + (relative_fade_pos / (1 + (1 - relative_fade_pos))) * fade_volume_diff) | float(0) }}
{%- elif curve == 'bezier' %}
{{ (start_volume + (relative_fade_pos * relative_fade_pos * (3 - 2 * relative_fade_pos)) * fade_volume_diff) | float(0) }}
{%- else %}
{{ (start_volume + relative_fade_pos * fade_volume_diff) | float(0) }}
{%- endif %}
- alias: >-
Fade can be aborted by sending an event - wait for event before we
continue with next fade step.
wait_for_trigger:
- alias: >-
'media_player_fade_volume_abort' event with matching
target_player entity
platform: event
event_type: media_player_fade_volume_abort
event_data:
target_player: "{{ target_player }}"
timeout:
milliseconds: 500
- alias: >-
If abort event was received we stop script execution right away.
Volume remains at current value.
if:
- alias: >-
Received the 'media_player_fade_volume_abort' event with
target_player of current script instance.
condition: template
value_template: "{{ wait.trigger != none }}"
then:
- stop: Script aborted by 'media_player_fade_volume_abort' event.
until:
- alias: Time passed in fade is close to desired duration.
condition: template
value_template: "{{ (as_timestamp(now()) - start_timestamp) >= 28.2 }}"
- alias: Ensure media player is set to target volume after fade finished
service: media_player.volume_set
data_template:
entity_id: "{{ target_player }}"
volume_level: "{{ target_volume }}"
icon: mdi:tune-vertical
description: ""
script.mbr_pause_alarm:
alias: mbr pause alarm
sequence:
- service: script.turn_off
metadata: {}
data: {}
target:
entity_id: script.fade_the_volume_of_a_media_player
- service: script.turn_off
metadata: {}
data: {}
target:
entity_id: script.test_alarm
- service: media_player.media_stop
target:
entity_id:
- media_player.roam
data: {}
description: ""
icon: mdi:alarm-off
and script:set_new_alarm:
alias: Set new alarm
sequence:
- service: calendar.create_event
metadata: {}
data:
summary: Alarm
description: ""
start_date_time: >
{% set x = today_at(states('input_datetime.new_alarm')) %} {{ x +
timedelta(days=1) if now() > x else x }}
end_date_time: >
{% set x = today_at(states('input_datetime.new_alarm')) +
timedelta(minutes=15) %} {{ x + timedelta(days=1) if now() +
timedelta(minutes=15) > x else x }}
target:
entity_id: calendar.alarm_mbr
- service: browser_mod.navigate
metadata: {}
data:
path: /lovelace-yaml/clock
mode: single
I use browser mod to hide the header and sidebar on this device, as well as to return to the alarm page after setting an alarm.
I’d (really) welcome suggestions for improvements. This has been a major step up from my existing alarm clock already – I hope it is helpful for others.