A different take on designing a Lovelace UI

Thank you :slight_smile:

but where can I do like the living room etc? Because in Lovelace ui it messes up everything when I change?

Mattias,
Or really anyone with the knowledge.
I have the Unifi UDM-Pro but I’m really struggling trying to get the footer to populate. Everything pops-up as should but shows “unknown” for values.
I’ve made the appropriate changes in !secrets and looked over "router_unifi.yaml to see where i may need some changes.
This instance of Unifi controller is not hosted anywhere other than on the UDM-pro.
I’ve tried doing the ssh into UDM without success.
Does something need to be enabled that I’m missing?
I do have the Unifi addon working, and have tried the same user and pass as that and still no go.

Hey buddy great work, would you mind sharing your Plex popup. Is this collected off of another computer or server (docker)?

I’m attempting to make an auto-populating card for switch groups, based of the code for the new light popup… however I am was scratching my head as to what format is needed for:

tap_action:
  action: toggle

I eventually figured it out.
Here is the button card template:

switch:
  template:
    - base
    #- circle
    - loader
  double_tap_action:
    action: fire-dom-event
    browser_mod:
      command: popup
      title: >
        [[[
          return !entity || entity.attributes.friendly_name;
        ]]]
      style:
        hui-entities-card:
          $: |
            #states {
              padding-top: 0.8em;
            }
      card:
        type: entities
        entities: >
          [[[
            if (entity) {
                let switches = [],
                    id = Boolean(entity.attributes.entity_id)
                        ? [entity.entity_id].concat(entity.attributes.entity_id)
                        : [entity.entity_id];

                for (let i = 0; i < id.length; i++) {
                    switches.push({
                        "type": "custom:mushroom-entity-card",
                        "entity": id[i],
                        "fill_container": false,
                        "primary_info": "name",
                        "secondary_info": "last-changed",
                        "icon_type": "icon",
                        "icon_color": "light-green",
                        "tap_action": { action: 'toggle' }
                    });
                }
                return switches;
            }
          ]]]

I’m still yet two wrap my head around how to set up the circle info to display “x/y” (where x is number of switches in the on state and y is the total of switches), then all that’s left is to fill the circle line out as a percentage of that.

I got it working, it was a combination of issues,

I had port 433 not 443 in my secretes but when I tested the code I used 443
and my HA user role didn’t have access to UniFiOS.

Thanks for the help, would not have been as much of an issue if the errors was more understandable but that’s a HA issue.

Love your config for the second screen. Could you share the Yaml?

hi, I have circle info to display “x/y” (where x is number of people home and y is the total of people).

might be a good start for what you are trying to do

this is in 2 parts this will fill in the circle based on the % of people home

  variables:
    circle_input:  >
      [[[
        return entity === undefined || Math.round(states['zone.home'].state / 3 * 100 ) ;
      ]]]

and this puts the value in the circle,

I can see an issue when that 10/10 might be to big and you will need to play with the font size, or just display x not x/y when all are on but that will not work if you have a group with more than 10 switches.

 custom_fields:
    circle: >
      [[[
          let input = variables.circle_input,
            radius = 20.5,
            circumference = radius * 2 * Math.PI;
          let inner_text = states['zone.home'].state
          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};
                }
                tspan {
                  font-size: 10px;
                }
              </style>
              <circle cx="25" cy="25" r="${radius}" stroke="#b2b2b2" stroke-width="1.5" fill="none" />
              <text x="50%" y="54%" fill="#8d8e90" font-size="14" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${inner_text}/3</text>
            </svg>
          `;
      ]]]

you could also use this template to get the inner text for the circle input

{{ expand('group.office_monitors') | selectattr('state','eq','on')|map(attribute='name') | list | count }} / {{ expand('group.office_monitors') | count }}

Full Home Card

- type: custom:button-card
  entity: input_select.state_home
  name: Home
  variables:
    circle_input:  >
      [[[
        return entity === undefined || Math.round(states['zone.home'].state / 3 * 100 ) ;
      ]]]
  hold_action:
    action: more-info
  tap_action:
    action: call-service
    service: input_select.select_next
    service_data:
      entity_id: input_select.state_home
  double_tap_action:
    action: call-service
    service: input_select.select_previous
    service_data:
      entity_id: input_select.state_home
  custom_fields:
    circle: >
      [[[
          let input = variables.circle_input,
            radius = 20.5,
            circumference = radius * 2 * Math.PI;
          let inner_text = states['zone.home'].state
          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};
                }
                tspan {
                  font-size: 10px;
                }
              </style>
              <circle cx="25" cy="25" r="${radius}" stroke="#b2b2b2" stroke-width="1.5" fill="none" />
              <text x="50%" y="54%" fill="#8d8e90" font-size="14" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${inner_text}/3</text>
            </svg>
          `;
      ]]]
  template:
    - base
    - icon_home
    - circle

if this is for light switches it might be better to look into switch as x Switch as X - Home Assistant

1 Like

Great work!

