A different take on designing a Lovelace UI

Hi @Mattias_Persson can you please tell me where i can reduce the font size of the headers?
Like Verdagsrum, Studio, Sovrum

thanks

I tried that exactly and this is what I end up with. The bar is shifted to the right and still overlapping my text. I don’t know if this has anything to do with the “extra_styles” template. I even tried commenting that out in my base template and it didn’t fix anything. I appreciate you trying to help by the way. Thank you for that.

image

I didn’t edit anything, just added custom fields to the base button card. Maybe this is the easiest way

CleanShot 2023-11-27 at 21.03.53@2x

      type: custom:button-card
      entity: sensor.kitchen_active
      name: Kitchen
      template:
        - base
        - icon_noodles

      custom_fields:
        timer:
          card:
            type: custom:timer-bar-card
            layout: full_row
            text_width: 0px
            entity: timer.dishwasher_remaining
            bar_direction: rt
            invert: true
      styles:
        card:
          - overflow: hidden
        custom_fields:
          timer:
            - display: block
            - position: absolute
            - top: 97%
            - left: -10%
            - width: 120%

That definitely gets me closer, but still not perfect. It seems that the change to “top” moves the location of the bar. But any change to the value has no effect whatsoever.

image

This is my current fan template code now.

fan:
  template:
    - base
  variables:
    timer: >
      [[[ return states["timer.fan_delay"].state == 'active' ]]]
  tap_action:
    action: toggle
  double_tap_action:
    action: more-info
    entity: timer.fan_delay
  styles:
    card:
      - overflow: hidden
    custom_fields:
      circle:
        - display: initial
        - width: 88%
        - margin: -3% 2% 0 0
        - justify-self: end
        - opacity: 1
      timer:
        - display: block
        - position: absolute
        - top: 75%
        - left: -13%
        - width: 125%
  custom_fields:
    circle:
      card:
        type: custom:button-card
        entity: timer.fan_delay
        aspect_ratio: 1/1
        show_icon: false
        show_name: false
        show_state: |
          [[[
            if (states['timer.fan_delay'].state == 'active')
              return true;
            return false;
          ]]]
        styles:
          card:
            - border: none
          state:
            - font-size: 12px
            - color: var(--contrast1)
    timer:
      card:
        type: custom:timer-bar-card
        entity: timer.fan_delay
        layout: full_row
        text_width: 0px
        invert: true
        bar_foreground: >
          [[[
            return variables.timer
                ? '#3182b7'
                : '#97989c';
          ]]]
        bar_background: var(--contrast20)
        modifications:
          - elapsed: 90%
            bar_foreground: 'rgb(255,0,0)'

Are your “base” and “extra_styles” templates the same as Mattias’s code? Or have you applied other changes. Once again, I really appreciate your help. I am new to all of the yaml and css stuff and really just piecing stuff together from google searches and lots of trial and error.

Did you delete the timer section in your base template grid area? And you have show_label: false in base, so I don’t know why you have row “l” in grid area … I would delete these 2 things and try my example above.


my base template:

Oh man!!! Thank you. You are a genius. I removed the timer row from my base template and now it works the way I want… Almost. It works on desktop, but I see no progress bar on mobile.

As to why I have both “l” and “s” in my base code and then both set to false. It is because on some cards I want to use the label and on others I want to show the state, but never both. I set them to false in the base code and then turn the secondary info I want on per card. I admit it may be clunky, but it is working. And now so is my timer card. Thank you so much for your help and patience.

image

Now onto my next customization. I have an idea and may need some help getting it to work.

Great that it worked out. For me I would recommend not to modify the base template. If you want to add more things, it would be better to create a new template and edit in the new one. :+1:

It worked out on the desktop, but I don’t see the progress bar on the mobile. Through testing it seems that the percentage is applied differently between desktop, tablet, and mobile. Not sure why since the cards are all rendered with a 1/1 aspect ratio.

adjust position for mobile

      styles:
        card:
          - overflow: hidden
        custom_fields:
          timer:
            - display: block
            - position: absolute
            - top: >
                [[[
                  return window.matchMedia('(max-width: 800px)').matches
                    ? '77%'
                    : '97%';
                ]]]
            - left: -15%
            - width: 120%

Thank you again so much for all of your help. I have been able to make all of my changes an it is all working. My final question has to do with another card. I have a presence cards that looks at my family group and the circle reports the number of family members at home if there is anyone at all.

