A different take on designing a Lovelace UI

it looks like it is not enough unfortunately.
I cleaned everything and I’m using new theme and button_card_templates.

sidebar template is:

  • sensor:
    • unique_id: sidebar
      state: template
      attributes:
      time: >

      pippo:topolino

what is weird is that when i look at the time span font-size is defined as inside the extra_styles.yaml

  1. font-size: calc(var(–button-card-font-size) - var(–z-axis-adjustment));

any idea?

the sidebar doesn’t load any styles from extra_styles.yaml so it has to be this

AWESOME Mattias! that fixed many problems, can you explain me the rationale behind that? I’m not undrstanding it!

Hi Mattias,

Like your work very mutch but i like to change the theme on it.
I found out that lot’s of your config is hard coded so i have to change a lot of the code.
To give you a impression it looks like this.


But i can also switch to your theme and everything is like you designed it

But i ran in to a strange problem.
If i use a Shelly dimmer the light on the icon is blue in stat of yellow.
The code below is where it’s coming from.

if (entity) {
          let hs = entity.attributes.hs_color === undefined,
              h = hs || entity.attributes.hs_color[0],
              s = hs || entity.attributes.hs_color[1],
              l_min = 28,
              l_max = 48,
              l_calc = entity.attributes.brightness / 2.54 * (l_max - l_min) / 100 + l_min;
          var light_color = 
              entity.attributes.color_mode === 'color_temp' ||
              entity.attributes.color_mode === 'brightness' ||
              entity.attributes.color_mode === 'unknown'
                  ? `hsl(204, 58%, ${l_calc}%);`
                  : `hsl(${h}, ${s}%, ${l_calc}%);`;
      }

I learned that this part is hard coded blue.

hsl(204, 58%, ${l_calc}%);

But sometimes out of the blue the light turned in to yellow.
But the when i switch to your theme it stays yellow

And it also can change out of the blue to blue.

If i change in the icon path this <path class="${state} light-color"
into <path class="${state} var(–button-card-light-color-no-temperature)

Then it’s working like it supposed to.
I don’t get the picture.
I’m completely lost here.

Hope you can help me with this part.
I still have to do the media but the rest is working fine now.
Thanks,
Karel

1 Like

nice work, im stealing the plex footer, what is driveing the cpu, gpu and ram usage on the plex popup

Right now I’m trying out the HASS.Agent for my Windows-based Plex server and my PC.

1 Like

Thanks, i will go with monitoring the usage on my docker container.
if you are using the enargy dashboard, thats a nice one to add to the footer.

Spot

  icon_light_recessed:
    styles:
      custom_fields:
        icon:
          - width: 95%
          - margin-left: -12%
          - margin-top: -20%
    custom_fields:
      icon: >
        [[[
          let style = `
            <style>
              @keyframes on {
                from {
                  transform: scaleY(0);
                }
                to {
                  transform: scaleY(1);
                }
              }
              .on {
                animation: on 0.1s;
                transform-origin: -100% 46%;
                animation-fill-mode: forwards;
              }
              @keyframes off {
                from {
                  transform: scaleY(1);
                }
                to {
                  transform: scaleY(0);
                }
              }
              .off {
                animation: off 0.1s;
                transform-origin: -100% 46%;
                animation-fill-mode: forwards;
              }
            </style>
          `,
            path = `
              <path fill="#9da0a2" d="M25 15.618c-9.382 0-17.058 4.196-17.058 9.382S15.618 34.382 25 34.382 42.058 30.186 42.058 25 34.382 15.618 25 15.618m8.18 4.77c0 .682-3.233 2.975-8.18 2.975s-7.783-2.35-7.725-3.131l.623-.213c1.979-.58 4.441-.989 7.102-.989s5.015.358 6.994.938M25 30.97c-8.325 0-13.647-3.531-13.647-5.97 0-1.177 1.955-2.783 4.173-3.994l1.146-.53c.296.95 3.374 3.293 8.343 3.319 4.808.025 8.464-2.256 8.701-3.213l1.12.491c2.218 1.211 3.811 2.749 3.811 3.926 0 2.439-5.322 5.97-13.647 5.97z"/>
            `,
            gradient = `
              <radialGradient id="lsource" cx="25.165" cy="13.615" fx="25.165" fy="13.615" r="7.941" 
                gradientTransform="matrix(-0.00353534,0.70731769,-1.7278701,-0.00863629,48.77824,1.4653142)" 
                gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e6deb3"/><stop offset="1" stop-color="#e6deb3" stop-opacity="0"/>
              </radialGradient>
              <radialGradient id="dlcup" cx="24.933" cy="10.064" fx="24.933" fy="10.064" r="13.627" 
                gradientTransform="matrix(-1.3891264,0.01690265,-0.01282326,-1.0538672,59.802527,28.064254)" 
                gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e6deb3"/><stop offset="1" stop-color="#e6deb3" stop-opacity="0"/>
              </radialGradient>
            `;
          if (variables.state === 'on') {
            return `
              <svg viewBox="0 0 50 50"> ${style} ${gradient} 
                <path d="M24.207 23.332c-1.704-.092-3.315-.477-4.653-1.112-1.091-.518-1.964-1.207-2.209-1.741-.101-.221-.095-.228.276-.353 1.064-.357 2.666-.7 4.099-.878 1.078-.134 1.773-.18 2.95-.194 1.526-.018 2.747.053 4.066.238 1.49.208 2.704.491 3.84.895l.594.209c.003 0-.008.041-.026.091-.16.454-1.226 1.213-2.442 1.739-1.916.829-4.256 1.227-6.496 1.107z" fill="url(#lsource)" paint-order="fill markers stroke"/>
                <path d="M23.975 30.944c-3.988-.158-7.549-1.114-10.046-2.696-1.863-1.18-2.845-2.635-2.484-3.678.295-.852 1.455-1.956 3.172-3.019.549-.34.856-.505 1.464-.788.562-.262.588-.272.6-.225.047.187.466.668.853.978 1.576 1.264 3.975 2.086 6.594 2.259a20.24 20.24 0 0 0 1.905 0c2.442-.162 4.841-.898 6.519-2.001.577-.379 1.047-.829 1.138-1.089.013-.038.036-.068.05-.068.058 0 1.139.492 1.367.623.615.352 1.202.748 1.72 1.16.387.308 1.021.942 1.255 1.256.211.283.442.725.503.964a1.97 1.97 0 0 1-.01.846c-.388 1.435-2.257 2.947-4.953 4.007-2.011.79-4.343 1.272-6.983 1.443a61.05 61.05 0 0 1-2.663.03z" fill="url(#dlcup)" paint-order="fill markers stroke"/>
                ${path}
              </svg>
            `;
          }
          if (variables.state === 'off') {
            return `
              <svg viewBox="0 0 50 50"> ${style}
                ${path}
              </svg>
            `;
          } else {
            return `
              <svg viewBox="0 0 50 50"> ${style} 
                ${path}
              </svg>
            `;
          }
        ]]]

