A different take on designing a Lovelace UI

Hey @Mattias_Persson, how did you get all those sensors from the Galaxy Tab? I have the same tablet mounted on my wall, but only got a couple sensors (light, movement) from binary sensors (over mqtt).
image

Oh, i guess i found it. There`s a Fully Kiosk Integration now on the HACS. Back when i started with a wall mounted tablet theres no suck thing. Its because that i have the old mqtt sensors. :rofl:

ive asked in button-card thread but no one seems to know.

Does anyone know how to change the color of where it says 44w under office header to red? its currently white

image

You can use your browsers Dev-Tools to check the element-id/class and css styles that have been applied to your label.

tried that doesnt really show much

How about this? I don’t have mini-graph-card so it’s just a sensor card.

graph

- type: custom:button-card
  entity: sensor.sensor_hall_temperature
  name: Sensor
  state_display: >
    [[[ return ' '; ]]]
  template:
    - base
    - circle
    - icon_climate
  custom_fields:
    icon: >
      <ha-icon icon="mdi:thermometer" style="color: #9da0a2;"></ha-icon>
    circle: >
      [[[
        if (entity) {
          return `
            <svg viewBox="0 0 50 50">
              <circle cx="25" cy="25" r="20.5" stroke="none" stroke-width="1.5" fill="rgba(255,255,255,0.04)" />
              <text x="50%" y="54%" fill="#8d8e90" font-size="14" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${parseInt(entity.state)}<tspan font-size="10">°C</tspan></text>
            </svg>
          `;
        }
      ]]]
    graph:
      card:
        type: sensor
        entity: >
          [[[ return entity.entity_id; ]]]
        graph: line
        card_mod:
          style: |
            .header, .value, .measurement {
              display: none !important;
            }
    push_graph: >
      [[[
        setTimeout(() => {
          let elt = this.shadowRoot,
            card = elt.getElementById('card'),
            container = elt.getElementById('container'),
            graph = elt.getElementById('graph');

          if (elt && card && container && graph) {
            card.insertBefore(graph, container);
          }
        }, 0);
        return null;
      ]]]
  styles:
    custom_fields:
      graph:
        - position: absolute
        - width: 100%
        - height: 100%
        - clip-path: inset(0 round var(--custom-button-card-border-radius))
        - left: 0
        - bottom: 0
8 Likes

Hey @Mattias_Persson, I tried out your code and the circle and card title scaling works on mobile! Also, I’m happy to report that the vertical positioning of the card title is now matching with the rest of the theme, even on mobile. I haven’t tried incorporating it into a button template yet but I plan to with the code you provided. Thanks!

I apologize but I’m having trouble understanding the repository. I don’t know if that’s the last. Because you have the plex integration but it doesn’t seem to be in the repository. Is that the last thing you have in production?

      - background-image: &media_background_image >
          [[[
            if (entity) {
              return entity.attributes.data !== undefined
                ? `url("${entity.attributes.data[variables.i].fanart}"), url("${entity.attributes.data[variables.i].poster}")`
                : `url("${variables.entity_picture}")`;
            }
          ]]]

Do you know why this error could be?

im really love your work. thanks for effort. i want show battery levels for persons. I need help for it.

IMG_20220521_001318|667x500

@Mattias_Persson so I was able to implement the code into the button_card_templates.yaml but it took some splicing of other portions of your code to make it work AND scaling properly for mobile. Thanks for the help!

For anyone wanting to use mini-graph-card as a button card, the code is below:

button_card_templates.yaml

  #################################################
  #                                               #
  #                  TEMPERATURE                  #
  #                                               #
  #################################################

  temperature:
    template:
      - base
    show_name: true # Hides Card Name
    show_state: true # Hides Card state
    state_display: >
      [[[ return '&nbsp;'; ]]]
    custom_fields:
      circle: >
        [[[ {
        const temperature = Math.round(entity.state);
        return `<svg viewBox="0 0 50 50"><circle cx="25" cy="25" r="20.5" stroke="#313638" stroke-width="1.5" fill="#FFFFFF08" style="
        transform: rotate(-90deg); transform-origin: 50% 50%;" />
        <text x="50%" y="54%" fill="#8d8e90" font-size="14" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle" dominant-baseline="middle">${temperature}°F</text></svg>`; } ]]]
      graph:
        card:
          type: "custom:mini-graph-card"
          height: 140
          hours_to_show: 24
          points_per_hour: 1
          line_width: 8
          font_size: 75
          decimals: 0
          show:
            name: false
            icon: false
            state: false
            legend: false
            labels: false
            labels_secondary: false
            points: false
          color_thresholds:
            - value: 0
              color: "#276696"
            - value: 69
              color: "#228C22"
            - value: 75
              color: "#d35400"
            - value: 76
              color: "#c0392b"
              
    styles:
          custom_fields:
            graph: [bottom: 0%, left: 0%, width: 128%, position: absolute, margin: 0% 0% -13% -14%]
            icon:
              - width: 67%
              - fill: "#9da0a2"
            circle:
              - display: initial
              - width: 90%
              - margin: -6% -5% 0 0
              - justify-self: end
              - opacity: 1

