A different take on designing a Lovelace UI

Please, does anyone have any advice?Thx alot

since there is no forcast attributes in the weather entity, this part is not working anymore.

How do I fix it?

1 Like

Yes it not working for me too after the new update.

Backward-incompatible changes

The previously deprecated forecast attribute of weather entities, has now been removed. Use the [weather.get_forecasts] service to get the forecast data instead.

this is what did to solve.

created a new sensor for the forecast only

configuration.yaml

template:
# My Weather Entities Daily
  - trigger:
      - platform: state
        entity_id: weather.home
    action:
      - service: weather.get_forecasts
        data:
          type: daily
        target:
          entity_id: weather.home
        response_variable: daily
    sensor:
      - name: Weather Forecast Daily
        icon: mdi:weather-cloudy
        state: "{{ daily['weather.home'].forecast[0].condition }}"
        attributes:
          forecast: "{{ daily['weather.home'].forecast }}"

sidebar.yaml

....
        weather: >
          {% set entity = 'weather.home' %}
          {% set entity_forcast = 'sensor.weather_forecast_daily' %}
          {% if not is_state(entity, 'unknown') %}
            {% set temp = state_attr(entity, 'temperature') | round %}
            {% set precip = state_attr(entity_forcast, 'forecast')[0]['precipitation'] | round %}
              Outside: {{ temp }}°C <br> {{- 'No risk of rain' if precip == 0
              else precip | string + '% Rain \u2614\uFE0F' }}
          {% else %}
            Weather Unavailable...
          {% endif %}
...
5 Likes

Oh nice thanks for sharing!.. I will try it :slight_smile:

Hey all,

i copied the git-hub-rep of matt and adapted the dashboard for my needs. It works very well.

I tried to make a template for covers/shades with a touch circle like it is in the light/circle template.
I thought that i was able to change everything i need to change, that this will work, but the functionallity is not complete.
works:

  • show cover position in card
    doesn’t work:
  • mouse icon grab on click
  • sending the values and move the cover

Here is my code and what i changed:

First i made a cover.yaml in button_card_templates folder:

cover:
  template:
    - base
    - circle
    - loader
  double_tap_action:
    action: fire-dom-event
    browser_mod:
      service: browser_mod.popup
      data:
        title: >
          [[[
            return !entity || entity.attributes.friendly_name;
          ]]]
        card_mod:
          style:
            #popup header
            .:
        content:
          type: entities
          card_mod:
            style: |
              #states {
                padding-top: 0.5em;
              }
          entities: >
            [[[
              if (entity) {
                  let covers = [],
                      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++) {
                      covers.push({
                          "type": "custom:mushroom-cover-card",
                          "entity": id[i],
                          "fill_container": false,
                          "primary_info": "name",
                          "secondary_info": "state",
                          "icon_type": "icon",
                          "show_brshow_position_control": true
                      });
                  }
                  return covers;
              }
            ]]]
  variables:
    circle_input: >
      [[[
        if (entity) {
            // if cover group get brightness from child to remove bounce
            let child = entity.attributes.entity_id,
                current_position = child && states[child[0]].attributes.current_position
                    ? states[child[0]].attributes.current_position
                    : entity.attributes.current_position;
            return current_position === 0
                ? 1
                : current_position
        }
      ]]]
    circle_input_unit: '%'

after that i added part for covers in circle.yaml