image

  - type: custom:button-card
    template:
      - base
      - icon_home2
    variables:
      timer: >
        [[[ return states["timer.leaving"].state == 'active' ]]]
      circle_input: >
        [[[ return states["sensor.people_home"].state]]]
    entity: group.humans
    name: Presence
    show_state: true
    state_display: >
      [[[
        if (entity) {
            return variables.state === 'home'
                ? variables.translate_home
                : variables.state === 'not_home'
                    ? variables.translate_not_home
                    : variables.state;
        }
        return variables.translate_unknown;
      ]]]
    tap_action:
      action: more-info
    styles:
      card:
        - --c-stroke-color-on: var(--contrast1)
        - --c-stroke-color-off: none
        - --c-fill-color-on: none
        - --c-fill-color-off: none
        - --c-stroke-width: 2.3
        - --c-stroke-width-dragging: 4
        - --c-font-color-on: var(--contrast1)
        - --c-font-color-off: none
        - --c-font-size: 14px
        - --c-font-weight: 700
        - --c-letter-spacing: -0.02rem
      custom_fields:
        circle:
          - display: initial
          - width: 88%
          - margin: -3% 2% 0 0
          - justify-self: end
          - opacity: 1
        timer:
          - display: block
          - position: absolute
          - top: >
              [[[
                return window.matchMedia('(max-width: 1200px)').matches
                    ? '82%'
                    : '89%';
              ]]]
          - left: -13%
          - width: 125%
    custom_fields:
      circle: >
        [[[
          if (variables.state_on) {
            return `
              <svg viewBox="0 0 50 50">
                <circle cx="25" cy="25" r="22" stroke='var(--c-stroke-color-on)' stroke-width='var(--c-stroke-width)' fill='var(--c-fill-color-on)' />
                <text x="50%" y="54%" fill='var(--c-font-color-on)' font-size='var(--c-font-size)' text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${variables.circle_input}</text>
              </svg>
            `;
          } else {
            return `
              <svg viewBox="0 0 50 50">
                <circle cx="25" cy="25" r="22" stroke='var(--c-stroke-color-off)' stroke-width='var(--c-stroke-width)' fill='var(--c-fill-color-off)' />
                <text x="50%" y="54%" fill='var(--c-font-color-off)' font-size='var(--c-font-size)' text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${entity.state}<tspan font-size='var(--c-unit-font-size)'>lx</tspan></text>
              </svg>
            `;
          }
        ]]]
      timer:
        card:
          type: custom:timer-bar-card
          entity: timer.leaving
          layout: full_row
          text_width: 0px
          invert: true
          bar_foreground: >
            [[[
              return variables.timer
                  ? '#3182b7'
                  : 'none';
            ]]]
          bar_background: none
          modifications:
            - elapsed: 90%
              bar_foreground: 'rgb(255,0,0)'

When everyone leaves it starts another timer. That part is working. As you can see, when noone is home the circle disappears, but I would like to show the timer countdown while it is active. I just can’t figure out how to replace the sensor circle with another button card for the timer.

image

If you have the time and are willing to help me figure this one out as well, I would be eternally grateful. Thanks in advance.

Can anyone help, I’ve got this amazing dashboard up and running :slight_smile: and in the process of adapting it to my needs. but cannot figure out how to change the button icons?

I’m sure simply my ignorance of custom button cards, When I try it doesn’t display anything

Can anyone help me to finish my Playstation 4 button card please, as I can’t quite get the styling to work as per this post earlier in the thread:

I would like a combination of the styling from conditional_media and media templates, so perhaps creating a separate template for media_ps4 would be better? When I reference conditional_media in my button card config, like this:

type: custom:button-card
entity: media_player.playstation_4
name: Playstation
template:
  - conditional_media
  - icon_ps4
tap_action:
  action: more-info

I get the blurred overlay at the bottom correctly, which shows the current game title as a marquee:

image

however I would like to hide both the entity name “PlayStation” and the icon when a game is either “playing” or “paused” (due to limited space on the card). Currently when in a state of standby, my card shows like this:

image

If i reference the template media instead of conditional_media the standby appearance is correct:

image

but the appearance when “playing” doesn’t have the blur overlay or marquee - although it does correctly hide the icon:

image

is anyone able to help me piece together the parts I need from the different templates in order to achieve this please?

Is it also possible to add a red spinning fan when the airconditioner is heating in stead of cooling? i cant manage to make this work :frowning:

That was something I struggled with when I first started down this rabbit hole. This dashboard doesn’t use the typical mdi icons that the rest of Home Assistant does. At least not directly. It uses SVG icons.

You can find all of the existing icons in the icons.yaml file. Each of them is named as “icon_xxx” and you would include this under the template for each button card.

