None of those custom cards are addons.
You’re right, I’ve changed the wording.
Fixed! Let me know if there’s any other bits you’d like explained!
This looks really nice and clean! Maybe you can also share how you created the “Good afternoon” part (along with the two lines below) on the Home page?
Thanks! Here’s the config for the Good Morning/Afternoon/Evening card:
Welcome:
Can also be repurposed as a general title card. 60px tall so needs a row height of at least this. Not a template - copy directly into your view config
- type: 'custom:button-card'
gridcol: 1/6
gridrow: 1/6
name: >-
[[[ var d = new Date(); var n = d.getHours(); if (0 <= n && n
< 12) return "Good morning."; else if (12 <= n && n < 18)
return "Good afternoon."; else if (18 <= n && n < 24) return
"Good evening."; else return "ERROR";]]]
styles:
card:
- width: 300px
- height: 60px
- background: none
- box-shadow: none
name:
- justify-self: start
- margin-left: 10px
- font-weight: normal
- font-size: xx-large
Active Entities:
Requirements:
- Header template (see top of page)
- Popup Card (or Browser Mod, if you prefer)
- Auto-Entities Card (+ Card Mod, if not installed)
First thing you need is a sensor to count the number of active devices (I’m using lights as an example):
Config.yaml:
- platform: template
sensors:
active_lights:
value_template: "{{ states.light|selectattr('state','equalto','on')|list|count }}"
This sensor won’t update automatically so you’ll need an automation to do this:
automation.yaml:
- alias: Update Light Count
initial_state: True
trigger:
platform: event
event_type: state_changed
condition:
condition: template
value_template: "{{ trigger.event.data.entity_id.startswith('light') }}"
action:
service: homeassistant.update_entity
entity_id: sensor.active_lights
Then add your card to your view:
- type: 'custom:button-card'
entity: sensor.active_lights
gridcol: 1/6
gridrow: 2/6
state:
- name: All lights off.
value: 0
- name: 1 light on.
value: 1
- name: '[[[ return `${entity.state} lights on.` ]]]'
operator: '>='
value: 2
template: header
tap_action:
action: more-info
Finally, define the popup card:
popup_cards:
sensor.active_lights:
title: ' '
card:
type: 'custom:auto-entities'
card:
type: entities
title: Active Lights
show_header_toggle: true
filter:
include:
- domain: light
state: 'on'
show_empty: true
Add this to your config as below:
title: My House
views:
- path: home #
title: Home #
theme: Backend-selected # Your view setup
badges: [] #
panel: true #
popup_cards: #
sensor.active_lights: #
title: ' ' #
card: #
type: 'custom:auto-entities' #
card: #
type: entities # defining the popup
title: Active Lights # card
show_header_toggle: true #
filter: #
include: #
- domain: light #
state: 'on' #
show_empty: true #
cards:
- type: 'custom:layout-card' #
column_width: 100% # For setting up panel view
layout: vertical #
cards:
- type: 'custom:layout-card' #
layout: grid # Grid
gridcols: 158px 158px 158px 158px 158px 158px # setup
gridrows: 158px 158px 158px 158px 158px 158px #
cards:
- entity: sensor.active_lights # the active lights card
gridcol: 1/6
gridrow: 2/6
state:
- name: All lights off.
value: 0
- name: 1 light on.
value: 1
- name: '[[[ return `${entity.state} lights on.` ]]]'
operator: '>='
value: 2
template: header
tap_action:
action: more-info
type: 'custom:button-card'
… not since 11 November…
…really?! I completely missed that, sorry! Original post updated accordingly.
Wow. this is exactly what I was looking for. How does it look like on a mobile?
I use a modified layout for mobile and it looks like this. I have been slowly tweaking the design to make it more universal across browsers, and make it more customisable. There’s now better indicators for bulb brightness and percentages, and I have made the media and camera widgets much more consistent in design. Can upload code updates if anyone is interested!
I will be very grateful if you can share your code. I have noticed that the current one has not been updated with the latest layout card settings. For some reason, I am getting everything in the middle of the screen
Question around your light count template. I use light groups and then group lights into rooms.
e.g. Office light group contains 2 lights, lounge light group contains 3 lights.
If both of these rooms are on the light count is 7 as the light group is counted in the template. Anyway to avoid this and only count the individual lights?
Edit. Don’t worry I managed to do it expanding the group and counting the entities that show on.
Updated: 30/03/2021
So with layout-card being rewritten to include better CSS support for grid elements I took the time to simplify and rewrite parts of my setup. The new layout features require layout-card 2.0 or later to work properly.
Most of my button-card templates now draw basic elements from a base
template - this means you can edit multiple cards simultaneously just by changing properties in the base
template.
I’ve also tweaked some UI elements to make it prettier and more consistent.
Also improved:
- Auto-generating/iterating grids, less work to add more elements and move them around
- Simplified yaml with fewer stacked elements
- Ability to adjust aspects of all cards
- Circular elements to display additional data, and can show a partial circle for percentage properties, using the
circle
andcircle_dynamic
templates
How to set up
Set YAML mode for Lovelace, install button-card and layout-card.
ui-lovelace.yaml:
button_card_templates: !include ui-resources.yaml #required for button card templates
popup_cards: !include ui-popups.yaml #if using browser
title: My Home
views:
- title: Home
path: home
type: custom:grid-layout #new simpler grid setting
layout:
grid-auto-columns: 150px #default card width is 150px (small) or 308px (medium)
grid-auto-rows: 150px #default card height is 150px
grid-template-rows: 50px 30px 50px #use if you want some specific row heights
grid-column-gap: 8px #default size
grid-row-gap: 8px #default size
cards:
- type: 'custom:button-card' #your first card
template: greeting #template specified in ui-resources.yaml
view_layout:
grid-column-start: 1 #grid x coordinate
grid-row-start: 1 #grid y coordinate
See next post for ui-resources.yaml and templates.
How to set up (part 2)
ui-resources.yaml:
base: #this is used by most cards for base characteristics
color: var(--state-icon-active-color)
show_state: true
styles:
card:
- width: 150px
- height: 150px
icon:
- width: 60px
- height: 60px
- top: 5px
- left: 10px
- position: absolute
name:
- bottom: 25px
- left: 10px
- position: absolute
- justify-self: start
state:
- bottom: 5px
- left: 10px
- position: absolute
- justify-self: start
- font-weight: lighter
circle: #this puts a data field in a circle on the card
custom_fields:
circle: >
[[[
const input = variables.circle_input;
const radius = 23;
const circumference = radius * 2 * Math.PI;
return `
<svg viewBox="0 0 50 50">
<circle cx="25" cy="25" r="${radius}" stroke="var(--disabled-text-color)" stroke-width="3" fill="none" />
<text x="50%" y="52%" fill="var(--primary-text-color)" font-size="12" text-anchor="middle" alignment-baseline="middle">${input}</text>
</svg>
`;
]]]
styles:
custom_fields:
circle:
- top: 15px
- right: 15px
- width: 50px
- position: absolute
circle_dynamic: #as per circle, but the circle will be partially complete depending on percentage
custom_fields:
circle_dynamic: >
[[[
if (entity.state === 'on' || entity.state === 'Printing') {
const input = variables.circle_input;
const radius = 23;
const circumference = radius * 2 * Math.PI;
return `
<svg viewBox="0 0 50 50">
<style>
circle {
transform: rotate(-90deg);
transform-origin: 50% 50%;
stroke-dasharray: ${circumference};
stroke-dashoffset: ${circumference - input / 100 * circumference};
}
</style>
<circle cx="25" cy="25" r="${radius}" stroke="var(--disabled-text-color)" stroke-width="3" fill="none" />
<text x="50%" y="52%" fill="var(--primary-text-color)" font-size="12" text-anchor="middle" alignment-baseline="middle">${input}%</text>
</svg>
`;
}
]]]
styles:
custom_fields:
circle_dynamic:
- top: 15px
- right: 15px
- width: 50px
- position: absolute
alarm: #for alarm_control_panel entities
lock:
enabled: true
template:
- base
state:
- icon: 'mdi:shield-check'
styles:
icon:
- color: var(--label-badge-green)
value: disarmed
- icon: 'mdi:shield-lock'
styles:
icon:
- color: var(--label-badge-red)
value: armed_away
styles:
lock:
- position: absolute
- top: 19px
- right: 12px
tap_action:
action: call-service
service: script.alarm_toggle
camera: #to display a live camera feed
template:
- base
show_live_stream: true
show_state: false
custom_fields:
background1: " "
background2: " "
styles:
card:
- width: 308px
- z-index: 2
img_cell:
- top: -10px
- left: -10px
- z-index: 1
- width: 318px
- height: 156px
entity_picture:
- width: 318px
- position: absolute
name:
- z-index: 4
custom_fields:
background1:
- z-index: 3
- bottom: 0px
- left: 0px
- position: absolute
- width: 308px
- height: 75px
- background-image: >
[[[ return `linear-gradient(to top, var(--card-background-color), var(--card-background-color-transparent)` ]]]
background2:
- z-index: 3
- bottom: 0px
- left: 0px
- position: absolute
- width: 154px
- height: 150px
- background-image: >
[[[ return `linear-gradient(to right, var(--card-background-color), var(--card-background-color-transparent)` ]]]
climate: #for climate entities
template:
- base
- circle
state_display: >-
[[[ return `Set: ${(entity.attributes.temperature)}°C` ]]]
variables:
circle_input: >
[[[ return `${(entity.attributes.current_temperature)}°C` ]]]
state:
- styles:
card:
- filter: opacity(25%)
icon:
- filter: grayscale(100%)
state:
- filter: opacity(0%)
value: unavailable
default: #a starting card if you're not sure what to use
template:
- base
color: var(--state-icon-active-color)
hold_action:
action: more-info
state:
- styles:
card:
- filter: opacity(50%)
icon:
- filter: grayscale(100%)
value: 'off'
- styles:
card:
- filter: opacity(25%)
icon:
- filter: grayscale(100%)
value: unavailable
tap_action:
action: toggle
garbage: #for use with the Garbage Collection (HACS) sensors
template:
- base
tap_action:
action: none
state_display: >-
[[[ if (entity.state == "2") return `in ${entity.attributes.days} days`;
else if (entity.state == "1") return "Tomorrow";
else if (entity.state == "0") return "Today"
]]]
greeting: #Good Morning/Afternoon/Evening
name: >-
[[[ var d = new Date(); var n = d.getHours(); if (0 <= n && n
< 12) return "Good morning."; else if (12 <= n && n < 18)
return "Good afternoon."; else if (18 <= n && n < 24) return
"Good evening."; else return "ERROR";]]]
styles:
card:
- width: 300px
- height: 60px
- background: none
- box-shadow: none
name:
- justify-self: start
- margin-left: 10px
- font-weight: normal
- font-size: xx-large
tap_action:
action: none
header: #a basic header for pages
tap_action:
action: none
show_icon: false
show_name: true
show_state: false
styles:
card:
- width: 300px
- height: 40px
- background: none
- box-shadow: none
name:
- justify-self: start
- margin-left: 10px
- font-weight: lighter
- font-size: x-large
light: #for light entities. brightness displayed in dynamic circle
template:
- base
- circle_dynamic
variables:
circle_input: >
[[[ return Math.round(entity.attributes.brightness / 2.54); ]]]
color: auto-no-temperature
hold_action:
action: more-info
show_state: true
state:
- styles:
card:
- filter: opacity(50%)
icon:
- filter: grayscale(100%)
label:
- background-color: var(--card-background-color)
value: 'off'
- styles:
card:
- filter: opacity(25%)
icon:
- filter: grayscale(100%)
label:
- background-color: var(--card-background-color)
value: unavailable
- operator: template
value: >-
[[[ if (entity.attributes.rgb_color == "255,255,255") return true; else return false ]]]
styles:
icon:
- color: var(--state-icon-active-color)
tap_action:
action: toggle
media: #for media players; set up for my Sonos system - cards will appear faded if media is paused
template:
- base
state_display: >-
[[[ if (entity.attributes.media_title == undefined) return `${entity.state}`[0].toUpperCase() + `${entity.state}`.substring(1);
else if (entity.attributes.media_artist == undefined) return `${entity.state}`[0].toUpperCase() + `${entity.state}`.substring(1) + `: ${entity.attributes.media_title}`;
else return `${entity.state}`[0].toUpperCase() + `${entity.state}`.substring(1) + `: ${entity.attributes.media_title} - ${entity.attributes.media_artist}` ]]]
state:
- styles:
card:
- filter: opacity(50%)
icon:
- filter: grayscale(100%)
value: 'paused'
- styles:
card:
- filter: opacity(25%)
icon:
- filter: grayscale(100%)
value: unavailable
custom_fields:
background1: " "
background2: " "
styles:
card:
- background-size: cover
- background-image: '[[[ return `url("${entity.attributes.entity_picture}")` ]]]'
- background-position: center center
- width: 308px
- height: 150px
icon:
- z-index: 2
name:
- z-index: 2
state:
- z-index: 2
custom_fields:
background1:
- top: 75px
- right: 0px
- position: absolute
- width: 308px
- height: 75px
- background-image: >
[[[ return `linear-gradient(to top, var(--card-background-color), var(--card-background-color-transparent)` ]]]
background2:
- top: 0px
- right: 154px
- position: absolute
- width: 154px
- height: 150px
- background-image: >
[[[ return `linear-gradient(to right, var(--card-background-color), var(--card-background-color-transparent)` ]]]
person: #for person entities
template:
- base
show_entity_picture: true
state:
- operator: '!='
styles:
card:
- filter: opacity(50%)
icon:
- filter: grayscale(100%)
value: home
styles:
icon:
- border-radius: 50%
sensor: #for sensor entities. needs work
template:
- base
task: #keep track of chores, displays when chore last done using an input_datetime, tap the card to reset the timer
#cards go red when the chore is overdue, defaults to 7 days
#to change the overdue limit on a specific card in ui-lovelace.yaml, add:
#variables:
# days: x (where x is number of days)
template:
- base
variables:
days: 7
state_display: >-
[[[
return html`
<ha-relative-time
.hass="${hass}"
.datetime="${(entity.attributes.timestamp)*1000}"
></ha-relative-time>`
]]]
state:
- operator: template
value: >-
[[[ if (Math.round(Date.now()/1000) - entity.attributes.timestamp > (variables.days*86400)) return true; else return false ]]]
styles:
icon:
- color: var(--label-badge-red)
tap_action:
action: call-service
service: input_datetime.set_datetime
service_data:
entity_id: entity
timestamp: '[[[return Math.round(Date.now()/1000);]]]'
confirmation:
text: '[[[ return `Complete this task?` ]]]'
hold_action:
action: more-info
weather: #for weather entities. I have only tested with Dark Sky
template:
- base
- circle
variables:
circle_input: '[[[ return `${entity.attributes.temperature}°C` ]]]'
show_state: true
state:
- icon: 'mdi:weather-night'
value: clear-night
- icon: 'mdi:weather-cloudy'
value: cloudy
- icon: 'mdi:weather-fog'
value: fog
- icon: 'mdi:weather-hail'
value: hail
- icon: 'mdi:weather-lightning'
value: lightning
- icon: 'mdi:weather-lightning-rainy'
value: lightning-rainy
- icon: 'mdi:weather-partly-cloudy'
value: partlycloudy
- icon: 'mdi:weather-pouring'
value: pouring
- icon: 'mdi:weather-rainy'
value: rainy
- icon: 'mdi:weather-snowy'
value: snowy
- icon: 'mdi:weather-snowy-rainy'
value: snowy-rainy
- icon: 'mdi:weather-sunny'
value: sunny
- icon: 'mdi:weather-windy'
value: windy
- icon: 'mdi:weather-windy-variant'
value: windy-variant
- icon: 'mdi:weather-cloudy-alert'
value: exceptional
Examples of cards
Alarm:
Camera:
Climate:
Default:
Garbage:
Greeting:
Header:
Light:
Media:
Person:
Sensor:
Task:
Weather:
Hey
So im using the above but have warnings in my logs about the automation already running. Do you ge this?
Yes. Doesn’t affect functionality but something I’ve been meaning to fix. Usually happens if several lights get turned on simultaneously. Will fix at some point
EDIT: I don’t get this any more because I use nodered for the automation - however try this and let me know if it works:
- alias: Update Light Count
initial_state: True
mode: restart #this line is new
trigger:
platform: event
event_type: state_changed
condition:
condition: template
value_template: "{{ trigger.event.data.entity_id.startswith('light') }}"
action:
service: homeassistant.update_entity
entity_id: sensor.active_lights
Did the floors get mopped yet? I laughed when I saw that todo as mine need mopping too.
4 months later and the floors still need mopping
I might be stupid here since I’ve never used YAML mode for my dashboards. But after following the updated instructions my new dashboard is nothing. Just a blank page, not even the Home Assistant header. I’m thinking I’m supposed to add cards at this stage, through YAML? Or can I convert it to storage mode now and add cards through the UI?