A different take on designing a Lovelace UI

Ah fair enough.
I’ve got a couple I can test against. :slight_smile:

One question about animation:
How do you make the animated icons? I mean is it via svg animation tools, or is it CSS animation… I don’t know how to make my own “moving” icons.
Do you use a software to get the code and insert into the animated icons template?
Thanks for this nice and hard work !!!

Would anyone know why the popups won’t work when the type is custom:button-card ?
if I use the standard button then the popups work fine.

I have the latest version of browser_mod and button-card.

thanks in advance

EDIT: Solved, comment:

Tool from Mattias: card-mod-helper

Hello community,

First of all, thank you Mattias for sharing this!

I just recently started to use HA, coming from homebridge and now I am trying to build up my dashboard using Mattias’ config.

I just got stuck when I was trying to build up a custom button card with some data from my devices.
I would like to eliminate the spacing between the two cards that contiain the battery level of different devices and have them close to each other.

My ui-lovelace.yaml looks like this

views:
  - type: custom:grid-layout
    path: 0
    layout:
      #default
      grid-gap: var(--custom-layout-card-padding)
      grid-template-columns: repeat(4, 1fr) 0
      grid-template-rows: repeat(2, fit-content(100%)) 0fr
      grid-template-areas: |
        "sidebar  .           .          .          ."
        "sidebar  batteries   .          .          ."
        "sidebar  .           .          .          ."
        "sidebar  footer  footer     footer         ."


    cards:
      - type: custom:button-card
        styles: #extra_styles fix
          card:
            - display: none

      - type: grid
        title: Batteries
        view_layout:
          grid-area: batteries
        columns: 1
        cards:
          - type: custom:button-card
            entity: device_tracker.tamas_iphone_xs_2
            triggers_update:
              - sensor.time
            name: >
              [[[ 
                return entity.attributes.friendly_name; 
              ]]]
            variables:
               battery: device_tracker.tamas_iphone_xs_2
            template: battery

          - type: custom:button-card
            entity: device_tracker.tamas_iphone_se
            triggers_update:
              - sensor.time
            name: >
             [[[ 
               return entity.attributes.friendly_name; 
             ]]]
            variables:
             battery: device_tracker.tamas_iphone_se
            template: battery
custombase:
  variables:
    state_on: >
      [[[ return ['on', 'home', 'cool', 'fan_only', 'playing'].indexOf(entity === undefined || entity.state) !== -1; ]]]
  aspect_ratio: 1/1
  show_state: false
  show_icon: false
  styles:
    grid:
      - grid-template-areas: |
          "n circle"
      - grid-template-columns: 1fr 1fr
      - grid-template-rows: auto
      - align-items: start
    name:
      - justify-self: start
      - padding-left: 10px
    card:
      - font-family: Sf Display, Roboto
      - border-radius: 15px
      - -webkit-tap-highlight-color: rgba(0,0,0,0)
      - transition: none
      - padding: 10% 9% 9% 10%
      - --mdc-ripple-color: >
          [[[
            return variables.state_on
              ? 'rgb(0, 0, 0)'
              : 'rgba(255, 255, 255, 0.3)';
          ]]]
      - color: >
          [[[
            return variables.state_on
              ? 'rgba(0, 0, 0, 0.6)'
              : 'rgba(255, 255, 255, 0.3)';
          ]]]
      - background-color: >
          [[[
            return variables.state_on
              ? 'rgba(255, 255, 255, 0.8)'
              : 'rgba(115, 115, 115, 0.2)';
          ]]]
      - height: 40px
      - width: 300px