To make additional ones, what I have done is to copy a previous icon and replace the SVG path for my new icon. This post provides a good tutorial for how to do it.

1 Like

Thanks so much for pointing me to those links, I’d half worked it out after some trial and error!

@mattpitts74 You can use all mdi icons in HA or all custom icons you have installed in HA, for example this way.


type: custom:button-card
    entity: alarm_control_panel.aqara_hub
    hold_action: !include ../shared/honeycomb/security.yaml
    template:
      - alarm
      - icon_alarm

Don’t mix different templates together. Use a conditional card for the button. You have a basic grid for 4 buttons. Actually you will have 5 of them, but that one will only be displayed under some conditions. So the grid will still have only 4.

I use this way myself, for example for the kitchen button, when the dishwasher is not working, it is the button for the lights, and if it is working it shows the button for the dishwasher instead. Use it like this

- type: grid
  title: Masters
  view_layout:
    grid-area: mainrooms
  columns: 2
  cards:
    # Button 1 with conditional state
    # SENSOR HOME = 0 NOBODYS HOME 
    - type: conditional
      conditions:
        - entity: sensor.people_home
          state: 0 #your sensor.people_home = 0
      card:
       # YOUR BUTTON WHEN NO ONES HOME
        type: custom:button-card
        entity: group.humans
        name: Presence
        template:
          - base
          - icon_tmp

    # SENSOR HOME STATE IS NOT 0 SOMEONES HOME 
    - type: conditional
      conditions:
        - entity: sensor.people_home
          state_not: 0 #your sensor.people_home with state not 0
      card:
       # YOUR BUTTON CONFIG WITH SOMEONES HOME
        type: custom:button-card
        entity: group.humans
        name: Presence
        template:
          - base
          - icon_tmp

    # button 2
    - type: custom:button-card
      entity: sensor.blah


    # button 3
    - type: custom:button-card
      entity: sensor.blah

    # button 4
    - type: custom:button-card
      entity: sensor.blah
1 Like

Hello everybody,

I would like to realize a custom To-Do list card with the design of matt’s theme or common household tasks, like cleaning, laundry … The tasks ar based on input_booleans.

Here is my code:

1.) tasks-card.js (added as a ressource in home assistant):

import {
  LitElement,
  html,
  css,
} from "https://unpkg.com/[email protected]/lit-element.js?module";

class ToDoListCard extends LitElement {
  static get properties() {
    return {
      hass: {},
      user1Entities: { type: Array },
      user2Entities: { type: Array },
      hasTasks: { type: Boolean },
    };
  }

  render() {
    const user1Tasks = this._getTasks(this.user1Entities, 'User 1');
    const user2Tasks = this._getTasks(this.user2Entities, 'User 2');

    this.hasTasks = user1Tasks.length > 0 || user2Tasks.length > 0;

    return html`
      <wired-card elevation="2" class="${this.hasTasks ? 'has-tasks' : 'no-tasks'}">
        ${user1Tasks.length > 0
          ? html`
              <div>
                <h3>User 1 Aufgaben:</h3>
                ${this._renderTasks(user1Tasks)}
              </div>
            `
          : ''}
        ${user2Tasks.length > 0
          ? html`
              <div>
                <h3>User 2 Aufgaben:</h3>
                ${this._renderTasks(user2Tasks)}
              </div>
            `
          : ''}
      </wired-card>
    `;
  }

  _getTasks(entities, userName) {
    return entities.filter(entity =>
      this.hass.states[entity].state === "on"
    ).map(entity => {
      const stateObj = this.hass.states[entity];
      return {
        entity,
        friendlyName: stateObj.attributes.friendly_name,
        user: userName,
      };
    });
  }

  _renderTasks(tasks) {
    return html`
      <ul>
        ${tasks.map(task => html`
          <li>
            <ha-icon .icon="${'mdi:' + task.entity.split('.')[1]}" style="color: #4b5254;"></ha-icon>
            (${task.user}) ${task.friendlyName}
          </li>
        `)}
      </ul>
    `;
  }

  setConfig(config) {
    if (!config.user1Entities || !config.juliaEntities) {
      throw new Error("Entities für User 1 und User 2 müssen definiert sein.");
    }
    this.user1Entities = config.user1Entities;
    this.user2Entities = config.user2Entities;
  }

  getCardSize() {
    return 4; // Je nach Anforderungen anpassen
  }