Is it possible to add this dashboard next to my current dashboard? If i follow the install instruction i loose all my current lovelace dashboards. I want to make this primary and but want to keep the other ones.

Thank you, this saves me a ton of head scratching!

It’s by far not perfect and I didn’t do it myself!

lovelace-ui:

type: custom:button-card
entity: weather.pirateweather
name: Berlin
template:
  - base
  - widget_weather
variables:
  temp_min: sensor.pirateweather_overnight_low_temperature_0d
  temp_max: sensor.pirateweather_daytime_high_temperature_0d
  precip: sensor.template_sensor_pirateweather_precip_probability_round

Button-card templates:

  widgets:
    aspect_ratio: 1
    variables: null
    show_icon: false
    show_state: true
    show_name: true
    tap_action:
      animation_card: |
        [[[
          const animation_speed_ms = 900;
          const animation = `card_bounce ${animation_speed_ms}ms cubic-bezier(0.22, 1, 0.36, 1)`;
          this.shadowRoot.getElementById("card").style.animation = animation;
          window.setTimeout(() => {
            this.shadowRoot.getElementById("card").style.animation = "none";
          }, animation_speed_ms)
        ]]]
    styles:
      card:
        - background-color: rgba(0,0,0,0.4)
        - font-color: rgb(255,255,255,0.9)
        - text-shadow: 0px 0px 1px black
        - letter-spacing: 0.05vw
      name:
        - color: rgb(255,255,255,0.6)
    extra_styles: |
      #name, #state {
        font-size: 1.1vw;
        letter-spacing: 0.05vw;
      }
      /* portrait */
      @media screen and (max-width: 1200px) {
        #name, #state {
          font-size: 2vw;
          letter-spacing: 0.05vw;
        }
      }
      /* phone */
      @media screen and (max-width: 800px) {
        #name, #state {
          font-size: 5.1vw;
          letter-spacing: 0.12vw;
        }
      }
      @keyframes card_bounce {
        0% {
          transform: scale(1);
        }
        15% {
          transform: scale(0.9);
        }
        25% {
          transform: scale(1);
        }
        30% {
          transform: scale(0.98);
        }
        100% {
          transform: scale(1);
        }
      }

and

  widget_weather:
    variables:
      temp_min: ''
      temp_max: ''
      precip: ''
    aspect_ratio: 1/1
    show_icon: false
    show_entity_picture: true
    show_name: true
    show_state: true
    show_label: true
    tap_action:
      action: more-info
    styles:
      grid:
        - grid-template-areas: |
            "n"        
            "temp"
            "i"
            "s"
            "l"
        - grid-template-columns: 1fr
        - grid-template-rows: min-content repeat(2, 1fr) repeat(2, min-content)
        - gap: 0%
      card:
        - color: rgba(255, 255, 255, 0.8)
        - background: |
            [[[
              if (states['sun.sun'].state == 'below_horizon'){
                return 'linear-gradient(to top, rgba(53,59,83,1) 0%, rgba(10,14,34,1) 100%)';
              } else
                return 'linear-gradient(to top, rgba(123,168,197,1) 0%, rgba(61,132,176,1) 100%)';
            ]]]                        
      name:
        - place-self: start
        - text-transform: uppercase
        - font-weight: 500
      img_cell:
        - justify-content: start
      icon:
        - width: 20%
      label:
        - place-self: start
        - margin-left: '-5px'
      custom_fields:
        temp:
          - place-self: start
    custom_fields:
      temp: |
        [[[ return entity.attributes.temperature + "°"; ]]]    
    label: |
      [[[
          if (window.navigator.userAgent.match(/iPhone/i)) {
            return         `<ha-icon
            icon="mdi:arrow-up-thin"
            style="width: 15px; height: 15px; margin-right: -6px;">
            </ha-icon><span> ${states[variables.temp_max].state}°</span>
                  <ha-icon
            icon="mdi:arrow-down-thin"
            style="width: 15px; height: 15px; margin-right: -6px;">
            </ha-icon><span> ${states[variables.temp_min].state}° </span>       
          `;
                }
          else {
                    return `<ha-icon
            icon="mdi:arrow-up-thin"
            style="width: 15px; height: 15px; margin-right: -5px;">
            </ha-icon><span> ${states[variables.temp_max].state}°</span>
                  <ha-icon
            icon="mdi:arrow-down-thin"
            style="width: 15px; height: 15px; margin-right: -5px; margin-left: -5px;">
            </ha-icon><span> ${states[variables.temp_min].state}° </span>       
                  <ha-icon
            icon="mdi:weather-pouring"
            style="width: 14px; height: 14px; margin-right: -2px; margin-left: -1px;">
            </ha-icon><span> ${states[variables.precip].state}%</span>
          `}
      ]]]
    entity_picture: |
      [[[
        return entity && entity.state
          ? `/local/animated-weather-icons/${entity.state}.svg`
          : '?';
      ]]]
    extra_styles: |
      [[[
        return `
          #name {
            font-size: 1vw;
          }
          #temp {
            font-size: 1.9vw;
          }
          #state {
            font-size: 0.60vw;
          }  
          #label {
            font-size: 0.60vw;
          }
          /* portrait */
          @media screen and (max-width: 1200px) {
            #name {
              font-size: 1.3vw;
            }
            #temp {
              font-size: 4.5vw;
            }
            #state {
              font-size: 1.1vw;
            }  
            #label {
              font-size: 1.1vw;
            }
          }
          /* phone */
          @media screen and (max-width: 800px) {
            #name {
              font-size: 3vw;
            }              
            #temp {
              font-size: 2.5vw;
            }    
            #state {
              font-size: 1.8vw;
            }
            #label {
              font-size: 2.25vw;
            }
                   
          }
          @keyframes card_bounce {
            0% {
              transform: scale(1);
            }
            15% {
              transform: scale(0.9);
            }
            25% {
              transform: scale(1);
            }
            30% {
              transform: scale(0.98);
            }
            100% {
              transform: scale(1);
            }
          }
        `
      ]]]