circle:
  styles:
    card:
      - --c-stroke-color-on: '#b0b0b0'
      - --c-stroke-color-off: none
      - --c-fill-color-on: none
      - --c-fill-color-off: rgba(255,255,255,0.04)
      - --c-stroke-width: 2.3
      - --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
    custom_fields:
      circle:
        - display: initial
        - width: 88%
        - margin: -3% 2% 0 0
        - justify-self: end
        - opacity: 1
  custom_fields:
    circle: >
      [[[
        if (entity) {
            let r = 22.1,
                c = r * 2 * Math.PI,
                tspan = '<tspan dx=".2" dy="-.4">',
                domain = entity.entity_id.split('.')[0],
                state = variables.state_on,
                input = variables.circle_input || ' ',
                unit = variables.circle_input_unit || ' ';
            /* * * * * * * * * * * * * * * * * *
            *                                 *
            *             CIRCLE              *
            *                                 *
            * * * * * * * * * * * * * * * * * */
            let circle = (state, input, unit) => {
                return `
                  <svg viewBox="0 0 50 50">
                    <style>
                      circle {
                        transform: rotate(-90deg);
                        transform-origin: 50% 50%;
                        stroke-dasharray: ${c};
                        stroke-dashoffset: ${typeof input === 'number' && c - input / 100 * c};
                        stroke-width: var(--c-stroke-width);
                        stroke: ${state ? 'var(--c-stroke-color-on)' : 'var(--c-stroke-color-off)'};
                        fill: ${state ? 'var(--c-fill-color-on)' : 'var(--c-fill-color-off)'};
                      }
                      text {
                        font-size: var(--c-font-size);
                        font-weight: var(--c-font-weight);
                        letter-spacing: var(--c-letter-spacing);
                        fill: var(--c-font-color);
                      }
                      tspan {
                        font-size: var(--c-unit-font-size);
                      }
                      #circle_value, tspan {
                        text-anchor: middle;
                        dominant-baseline: central;
                      }
                    </style>
                    <circle id="circle_stroke" cx="25" cy="25" r="${r}"/>
                    <text id="circle_value" x="50%" y="52%">${input}${tspan}${unit}</tspan></text>
                  </svg>
                  ${domain === 'light' && `
                      <input id="circle_slider" type="range" min="0" max="100" value="${input}">
                  `}
                `;
            }
           /* * * * * * * * * * * * * * * * * *
            *                                 *
            *              LIGHT              *
            *                                 *
            * * * * * * * * * * * * * * * * * */
            if (domain === 'light') {
                // wait 0ms for shadow dom
                setTimeout(() => {
                    // then get elements
                    let elt = this.shadowRoot,
                        circle_slider = elt.getElementById('circle_slider'),
                        circle_value = elt.getElementById('circle_value'),
                        circle_stroke = elt.getElementById('circle_stroke');
                    // approximate position of thumb relative to circle
                    circle_slider.style.top = `${(circle_slider.value - 50) / 1.66 - 1}%`;
                    // debug position
                    let debug = false;
                    if (debug) circle_slider.style.opacity = 0.3;
                    // pass each event to handler
                    ['click', 'input', 'mousedown', 'mouseup', 'touchstart', 'touchend'].forEach((event) => {
                        circle_slider.addEventListener(event, handler, { passive: true })
                    });
                    function handler(event) {
                        // "this" refers to slider
                        if (event.target === this) {
                            // bypass button-card tap_action
                            event.stopPropagation();
                            // update circle_value
                            circle_value.innerHTML = `${this.value}${tspan}${unit}</tspan>`;
                            // update stroke
                            circle_stroke.style.strokeDashoffset = c - this.value / 100 * c;
                            circle_stroke.style.strokeWidth = 'var(--c-stroke-width-dragging)';
                            // set cursor while dragging
                            if (event.type === 'mousedown' || event.type === 'input') {
                                this.style.cursor = 'grabbing';
                            } else {
                                this.style.cursor = 'grab';
                            }
                            // reset stroke width if value doesn't change
                            if (input == this.value && (event.type === 'click' || event.type === 'touchend'))
                                circle_stroke.style.strokeWidth = 'var(--c-stroke-width)';
                            // on release
                            if (event.type === 'mouseup' || event.type === 'touchend') {
                                // display loader if brightness is 0
                                if (circle_slider.value == 0 && elt.getElementById('loader')) {
                                    elt.getElementById('loader').style.display = 'initial';
                                    elt.getElementById('circle').style.display = 'none';
                                }
                                // set brightness
                                hass.callService('light', 'turn_on', {
                                    entity_id: entity.entity_id,
                                    brightness_pct: this.value
                                });
                            }
                        }
                    }
                }, 0);
                return circle(state, input, unit);
            }
           /* * * * * * * * * * * * * * * * * *
            *                                 *
            *              COVER              *
            *                                 *
            * * * * * * * * * * * * * * * * * */
            else if (domain === 'cover') {
                // wait 0ms for shadow dom
                setTimeout(() => {
                    // then get elements
                    let elt = this.shadowRoot,
                        circle_slider = elt.getElementById('circle_slider'),
                        circle_value = elt.getElementById('circle_value'),
                        circle_stroke = elt.getElementById('circle_stroke');
                    // approximate position of thumb relative to circle
                    circle_slider.style.top = `${(circle_slider.value - 50) / 1.66 - 1}%`;
                    // debug position
                    let debug = false;
                    if (debug) circle_slider.style.opacity = 0.3;
                    // pass each event to handler
                    ['click', 'input', 'mousedown', 'mouseup', 'touchstart', 'touchend'].forEach((event) => {
                        circle_slider.addEventListener(event, handler, { passive: true })
                    });
                    function handler(event) {
                        // "this" refers to slider
                        if (event.target === this) {
                            // bypass button-card tap_action
                            event.stopPropagation();
                            // update circle_value
                            circle_value.innerHTML = `${this.value}${tspan}${unit}</tspan>`;
                            // update stroke
                            circle_stroke.style.strokeDashoffset = c - this.value / 100 * c;
                            circle_stroke.style.strokeWidth = 'var(--c-stroke-width-dragging)';
                            // set cursor while dragging
                            if (event.type === 'mousedown' || event.type === 'input') {
                                this.style.cursor = 'grabbing';
                            } else {
                                this.style.cursor = 'grab';
                            }
                            // reset stroke width if value doesn't change
                            if (input == this.value && (event.type === 'click' || event.type === 'touchend'))
                                circle_stroke.style.strokeWidth = 'var(--c-stroke-width)';
                            // on release
                            if (event.type === 'mouseup' || event.type === 'touchend') {
                                // display loader if position is 0
                                if (circle_slider.value == 0 && elt.getElementById('loader')) {
                                    elt.getElementById('loader').style.display = 'initial';
                                    elt.getElementById('circle').style.display = 'none';
                                }
                                // set position
                                hass.callService('cover', 'set_cover_position', {
                                    entity_id: entity.entity_id,
                                    position: this.value
                                });
                            }
                        }
                    }
                }, 0);
                return circle(state, input, unit);
            }
           /* * * * * * * * * * * * * * * * * *
            *                                 *
            *             PERSON              *
            *                                 *
            * * * * * * * * * * * * * * * * * */
            else if (domain === 'person') {
                let time = c => {
                    let s = (c/1e3),
                        m = (c/6e4),
                        h = (c/36e5),
                        d = (c/864e5);
                    return s < 60
                        ? parseInt(s) + 's'
                        : m < 60 ? parseInt(m) + 'm'
                        : h < 24 ? parseInt(h) + 'h'
                        : parseInt(d) + 'd';
                };
                let input = states[variables.retain] === undefined || states[variables.retain].state === 'unavailable'
                        ? time(Date.now() - Date.parse(entity.last_changed))
                        : time(Date.now() - Date.parse(states[variables.retain].state)),
                    unit = ' ';
                return circle(state, input, unit);
            }
           /* * * * * * * * * * * * * * * * * *
            *                                 *
            *             CLIMATE             *
            *                                 *
            * * * * * * * * * * * * * * * * * */
            else if (domain === 'climate') {
                return circle(state, input, unit);
            }
           /* * * * * * * * * * * * * * * * * *
            *                                 *
            *              OTHER              *
            *                                 *
            * * * * * * * * * * * * * * * * * */
            else if (variables.state_on) {
                return circle(state, input, unit);
            }
        }
      ]]]

There is one thing i am not able to work with (in circle.yaml):

            let circle = (state, input, unit) => {
                return `
                  <svg viewBox="0 0 50 50">
                    <style>
                      circle {
                        transform: rotate(-90deg);
                        transform-origin: 50% 50%;
                        stroke-dasharray: ${c};
                        stroke-dashoffset: ${typeof input === 'number' && c - input / 100 * c};
                        stroke-width: var(--c-stroke-width);
                        stroke: ${state ? 'var(--c-stroke-color-on)' : 'var(--c-stroke-color-off)'};
                        fill: ${state ? 'var(--c-fill-color-on)' : 'var(--c-fill-color-off)'};
                      }
                      text {
                        font-size: var(--c-font-size);
                        font-weight: var(--c-font-weight);
                        letter-spacing: var(--c-letter-spacing);
                        fill: var(--c-font-color);
                      }
                      tspan {
                        font-size: var(--c-unit-font-size);
                      }
                      #circle_value, tspan {
                        text-anchor: middle;
                        dominant-baseline: central;
                      }
                    </style>
                    <circle id="circle_stroke" cx="25" cy="25" r="${r}"/>
                    <text id="circle_value" x="50%" y="52%">${input}${tspan}${unit}</tspan></text>
                  </svg>
                  ${domain === 'light' && `
                      <input id="circle_slider" type="range" min="0" max="100" value="${input}">
                  `}

The problem for me is that the covers do not have a state which is on or off like lights.

At last from ui-lovelace.yaml the custom button card:

          - type: custom:button-card
            entity: cover.jalousie_fenster_wohnzimmer
            name: Jalousien
            state_display: >
              [[[
                if (variables.state === 'playing') {
                    return 'läuft';
                }
                if (variables.state === true) {
                    return variables.translate_unknown;
                }
              ]]]
            double_tap_action:
              !include popup/wohnzimmer_tv.yaml
            template:
              - cover
              - base

Can anyone help me to find out what i have done wrong?

Thank you very much.

BR Marc

Great job on this!
I have been using it for a while and works fine.
However, I cannot seem to figure out how to change the opacity of buttons and colour fonts in general.

Eg. when i change from 0.25 to 0.6 as described below nothing happens. I would think the buttons would become less transparent when the device is not activated but they doesnt.

base:
  template:
    - settings
    - tilt
    - extra_styles
  variables:
    state_on: >
      [[[ return ['on', 'home', 'cool', 'fan_only', 'playing', 'unlocked', 'open', 'heat_cool', 'cleaning'].indexOf(!entity || entity.state) !== -1; ]]]
    state: >
      [[[ return !entity || entity.state; ]]]
    entity_id: >
      [[[ return !entity || entity.entity_id; ]]]
    entity_picture: >
      [[[ return !entity || entity.attributes.entity_picture; ]]]
    timeout: >
      [[[ return !entity || Date.now() - Date.parse(entity.last_changed); ]]]
    is_youtube: >
      [[[
        let is_youtube = entity?.attributes?.app_id === 'com.google.ios.youtube',
            sensor = this?._config?.triggers_update,
            media_title = entity?.attributes?.media_title,
            watching_title = states[sensor]?.attributes?.title;
        if (is_youtube && media_title === watching_title) {
            return true;
        }
      ]]]
  aspect_ratio: 1/1
  show_state: true
  show_icon: false
  state_display: >
    [[[ if (variables.state === true) return variables.translate_unknown; ]]]
  tap_action:
    ui_sound_tablet: |
      [[[
        let screensaver = states[variables.entity_tablet] === undefined ||
            states[variables.entity_tablet].state;

        if (variables.state === 'off' && screensaver === 'off') {
            hass.callService('media_player', 'play_media', {
                entity_id: variables.entity_browser_mod,
                media_content_id: '/local/sound/on.m4a',
                media_content_type: 'music'
            });
        }
        if (variables.state_on && screensaver === 'off') {
            hass.callService('media_player', 'play_media', {
                entity_id: variables.entity_browser_mod,
                media_content_id: '/local/sound/off.m4a',
                media_content_type: 'music'
            });
        }
      ]]]
    card_bounce: |
      [[[
        // add animation
        if (this.getElementsByTagName("style").length === 0) {

            // phone condition
            let mq = window.matchMedia('(max-width: 800px)').matches;

            let style = document.createElement('style');

            style.innerHTML = `
                @keyframes card_bounce {
                    0%   { transform: scale(1); }
                    10%  { transform: scale(${ mq ? '0.92' : '0.94' }); }
                    25%  { transform: scale(1); }
                    30%  { transform: scale(${ mq ? '0.96' : '0.98' }); }
                    100% { transform: scale(1); }
                }
            `;

            this.appendChild(style);
        }

        // duration
        let duration = 800;

        // animate
        this.style.animation = `card_bounce ${duration}ms cubic-bezier(0.22, 1, 0.36, 1)`;

        // reset
        window.setTimeout(() => { this.style.animation = "none"; }, duration + 100)
      ]]]
    action: toggle
    haptic: medium
  double_tap_action:
    haptic: success
  hold_action:
    action: block
  styles:
    grid:
      - grid-template-areas: |
          "icon  circle"
          "n     n"
          "s     s"
      - grid-template-columns: repeat(2, 1fr)
      - grid-template-rows: auto repeat(2, min-content)
      - gap: 1.3%
      - align-items: start
      - will-change: transform
    name:
      - justify-self: start
      - line-height: 121%
    state:
      - justify-self: start
      - line-height: 115%
    card:
      - border-radius: var(--button-card-border-radius)
      - border-width: 0
      - -webkit-tap-highlight-color: rgba(0,0,0,0)
      - transition: none
      - --mdc-ripple-color: >
          [[[
            return variables.state_on
                ? 'rgb(0, 0, 0)'
                : '#97989c';
          ]]]
      - color: >
          [[[
            return variables.state_on
                ? '#4b5254'
                : '#97989c';
          ]]]
      - background-color: >
          [[[
            return variables.state_on
                ? 'rgba(255, 255, 255, 0.85)'
                : 'rgba(115, 115, 115, 0.25)';
          ]]]

From:

      - background-color: >
          [[[
            return variables.state_on
                ? 'rgba(255, 255, 255, 0.85)'
                : 'rgba(115, 115, 115, 0.25)';
          ]]]

to:

      - background-color: >
          [[[
            return variables.state_on
                ? 'rgba(255, 255, 255, 0.85)'
                : 'rgba(115, 115, 115, 0.6)';
          ]]]

I have tried making a quick restart and even cleared my browser cache and cookies.

Hi, I have a problem with the sensor:

- trigger:
    - platform: state
      entity_id: weather.forecast_home
  action:
    - service: weather.get_forecast
      data:
        type: daily
      target:
        entity_id: weather.forecast_home
      response_variable: daily
  sensor:
    - name: Weather Forecast Daily
      unique_id: weather_forecast_daily
      icon: mdi:weather-cloudy
      state: "{{ daily['weather.forecast_home'].forecast[0].condition }}"
      attributes:
        forecast: "{{ daily['weather.forecast_home'].forecast }}"

the service works:

but I cant understand what I do wrong…

1 Like

Hi I followed your guide but I didn’t get it to work :frowning: Any idea whats wrong?


      #################################################
      #                                               #
      #                    SIDEBAR                    #
      #                                               #
      #################################################

      - 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
              double_tap_action:
                action: call-service
                service: input_boolean.turn_off
                service_data:
                  entity_id: input_boolean.laundry_display
              template: laundry
              
          - type: custom:decluttering-card
            template: climate_card
            variables:
            - title: Schlafizmmer
            - entity_display: sensor.espresense_bt_sensor_a4c1389600b7_temperature
            - entity_temperature: sensor.espresense_bt_sensor_a4c1389600b7_temperature
            - entity_valve: sensor.espresense_bt_sensor_a4c1381278a0_temperature                

      #################################################
      #                                               #
      #                  VARDAGSRUM                   #
      #                                               #
      #################################################

Sorry for another stupid question but how do I get a background image over the whole screen?

have you install decluttering card? if yes, clear cache and reload resource etc.

1 Like

Yes. I have rebooted, cleared cache and also rebootet my proxmox server. Nothing :frowning:

Apparently I’m too stupid to understand it. Could you explain to a dumb guy like me how exactly you integrated the icons with a code snippet? :slight_smile: Thanks a lot

Did u manage to include the code correctly because I have exactly the same problem.

whats yoru issue?

I really love your chips section at the top and the header on the right side, is there a way you could share how you created them?? I would like to use them. Hope to hear from you!

Hi,
First thank you for this !
I would like change some icons. But i don’t know how, coult you show me how to add a custom icon.
Is it possible to put a PNG image ?
Thanks

this is not that easy :slight_smile:. Either you work your way through his great (and thanks for your sharing!) GIT or you try a “simpler” approach first, as I’ve already copied and integrated some of these things I liked from @VietNgoc GIT.

Footer bottom:

Footer adaption for having the “chip” card at the bottom or top.

lovelace.yaml:

      #portrait
      "(max-width: 1200px)":
      grid-gap: calc(var(--custom-layout-card-padding) * 0.9)
      grid-template-columns: repeat(4, 1fr) 0
      grid-template-rows: 0 repeat(3, fit-content(100%)) 0fr
      grid-template-areas: |
        ".         .           .       .       ."
        "footer    footer    footer    footer    ."
        "sidebar  vardagsrum  studio  sovrum  ."
        "sidebar  media       övrigt  hemma   ."
    cards:

      #################################################
      #                                               #
      #                    FOOTER                     #
      #                                               #
      #################################################

      - type: horizontal-stack
        view_layout:
          grid-area: footer
        cards:

          - type: custom:button-card
            name: Papa
            tap_action: !include popup/hemma_mattias.yaml
            entity: person.michael
            template:
              - chips
            styles:
              icon:
                - padding: 3px
                - background-color: null
                - width: 50%
            triggers_update: sensor.time

          - type: custom:button-card
            name: Kameras
            entity: alarm_control_panel.blink_schweden
            tap_action: !include popup/blink.yaml
            icon: >
              [[[
                const state = states['alarm_control_panel.blink_schweden'].state;
                return state === 'armed_away' ? 'mdi:shield-home' : 'mdi:shield-off';
              ]]]
            template:
              - chips
            state_display: >
              [[[
                let state = states['alarm_control_panel.blink_schweden'].state;
                if (state == 'armed_away') return 'Aktiv';
                if (state == 'disarmed') return 'Aus';
                return '';
              ]]]
            styles:
              icon:
                - color: >
                    [[[
                      return variables.state_on
                        ? 'green'
                        : '#97989c';
                    ]]]

          - type: custom:button-card
            name: Pi-Hole
            entity: switch.pi_hole
            tap_action: !include popup/avm.yaml
            icon: mdi:pi-hole
            template:
              - chips
            state_display: >
              [[[
                let state = states['switch.pi_hole'].state;
                if (state == 'on') return 'Aktiv';
                if (state == 'off') return 'Aus';
                return '';
              ]]]
            styles:
              icon:
                - color: >
                    [[[
                      return variables.pihole
                        ? 'green'
                        : '#97989c';
                    ]]]

          - type: custom:button-card
            name: Lichter
            entity: sensor.current_lights_on_test
            tap_action: !include popup/unten.yaml
            icon: >
              [[[
                return parseInt(states['sensor.current_lights_on_test'].state) > 0
                  ? 'mdi:lightbulb-group'
                  : 'mdi:lightbulb-group-off';
              ]]]
            template:
              - chips
            state_display: >
              [[[
                let lightCount = parseInt(states['sensor.current_lights_on_test'].state);
                if (lightCount == 0) return 'Alles aus';
                if (lightCount == 1) return '1 Licht an';
                return `${lightCount} Lichter an`;
              ]]]
            styles:
              icon:
                - color: >
                    [[[
                      return variables.state_on
                        ? '#3182b7'
                        : '#97989c';
                    ]]]

          - type: custom:button-card
            name: Media
            entity: sensor.currently_playing
            icon: >
              [[[
                return parseInt(states['sensor.currently_playing'].state) > 0
                  ? 'mdi:television-speaker-off'
                  : 'mdi:television-speaker';
              ]]]
            tap_action: !include popup/currently_playing.yaml
            template:
              - chips
            state_display: >
              [[[
                  if (variables.playing == '0 Playing') return 'None';
                  else return variables.playing;
              ]]]
            styles:
              icon:
                - color: >
                    [[[
                      return variables.playing
                        ? '#3182b7'
                        : '#97989c';
                    ]]]

          - type: custom:button-card
            name: Ambilight
            entity: light.tv_wohnzimmer_ambilight
            icon: >
              [[[
                const state = states['light.tv_wohnzimmer_ambilight'].state;
                return state === 'on' ? 'mdi:television-ambient-light' : 'phu:lightstrip-tv-off';
              ]]]
            template:
              - chips
            state_display: >
              [[[
                let state = states['light.tv_wohnzimmer_ambilight'].state;
                if (state == 'on') return 'An';
                if (state == 'off') return 'Aus';
                return '';
              ]]]
            styles:
              icon:
                - color: >
                    [[[
                      return variables.state_on
                        ? '#3182b7'
                        : '#97989c';
                    ]]]
            tap_action:
              action: toggle

          - type: custom:button-card
            name: Batterie
            entity: sensor.devices_with_low_battery
            icon: >
              [[[
                return parseInt(states['sensor.devices_with_low_battery'].state) > 0
                  ? 'mdi:battery-alert'
                  : 'mdi:battery';
              ]]]
            tap_action: !include popup/lowbattery.yaml
            template:
              - chips
            state_display: >
              [[[
                let batteryCount = parseInt(states['sensor.devices_with_low_battery'].state);
                if (batteryCount== 0) return '0 Leer';
                if (batteryCount== 1) return '1 Leer';
                return `${batteryCount} Leer`;
              ]]]
            styles:
              icon:
                - color: >
                    [[[
                      return variables.battery 
                        ? '#cc2323'
                        : '#97989c';
                    ]]]

          - type: custom:button-card
            name: Processor
            entity: sensor.system_monitor_processor_use
            show_icon: true
            template:
              - chip_system
            tap_action:
              !include popup/system.yaml

you need to adapt the chip.yaml to your system variables/sensor, for example:

chips:
  template:
    - settings
    - tilt
    - extra_styles
  variables:
#    state_on: >
#      [[[ return ['on', 'home', 'Normal', 'armed_away', 'Night', 'Aktiv', 'cool', 'fan_only', 'unlocked', 'open', 'streaming', 'yes', '1'].indexOf(!entity || entity.state) !== -1; ]]]
    state_on: >
      [[[
        const state = entity ? entity.state : null;
        const numState = Number(state);
        return ['on', 'home', 'Normal', 'armed_away', 'Night', 'Aktiv', 'cool', 'fan_only', 'unlocked', 'open', 'streaming', 'yes'].includes(state) ||
               (!isNaN(numState) && numState >= 1);
      ]]]
    state: >
      [[[ return !entity || entity.state; ]]]
    light_on: >
      [[[ return states['sensor.current_lights_on_test'].state;]]]
    battery: >
      [[[ return states['sensor.devices_with_low_battery'].state;]]]
    playing: >
      [[[ return states['sensor.currently_playing'].attributes.device_playing; ]]]
    pihole: >
      [[[ return states['switch.pi_hole'].state;]]]

good luck, Michael
PS: As new user I´m only allowed to add one example picture.

1 Like

Guys it’s time to move to ha-fusion add-on.

Is a simple official integration that let you built cool dashboard with no effort.

Is still in beta but enough stable from experience.
Well developed by @Mattias_Persson. Thank you so much

2 Likes

For anyone with a missing/black Background after updating to Home Assistant 2024.05. Change the following in tablet.yaml and restart Home-Assistant afterwards or reload the themes via frontend.reload_themes.

Before:

  card-mod-root: |
    #view {
      background: url('/local/background.png');
      background-size: cover;
    }

After:

  card-mod-root: |
    #view {
      background: url('/local/background.png') !important;
      background-size: cover !important;
    }
8 Likes