  static get styles() {
    return css`
      :host {
        font-family: 'SF Pro Text', 'Roboto', system-ui, sans-serif;
        border-radius: 10% !important;
      }
      .has-tasks {
        background-color: rgba(255, 255, 255, 0.85);
      }
      .no-tasks {
        background-color: #3b4041;
      }
      wired-card {
        padding: 16px;
        display: block;
        font-size: 18px;
      }
      ul {
        list-style-type: none;
        padding: 0;
      }
      li {
        display: flex;
        align-items: center;
        margin-bottom: 8px;
      }
      ha-icon {
        margin-right: 8px;
      }
    `;
  }
}
customElements.define("to-do-list-card", ToDoListCard);

And here is the code in ui-lovelace.yaml:

      - type: grid
        title: To-Do's
        view_layout:
          grid-area: vardagsrum
        columns: 1
        cards:

          - type: 'custom:to-do-list-card'
            user1Entities:
            - input_boolean.user1_aufraumen
            - input_boolean.user1_boden_wischen
            - input_boolean.user1_deko_tauschen
            - input_boolean.user1_deko_kisten_wechseln
            - input_boolean.user1_dusche_putzen
            - input_boolean.user1_einkaufe_versorgen
            - input_boolean.user1_esstisch_putzen
            - input_boolean.user1_fenster_putzen
            - input_boolean.user1_fruhstuck_richten
            - input_boolean.user1_kaffeemaschine_entkalken
            - input_boolean.user1_kuchenarbeitsplatte_putzen
            - input_boolean.user1_kuhlschrank_ausmisten
            - input_boolean.user1_mull_rausbringen_und_neue_tuten_einsetzen
            - input_boolean.user1_pflanzen_versorgen
            - input_boolean.user1_schrank_und_zimmerturen_wischen
            - input_boolean.user1_spiegel_abwischen
            - input_boolean.user1_spul_und_waschbecken_reinigen
            - input_boolean.user1_spulmaschine_ein_und_ausraumen
            - input_boolean.user1_staubsaugen
            - input_boolean.user1_staubwischen
            - input_boolean.user1_tiere_versorgen
            - input_boolean.user1_toilette_putzen
            - input_boolean.user1_vorratsschrank_ausmisten
            - input_boolean.user1_wasche_hochbringen_und_sortieren
            - input_boolean.user1_wasche_waschen_und_aufhangen
            user2Entities:
            - input_boolean.user2_aufraumen
            - input_boolean.user2_boden_wischen
            - input_boolean.user2_deko_tauschen
            - input_boolean.user2_deko_kisten_wechseln
            - input_boolean.user2_dusche_putzen
            - input_boolean.user2_einkaufe_versorgen
            - input_boolean.user2_esstisch_putzen
            - input_boolean.user2_fenster_putzen
            - input_boolean.user2_fruhstuck_richten
            - input_boolean.user2_kaffeemaschine_entkalken
            - input_boolean.user2_kuchenarbeitsplatte_putzen
            - input_boolean.user2_kuhlschrank_ausmisten
            - input_boolean.user2_mull_rausbringen_und_neue_tuten_einsetzen
            - input_boolean.user2_pflanzen_versorgen
            - input_boolean.user2_schrank_und_zimmerturen_wischen
            - input_boolean.user2_spiegel_abwischen
            - input_boolean.user2_spul_und_waschbecken_reinigen
            - input_boolean.user2_spulmaschine_ein_und_ausraumen
            - input_boolean.user2_staubsaugen
            - input_boolean.user2_staubwischen
            - input_boolean.user2_tiere_versorgen
            - input_boolean.user2_toilette_putzen
            - input_boolean.user2_vorratsschrank_ausmisten
            - input_boolean.user2_wasche_hochbringen_und_sortieren
            - input_boolean.user2_wasche_waschen_und_aufhangen
            template:
              - base 
            style:
              border-radius: 10%;

In terms of functionality it is working. But I struggle with theming.

1.) Size: the card should always have the exact size (same size as he swipe card in matt’s Theme. Never more, never less. I couldn’t find the percentage (is it 30%?)

2.) The background, font- (color and style) and icon color should be the same as the other buttons in matt’s theme (background color white when there are tasks, background color grey (?) when there are no tasks.

3.) Also the Card should have the rounded corners - exactly like the other cards.

Ist shouldn’t be a big deal but I tried for hours to get the styling done without success, so I appreciate any help!

Optional:
If anybody has an Idea on how to deal with the scenario of too many tasks to display inside the card so the space is not enough - that would also be great. I guess I could limit the number of tasks to display in the card and implement a popup …

Thank you very much in advance!

Hi, Have you got the overflow issue sorted?

Thank you so much. For some reason I thought the conditional cards this way would need to be stacked together some other way. It is working as you said. I really appreciate all of your help again. Is there a way I can buy you a coffee or donate something for your time?

1 Like