ui-lovelace.yaml

      #################################################
      #                                               #
      #                    BEDROOM                    #
      #                                               #
      ################################################@

      - type: grid
        title: Bedroom
        view_layout:
          grid-area: bedroom
        columns: 2
        cards:

          - type: custom:button-card
            entity: sensor.bedroom_temperature
            name: Temp
            tap_action: !include popup/bedroom_temperature.yaml
            hold_action: 
              action: none
            custom_fields:
              graph:
                card:
                  entities:
                    - entity: sensor.bedroom_temperature
                      color_thresholds:
                        - value: 0
                          color: "#276696"
                        - value: 69
                          color: "#228C22"
                        - value: 75
                          color: "#d35400"
                        - value: 76
                          color: "#c0392b"
                    - entity: sensor.nightstate
                      color: gray
                      y_axis: secondary
                      show_line: false
                      show_points: false
                      show_legend: false
                      show_labels: false
            template:
              - temperature
              - icon_temp

          - type: custom:button-card
            entity: binary_sensor.bedroom_occupancy
            name: Occupancy
            tap_action: !include popup/bedroom_occupancy.yaml
            template:
              - base
              - icon_occupancy
5 Likes

Hi!
First I want to thank all of you for putting all these code up here and helping each others.

I’m trying to use the footer to show how many of my batteries level are under a certain threshold.
But I’m not really good at javascript so I tried to do it in jinja2.
But the problem is that even though I convert it to integer, it seems to not recognize is at integer.
the code is like this.
How come?
Is it possible to make this in javascript?

{% set ignore_entities = ['sensor.bass_boss_battery_sensor','sensor.lenovo_tb_x306f_battery_level'] %}
  {% set test = states.sensor
    | selectattr('attributes.device_class', 'defined')
    | selectattr('attributes.device_class', 'eq', 'battery')
    | rejectattr('entity_id', 'in', ignore_entities)
    | map(attribute='state')
    | reject('in', ['unknown', 'unavailable'])
    | map('int', -1) | select('le', 60)
    |select('ge', 0)
    | list | count
%}

{% if test == 0 %}

{% else %}
  {{ test  | int }}
{% endif %}
1 Like

You can use this template by replacing === 'playing' with < 50

or something like this

let list = [],
  exclude = 'sensor.test_battery';
Object.keys(states).forEach(key => {
  let device_class = states[key].attributes.device_class,
    state = states[key].state;
  if (key !== exclude && device_class === 'battery' && state < 50) {
    list.push(key);
  }
});
return list.length;
1 Like
- type: custom:button-card
  ...
  template: person
  custom_fields:
    circle: >
      [[[
        let battery = states['sensor.iphone_battery_level'];
        if (entity && battery) {
          let stroke = variables.state === 'home' ? '#b2b2b2' : 'none',
          fill = variables.state !== 'home' ? 'rgba(255,255,255,0.04)' : 'none';
          return `
            <svg viewBox="0 0 50 50">
              <circle cx="25" cy="25" r="20.5" stroke="${stroke}" stroke-width="1.5" fill="${fill}" />
              <text x="50%" y="54%" fill="#8d8e90" font-size="14" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${battery.state}<tspan font-size="10">%</tspan></text>
            </svg>
          `;
        }
      ]]]