AC

  icon_climate:
    styles:
      custom_fields:
        icon:
          - width: 90%
          - margin-top: -5%
          #- margin-left: -12%
          
    custom_fields:
      icon: >
        [[[
          let style = `
            <style>
              @keyframes bottom-flap {
                0% {
                    transform: rotateX(100deg);
                }
                100% {
                    transform: rotateX(0deg);
                }
              }
              .bottom-flap{
                animation: bottom-flap 4s ease-in-out;
                transform-origin: top;
                transform-box: fill-box;
              }
              @keyframes flow-control {
                0% {
                    transform: rotateX(100deg);
                }
                50% {
                    transform: rotateX(90deg);
                }
                100% {
                    transform: rotateX(0deg);
                }
              }
              .flow-control{
                animation: flow-control 5s ease-in-out;
                transform-origin: top;
                transform-box: fill-box;
              }
            </style>
          `,
            path = `
              <path fill="url(#aircon_paint2_linear_1_2)" id="aircon-unit" d="M1.21287 15.9548C0.98527 15.9548 0.801849 16.1382 0.801849 16.3658V31.8749C0.801849 32.1025 0.98527 32.2859 1.21287 32.2859H42.9177V32.5189C42.9177 32.6006 43.0462 32.6648 43.2055 32.6648H46.0787C46.062 32.7554 46.0539 32.8473 46.0546 32.9393V33.6502C46.0546 33.8685 46.0907 34.0452 46.1362 34.0452H47.3961C47.4416 34.0452 47.4778 33.8685 47.4778 33.6502V32.9393C47.4784 32.8473 47.4703 32.7554 47.4537 32.6648H47.6491C47.8085 32.6648 47.937 32.6006 47.937 32.5189V32.2859H48.7872C49.0148 32.2859 49.1982 32.1025 49.1982 31.8749V16.3658C49.1982 16.1382 49.0148 15.9548 48.7872 15.9548H1.21287ZM2.72978 23.7576H47.3613C47.4001 23.7576 47.4322 23.7897 47.4322 23.8312V24.4404C47.4322 24.4819 47.4001 24.5167 47.3613 24.5167H2.72844C2.68961 24.5167 2.65748 24.4819 2.65748 24.4404V23.8312C2.65748 23.7897 2.68961 23.7576 2.72844 23.7576H2.72978Z"/>
            `,
            gradient = `
              <linearGradient id="aircon_paint0_linear_1_2" x1="22.9118" y1="35.1798" x2="22.9118" y2="32.2111" gradientUnits="userSpaceOnUse">
                <stop stop-color="#CCCCCC"/>
                <stop offset="1" stop-color="#585858"/>
              </linearGradient>
              <linearGradient id="aircon_paint1_linear_1_2" x1="22.8125" y1="32.9688" x2="22.8125" y2="34.5938" gradientUnits="userSpaceOnUse">
                <stop stop-color="#4D4D4D"/>
                <stop offset="1" stop-color="#848484"/>
              </linearGradient>
              <linearGradient id="aircon_paint2_linear_1_2" x1="25.375" y1="14.625" x2="25.9375" y2="60.75" gradientUnits="userSpaceOnUse">
                <stop stop-color="#CCCCCC"/>
                <stop offset="1" stop-color="#2C2C2C"/>
              </linearGradient>
            `;
          if (variables.state !== 'off') {
            return `
              <svg viewBox="0 0 50 50"> ${gradient} 
                ${path}
                <path id="bottom-flap" d="M3.38065 31.8435H40.2322C40.2647 31.8435 40.2908 31.8697 40.2908 31.9022C40.3213 32.714 40.6142 33.7062 40.7243 34.4498L2.88846 34.4536C2.969 33.7186 3.32197 32.7133 3.32197 31.9022C3.32197 31.8697 3.34814 31.8435 3.38065 31.8435V31.8435Z" fill="url(#aircon_paint0_linear_1_2)"/>
                <path id="flow-control" d="M4.01023 31.2383H39.6673C39.6988 31.2383 39.7241 31.265 39.7241 31.2982C39.7756 33.3753 40.037 33.1396 40.1436 33.8985L3.534 33.9025C3.61192 33.1523 3.88716 33.4851 3.95346 31.2982C3.95346 31.265 3.97878 31.2383 4.01023 31.2383L4.01023 31.2383Z" fill="url(#aircon_paint1_linear_1_2)"/>
                ${style}
              </svg>
            `;
          }
          if (variables.state === 'off') {
            return `
              <svg viewBox="0 0 50 50"> ${style}
                <path fill="#9da0a2" id="aircon-unit" d="M1.21287 15.9548C0.98527 15.9548 0.801849 16.1382 0.801849 16.3658V31.8749C0.801849 32.1025 0.98527 32.2859 1.21287 32.2859H42.9177V32.5189C42.9177 32.6006 43.0462 32.6648 43.2055 32.6648H46.0787C46.062 32.7554 46.0539 32.8473 46.0546 32.9393V33.6502C46.0546 33.8685 46.0907 34.0452 46.1362 34.0452H47.3961C47.4416 34.0452 47.4778 33.8685 47.4778 33.6502V32.9393C47.4784 32.8473 47.4703 32.7554 47.4537 32.6648H47.6491C47.8085 32.6648 47.937 32.6006 47.937 32.5189V32.2859H48.7872C49.0148 32.2859 49.1982 32.1025 49.1982 31.8749V16.3658C49.1982 16.1382 49.0148 15.9548 48.7872 15.9548H1.21287ZM2.72978 23.7576H47.3613C47.4001 23.7576 47.4322 23.7897 47.4322 23.8312V24.4404C47.4322 24.4819 47.4001 24.5167 47.3613 24.5167H2.72844C2.68961 24.5167 2.65748 24.4819 2.65748 24.4404V23.8312C2.65748 23.7897 2.68961 23.7576 2.72844 23.7576H2.72978Z"/>
              </svg>
            `;
          } else {
            return `
              <svg viewBox="0 0 50 50"> ${style} 
                <path fill="#9da0a2" id="aircon-unit" d="M1.21287 15.9548C0.98527 15.9548 0.801849 16.1382 0.801849 16.3658V31.8749C0.801849 32.1025 0.98527 32.2859 1.21287 32.2859H42.9177V32.5189C42.9177 32.6006 43.0462 32.6648 43.2055 32.6648H46.0787C46.062 32.7554 46.0539 32.8473 46.0546 32.9393V33.6502C46.0546 33.8685 46.0907 34.0452 46.1362 34.0452H47.3961C47.4416 34.0452 47.4778 33.8685 47.4778 33.6502V32.9393C47.4784 32.8473 47.4703 32.7554 47.4537 32.6648H47.6491C47.8085 32.6648 47.937 32.6006 47.937 32.5189V32.2859H48.7872C49.0148 32.2859 49.1982 32.1025 49.1982 31.8749V16.3658C49.1982 16.1382 49.0148 15.9548 48.7872 15.9548H1.21287ZM2.72978 23.7576H47.3613C47.4001 23.7576 47.4322 23.7897 47.4322 23.8312V24.4404C47.4322 24.4819 47.4001 24.5167 47.3613 24.5167H2.72844C2.68961 24.5167 2.65748 24.4819 2.65748 24.4404V23.8312C2.65748 23.7897 2.68961 23.7576 2.72844 23.7576H2.72978Z"/>
              </svg>
            `;
          }
        ]]]

