A different take on designing a Lovelace UI

You can try this Fit content · Issue #41 · matt8707/hass-config · GitHub

1 Like

Can someone try this out, I think I got it to work…

2

Edit:

on view change the text scrolls even if it fits

3 Likes

So i am a noob to all of this (literally started like 1 week ago), but this lovelace UI is so perfect, i had to try and use it.
(if this is already asked and answered, sorry, but this thread has 2362 replies at time of writing, and i cannot read all of em)

I am running into the following issue.
I want one of the tiles to control a group of lights, so i got a light group going, linked the entity, all is good!
However, the lights in question also have RGB, so i wanted to use the color picker, and i get this:

Each of these three works independent of one another, so i can set the colors of each of the 3 lights separately. Which is not a bad thing i guess, but my goal was to have all 3 lights be in sync all the time.

Maybe i just missed something obvious?

1 Like

You’ll have to define light groups to achieve what you want.

Thanks! Here’s the fix:

(and also add light group, update entities · matt8707/hass-config@857931c · GitHub)


If you’re interested in why… I used to use light groups, using hue grouped lights used to be faster (frontend) plus consolidate_entities was broken

hoped this would also work on a regular custom:button-card state or label, but unfortunately it doesnt scroll the full text, but only the bit that is actually visible on first sight…

      - type: custom:button-card
        entity: sensor.buienradar_symbol # replace with any sensor which state is a longer text...
        template: extra_styles
        aspect_ratio: 1/1
        show_icon: false
        show_name: true
        show_state: true
        styles:
          state:
            - animation: marquee 20s linear infinite

would you know what that is?

using the marquee tag does work in that respect:

      <marquee>
      <span style='color: var(--primary-color);align-items: center;'>${alarm} at ${entity.state}</span>
      </marquee>

Probably

styles:
  state:
    - overflow: visible

but I don’t think it’s a good idea to add ResizeObserver to every button-card, performance wise

1 Like

confirmed, thank you very much!

OMG that was the solution, I was wondering why the consolidate entities switch didn’t make any difference! Thanks!

I also noticed the difference with a group and a light-group, so for anyone reading this in the future, a light group is the way to go.
A regular group creates multiple issues, like the brightness percentage in the dashboard becoming NaN% as well as the slider doesn’t want to hold the position.
So go with light groups.

@CDRX2 , i did, regular groups made created much more issues (read above)
@Mattias_Persson, again thank you soo much!

It worked for me. Thanks a lot

1 Like

it’s messy, but here it is @gerard33

  icon_xbox:
    styles:
      custom_fields:
        icon:
          - width: 150%
          - margin-left: -8%
          - margin-top: 3%
    custom_fields:
      icon: >
        [[[
          if (variables.state === 'on' && variables.timeout < 2000) {
            return `
              <svg viewBox="0 0 50 50">
                <style>
                @keyframes on {
                  50% {
                    transform: translateY(0);
                  }
                  100% {
                    transform: translateY(-45%);
                  }
                }
                .on {
                  animation: on 2s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
                }
              </style>
              <g style="clip-path: url(#mask);">
                <g class="on">

                  <path fill="#107C10" d="M4.102 21.033C6.211 22.881 8.977 24 12 24c3.026 0 5.789-1.119 7.902-2.967 1.877-1.912-4.316-8.709-7.902-11.417-3.582 2.708-9.779 9.505-7.898 11.417zm11.16-14.406c2.5 2.961 7.484 10.313 6.076 12.912C23.002 17.48 24 14.861 24 12.004c0-3.34-1.365-6.362-3.57-8.536 0 0-.027-.022-.082-.042-.063-.022-.152-.045-.281-.045-.592 0-1.985.434-4.805 3.246zM3.654 3.426c-.057.02-.082.041-.086.042C1.365 5.642 0 8.664 0 12.004c0 2.854.998 5.473 2.661 7.533-1.401-2.605 3.579-9.951 6.08-12.91-2.82-2.813-4.216-3.245-4.806-3.245-.131 0-.223.021-.281.046v-.002zM12 3.551S9.055 1.828 6.755 1.746c-.903-.033-1.454.295-1.521.339C7.379.646 9.659 0 11.984 0H12c2.334 0 4.605.646 6.766 2.085-.068-.046-.615-.372-1.52-.339C14.946 1.828 12 3.545 12 3.545v.006z"/>
                </g>
              </g>
              <defs>
                <clipPath id="mask">
                  <path d="M4.102 21.033C6.211 22.881 8.977 24 12 24c3.026 0 5.789-1.119 7.902-2.967 1.877-1.912-4.316-8.709-7.902-11.417-3.582 2.708-9.779 9.505-7.898 11.417zm11.16-14.406c2.5 2.961 7.484 10.313 6.076 12.912C23.002 17.48 24 14.861 24 12.004c0-3.34-1.365-6.362-3.57-8.536 0 0-.027-.022-.082-.042-.063-.022-.152-.045-.281-.045-.592 0-1.985.434-4.805 3.246zM3.654 3.426c-.057.02-.082.041-.086.042C1.365 5.642 0 8.664 0 12.004c0 2.854.998 5.473 2.661 7.533-1.401-2.605 3.579-9.951 6.08-12.91-2.82-2.813-4.216-3.245-4.806-3.245-.131 0-.223.021-.281.046v-.002zM12 3.551S9.055 1.828 6.755 1.746c-.903-.033-1.454.295-1.521.339C7.379.646 9.659 0 11.984 0H12c2.334 0 4.605.646 6.766 2.085-.068-.046-.615-.372-1.52-.339C14.946 1.828 12 3.545 12 3.545v.006z"/>
                </clipPath>
              </defs>
              <path fill="#107C10" d="M4.102 21.033C6.211 22.881 8.977 24 12 24c3.026 0 5.789-1.119 7.902-2.967 1.877-1.912-4.316-8.709-7.902-11.417-3.582 2.708-9.779 9.505-7.898 11.417zm11.16-14.406c2.5 2.961 7.484 10.313 6.076 12.912C23.002 17.48 24 14.861 24 12.004c0-3.34-1.365-6.362-3.57-8.536 0 0-.027-.022-.082-.042-.063-.022-.152-.045-.281-.045-.592 0-1.985.434-4.805 3.246zM3.654 3.426c-.057.02-.082.041-.086.042C1.365 5.642 0 8.664 0 12.004c0 2.854.998 5.473 2.661 7.533-1.401-2.605 3.579-9.951 6.08-12.91-2.82-2.813-4.216-3.245-4.806-3.245-.131 0-.223.021-.281.046v-.002zM12 3.551S9.055 1.828 6.755 1.746c-.903-.033-1.454.295-1.521.339C7.379.646 9.659 0 11.984 0H12c2.334 0 4.605.646 6.766 2.085-.068-.046-.615-.372-1.52-.339C14.946 1.828 12 3.545 12 3.545v.006z"/>
            </svg>
          `;
          }

          return variables.state === 'on' && variables.timeout > 2000 ? `
            <svg viewBox="0 0 50 50">
            <g style="clip-path: url(#mask);">
              <g style="transform: translateY(-45%);">
                <path fill="#107C10" d="M4.102 21.033C6.211 22.881 8.977 24 12 24c3.026 0 5.789-1.119 7.902-2.967 1.877-1.912-4.316-8.709-7.902-11.417-3.582 2.708-9.779 9.505-7.898 11.417zm11.16-14.406c2.5 2.961 7.484 10.313 6.076 12.912C23.002 17.48 24 14.861 24 12.004c0-3.34-1.365-6.362-3.57-8.536 0 0-.027-.022-.082-.042-.063-.022-.152-.045-.281-.045-.592 0-1.985.434-4.805 3.246zM3.654 3.426c-.057.02-.082.041-.086.042C1.365 5.642 0 8.664 0 12.004c0 2.854.998 5.473 2.661 7.533-1.401-2.605 3.579-9.951 6.08-12.91-2.82-2.813-4.216-3.245-4.806-3.245-.131 0-.223.021-.281.046v-.002zM12 3.551S9.055 1.828 6.755 1.746c-.903-.033-1.454.295-1.521.339C7.379.646 9.659 0 11.984 0H12c2.334 0 4.605.646 6.766 2.085-.068-.046-.615-.372-1.52-.339C14.946 1.828 12 3.545 12 3.545v.006z"/>
              </g>
            </g>
            <defs>
              <clipPath id="mask">
                <path d="d="M4.102 21.033C6.211 22.881 8.977 24 12 24c3.026 0 5.789-1.119 7.902-2.967 1.877-1.912-4.316-8.709-7.902-11.417-3.582 2.708-9.779 9.505-7.898 11.417zm11.16-14.406c2.5 2.961 7.484 10.313 6.076 12.912C23.002 17.48 24 14.861 24 12.004c0-3.34-1.365-6.362-3.57-8.536 0 0-.027-.022-.082-.042-.063-.022-.152-.045-.281-.045-.592 0-1.985.434-4.805 3.246zM3.654 3.426c-.057.02-.082.041-.086.042C1.365 5.642 0 8.664 0 12.004c0 2.854.998 5.473 2.661 7.533-1.401-2.605 3.579-9.951 6.08-12.91-2.82-2.813-4.216-3.245-4.806-3.245-.131 0-.223.021-.281.046v-.002zM12 3.551S9.055 1.828 6.755 1.746c-.903-.033-1.454.295-1.521.339C7.379.646 9.659 0 11.984 0H12c2.334 0 4.605.646 6.766 2.085-.068-.046-.615-.372-1.52-.339C14.946 1.828 12 3.545 12 3.545v.006z"/>
              </clipPath>
            </defs>
            <path fill="#107C10" d="M4.102 21.033C6.211 22.881 8.977 24 12 24c3.026 0 5.789-1.119 7.902-2.967 1.877-1.912-4.316-8.709-7.902-11.417-3.582 2.708-9.779 9.505-7.898 11.417zm11.16-14.406c2.5 2.961 7.484 10.313 6.076 12.912C23.002 17.48 24 14.861 24 12.004c0-3.34-1.365-6.362-3.57-8.536 0 0-.027-.022-.082-.042-.063-.022-.152-.045-.281-.045-.592 0-1.985.434-4.805 3.246zM3.654 3.426c-.057.02-.082.041-.086.042C1.365 5.642 0 8.664 0 12.004c0 2.854.998 5.473 2.661 7.533-1.401-2.605 3.579-9.951 6.08-12.91-2.82-2.813-4.216-3.245-4.806-3.245-.131 0-.223.021-.281.046v-.002zM12 3.551S9.055 1.828 6.755 1.746c-.903-.033-1.454.295-1.521.339C7.379.646 9.659 0 11.984 0H12c2.334 0 4.605.646 6.766 2.085-.068-.046-.615-.372-1.52-.339C14.946 1.828 12 3.545 12 3.545v.006z"/>
          </svg>
          ` : `
            <svg viewBox="0 0 50 50">
              <path fill="#9da0a2" d="M4.102 21.033C6.211 22.881 8.977 24 12 24c3.026 0 5.789-1.119 7.902-2.967 1.877-1.912-4.316-8.709-7.902-11.417-3.582 2.708-9.779 9.505-7.898 11.417zm11.16-14.406c2.5 2.961 7.484 10.313 6.076 12.912C23.002 17.48 24 14.861 24 12.004c0-3.34-1.365-6.362-3.57-8.536 0 0-.027-.022-.082-.042-.063-.022-.152-.045-.281-.045-.592 0-1.985.434-4.805 3.246zM3.654 3.426c-.057.02-.082.041-.086.042C1.365 5.642 0 8.664 0 12.004c0 2.854.998 5.473 2.661 7.533-1.401-2.605 3.579-9.951 6.08-12.91-2.82-2.813-4.216-3.245-4.806-3.245-.131 0-.223.021-.281.046v-.002zM12 3.551S9.055 1.828 6.755 1.746c-.903-.033-1.454.295-1.521.339C7.379.646 9.659 0 11.984 0H12c2.334 0 4.605.646 6.766 2.085-.068-.046-.615-.372-1.52-.339C14.946 1.828 12 3.545 12 3.545v.006z"/>
            </svg>
          `;
        ]]]