2 Likes

Anyone receiving this error?

Error: Failed to execute ‘define’ on ‘CustomElementRegistry’: the name “mod-card” has already been used with this registry

Been searching around for a solution and no luck so far.

I did come across that thread but if I’m following what’s mentioned correctly, that specific issue which caused this same error as well was fixed. I was assuming it was something related with how I setup your Lovelace dashboard (which is awesome by the way) but after digging around I’m at a loss. I’ll maybe reopen that issue over on the card-mod repo to see if there’s a solution.

perhaps someone that uses Plex is interrested, i make a workaround for the Recent Plex Widget, with this its possible to show only the “last added unwatched Movies”, and not the “last added Watched / unwatched Movies”.

add a sensor template in your configuration.yaml

- platform: template
    sensors:
      plex_unwatched:
        value_template: "{{ states('sensor.plex_recently_added_movies') }}"
        attribute_templates:
          data: "{{ state_attr('sensor.plex_recently_added_movies', 'data') | selectattr('flag', 'defined') | selectattr('flag', 'eq', true) | list }}"

and then add the name of the sensor in die ui-lovelance “sensor.plex_unwatched”.
thats all, now you have the latest unwatched movies.

2 Likes

Hi @Mattias_Persson, great ui! I’ve been trying to replicate this for my needs, but I’ve run into a problem. The conditional media card does not work at all. I triple checked the code, but no errors in the physical code. The error I’m getting does explain what’s wrong, but even when I try to fix that, the error persists. My limited knowledge of ha is keeping me from figuring out what is wrong.

Here is the error:

Thanks!

Thank you. I am not using your files at all, but that info pointed me to some docs about CSS (of wich I have no idea until now :).
I managed to make my own “grid”, but the spacing among the groups of cards does not work even using the CSS documentation.
It appears that it ignores that part

    type: custom:grid-layout
    layout:
      column-gap: 5px;
      row-gap: 10px;
      grid-template-areas: |
        "sidebar  card01  card02"
        "sidebar  card03  card04"
        "sidebar  footer  footer"

Also grid-gap: does not work.
Is HA overwrites this someplace else?

Progress so far:

This is one of the group that contains 4 cards:

      - type: vertical-stack
        title: Bathroom
        cards:
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                aspect_ratio: 1
                color: auto
                color_type: card
                entity: switch.livolo_bath_right
                icon: mdi:wall-sconce-flat
                name: LED
                haptic: success
                hold_action:
                  action: more-info
                show_last_changed: false
                show_state: false
                state:
                  - styles:
                      card:
                        - filter: opacity(50%)
                        - background: dark gray
                        - font-size: 18px
                        - color: white
                      icon:
                        - filter: grayscale(100%)
                    value: 'off'
                  - styles:
                      card:
                        - background: white
                        - filter: opacity(80%)
                        - font-size: 18px
                        - color: gray
                      icon:
                        - filter: grayscale(100%)
                        - color: gray
                    value: 'on'
                card_mod:
                  style: |
                    ha-card {border-radius: 15px;}
              - type: custom:button-card
                aspect_ratio: 1
                color: auto
                color_type: card
                entity: switch.livolo_bath_left
                name: Spot
                haptic: success
                hold_action:
                  action: more-info
                show_last_changed: false
                show_state: true
                state:
                  - styles:
                      card:
                        - filter: opacity(50%)
                        - background: dark gray
                        - '--mdc-ripple-color': gray
                        - '--mdc-ripple-press-opacity': 0
                        - font-size: 18px
                      icon:
                        - filter: grayscale(100%)
                      label:
                        - font-size: 14px
                    value: 'off'
                  - styles:
                      card:
                        - background: white
                        - '--mdc-ripple-color': gray
                        - '--mdc-ripple-press-opacity': 0.2
                        - font-size: 18px
                        - color: gray
                      icon:
                        - filter: grayscale(100%)
                        - color: gray
                      label:
                        - font-size: 11px
                    value: 'on'
                card_mod:
                  style: |
                    ha-card {border-radius: 15px;}
          - type: horizontal-stack
            cards:
              - type: custom:mini-graph-card
                color_thresholds:
                  - color: '#039BE5'
                    value: 10
                  - color: '#0da035'
                    value: 25
                  - color: '#e45e65'
                    value: 30
                entities:
                  - entity: sensor.ewelink_th02_temperature
                    index: 0
                    name: Temperature
                hours_to_show: 24
                points_per_hour: 1
                refresh_interval: 120
                align_state: left
                show:
                  icon: false
                  icon_adaptive_color: true
                  name: true
                  name_adaptive_color: false
                  points: false
                  labels: false
                title: Outside °C
                card_mod:
                  style: |
                    ha-card {border-radius: 15px;}
              - type: custom:button-card
                aspect_ratio: 1
                color: auto
                color_type: card
                entity: switch.sonoff_zbr3_02
                name: Kitchen Floor
                haptic: success
                hold_action:
                  action: more-info
                show_last_changed: false
                show_state: true
                state:
                  - styles:
                      card:
                        - filter: opacity(50%)
                        - background: dark gray
                        - '--mdc-ripple-color': gray
                        - '--mdc-ripple-press-opacity': 0
                        - font-size: 18px
                      icon:
                        - filter: grayscale(100%)
                      label:
                        - font-size: 14px
                    value: 'off'
                  - styles:
                      card:
                        - filter: opacity(60%)
                        - background: white
                        - '--mdc-ripple-color': gray
                        - '--mdc-ripple-press-opacity': 0.2
                        - font-size: 18px
                        - color: gray
                      icon:
                        - filter: grayscale(100%)
                        - color: gray
                      label:
                        - font-size: 11px
                    value: 'on'
                card_mod:
                  style: |
                    ha-card {border-radius: 15px;}
        view_layout:
          grid-area: card01
        card_mod:
          style: |
            ha-card {margin: 20px;}

Thank you

ps.
I do not use reference files since my UI is rather simple.

Hi @Mattias_Persson
great UI, thanks for effort
How do I get these button cards centered in a vertical stack. I am trying to make a vertical stack with cards to navigate through different views on my dashboard

Capture

      - type: vertical-stack
        view_layout:
          grid-area: sidebar
        cards:

          - type: custom:button-card
            entity: sensor.template_sidebar
            template: sidebar

          - type: conditional
            conditions:
              - entity: input_boolean.laundry_display
                state: 'on'
            card:
              type: custom:button-card
              entity: timer.laundry
              template: laundry

          - type: vertical-stack
            cards:
              - type: custom:button-card
                name: floor 1
                icon: none
                template: sidebar_navigation
                
              - type: custom:button-card
                name: floor2
                icon: none
                template: sidebar_navigation
            
              - type: custom:button-card
                name: floor3
                icon: none
                template: sidebar_navigation
                
              - type: custom:button-card
                name: outside
                icon: none
                template: sidebar_navigation

  sidebar_navigation:
    size: 4.4em
    color: '#9da0a2'
    styles:
      grid:
        - grid-template-areas: '"n"'
        - grid-column-gap: 0.3em
      name:
        - font-size: 1em
        - letter-spacing: 0.015em
      card:
        - color: '#9da0a2'
        - border-radius: 0.6em
        - padding: 1em 1em 1em 1.2em
        - width: 13em
        - font-size: 1.06em
        - font-weight: 500
        - letter-spacing: 0.015em
        - background: '#FFFFFF10'
        - height: 3.5em


  icon-only:
    show_name: false
    color: '#9da0a2'
    styles:
      card:
        - color: '#9da0a2'
        - border-radius: 0.6em
        - width: 4em
        - height: 3.7em
        - background: '#FFFFFF10'