battery:
  template:
    - custombase
    - circle
  triggers_update: sensor.time
  hold_action:
    action: none
  custom_fields:
    circle: >
      [[[
          let input = states[variables.battery].attributes.battery_level,
            radius = 20.5,
            circumference = radius * 2 * Math.PI;
          var color = "#27C950";
            
          if (input <= 20) {
            color = "#FDD60F";
          } else if (input <= 40) {
            color = "#FDD60F";
          }
          else {
            color = "#27C950";
          }
          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="${color}" stroke-width="5" fill="none" stroke-linecap="round"/>
              <text x="50%" y="54%" fill="#8d8e90" font-size="14" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${input}<tspan font-size="10">%</tspan></text>
            </svg>
          `;
      ]]]

I really just started to playing around and trying to get the whole picture.
Can you give some hint or direction where to look, pelase?
I do not get it why I have the vertical spacing between my custom cards.

Thank you in advance!

Perfect, that worked like a charm :slight_smile: Thanks!

Which one do you recommend then ? Firetab ?

What if you put a picture with opacity: 0 on top of the card?

          - type: custom:button-card
            name: Street camera
            custom_fields:
              picture:
                card:
                  type: picture-elements
                  camera_image: camera.street_camera_ch01_sub
                  camera_view: live
                  elements:
                    - type: image
                      image: /local/bg.png
                      tap_action:
                        !include popup/street_camera.yaml
                      style:
                        opacity: 0
                        top: 50%
                        left: 50%
                        width: 100%
            styles:
              custom_fields:
                picture:
                  [top: -27%, left: -55%, width: 190%, position: absolute]
            template: base
1 Like

Here you go. Keep in mind this will popup on all devices.

automations.yaml

- id: '1642017204324'
  alias: System - Dashboard - Display Doorbell Cam
  description: ''
  trigger:
  - platform: state
    entity_id: binary_sensor.doorbell
    to: 'on'
    id: pressed
    from: 'off'
  condition: []
  action:
  - service: browser_mod.popup
    data:
      large: true
      title: Doorbell
      style:
        $: ".mdc-dialog__surface {\n  background: transparent !important;\n  border-style:\
          \ none !important;\n  border: 0px !important;\n  box-shadow: none;\n}\n"
      card:
        type: vertical-stack
        cards:
        - type: custom:layout-card
          layout_type: grid
          layout:
            grid-gap: 0.4vw
            grid-template-columns: auto 80% auto
            grid-template-rows: 95%
            grid-template-areas: '".   cam1   ."

              '
            mediaquery:
              '(max-width: 800px)':
                grid-gap: 1.5vw
                grid-template-columns: auto
                grid-template-rows: auto
                grid-template-areas: '"cam1"

                  '
          cards:
          - type: picture-entity
            entity: camera.ringeklokke
            state_image: entity.attributes.entity_picture
            show_info: false
            show_state: false
            show_name: false
            camera_view: live
            view_layout:
              grid-area: cam1

PS: the grid you could probably do without, but I made it so it would be ready for an extra camera in the future.

Sure :slight_smile:

Garage (I haven’t finished animating it, yet)

              <path fill="#9da0a2" d="M24.933 11.355l19.948 3.894c.082.14.09 1.51 0 1.8l-39.761-.033c-.089-.283-.085-1.618.01-1.821z"/>
              <rect fill="#9da0a2" width="1.966" height="20.53" x="5.006" y="18.115" rx=".256" paint-order="fill markers stroke"/>
              <rect fill="#9da0a2" width="1.966" height="20.53" x="43.028" y="18.115" rx=".256" paint-order="fill markers stroke"/>
              <rect fill="#707070" id="port1" width="34.328" height="5.932" x="7.836" y="18.115" rx=".186" paint-order="fill markers stroke"/>
              <rect fill="#707070" id="port2" width="34.328" height="7.032" x="7.836" y="24.863" rx=".186" ry=".22" paint-order="fill markers stroke"/>
              <rect fill="#707070" id="port3" width="34.328" height="5.932" x="7.836" y="32.71" rx=".186" paint-order="fill markers stroke"/>

Camera (working on a more realistic version that lights up the red LEDs if night vision is on)

                  <path id="BottomBody" d="M25.4029 33.1482C32.5202 33.1572 34.049 32.8907 34.049 32.8907L34.0476 42.9924C34.0476 42.9924 33.7847 43.7983 25.2763 43.7668C17.5461 43.7986 17.0074 43.0119 17.0074 43.0119L17.0356 32.894C17.0356 32.894 18.7469 33.1734 25.403 33.1482L25.4029 33.1482Z" fill="#9da0a2"/>
                  <path id="rect1631" d="M25.5681 5.15724C30.289 5.15724 34.1007 8.92111 34.0896 13.5964L34.0441 32.6626C34.0441 32.6626 31.7224 33.0036 25.5247 32.9528C17.2365 32.8848 17.0033 32.6511 17.0033 32.6511L17.0467 13.5964C17.0573 8.92111 20.8473 5.15724 25.5681 5.15724H25.5681Z" fill="#9da0a2"/>
                  <path id="path26713" d="M25.5876 6.28206C29.8706 6.28206 33.3106 8.93542 33.3187 12.2313L33.3408 21.1665C32.9929 24.5645 30.6552 25.8211 25.5363 25.9361C20.3005 26.0536 17.8828 24.0377 17.8128 21.0478L17.8564 12.2313C17.8727 8.93545 21.3045 6.28206 25.5876 6.28206H25.5876Z" fill="#b6b9bc"/>
                  <path id="rect3378" d="M32.5782 13.4998C32.5782 9.61689 29.4304 6.46918 25.5475 6.46918C21.6646 6.46918 18.5169 9.61689 18.5169 13.4998V14.8764C18.5169 18.7593 21.6646 21.907 25.5475 21.907C29.4304 21.907 32.5782 18.7593 32.5782 14.8764V13.4998Z" fill="#212121"/>
                  <path id="rect4038" d="M29.7107 13.6797C29.7107 11.248 27.8357 9.27679 25.5226 9.27679C23.2096 9.27679 21.3345 11.248 21.3345 13.6797V14.5418C21.3345 16.9734 23.2096 18.9447 25.5226 18.9447C27.8357 18.9447 29.7107 16.9734 29.7107 14.5418V13.6797Z" fill="#1A1A1A"/>
                  <path id="path5422" d="M25.5318 16.8095C27.0803 16.8095 28.3356 15.5542 28.3356 14.0057C28.3356 12.4572 27.0803 11.2019 25.5318 11.2019C23.9833 11.2019 22.728 12.4572 22.728 14.0057C22.728 15.5542 23.9833 16.8095 25.5318 16.8095Z" fill="#333333"/>
                  <path id="circle21643" opacity="0.830128" d="M25.5436 16.1275C26.722 16.1275 27.6773 15.1722 27.6773 13.9938C27.6773 12.8155 26.722 11.8602 25.5436 11.8602C24.3653 11.8602 23.41 12.8155 23.41 13.9938C23.41 15.1722 24.3653 16.1275 25.5436 16.1275Z" fill="#212121"/>
                  <path id="CameraLens" d="M25.5279 14.541C25.8234 14.541 26.063 14.3014 26.063 14.0059C26.063 13.7103 25.8234 13.4708 25.5279 13.4708C25.2324 13.4708 24.9928 13.7103 24.9928 14.0059C24.9928 14.3014 25.2324 14.541 25.5279 14.541Z" fill="#1A1A1A"/>

3 Likes

How have you done the circles? Please share? :slight_smile:

In the meantime I have found the CSS property that should be changed to have the layout that I want:

If I change the padding-bottom property from 100% to 15% then it looks exactly what I want:

Can you give me advice how can I influence this property from the config, please?
Thank you in advance!

Hi! If someone is interested, here I leave the code of this button in which the clock is activated in a countdown. Thanks for this thread! I’m learning a lot :slight_smile:

custom_fields:
  icon_fan: >
    [[[ if (entity.state === 'on') { return '<svg fill="grey" version="1.1"
    viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"
    preserveAspectRatio="xMidYMid"
    xmlns:svg="http://www.w3.org/2000/svg"><defs/><metadata/><path d="M0
    0h24v24H0zM18 2 18 2 18 2 18 2 18 2 18 2 18 2" fill="none"/><path
    transform="scale(.5)" d="m18 2v4h12v-4zm6 6c-9.94 0-18 8.06-18 18s8.04 18 18
    18 18-8.06
    18-18c0-4.24-1.4795-8.1407-3.9395-11.221l2.8398-2.8398c-0.86-1.02-1.8003-1.9803-2.8203-2.8203l-2.8398
    2.8418c-3.1-2.48-7.0002-3.9609-11.24-3.9609zm0 4c7.74 0 14 6.26 14 14s-6.26
    14-14 14-14-6.26-14-14 6.26-14 14-14zm0.52148 5.5879c-0.0063 0.0054-0.01322
    0.01018-0.01953 0.01563 0.0039 0.000824 0.0079 0.000718 0.01172 0.002
    0.0031-0.0058 0.0047-0.01178 0.0078-0.01758zm0.12695 6.7109c-0.0026
    0.000237-0.0052 0.0017-0.0078 0.002 0.000684 0.0039 0.0012 0.0078 0.002
    0.01172 0.0018-0.0046 0.004-0.0091 0.0059-0.01367z" stroke-width="2"/><rect
    x="11" y="8" width="2" height="6" fill="grey" fill-rule="evenodd"
    stroke-width=".5"><animateTransform attributeName="transform"
    attributeType="auto" type="rotate" values="1.1614052573951312
    12.082738110421964 12.604827265860937;1.1614052573951312 12.082738110421964
    12.604827265860937" calcMode="spline" keyTimes="0;1" keySplines="0 0 1 1"
    dur="10s" begin="0s" repeatCount="1" additive="sum" accumulate="none"
    fill="grey"/><animateTransform attributeName="transform"
    attributeType="auto" type="rotate" values="358.79 11.994349762773645
    12.56063309203678;358.79 11.994349762773645 12.56063309203678"
    calcMode="spline" keyTimes="0;1" keySplines="0 0 1 1" dur="5s" begin="0s"
    repeatCount="indefinite" additive="sum" accumulate="none"
    fill="grey"><animateTransform attributeName="transform" attributeType="auto"
    type="rotate" values="0 11.994349762773645 12.753875785289079;365
    11.994349762773645 12.753875785289079" calcMode="spline" keyTimes="0;1"
    keySplines="0 0 1 1" dur="5s" begin="0s" repeatCount="indefinite"
    additive="sum" accumulate="none"
    fill="grey"/></animateTransform><animateTransform attributeName="transform"
    attributeType="auto" type="rotate" values="0 11.950155588949485
    12.6212932638166;360 11.950155588949485 12.6212932638166" calcMode="spline"
    keyTimes="0;1" keySplines="0 0 1 1" dur="10s" begin=";1s" repeatCount="1"
    additive="sum" accumulate="none" fill="grey"/><animateTransform
    attributeName="transform" attributeType="auto" type="rotate" values="0
    12.007767433524364 12.788740065446536;90 12.007767433524364
    12.788740065446536;180 12.007767433524364 12.788740065446536;260
    12.007767433524364 12.788740065446536;320 12.007767433524364
    12.788740065446536;360 12.007767433524364 12.788740065446536"
    calcMode="spline" keyTimes="0;0.2;0.4;0.6;0.8;1" keySplines="0 0 1 1;0 0 1
    1;0 0 1 1;0 0 1 1;0 0 1 1" dur="10s" begin="0s" repeatCount="indefinite"
    additive="sum" accumulate="none" fill="grey"/><animateTransform
    attributeName="transform" attributeType="auto" type="rotate" values="0
    11.945267433524364 12.976240065446536;180 11.945267433524364
    12.976240065446536;360 11.945267433524364 12.976240065446536"
    calcMode="spline" keyTimes="0;0.5;1" keySplines="0 0 1 1;0 0 1 1" dur="10s"
    begin="0s" repeatCount="indefinite" additive="sum" accumulate="none"
    fill="grey"/></rect></svg>'; } else { return '<svg viewBox="0 0 24 24"><path
    fill="#9da0a2" d="M15 1H9v2h6V1zm-4
    13h2V8h-2v6zm8.03-6.61l1.42-1.42c-.43-.51-.9-.99-1.41-1.41l-1.42 1.42C16.07
    4.74 14.12 4 12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9 9-4.03
    9-9c0-2.12-.74-4.07-1.97-5.61zM12 20c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7
    7-3.13 7-7 7z"/></svg>'; } ]]]
entity: input_boolean.temp_2h
name: 2 HORAS  
show_state: false
style:
  left: 80.05%
  top: 37.9%
  width: 10%
styles:
  custom_fields:
    icon_fan:
      - top: 9%
      - left: 10%
      - width: 3.2vw
      - position: absolute
tap_action:
  action: toggle
template: switch
type: custom:button-card

gif-reloj

3 Likes

https://matt8707.github.io/card-mod-helper/

Thanks Mattias for sharing your excellent work!

I started 4-5days back and this looks perfect for my needs. Experts I am curious about the icons for various custom-buttons like for Climate, Lamps etc, where I could find them or change for my needs? I do not see them in button-card- templates etc. May be I missed to look at the right place. Any pointers ?

Hi,
Im following the guide to add this design to my HA.

I have done the steps to add the cards on hacs frontend, copied the files and atted the lines in configuration.yaml.
Nest step should be to restart HA. But i get an error code:

Error loading /config/configuration.yaml: mapping values are not allowed here
in “/config/include/automation.yaml”, line 188, column 66

Anyone had this or knows the what might be wrong?

Thanks for pointing to the right direction, it helped a lot.
I managed to achieve what I wanted, I have used vertical stack instead of grid and applied the margin inside the themes.yaml file once I identified the element via the tool that you mentioned.

Cheers

1 Like

hey, do you mind sharing your current lovelace? really like the look of your icons, wanted to know how your template and entity card is like for the door in security

Thank you Matthias for your awesome work. I have learnt alot css, javascript and even some svg just studying your code. So big thanks for all the inspiration.

I have kind off done my own take one your take :stuck_out_tongue: I have a tablet in portrait mode, located on the wall just beside our main entrance door. I use it mostly for displaying good to know information about my house/cars world outside etc, not so much for control entities. So i build a simple grid that features apple inspired widgets focusing more on information and a little bit less on controlling entities and it look like this:

With a swipe i can change view to see all my entities if needed.

If someone see something they like just ask and i will explain/give you the code :slight_smile:

15 Likes

Your cards are very beautiful.
Inspired by Apple’s weather app, I tried to recreate the various cards and at the moment this is the result.
I miss the wind card and the pressure card.
If anyone would like to help me finish them.

4 Likes

Looks really good , i also drawn some inspiration from the ios weather app :smiley: can you share the uv-card please :slight_smile: