A different take on designing a Lovelace UI

That Looks great, can you please share your sidebar / sidebar_time Templates Sensor? :wink:

Change background color through circle_input? Someone might already have done this, but I can’t find the solution I am looking for. I want to change the background color of the circle depending on the state of an entity, in this case my heat pump. I want it to be red if the pump is running, otherwise off. my code so far is below
Screenshot 2023-10-29 170232

I get the color, but not in the circle, and it also throws me an error message I didn’t have before. My skills at CSS is on a “copy paste and se if I can figure it out” kind of level…

Impressed with the original work of many of y’all in here and hope for some guidance.

 - type: custom:button-card
            entity: climate.varmepumpen
            hold_action:
              !include popup/heat_pump.yaml
            show_name: true
            template:
              - base
              - icon_ac
              - circle
              - loader
            variables:
              circle_input: |
                [[[
                var entity_id = 'climate.varmepumpen';
                if (entity && entity.state !== 'off') {
                  var preset_mode = entity.attributes.preset_mode;
                  var backgroundColor = 'red';
                  return `<div style="background-color: ${backgroundColor}">${preset_mode}</div>`;
                  }
                  return null;
                ]]]

1 Like

Hi,
I need help with the last part in my setup. It has been working on my old setup (not docker). See pic above.Now i need help with the theme, its looks messy. I have not change anything.

Background dosent show (only black):

Text style looks odd and the layout on the phone is messy.

On the “Media” section when i hover over media card its not “round”.

I also cant find a sulution to make/delete “Groups” in the “Helper” tab when im running docker.
Just now i cant group any lights in setup…

I have tryied restart and clean the browser, also checked the “Tablet.yaml” in Themes folder

This is my old setup when it was working:

Look at the styles section of the circle template:


circle:
  styles:
    card:
      - --c-fill-color-on: none
      - --c-fill-color-off: rgba(255,255,255,0.04)

You could do sth like this:


      - --c-fill-color-on: >
          [[[
            let d = entity.entity_id.split('.')[0];
            return (d == 'climate')
              ? 'red'
              : 'none';
          ]]]

1 Like

@cperuffo3 would you mind sharing the code for the sensor tile on the left side?

Thanks! It almost works. Climate doesn’t return “on”, but in principle this works. I need it to turn red when climate is not off rather than on.
So I guess this should do the trick?

--c-fill-color-on: >
  [[[
    let d = entity.entity_id.split('.')[0];
    return (d === 'climate' && entity.state !== 'off')
      ? 'red'
      : 'none';
  ]]]
1 Like

Hey everyone!

Does someone have a working swipe card? I tried to modify the media swipe card but I can’t get it to work. I just want a second pane behind some other panels.

1 Like

:wave: , hi there.
Do I need to add an icon pack first?
Or can copy & past this code in the icon template config?

I did the same couple days ago, because i got some new lights in HA.
So, heres my yaml:

          - type: custom:swipe-card
            parameters:
              speed: 550
              spaceBetween: 40
              threshold: 5
            cards:

              - type: grid
                columns: 2
                cards:

                  - type: custom:button-card
                    entity: light.sofalicht
                    name: Sofalicht
                    template:
                      - light
                      - icon_hue

                  - type: custom:button-card
                    entity: light.schreibtischlicht
                    name: Schreibtisch
                    template:
                      - light
                      - icon_hue

                  - type: custom:button-card
                    entity: light.kochlicht_light
                    name: Kochlicht
                    template:
                      - light
                      - icon_hue

                  - type: custom:button-card
                    entity: light.elements_5d2a
                    name: Nanoleaf
                    template:
                      - light
                      - icon_nanoleaf

              - type: grid
                columns: 2
                cards:

                  - type: custom:button-card
                    entity: light.terrassenlicht
                    name: Terrassenlicht
                    template:
                      - light
                      - icon_ledstripe

Hope, this helps.

Did you mange it to work?
Where did you put the code finally?

Ha! Works. Thank you. I messed up with the Grid. That was the reason why it wasn’t working for me.

Maybe you can help me with a other thing. When swiping the “Media” Card the Header get’s replaced with “Spelare”. I want to achieve the following: Default on load the Card should say “Obergeschoss” swiping it should change it to “Untergeschoss”.

Edit: Found it. It’s inside the “button_card_templates” media.yaml

2 Likes

I gave up with the code VietNgoc provided. It’s too much hassle to get it working. I would recommend to pick the things you want and build it in Matts default dashboard.

2 Likes

Not yet. Strange though - I tested it with the light domain, and that went well. But when tested for the climate unit, i don’t get a color change and I get errors for some of the other buttons.

Post your complete code.

This is for the block where the button resides:

      - type: grid
        title: Studio
        view_layout:
          grid-area: studio
        columns: 2
        cards:

          - type: custom:button-card
            entity: sensor.snitt_temperatur
            name: Temperatur
            hold_action:
              !include popup/test.yaml
            template:
              - temperature
              - icon_temp
            custom_fields:
              graph:
                card:
                  entities:
                    - entity: sensor.snitt_temperatur
                    - entity: binary_sensor.natt
                      color: orange
                      y_axis: secondary
                      show_line: false
                      show_points: false
                      show_legend: false
                      show_labels: false
          
          - type: custom:button-card
            entity: climate.varmepumpen
            hold_action:
              !include popup/heat_pump.yaml
            show_name: true
            template:
              - base
              - icon_ac
              - circle
              - loader
            variables:
              circle_input: |
                [[[
                var entity_id = 'climate.varmepumpen';
                  return entity && entity.state !== 'off' ? entity.attributes.preset_mode : null;
                ]]]
                
            name: >
              [[[
                return entity && entity.state !== 'off' ? entity.attributes.fan_mode : null;
              ]]]
            styles:
              name:
                - font-size: x-small
                - justify-self: right
                - font-weight: bold

I meant the circle template. Or are you using the original one? If so, what errors do you receive?

I have the original circle template, I want to try to change the colour of the outside.

Let’s say, climate state is “auto” turn green, when state is anything else turn red.

circle.yaml:

circle:
  styles:
    card:
      - --c-stroke-color-on: '#b0b0b0'
      - --c-stroke-color-off: none
      - --c-fill-color-on: >
            [[[
              let d = entity.entity_id.split('.')[0];
              return (d === 'climate' && entity.state !== 'off')
              ? 'red'
              : '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' && state) {

                // 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);
            }

           /* * * * * * * * * * * * * * * * * *
            *                                 *
            *             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);
            }
        }

Then this shows upp on the following card as an error: clima