1 Like

GIF-220113_131806
this is my version of icon XBOX

  icon_xrotate:
    styles:
      custom_fields:
        icon:
          - width: 90%
          - margin-left: -10%
          - fill: >
              [[[
                return (variables.state === 'on' || variables.state === 'playing') ? '#107b10' : '#9da0a2';
              ]]]          
    custom_fields:
      icon: >
        [[[
          let state;
          if (variables.state === 'on' && variables.timeout < 2000) {
            state = 'on';
          } 
          if (variables.state === 'off' && variables.timeout < 2000) {
            state = 'off';
          }
          if (variables.state === 'on' && variables.timeout > 2000) {
            state = 'on_timeout';
          }
          return `
            <svg viewBox="0 0 1331.67 1333.33">
              <style>
                @keyframes on {
                  0% {
                    transform: rotate(0deg) translate(0%, 0%);
                  }
                  100% {
                    transform: rotate(-360deg) translate(-1.5%, 0%);
                  }
                }
                @keyframes off {
                  0% {
                    transform: rotateY(-360deg) translate(-1.5%, 0%);
                  }
                  45% {
                    transform: rotateY(-40deg);
                  }
                  55% {
                    transform: rotateY(0deg);
                  }
                  65% {
                    transform: rotateY(-15deg);
                  }
                  75% {
                    transform: rotateY(0deg);
                  }
                  85% {
                    transform: rotateY(-5deg);
                  }
                  95% {
                    transform: rotateY(0deg);
                  }
                }
                .on {
                  animation: on 1.1s;
                  animation-fill-mode: forwards;
                  transform-origin: 50% 50%;
                  transition-timing-function: cubic-bezier(0.85, 0, 0.15, 1);
                }
                .off {
                  animation: off 1.1s linear;
                  animation-delay: 0.05s;
                  animation-fill-mode: both;
                  transform-origin: 45% 41%;
                }
              </style>
              <path class="${state}" d="M665.83 534.66s1.66 0 0 0c200.91 152.76 541.3 528.02 438.35 634.29-117.89 102.95-270.65 164.39-438.35 164.39-167.7 0-322.13-61.44-438.35-164.39-104.61-106.27 237.44-481.53 436.69-632.63 0-1.66 1.66-1.66 1.66-1.66zm347.03-436.7C911.57 36.52 800.32-.01 665.83-.01c-134.5 0-245.74 36.53-347.03 97.97-1.66 0-1.66 1.66-1.66 3.32s1.66 1.66 3.32 1.66c129.51-28.23 325.44 83.02 343.71 94.65h3.32c18.26-11.62 214.2-122.87 343.71-94.65 1.66 0 3.32 0 3.32-1.66s0-3.32-1.66-3.32zm-813.61 92.98c-1.66 0-1.66 1.66-3.32 1.66C74.72 313.81 0 481.52 0 665.82c0 151.1 51.48 292.24 136.16 403.49 0 1.66 1.66 1.66 3.32 1.66s1.66-1.66 0-3.32C88 909.91 348.69 529.67 483.19 370.26l1.66-1.66c0-1.66 0-1.66-1.66-1.66-204.23-202.57-272.31-180.99-283.93-176.01zm649.23 174.35l-1.66 1.66s0 1.66 1.66 1.66C982.98 528.01 1242 908.26 1192.19 1066v3.32c1.66 0 3.32 0 3.32-1.66 84.68-111.25 136.16-252.39 136.16-403.49 0-184.31-74.72-352.01-197.59-473.22-1.66-1.66-1.66-1.66-3.32-1.66-9.96-3.32-78.04-24.91-282.27 176.01z"/>
            </svg>
          `;
        ]]]