4 Likes

Thanks friend. Very nice

Great! Yes, read the note here GitHub - custom-cards/button-card: ❇️ Lovelace button-card for home assistant

I’m open to any suggestions :+1:

Here’s an explanation:

Maybe it’s better to set a theme variable for color_temp?

# pseudocode
if theme_variable is defined return mycolor
else return '--button-card-light-color'

themes.yaml

button-card-light-color-temp: hsl(204, 58%, 40%)

extra_styles.yaml replace first part with

if (entity) {
    if (entity.entity_id.split('.')[0] === 'light' && variables.state_on) {

        // theme variable and conditions
        let style = getComputedStyle(document.body),
            theme_var = style.getPropertyValue('--button-card-light-color-temp'),
            is_hsl = theme_var.startsWith('hsl('),
            is_color_temp = entity.attributes.color_mode === 'color_temp';

        if (is_hsl && is_color_temp && entity.attributes.brightness) {

            // calculate lightness in hsl
            let regex_pattern = /(\d+)(?!.*\d)/g,
                brightness = entity.attributes.brightness / 2.54,
                lightness = parseFloat(theme_var.match(regex_pattern)[0]),
                min = lightness - 10,
                max = lightness + 10,
                calc_lightness = brightness * (max - min) / 100 + min;

            var light_color = theme_var.replace(regex_pattern, calc_lightness);
        }
        else {
            var light_color = 'var(--button-card-light-color)';
        }
    }
}

Hi Mattias,
Wow that is very fast.
Yes that is the solution it’s working like a charm.
I have almost everything working now.
If I’m finished I can share the code with you if you like.

Thanks for your help.
Best regards,
Karel

3 Likes

@Mattias_Persson Thank you for the awesome update. I’m currently in the process of migrating my older config to your new baseline.

I noticed on my Galaxy Tab A7 (1200 x 2000px), that the Footer Buttons are slightly cut off at the Bottom and that I can scroll the entire Dashboard for a few mm in Fully Kiosk.

Is there any easy way, I can reduce some gaps/margins/paddings to make the entire dashboards a tiny bit less in height?

Thank you :slight_smile:

@Mattias_Persson How do you deal with updating other containers like: influxdb, plex etc and do you have any notifications? I understand you are using the watchtower to update the HA?

By not setting tags, so the images defaults to latest. I have notifications for ha releases and run watchtower at least once a month, which updates all containers. There’s nothing more to it.

https://containrrr.dev/watchtower/

‏‏‎ ‎‏‏‎ ‎

1 Like

Oh my god, your code lead me to the answer I have been looking for. I have spent two days trying to style the card.

You were so close! so I am assuming you already worked it out but if you didn’t.

button-card:nth-child(1): {
  *some CSS styling here*
}

You selected correctly. You just referenced #root again instead of styling directly. Thanks so much for helping me work this out.

@Mattias_Persson you are a legend. What you have created here is an absolute work of art and one of a kind. Thank you so much for contributing to this community.

1 Like

@Mattias_Persson

hi,

i try to make that the icon of my climates changes to another icon when the battery of the device is under 10%. the problem is here its no attribute of the entity its a always a different sensor for each device.

i tried it that way but i dont know how to make the template that i can use the code for all my climates,

here is my climate entity, and the battery sensor

climate.heizung_badezimmer
sensor.heizung_badezimmer_battery_level