1 Like

Very long time since I’ve been here.
I have updated to the build from a week or so back.
A lot of work getting everything to work together again, but did a lot of nice fixes for my own environment.
However this is the first time I have enabled “tilt”
And I am noticing with the tilt enabled the cards seem a lot more fuzzy/blurry, especially noticeable on text

image image

With tilt enabled (left) and disabled (right)
I don’t see this fuzziness for anyone else’s setup, is this something anyone has encountered before?

In the “inspect” view of the browser the following line seems to cause the fuzziness

Transform: perspective(800px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)

Which is a part of:

<ha-card id="card" class="button-card-main type-custom-button-card" style="border-radius: var(--button-card-border-radius); --mdc-ripple-color: #97989c; color: rgb(151, 152, 156); background-color: rgba(115, 115, 115, 0.25); --c-stroke-color-on: #b0b0b0; --c-stroke-color-off: #313638; --c-fill-color-on: none; --c-fill-color-off: rgba(255,255,255,0.04); --c-stroke-width-on: 2.3; --c-stroke-width-off: 1.5; --c-stroke-width-dragging: 4; --c-font-color: #97989c; --c-font-size: 14px; --c-unit-font-size: 10.5px; --c-font-weight: 700; --c-letter-spacing: -0.02rem; position: absolute; transform: perspective(800px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1); will-change: transform;">

And a second question, I am positive I have seen the question and answer somewhere in the thread, but I cannot find it anywhere, even after searching and going to the thread.
But, when swiping on a swiper card, I am not able to change a dimmer light level by clicking and holding on the percentage circle
It works outside of the swiper card, and as I said the solution is out there but I cannot seem to find it, if anyone knows, please let me know

Yeah, but barely noticeable on my end, not like yours… Probably how your browser renders 3d transform?

Try some parameters https://swiperjs.com/swiper-api

type: custom:swipe-card
parameters:
  touchStartPreventDefault: false

Thanks Mattias, I will try some different browsers, and otherwise just disable the effect, I have lived without it thus far so I should be able to live without it permanently :stuck_out_tongue:

The swipe-card issue was instantly solved with your suggestion, thank you so much.

And also: Thanks a LOT for updating this project, improving it every step.
It has already come a long way, the code is a lot clearer, I have learned so much and I really love this project.
even though I still don’t have a tablet on my wall to put this on :wink:
Can’t wait to see what type of improvements you think of next!

1 Like

yes, as long as you enable the theme at a dashboard level not a system level

I try to set a picture as a background in a button-card, could anyone give me a hint how to do this?

And the new ps5-mqtt integration had now an activity-sensor, where I can see the game I play at the moment. Is it possible to show this in the PS5-Card isntead of the icon?

@Mattias_Persson

hi

understand i this code correct? the tilt is only alowed on this devices?

because on my Windows Laptop the Tilt is gone

 if (window.navigator.userAgent.match(/Macintosh; Intel Mac OS X/i)) {

@htpc2308

Yeap, you are correct.
You can replace the /Macintosh; Intel Mac OS X/i with your own useragent if you like.
Or if you want to make it more broad replace .match with .includes
For instance to make the tilt work with ANY firefox browser version:
if (window.navigator.userAgent.includes("Firefox")) {
Could even make it more broad by using the whole if/else as an exclude by switching the true and false
for instance, if you want to make the tilt only NOT work on firefox you can use:

if (window.navigator.userAgent.includes("Firefox")) {
        return false;
    }
    return true;

Play with the setting to get it to work on the device you want it.
For me, using it on android causes major slowdowns, so I only want it to show on Firefox
Since on mobile I always use the Home Assistant app anyway

2 Likes

Add something like this to the button-card-template of the card and play with the settings to achieve the result you want

styles:
  card:
    - align-self: middle
    - background-color: none
    - background-position: center center
    - background-repeat: no-repeat
    - background-image: image.png

Can you change the start/end of the marquee effect? Now the text is scrolling to the left side of the card true the icon and img_cell. Will it possible to start just where the name is and scroll to the right side.

marquee