```[Processing: Screenrecorder-2022-01-13-13-00-14-380~2.mp4...]()
4 Likes

Sorry I forgot the dash between light and groups, of course I meant light-groups, the link points to that documentation :wink:

I play media from my Squeezbox to all my players in the house. When playing songs from spotify, i see the art cover. but from some radio stations, there is no cover provided. so the mini media player shows me the default art cover.

I dont find a solution to this, so i want to show a other image, that looks better.

The only way i see to check if it is spotify or radio, is the media_content_id. When playing spotify, it gives me “spotify: xxxxxxxxxx” when playing radio, it starts with" https: xxxxxxx". I made a sensor, that splits this to “https” and “spotify”.
Now i change the entity_image between “https” and “spotify” with this

type: custom:config-template-card
variables:
  - states['sensor.radio_split'].state
style:
  left: 80%
  top: 35%
entities:
  - media_player.homeyk
card:
  type: custom:button-card
  entity: media_player.homeyk
  size: 100%
  show_entity_picture: true
  show_name: false
  entity_picture: |-
    "${ if (vars[0] === 'spotify') { '/local/image/spotify.svg' }
      else if (vars[0] === 'http') { '/local/image/radio.svg' }
      else { 'xxxxxxxxxxx' }
    }"

what i dont get, is the “else” at the end. I want that it shows the entity-picture that is give from the media player. Something like {{ state_attr(‘media_player.homeyk’, ‘entity_picture’) }}. How can i do this? I know its not something special for this thread, but i hope someone can help.

UPDATE:
I use a conditional, that works.

Does somebody already have a button / popup for a 3d-printer?

What has to be configured for the last_changed sensors to work? Can seem to get that one.

Are you talking about person/home? Nothing if you remove triggers_update and variables. If you want the timestamp to survive restarts hass-config@9738395

Never used config-template-card but it should be states['media_player.homeyk'].attributes.entity_picture

Hi Pex

Strangely enough, the calendar doesn’t show me tomorrow’s appointment. It only appears the next day

button:
grafik

In the calender, however, this is displayed as normal.
Calendar:
grafik

normaly they must show me: Hello Test

Any Idea?

Does anybody need more variables? The idea is that this part can easily be pasted when button_card_templates.yaml is updated.

4 Likes