the next is for example

climate.heizung_keller
sensor.heizung_keller_battery_level
[[[
          if (entity) {
            let battery = '',
            test = states['sensor.heizung_badezimmer_battery_level'];
              if (test.state > '10') 
              {
                return `
                  <svg viewBox="0 0 50 50">
                    <path d="M36.8 1.2v1.7a5.34 5.34 0 0 1-5.3 5.3H18.4a5.34 5.34 0 0 1-5.3-5.3V1.2c-2.6.4-4.7 2.8-4.7 5.6v36.5c0 3.1 2.6 5.7 5.7 5.7h21.8c3.1 0 5.7-2.6 5.7-5.7V6.8c0-2.8-2.1-5.2-4.8-5.6zm-1.7 35.6c-.2 0-.4 0-.5-.1-.4-.1-1.2-.2-2.4-.6-.5-.2-.8-.3-1.2-.4-.3-.1-.7-.3-1.4-.5-1-.4-1.5-.5-1.9-.6-.5-.1-1.1-.2-1.9-.2s-1.4.2-1.9.4c-1 .3-1.8.7-2.1.9l-.6.3a9.75 9.75 0 0 1-1.4.6c-.3.1-.9.3-1.6.3h-.3c-.4 0-1 0-2-.2-.3-.1-.6-.1-.8-.2v-2.7l1.3.3c.5.1 1.3.2 1.7.2.5 0 .9-.2 1.1-.2.4-.1.6-.2 1-.4.2-.1.4-.2.7-.4.4-.2 1.3-.7 2.5-1 .6-.2 1.4-.4 2.5-.5s2 .1 2.5.2c.6.1 1.2.3 2.2.7l1.5.5c.3.1.6.2 1 .4 1 .3 1.8.5 2.1.5h.1v2.7zm0-6c-.2 0-.4 0-.5-.1-.4-.1-1.2-.2-2.4-.6-.5-.2-.8-.3-1.2-.4-.3-.1-.7-.3-1.4-.5-1-.4-1.5-.5-1.9-.6-.5-.1-1.1-.2-1.9-.2s-1.4.2-1.9.4c-1 .3-1.8.7-2.1.9l-.6.3a9.75 9.75 0 0 1-1.4.6c-.3.1-.9.3-1.6.3h-.3c-.4 0-1 0-2-.2-.3-.1-.6-.1-.8-.2v-2.7l1.3.3c.5.1 1.3.2 1.7.2.5 0 .9-.2 1.1-.2.4-.1.6-.2 1-.4.2-.1.4-.2.7-.4.4-.2 1.3-.7 2.5-1 .6-.2 1.4-.4 2.5-.5s2 .1 2.5.2c.6.1 1.2.3 2.2.7l1.5.5c.3.1.6.2 1 .4 1 .3 1.8.5 2.1.5h.1v2.7zm0-6c-.2 0-.4 0-.5-.1-.4-.1-1.2-.2-2.4-.6-.5-.2-.8-.3-1.2-.4-.3-.1-.7-.3-1.4-.5-1-.4-1.5-.5-1.9-.6-.5-.1-1.1-.2-1.9-.2s-1.4.2-1.9.4c-1 .3-1.8.7-2.1.9l-.6.3c-.4.2-.8.4-1.4.6-.3.1-.9.3-1.6.3h-.3c-.4 0-1 0-2-.2-.3-.1-.6-.1-.8-.2v-2.7l1.3.3c.5.1 1.3.2 1.7.2.5 0 .9-.2 1.1-.2.4-.1.6-.2 1-.4.2-.1.4-.2.7-.4.4-.2 1.3-.7 2.5-1 .6-.2 1.4-.4 2.5-.5s2 .1 2.5.2c.6.1 1.2.3 2.2.7l1.5.5c.3.1.6.2 1 .4 1 .3 1.8.5 2.1.5h.1v2.7zM15.7 1.9v-.8h18.6V3c0 1.5-1.2 2.8-2.8 2.8H18.4c-1.5 0-2.8-1.2-2.8-2.8V1.9z"/>
                  </svg>
                `;
              }
              if (test.state <= '10') 
              {
                return `
                  <ha-icon icon="mdi:battery-low"></ha-icon>
                `;
              }  
          }    
        ]]] 

i must somehow replace the “climate” in the name of the entity and replace it with “sensor” and add at the end the “_battery_level”

OMG, this got me stumped the whole evening.

would you be willing to share your configuration files with us? possibly a git backup