A different take on designing a Lovelace UI

did you found a solution? get the same problem

I’ve just finished bleeding of the setup for the old one, I will enjoy it for a while before to try the new!

1 Like

yeah and its still pre beta… not really all the functions and bugs. You can’t make it the default Dashboard, what is a no go atm.

I got a request via chat to add some more picture, if naybody like I can add the adapted code for my personal need which I have taken out of these thread

1buero

Büro/Office: I add in all main rooms icons in the upper right corner which indicate if the radiator is on or off, if on then the icon is blue. Beside the temp icon I add an window open or close icon which check if window open detection is on then there is an grey icon and when the window is detected as open over the radiator then the icon is blue.
By the weather part I just add a fourth indication and middle the whole part for my person look and feel.

climate_on

The radiator background gets white when heating is on, the icon gets blue and the valve state of the ventil is under the icon whit the related value. When the radiator is in eco mode the icon will be green otherwise gray as above. Current temp is in the circle and on the left side the expected temp.

1kueche

Dishwasher integration with related pop up taken from Ngoc git.

2sidebar

Add small icon for blink cameras, pi-hole, low battery, ambilight, light who are on and battery state of the tablet.

Media part actually with gallery function, which is played stored pictures when no media.player is playing. Currently I´m adding trakt as sensor which is working and maybe add kodi as well.

The popup for the lights filter first and second floor and show only active lamps.

battery

Battery icon everywhere where it is needed appear when battery is low.

All set up flexible as possible for me over variables to add the additional function icons everywhere I need to have them.

3 Likes

Hello, I have been trying to implement this now for some days but I’m struggling with the background of the popup. I can’t find anywhere where it gets its background. Can you please point me in the right direction?

Thx.

Hello, can you share the codes, I’m interested in the ones from the thermostats. Tnx

Hi, here the code for the radiator:
I split the battery and valve parts, so that I can add them in other cards where I want to have these parts alone.

                  - type: custom:button-card
                    entity: climate.wohnzimmer
                    name: Front
                    show_state: false
                    tap_action:
                      action: more-info
                    custom_fields:
                      graph:
                        card:
                          entities:
                            - entity: sensor.wohnzimmer_temperature
                            - entity: sensor.wohnzimmer_position
                              color: gray
                              y_axis: secondary
                    variables:
                      valve: '[[[ return states["sensor.wohnzimmer_position"].state]]]'
                      battery_temp: '[[[ return states["sensor.wohnzimmer_battery"].state]]]'
                    triggers_update:
                      - sensor.wohnzimmer_temperature
                      - sensor.wohnzimmer_position
                    template: [valve, radiator, battery_temp]

radiator:
  template:
    - base
    - circle
  variables:
    state: >
      [[[
        return entity?.attributes.hvac_action;
      ]]]
    state_on: >
      [[[
        return entity?.attributes.hvac_action === 'heating';
      ]]]
    circle_input: >
      [[[
        if (entity) {
          const temp = entity.attributes.current_temperature;
          return parseFloat(temp).toFixed(1);
        }
      ]]]
    circle_input_unit: '°C'
  state_display: >
    [[[ return entity.attributes.hvac_action === 'idle' ? 'Aus' : 'Heizen'; ]]]
  custom_fields:
    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
          points: false
          fill: fade
          animate: true
        color_thresholds:
          - color: '#276696'
            value: 17
          - color: '#22878c'
            value: 18
          - color: '#228c3d'
            value: 19
          - color: '#d35400'
            value: 20
          - color: '#e6370b'
            value: 21
          - color: '#cc1f08'
            value: 22
        card_mod:
          style: |
            :host{
                  --ha-card-border-width: 0px;
                 }
    icon: |
      [[[
        var action = entity.attributes.hvac_action;
        var eco_mode = entity.attributes.eco_mode;
        var state = entity.state;
        var svg_color;
    
        if (state === 'off') {
          svg_color = '#127612';
        } else if (action === 'heating') {
          svg_color = '#3182b7'; 
        } else if (action === 'idle' && eco_mode === 'ON') {
          svg_color = '#127612';
        } else {
          svg_color = '#97989c';
        }
    
        var temperature = entity.attributes.temperature;
        var current_temperature = entity.attributes.current_temperature;
        var text_color;
    
        if (temperature === current_temperature) {
          text_color = '#97989c';  // Grau, wenn gleich
        } else {
          text_color = action === 'heating' ? '#3182b7' : (action === 'idle' && eco_mode === 'ON' ? '#127612' : '#3182b7');
        }
        return `<div style="display: flex; align-items: center; justify-content: flex-start; position: relative; width: 87%;">
                  <svg viewBox="-949 951 100 125"><style>@keyframes animate{0%{transform: scale(0.85);}20%{transform: scale(1.1);}40%{transform: scale(0.95);}60%{transform: scale(1.03);}80%{transform: scale(0.97);}100%{transform: scale(1);}}.animate{animation: animate 0.8s; transform-origin: center;}</style>
        <path fill="#9da0a2" d="M-895.9,1020.1v-56.7c0-5.5-4.5-10-10-10c-5.5,0-10,4.5-10,10v56.7c-3.7,3-6,7.6-6,12.4c0,8.8,7.2,16,16,16 c8.8,0,15.9-7.2,15.9-16C-890,1027.7-892.2,1023.1-895.9,1020.1z M-905.9,1045.1c-6.9,0-12.5-5.6-12.5-12.5c0-4.4,2.3-8.4,6-10.7 v-58.4c0-3.6,2.9-6.6,6.6-6.6c3.6,0,6.6,2.9,6.6,6.6v58.4c3.6,2.3,6,6.3,6,10.7C-893.4,1039.5-899,1045.1-905.9,1045.1z"/>
        <path fill="${svg_color}" d="M-902.8,1024v-22.9c0-1.7-1.4-3.1-3.1-3.1c-1.7,0-3.1,1.4-3.1,3.1v22.9c-3.5,1.3-6,4.6-6,8.5c0,5,4.1,9.1,9.1,9.1 c5,0,9.1-4.1,9.1-9.1C-896.8,1028.6-899.3,1025.3-902.8,1024z"/>
        <rect fill="#9da0a2" x="-892.4" y="974.9" width="7.7" height="3.4"/>
        <rect fill="#9da0a2" x="-892.4" y="986.3" width="16.3" height="3.4"/>
        <rect fill="#9da0a2" x="-892.4" y="997.6" width="7.7" height="3.4"/>
        <rect fill="#9da0a2"v x="-892.4" y="1008.9" width="16.3" height="3.4"/>
        </svg>
                  <span style="position: absolute; right: -12%; top: 5%; transform: translateY(-50%); font-size: calc(var(--button-card-font-size) * 0.6);">${temperature}°C</span>
                </div>`;
      ]]]
  styles:
    card:
      - background-color: >
          [[[
            return entity?.attributes.hvac_action === 'heating'
                ? 'rgba(255, 255, 255, 0.85)'
                : 'rgba(115, 115, 115, 0.25)';
          ]]]
      - color: >
          [[[
            return entity?.attributes.hvac_action === 'heating'
                ? '#3182b7'
                : '#97989c';
          ]]]
    custom_fields:
      icon:
        - width: 108%
        - margin-left: -27%
      temperature:
        - font-size: calc(var(--button-card-font-size) * 0.6)
        - right: 10%
        - top: 50%
        - transform: translateY(-50%)
      graph:
        - bottom: 0%
        - left: 0%
        - width: 130%
        - position: absolute
        - margin: 0% 0% -13% -15%
      circle:
        - display: initial
        - width: 90%
        - margin: -6% -5% 0 0
        - justify-self: end
        - opacity: 1
        - --c-fill-color-on: rgba(255,255,255,0.04)
        - --c-stroke-color-on: '#3182b7'
        - --c-fill-color-off: rgba(255,255,255,0.04)
        - --c-stroke-color-off: 'none'
    style: |
      ha-card {
              box-shadow: none;
              }
    name:
      - place-self: start
      - margin-top: -55%

valve:
  styles:
    custom_fields:
      valve:
        - position: absolute
        - right: -6%
        - top: 58%
        - font-size: calc(var(--button-card-font-size) * .2 * var(--card-phone))
        - transition: top 250ms ease-out
        - '--text-color-sensor': >-
           [[[ 
             var hvac_action = states[entity.entity_id].attributes.hvac_action;
             if (hvac_action === 'heating' || variables.valve > 50) return "#3182b7";
             else if (variables.valve >= 1 && hvac_action === 'heating') return "#3182b7";
             return "#97989c";
           ]]]
  custom_fields:
    valve: |
      [[[
        var hvac_action = states[entity.entity_id].attributes.hvac_action;
        var valve_icon = hvac_action === 'heating' ? 'open' : 'closed';
        return `<ha-icon icon="mdi:valve-${valve_icon}" style="width:10%; color: var(--text-color-sensor);"></ha-icon><span>${variables.valve}%</span>`;
      ]]]

battery_temp:
  custom_fields:
    battery_temp: |
      [[[
        if (variables.battery_temp == '100') { 
          return `<ha-icon icon="" style="width:100%;display:flex;color: #EF4F1A;"></ha-icon>`;
        }
        if (variables.battery_temp == 'unavailable') { 
          return `<ha-icon icon="mdi:battery-alert-variant-outline" style="width:100%;display:flex;display:flex;color: #EF4F1A;"></ha-icon>`;
        }
        if (variables.battery_temp <= '10') { 
          return `<ha-icon icon="mdi:battery-alert-variant-outline" style="width:100%;display:flex;color: #EF4F1A;"></ha-icon>`;
        }
        if (variables.battery_temp <= '20') {
          return `<ha-icon icon="mdi:battery-20" style="width:100%;display:flex;color: #EF4F1A;"></ha-icon>`;
        }
        if (variables.battery_temp == 'on') { 
          return `<ha-icon icon="mdi:battery-alert-variant-outline" style="width:100%;display:flex;color: #EF4F1A;"></ha-icon>`;
        }
        else {
          return `<ha-icon icon="" style="width:100%;display:flex;color: #EF4F1A;"></ha-icon>`;
        }
      ]]]
  styles:
    custom_fields:
      battery_temp:
        - position: absolute
#        - height: auto
        - transition: 0.25s
        - width: 14%
#        - right: -9%
#        - padding: 5%
#        - margin: -10%
#        - top: 65%
        - right: |
            [[[
              return entity.state === 'on'
                ? '-11%'
                : '-11%';
            ]]]
        - top: |
            [[[
              return entity.state === 'on'
                ? '44%'
                : '44%';
            ]]]

the sensors I used:

#  - platform: template
#    sensors:
      wohnzimmer_temperature:
        friendly_name: Wohnzimmer Temperatur
        unit_of_measurement: '°C'
        value_template: "{{ state_attr('climate.wohnzimmer', 'current_temperature') }}"

#  - platform: template
#    sensors:
      wohnzimmer_position:
        friendly_name: Wohnzimmer Position
        unit_of_measurement: '%'
        value_template: "{{ state_attr('climate.wohnzimmer', 'position') }}"

I hope I didn´t missed anything

Hi VietNgoc,

thanks for sharing your git. Can you tell me how you define the related kodi sensor for the movie intergration:

sensor.kodi_added_movie_*
with
added_movie

      - type: grid
        title: Movie
        view_layout:
          grid-area: movie
        columns: 1
        cards:
          - type: custom:auto-entities
            filter:
              include:
                - entity_id: 'sensor.kodi_added_movie_*'
                  options:
                    type: custom:button-card
                    template:
                      - added_movies
            card:
              type: custom:swipe-card
              parameters:
                watchSlidesProgress: false
                slidesPerView: 1
                speed: 700
                roundLengths: true
                grabCursor: false
                followFinger: false
                pagination:
                  type: bullets
                  clickable: true
                  dynamicBullets: true
                  dynamicMainBullets: 1
                effect: cube
                cubeEffect:
                  shadow: false
                autoplay:
                  delay: 10000
                  disableOnInteraction: false
                  pauseOnMouseEnter: true
            card_param: cards
            sort:
              count: 10

the needed part for my need from your media integration I got, but the kodi sensor confuses me…

swipe

I am trying to make a dashboard for mobile

I use a swiper card, but at the right, the card is not fully there
i tried card width, but that doesnt change

  - type: custom:swipe-card
    card_width: auto

any ideas ?

edit: when I resize my window on desktop and resize it to original, it is ok…

as rquested by message:
widget_weather.yaml

widget_weather:
  template:
    - base
  variables:
    temp_min: ''
    temp_max: ''
    humidity: ''
    feels: ''
    current_weather: ''
    is_bellow_horrizon: >
      [[[
        if (states['sun.sun'].state == 'below_horizon') {
            return true;
        }
      ]]]
  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: 1fr 1fr
      - gap: 0%
      - overflow: visible
    card:
      - padding: 11.5% 10.5% 10.5% 11.5%
      - color: rgba(255, 255, 255, 0.6)
      - background: >
          [[[
            let weather = states[variables.current_weather].state.toLowerCase();
            let now = new Date();
            let isDaytime = now.getHours() >= 6 && now.getHours() < 18;
            if (!isDaytime) {
              return `linear-gradient(to top, rgba(255, 192, 203, 0.5) 0%, rgba(90,113,157,0.4) 100%) 100% / cover, url(/local/svg/weather/gif/${weather}-night.gif)`;
            } else {
              return `linear-gradient(to top, rgba(255, 192, 203, 0.5) 0%, rgba(90,113,157,0.4) 100%) 100% / cover, url(/local/svg/weather/gif/${weather}-day.gif)`;
            }
          ]]]
      - background-size: cover
    state:
#      - text-transform: uppercase
      - margin-top: 5px
      - line-height: 100%
    name:
      - place-self: start
#      - text-transform: uppercase
      - font-weight: 500
      - font-family: Futura
      - letter-spacing: 0.1vw
      - margin-top: -7%
    img_cell:
      - justify-content: start
      - overflow: visible
    icon:
      - width: 25%
      - margin-top: -26px
    label:
      - place-self: center
      - text-align: center
      - margin-top: 3%
      - margin-bottom: -7%
      - align-items: center
    custom_fields:
      temp:
        - place-self: start
        - margin-top: -27px
        - font-family: Futura
        - color: rgba(240, 255, 255, 0.8)
  label: >
    [[[
      return `
        <table style="width: 100%; text-align: left;">
          <tr>
            <td style="width: 20%;">
              <ha-icon icon="mdi:water-percent" style="width: 1.5em; height: 1.5em;"></ha-icon>
            </td>
            <td style="width: 30%;">
              <span>${states[variables.humidity].state}%</span>
            </td>
            <td style="width: 50%; text-align: center;">
              -
            <td style="width: 20%;">
              <ha-icon icon="mdi:weather-windy" style="width: 1.5em; height: 1.5em;"></ha-icon>
            </td>
            <td style="width: 30%;">
              <span>${states[variables.temp_max].state}kmh</span>
            </td>
          </tr>
          <tr>
            <td style="width: 20%;">
              <ha-icon icon="mdi:thermometer" style="width: 1.5em; height: 1.5em;"></ha-icon>
            </td>
            <td style="width: 30%;">
              <span>${states[variables.temp_min].state}°C</span>
            </td>
            <td style="width: 50%; text-align: center;">
              -
            </td>
            <td style="width: 20%;">
              <ha-icon icon="mdi:weather-sunny" style="width: 1.5em; height: 1.5em;"></ha-icon>
            </td>
            <td style="width: 30%;">
              <span>${states[variables.feels].state}°C</span>
            </td>
          </tr>
        </table>
      `
    ]]]
  custom_fields:
    temp: >
      [[[ return entity.attributes.temperature + "°C"; ]]]
  entity_picture: >
    [[[
      let weather = states[variables.current_weather].state.toLowerCase();
      if ((weather == 'sunny') && (states['sun.sun'].state == 'above_horizon'))
        return "/local/svg/weather/clear-day.svg";
        if ((weather == 'sunny') || (weather == 'clear-night') && (states['sun.sun'].state == 'below_horizon'))
          return "/local/svg/weather/clear-night.svg";
            if (weather == 'fog')
              return "/local/svg/weather/fog.svg";
                if ((weather == 'partlycloudy') && (states['sun.sun'].state == 'above_horizon'))
                  return "/local/svg/weather/partly-cloudy-day.svg";
                    if ((weather == 'partlycloudy') && (states['sun.sun'].state == 'below_horizon'))
                      return "/local/svg/weather/partly-cloudy-night.svg";
                        if (weather == 'rainy')
                          return "/local/svg/weather/rain.svg";
                            if (weather == 'sleet')
                              return "/local/svg/weather/sleet.svg";
                                if (weather == 'snow')
                                  return "/local/svg/weather/snow.svg";
                                    if (weather == 'cloudy')
                                      return "/local/svg/weather/cloudy.svg";
      else (weather == 'wind')
        return "/local/svg/weather/wind.svg";
    ]]]
  extra_styles: |
    [[[
      return `
        #name {
          font-size: 0.9vw;
        }
        #temp {
          font-size: 2.0vw;
        }
        #state {
          font-size: 0.9vw;
        }
        #label {
          font-size: 0.7vw;
          white-space: wrap;
        }
        @media screen and (max-width: 1500px) {
          #name {
            font-size: 1.1vw;
          }
          #temp {
            font-size: 2vw;
          }
          #state {
            font-size: 1.2vw;
          }
          #label {
            font-size: 0.7vw;
          }
        @media screen and (max-width: 800px) {
          #name {
            font-size: 3vw;
            margin-top: -5%;
          }
          #temp {
            font-size: 4vw;
            margin-top: -4px !important;
          }
          #state {
            font-size: 2.1vw;
          }
          #icon {
            display: none !important;
          }
          #label {
            font-size: 1.5vw;
            font-weight: 500 !important;
            margin-top: 5%;
            margin-bottom: 0%;
          }
        }
      `
    ]]]

ui_lovelace.yaml:

                  - type: custom:button-card
                    entity: weather.openweathermap
                    name: Steninge
                    double_tap_action:
                      action: more-info
                    tap_action:
                      !include popup/weather.yaml
                    template:
                      - widget_weather
                    variables:
                      temp_min: sensor.aussen_rain
                      temp_max: sensor.aussen_wind
                      humidity: sensor.aussen_humidity
                      feels: sensor.aussen_feels
                      current_weather: weather.openweathermap

sensors need to defined and pictures must be in the right folders.

1 Like

Andrew, can you explane me exactly what you do to fit it at a iPad?