A different take on designing a Lovelace UI

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

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

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

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

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

gif-reloj

3 Likes

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

Thanks Mattias for sharing your excellent work!

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

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

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

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

Anyone had this or knows the what might be wrong?

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

Cheers

1 Like

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

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

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

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

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

15 Likes

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

4 Likes

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

your cards look pretty nice, could you please share the yaml code? That would be awesome

Shure thing :slight_smile:

In button_card templates add the following.

  widgets:
    aspect_ratio: 1
    variables:
      forecast1pic: |
        [[[ 
          var state = states['weather.smhi_home'].attributes.forecast[1]['condition'];
          if (state == "cloudy") return 'url("/local/w/cloudy.png")';
          else if (state == "clear-night") return 'url("/local/w/moon-phase.png")';
          else if (state == "fog") return 'url("/local/w/fog.png")';
          else if (state == "hail") return 'url("/local/w/snow.png")';
          else if (state == "lightning") return 'url("/local/w/lightning.png")';
          else if (state == "lightning-rainy") return 'url("/local/w/lightning.png")';
          else if (state == "partlycloudy") return 'url("/local/w/partiallycloudy1.png")';
          else if (state == "pouring") return 'url("/local/w/rainy.png")';
          else if (state == "rainy") return 'url("/local/w/rainy.png")';
          else if (state == "snowy") return 'url("/local/w/snow.png")';
          else if (state == "snowy-rainy") return 'url("/local/w/sleet.png")';
          else if (state == "sunny") return 'url("/local/w/sunny.png")';
          else if (state == "windy") return 'url("/local/w/windy.png")';
          else if (state == "windy-variant") 'url("/local/w/windy.png")';
          else if (state == "exceptional") return 'url("/local/w/sunny.png")';
        ]]]
      forecast2pic: |
        [[[ 
          var state = states['weather.smhi_home'].attributes.forecast[2]['condition'];
          if (state == "cloudy") return 'url("/local/w/cloudy.png")';
          else if (state == "clear-night") return 'url("/local/w/moon-phase.png")';
          else if (state == "fog") return 'url("/local/w/fog.png")';
          else if (state == "hail") return 'url("/local/w/snow.png")';
          else if (state == "lightning") return 'url("/local/w/lightning.png")';
          else if (state == "lightning-rainy") return 'url("/local/w/lightning.png")';
          else if (state == "partlycloudy") return 'url("/local/w/partiallycloudy1.png")';
          else if (state == "pouring") return 'url("/local/w/rainy.png")';
          else if (state == "rainy") return 'url("/local/w/rainy.png")';
          else if (state == "snowy") return 'url("/local/w/snow.png")';
          else if (state == "snowy-rainy") return 'url("/local/w/sleet.png")';
          else if (state == "sunny") return 'url("/local/w/sunny.png")';
          else if (state == "windy") return 'url("/local/w/windy.png")';
          else if (state == "windy-variant") 'url("/local/w/windy.png")';
          else if (state == "exceptional") return 'url("/local/w/sunny.png")';
        ]]]
      forecast3pic: |
        [[[ 
          var state = states['weather.smhi_home'].attributes.forecast[3]['condition'];
          if (state == "cloudy") return 'url("/local/w/cloudy.png")';
          else if (state == "clear-night") return 'url("/local/w/moon-phase.png")';
          else if (state == "fog") return 'url("/local/w/fog.png")';
          else if (state == "hail") return 'url("/local/w/snow.png")';
          else if (state == "lightning") return 'url("/local/w/lightning.png")';
          else if (state == "lightning-rainy") return 'url("/local/w/lightning.png")';
          else if (state == "partlycloudy") return 'url("/local/w/partiallycloudy1.png")';
          else if (state == "pouring") return 'url("/local/w/rainy.png")';
          else if (state == "rainy") return 'url("/local/w/rainy.png")';
          else if (state == "snowy") return 'url("/local/w/snow.png")';
          else if (state == "snowy-rainy") return 'url("/local/w/sleet.png")';
          else if (state == "sunny") return 'url("/local/w/sunny.png")';
          else if (state == "windy") return 'url("/local/w/windy.png")';
          else if (state == "windy-variant") 'url("/local/w/windy.png")';
          else if (state == "exceptional") return 'url("/local/w/sunny.png")';
        ]]]
      forecast4pic: |
        [[[ 
          var state = states['weather.smhi_home'].attributes.forecast[4]['condition'];
          if (state == "cloudy") return 'url("/local/w/cloudy.png")';
          else if (state == "clear-night") return 'url("/local/w/moon-phase.png")';
          else if (state == "fog") return 'url("/local/w/fog.png")';
          else if (state == "hail") return 'url("/local/w/snow.png")';
          else if (state == "lightning") return 'url("/local/w/lightning.png")';
          else if (state == "lightning-rainy") return 'url("/local/w/lightning.png")';
          else if (state == "partlycloudy") return 'url("/local/w/partiallycloudy1.png")';
          else if (state == "pouring") return 'url("/local/w/rainy.png")';
          else if (state == "rainy") return 'url("/local/w/rainy.png")';
          else if (state == "snowy") return 'url("/local/w/snow.png")';
          else if (state == "snowy-rainy") return 'url("/local/w/sleet.png")';
          else if (state == "sunny") return 'url("/local/w/sunny.png")';
          else if (state == "windy") return 'url("/local/w/windy.png")';
          else if (state == "windy-variant") 'url("/local/w/windy.png")';
          else if (state == "exceptional") return 'url("/local/w/sunny.png")';
        ]]]
      forecast5pic: |
        [[[ 
          var state = states['weather.smhi_home'].attributes.forecast[5]['condition'];
          if (state == "cloudy") return 'url("/local/w/cloudy.png")';
          else if (state == "clear-night") return 'url("/local/w/moon-phase.png")';
          else if (state == "fog") return 'url("/local/w/fog.png")';
          else if (state == "hail") return 'url("/local/w/snow.png")';
          else if (state == "lightning") return 'url("/local/w/lightning.png")';
          else if (state == "lightning-rainy") return 'url("/local/w/lightning.png")';
          else if (state == "partlycloudy") return 'url("/local/w/partiallycloudy1.png")';
          else if (state == "pouring") return 'url("/local/w/rainy.png")';
          else if (state == "rainy") return 'url("/local/w/rainy.png")';
          else if (state == "snowy") return 'url("/local/w/snow.png")';
          else if (state == "snowy-rainy") return 'url("/local/w/sleet.png")';
          else if (state == "sunny") return 'url("/local/w/sunny.png")';
          else if (state == "windy") return 'url("/local/w/windy.png")';
          else if (state == "windy-variant") 'url("/local/w/windy.png")';
          else if (state == "exceptional") return 'url("/local/w/sunny.png")';
        ]]]
      ypos: |
        [[[
          if (states["sensor.time"].state.split(":")[0] == "06"){ 
             return "49";} 
          else if (states["sensor.time"].state.split(":")[0] == "07"){ 
             return "47.9";}
          else if (states["sensor.time"].state.split(":")[0] == "08"){ 
             return "46.8";}
          else if (states["sensor.time"].state.split(":")[0] == "09"){ 
             return "45.8";}
          else if (states["sensor.time"].state.split(":")[0] == "10"){ 
             return "45.4";}
          else if (states["sensor.time"].state.split(":")[0] == "11"){ 
             return "45.1";}
          else if (states["sensor.time"].state.split(":")[0] == "12"){ 
             return "45";}
          else if (states["sensor.time"].state.split(":")[0] == "13"){ 
             return "45.1";}
          else if (states["sensor.time"].state.split(":")[0] == "14"){ 
             return "45.4";}
          else if (states["sensor.time"].state.split(":")[0] == "15"){ 
             return "45.8";}
          else if (states["sensor.time"].state.split(":")[0] == "16"){ 
             return "46.8";}
          else if (states["sensor.time"].state.split(":")[0] == "17"){ 
             return "47.9";}
          else if (states["sensor.time"].state.split(":")[0] == "18"){ 
             return "49";}
          else if (states["sensor.time"].state.split(":")[0] == "19"){ 
             return "50.6";}
          else if (states["sensor.time"].state.split(":")[0] == "21"){ 
             return "54.8";}        
          else if (states["sensor.time"].state.split(":")[0] == "22") { 
            return "56.9";}
          else if (states["sensor.time"].state.split(":")[0] == "23") { 
            return "59.4";}
          else if (states["sensor.time"].state.split(":")[0] == "24") { 
            return "60.6";}
        ]]]
      xpos: |
        [[[    
          return states["sensor.time"].state.split(":")[0] * 4.16;
        ]]]
      line: |
        [[[
          if (states['sun.sun'].attributes.next_rising.substr(11,2) == "06"){ 
             return "49";}
        ]]]
    show_icon: false
    show_state: true
    show_name: true
    tap_action:
      animation_card: |
        [[[
          const animation_speed_ms = 900;
          const animation = `card_bounce ${animation_speed_ms}ms cubic-bezier(0.22, 1, 0.36, 1)`;
          this.shadowRoot.getElementById("card").style.animation = animation;
          window.setTimeout(() => {
            this.shadowRoot.getElementById("card").style.animation = "none";
          }, animation_speed_ms)
        ]]]
    styles:
      card:
        - background-color: rgba(0,0,0,0.4)
        - font-color: rgb(255,255,255,0.9)
        - text-shadow: 0px 0px 1px black
        - letter-spacing: 0.05vw
      name:
        - color: rgb(255,255,255,0.6)
    extra_styles: |
      @keyframes card_bounce {
        0% {
          transform: scale(1);
        }
        15% {
          transform: scale(0.9);
        }
        25% {
          transform: scale(1);
        }
        30% {
          transform: scale(0.98);
        }
        100% {
          transform: scale(1);
        }
      }

Calendar/event widget:

type: custom:button-card
view_layout:
  grid-area: three
entity: sensor.dayoftheweek
template:
  - widgets
tap_action:
  action: fire-dom-event
  browser_mod:
    command: popup
    title: Kalender
    hide_header: false
    card:
      type: vertical-stack
      cards:
        - type: calendar
          entities:
            - calendar.ical_daniel_kalender
          initial_view: dayGridMonth
          style: |
            ha-card {
              box-shadow: none;
            }
show_state: false
name: Händelser
styles:
  name:
    - position: absolute
    - top: 20px
    - left: 20px
    - color: red
    - font-weight: bold
    - text-transform: uppercase
  custom_fields:
    planned:
      - position: absolute
      - top: 15px
      - left: 4px
    event1title:
      - position: absolute
      - top: 85px
      - left: 20px
      - color: rgb(255,255,255,0.6
      - font-weight: bold
      - text-transform: uppercase
    event1:
      - position: absolute
      - top: 107px
      - left: 20px
    event1time:
      - position: absolute
      - color: rgb(255,255,255,0.6)
      - top: 132px
      - left: 20px
      - font-size: 14px
    event2title:
      - position: absolute
      - top: 166px
      - left: 20px
      - color: rgb(255,255,255,0.6)
      - font-weight: bold
      - text-transform: uppercase
    event2:
      - position: absolute
      - color: rgb(255,255,255,0.9)
      - top: 188px
      - left: 20px
    event2time:
      - position: absolute
      - top: 212px
      - left: 20px
      - color: rgb(255,255,255,0.6)
      - font-size: 14px
custom_fields:
  planned:
    card:
      type: markdown
      style: |
        ha-card {
        --ha-card-background: transparent;
        color: rgb(255,255,255,0.6);}
        }
      content: >
        <font size="3"> {% set midnight = now().replace(hour=0, minute=0,
        second=0, microsecond=0).timestamp() %}

        {% set event = state_attr('calendar.ical_daniel_kalender', 'start_time')
        | as_timestamp %}

        {% set delta = ((event - midnight) // 86400) | int %}


        {% if delta == 0 %}
          Du har en händelse idag!
        {% else %}
          Inget planerat idag.
        {% endif %}
  event1title: |
    [[[
     var gsDayNames = [
      'Söndag',
      'Måndag',
      'Tisdag',
      'Ondag',
      'Torsdag',
      'Fredag',
      'Lördag'
    ];
    var date = states['sensor.ical_daniel_kalender_event_0'].attributes.start;
    var d = new Date(date);
    var dayName = gsDayNames[d.getDay()];
    return dayName
    ]]]
  event1: >
    [[[ return states['sensor.ical_daniel_kalender_event_0'].attributes.summary;
    ]]]
  event1time: |
    [[[
      return "Kl: " + (states['sensor.ical_daniel_kalender_event_0'].attributes.start).substr(11,5);
      ]]]
  event2title: |
    [[[
     var gsDayNames = [
      'Söndag',
      'Måndag',
      'Tisdag',
      'Ondag',
      'Torsdag',
      'Fredag',
      'Lördag'
    ];
    var date = states['sensor.ical_daniel_kalender_event_1'].attributes.start;
    var d = new Date(date);
    var dayName = gsDayNames[d.getDay()];
    return dayName
    ]]]
  event2: >
    [[[ return states['sensor.ical_daniel_kalender_event_1'].attributes.summary;
    ]]]
  event2time: |
    [[[
      return "Kl: " + (states['sensor.ical_daniel_kalender_event_1'].attributes.start).substr(11,5);
      ]]]

Sun widget (if someone could help me to calculate a more realistic curve that would be awesome, now it just looks cool :stuck_out_tongue:

type: custom:button-card
view_layout:
  grid-area: four
template:
  - widgets
tap_action:
  action: none
styles:
  custom_fields:
    suntop:
      - position: absolute
      - top: 2%
      - left: 2%
      - font-size: 14px
    sunbottom:
      - position: absolute
      - bottom: 2%
      - left: 2%
      - font-size: 14px
    sun:
      - position: absolute
      - width: 100%
      - height: 100%
      - top: 0%
custom_fields:
  suntop: |
    [[[
      if (states['sun.sun'].state == 'below_horizon'){
        return `<div align="left"> <ha-icon icon="ios:sunrise" style="width: 25px; height: px; color:rgb(255,255,255,0.5); padding: 10px;"> </ha-icon><b>SOL</b></font><br> <p style="margin-left:10px; margin-top:-5px; font-size:36px">${states['sun.sun'].attributes.next_rising.substr(11,5)}</font></div>`;
      }
      else {
             return `<div align="left"> <ha-icon
             icon="ios:sunset"
             style="width: 25px; height: px; color:rgb(255,255,255,0.5); padding: 10px;">
             </ha-icon><b>SOL</b></font><br>
             <p style="margin-left:10px; margin-top:-5px; font-size:36px">${states['sun.sun'].attributes.next_setting.substr(11,5)}</div>`; }
         ]]]
  sunbottom: |
    [[[
      if (states['sun.sun'].state == 'below_horizon'){
        return `<div align="left"> <ha-icon icon="ios:sunset" style="width: 25px; height: px; color:rgb(255,255,255,0.5); padding: 10px;"> </ha-icon></font> ${states['sun.sun'].attributes.next_setting.substr(11,5)}</div>`; 
      }
      else {
             return `<div align="left"> <ha-icon
             icon="ios:sunrise"
             style="width: 25px; height: px; color:rgb(255,255,255,0.5); padding: 10px;">
             </ha-icon></font> ${states['sun.sun'].attributes.next_rising.substr(11,5)}</div>`; }
         ]]]
  sun: |
    [[[    
        return `
          <svg width="100%" height="100%">
          <defs>
            <filter id="f2" x="-50%" y="-50%" width="200%" height="200%">
            <feOffset result="offOut" in="SourceGraphic" dx="0" dy="0" />
            <feGaussianBlur result="blurOut" in="offOut" stdDeviation="4" />
            <feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
            </filter>
            <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
            <stop offset="0%" style="stop-color:rgb(0,0,0);stop-opacity:0" />
            <stop offset="50%" style="stop-color:rgb(255,255,255);stop-opacity:1" />
            <stop offset="100%" style="stop-color:rgb(0,0,0);stop-opacity:0" />
           </linearGradient>
          </defs>
          <circle cx="50%" cy="125%" r="80%" stroke="url(#grad1)" stroke-width="1.2%" fill="none" /> 
          <line x1="100%" y1="${variables.line}%" x2="0" y2="${variables.line}%" style="stroke:rgb(255,255,255,0.0);stroke-width:0.5%" />
          <circle cx="${variables.xpos}%" cy="${variables.ypos}%" rx="5" r="10" fill="rgb(255,255,255,1" filter="url(#f2)"/>    

      `; 
    ]]]

12 Likes

Weather forecast:

type: custom:button-card
view_layout:
  grid-area: five
template:
  - widgets
name: PROGNOS
styles:
  name:
    - position: absolute
    - top: 20px
    - left: 20px
    - font-size: 14px
    - font-weight: bold
  custom_fields:
    dayonetitle:
      - position: absolute
      - top: 40px
      - left: 0px
      - width: 100%
      - padding: 20px
      - border-bottom: 1px solid rgb(255,255,255,0.1)
      - text-transform: lowercase
      - font-weight: bold
    dayonerainicon:
      - position: absolute
      - top: 50px
      - right: 32px
      - padding: 0px
    dayonepic:
      - position: absolute
      - width: 120px
      - height: 120px
      - left: 37%
      - top: 118px
      - background-position: 0% 0%
      - transform: translate(0%, -50%)
      - color: rgb(255,255,255,0.6)
      - background-size: 28%
      - background-repeat: no-repeat
      - background-image: |
          [[[ return variables.forecast1pic ]]]
    dayonerainbar:
      - position: absolute
      - top: 68px
      - right: 15px
    daytwotitle:
      - top: 98px
      - text-transform: lowercase
      - font-weight: bold
      - left: 0px
      - width: 100%
      - border-bottom: 1px solid rgb(255,255,255,0.1)
      - padding: 20px
      - position: absolute
      - font-size: 16px
    daytworainicon:
      - top: 110px
      - right: 32px
      - padding: 0px
      - position: absolute
    daytwopic:
      - width: 120px
      - height: 120px
      - left: 37%
      - top: 177px
      - background-position: 0% 0%
      - transform: translate(0%, -50%)
      - color: grey
      - position: absolute
      - background-size: 28%
      - background-repeat: no-repeat
      - background-image: |
          [[[ return variables.forecast2pic ]]]
    daytworainbar:
      - top: 127px
      - right: 15px
      - position: absolute
    day3title:
      - top: 157px
      - text-transform: lowercase
      - font-weight: bold
      - left: 0px
      - width: 100%
      - padding: 20px
      - position: absolute
      - font-size: 16px
    day3rainicon:
      - top: 169px
      - right: 32px
      - padding: 0px
      - position: absolute
    day3pic:
      - width: 120px
      - height: 120px
      - left: 37%
      - top: 236px
      - background-position: 0% 0%
      - transform: translate(0%, -50%)
      - color: grey
      - position: absolute
      - background-size: 28%
      - background-repeat: no-repeat
      - background-image: |
          [[[ return variables.forecast3pic ]]]
    day3rainbar:
      - top: 186px
      - right: 15px
      - position: absolute
custom_fields:
  dayonetitle: |
    [[[    
    return `<div align="left"> ${states['sensor.daytwo'].state}</div>`;
       ]]] 
  dayonepic: |
    [[[
          return (states['weather.smhi_home'].attributes.forecast[1]["temperature"]) +
          "° " +
          (states['weather.smhi_home'].attributes.forecast[1]["templow"]) + "°";
          ]]]
  dayonerainbar: |
    [[[                       
      if (states["weather.smhi_home"].state != "unavaliable") { 
        let input = states['weather.smhi_home'].attributes.forecast[1]['precipitation'],
          state = input * 10,
          color = '#79d5ff';
        return `
          <svg width="50" height="5">
            <rect x="0" y="0" rx="3" ry="3" width="106" height="5" stroke="#1f4455" fill="#1f4455" stroke-width="0"/>
            <rect x="0" y="0" rx="2" ry="2" width="${state}" height="5" fill="${color}" stroke-width="0"/>
            
        `;                     
      } 
    ]]]
  dayonerainicon: |
    [[[  return `<div> <ha-icon
             icon="ios:drop"
             style="width: 15px; height: 15px; color:rgb(255,255,255,0.6);"> </div>`;
       ]]] 
  daytwotitle: |
    [[[    
    return `<div align="left"> ${states['sensor.daythree'].state}</div>`;
       ]]] 
  daytwopic: |
    [[[
          return (states['weather.smhi_home'].attributes.forecast[2]["temperature"]) +
          "°  " +
          (states['weather.smhi_home'].attributes.forecast[2]["templow"]) + "°";
          ]]]
  daytworainbar: |
    [[[                       
      if (states["weather.smhi_home"].state != "unavaliable") { 
        let input = states['weather.smhi_home'].attributes.forecast[2]['precipitation'],
          state = input * 10,
          color = '#79d5ff';
        return `
          <svg width="50" height="5">
            <rect x="0" y="0" rx="3" ry="3" width="106" height="5" stroke="#1f4455" fill="#1f4455" stroke-width="0"/>
            <rect x="0" y="0" rx="2" ry="2" width="${state}" height="5" fill="${color}" stroke-width="0"/>
            
        `;                     
      } 
    ]]] 
  daytworainicon: |
    [[[  return `<div> <ha-icon
             icon="ios:drop"
             style="width: 15px; height: 15px; color:rgb(255,255,255,0.6);"> </div>`;
       ]]]
  day3title: |
    [[[    
    return `<div align="left"> ${states['sensor.dayfour'].state}</div>`;
       ]]] 
  day3pic: |
    [[[
          return (states['weather.smhi_home'].attributes.forecast[3]["temperature"]) +
          "°  " +
          (states['weather.smhi_home'].attributes.forecast[3]["templow"]) + "°";
          ]]]
  day3rainbar: |
    [[[                       
      if (states["weather.smhi_home"].state != "unavaliable") { 
        let input = states['weather.smhi_home'].attributes.forecast[3]['precipitation'],
          state = input * 10,
          color = '#79d5ff';
        return `
          <svg width="50" height="5">
            <rect x="0" y="0" rx="3" ry="3" width="106" height="5" stroke="#1f4455" fill="#1f4455" stroke-width="0"/>
            <rect x="0" y="0" rx="2" ry="2" width="${state}" height="5" fill="${color}" stroke-width="0"/>
            
        `;                     
      } 
    ]]]
  day3rainicon: |
    [[[  return `<div> <ha-icon
             icon="ios:drop"
             style="width: 15px; height: 15px; color:rgb(255,255,255,0.6);"></div>`;
       ]]]

Battery widget:

type: custom:button-card
view_layout:
  grid-area: seven
template:
  - widgets
name: BATTERIER
styles:
  name:
    - font-size: 14px
    - font-weight: bold
    - top: 20px
    - left: 20px
    - position: absolute
  custom_fields:
    iphonese:
      - top: 40px
      - left: '-5px'
      - width: 110%
      - border-bottom: 1px solid rgb(255,255,255,0.1)
      - padding: 10px
      - position: absolute
      - font-size: 16px
      - text-shadow: 0px 0px 1px black
    iphonesebattery:
      - top: 60px
      - right: 15px
      - position: absolute
      - font-size: 14px
    iphone11:
      - top: 100px
      - left: '-5px'
      - width: 110%
      - border-bottom: 1px solid rgb(255,255,255,0.1)
      - padding: 10px
      - position: absolute
      - font-size: 16px
      - text-shadow: 0px 0px 1px black
    iphone11battery:
      - top: 120px
      - right: 15px
      - position: absolute
      - font-size: 14px
    ipad:
      - top: 160px
      - left: '-5px'
      - width: 110%
      - padding: 10px
      - position: absolute
      - font-size: 16px
      - text-shadow: 0px 0px 1px black
    ipadbattery:
      - top: 180px
      - right: 15px
      - position: absolute
      - font-size: 14px
custom_fields:
  iphonese: |
    [[[
           return `<div align="left"> <ha-icon
             icon="mdi:cellphone"
             style="width: 20px; height: 20px; color:grey; padding: 10px;">
             </ha-icon>iPhone SE</div>`
         ]]]
  iphonesebattery: |
    [[[                       
      if (states["sensor.iphone_battery_level"].state > 40) { 
        let input = states["sensor.iphone_battery_level"].state / 3,
          state = states["sensor.iphone_battery_level"].state,
          color = '#08ff4e';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="${input}" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;                    
      } if (states["sensor.iphone_battery_level"].state > 15) {   
        let input = states["sensor.iphone_battery_level"].state / 3,
          state = states["sensor.iphone_battery_level"].state,
          color = 'orange';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="${input}" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;  
      } else {   
        let state = states["sensor.iphone_battery_level"].state,
          color = 'red';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="5" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;  
      } 
    ]]]  
  iphone11: |
    [[[
           return `<div align="left"> <ha-icon
             icon="mdi:cellphone"
             style="width: 20px; height: 20px; color:grey; padding: 10px;">
             </ha-icon>iPhone 11</div>`
         ]]]
  iphone11battery: |
    [[[                       
      if (states["sensor.malins_iphone_battery_level"].state > 40) { 
        let input = states["sensor.malins_iphone_battery_level"].state / 3,
          state = states["sensor.malins_iphone_battery_level"].state,
          color = '#08ff4e';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="${input}" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;                    
      } if (states["sensor.malins_iphone_battery_level"].state > 15) {   
        let input = states["sensor.malins_iphone_battery_level"].state / 3,
          state = states["sensor.malins_iphone_battery_level"].state,
          color = 'orange';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="${input}" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;  
      } else {   
        let state = states["sensor.malins_iphone_battery_level"].state,
          color = 'red';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="5" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;  
      } 
    ]]]
  ipad: |
    [[[
           return `<div align="left"> <ha-icon
             icon="mdi:tablet-android"
             style="width: 20px; height: 20px; color:grey; padding: 10px;">
             </ha-icon>iPad Pro</div>`
         ]]]
  ipadbattery: |
    [[[                       
      if (states["sensor.daniels_ipad_battery_level"].state > 40) { 
        let input = states["sensor.daniels_ipad_battery_level"].state / 3,
          state = states["sensor.daniels_ipad_battery_level"].state,
          color = '#08ff4e';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="${input}" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;                    
      } if (states["sensor.daniels_ipad_battery_level"].state > 15) {   
        let input = states["sensor.daniels_ipad_battery_level"].state / 3,
          state = states["sensor.daniels_ipad_battery_level"].state,
          color = 'orange';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="${input}" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;  
      } else {   
        let state = states["sensor.daniels_ipad_battery_level"].state,
          color = 'red';
        return `
          <svg width="86" height="24">
            <rect x="45" y="1" rx="5" ry="5" width="40" height="18" stroke="gray" fill="transparent" stroke-width="2"/>
            <rect x="48" y="4" rx="3" ry="3" width="5" height="12" fill="${color}" stroke-width="0"/>
            <text x="5" y="15" fill="grey">${state}%</text>
        `;  
      } 
    ]]]

News widget

type: custom:button-card
view_layout:
  grid-area: six
template:
  - widgets
tap_action:
  action: fire-dom-event
  browser_mod:
    command: popup
    title: Rubriker
    hide_header: false
    large: false
    card:
      type: markdown
      style: |
        ha-card {
          box-shadow: none;
          letter-spacing: 0.05vw;
          background: transparent;
          text-shadow: 1px 1px 0px black;
        }
      content: |-
        <ul>
         <font size="4px" color="grey"><b>SVT NYHETER</font></b> <font size="3px">
         <li> {{ state_attr('sensor.svt', 'entries')[0]["title"] }} </li>
         <li> {{ state_attr('sensor.svt', 'entries')[1]["title"] }}</li>
         <li> {{ state_attr('sensor.svt', 'entries')[2]["title"] }}</li>
         <br><br> <font size="4px" color="grey"><b>AFTONBLADET</font></b><font size="3px">
         <li> {{ state_attr('sensor.aftonbladet', 'entries')[0]["title"] }}</li>
         <li> {{ state_attr('sensor.aftonbladet', 'entries')[1]["title"] }}</li>
         <li> {{ state_attr('sensor.aftonbladet', 'entries')[2]["title"] }}</li><br><br>
         <font size="4px" color="grey"><b>TECHRADAR</font></b> <font size="3px">
         <li> {{ state_attr('sensor.techradar', 'entries')[0]["title"] }}</li>
         <li> {{ state_attr('sensor.techradar', 'entries')[1]["title"] }}</li>
         <li> {{ state_attr('sensor.techradar', 'entries')[2]["title"] }}</li>
        </ul>
name: Rubriker
styles:
  name:
    - position: absolute
    - left: 20px
    - top: 20px
    - color: rgb(255,255,255,0.9)
    - font-size: 24px
    - font-weight: bold
  custom_fields:
    news:
      - position: absolute
      - z-index: 1
      - left: 5px
      - top: 45px
    tapactionfix:
      - position: absolute
      - z-index: 2
      - width: 100%
      - height: 100%
      - left: 0px
      - top: 0px
custom_fields:
  tapactionfix: |
    <div></div>
  news:
    card:
      type: markdown
      style: |
        ha-card {
          text-align: left;;
        }
      content: >-
        <font size="3px" color="#9c9d9d"><b>SVT NYHETER</font> </b><font
        size="3px">

        {{ state_attr('sensor.svt', 'entries')[0]["title"] }}

        <br> <font size="3px" color="#9c9d9d"><b>AFTONBLADET</font></b><font
        size="3px">

        {{ state_attr('sensor.aftonbladet', 'entries')[0]["title"] }}

11 Likes

very nice! Where do you get your news sensors from?

That is rss feeds that i get using this awesome integration:

1 Like

Damn @danieljarhult

That looks very nice!!

I just can’t get it right yet. The calendar in particular interests me very much. I copied your code, of course I had to change some data. I have a calendar that creates calendar.private. But sensors have also been added to the events in your code. how did you create it I only have one calendarI just can’t get it right yet. The calendar in particular interests me very much. I copied your code, of course I had to change some data. I have a calendar that creates calendar.private. But sensors have also been added to the events in your code. how did you create it I only have one calendar.

I use the iCal sensor integration (https://github.com/tybritten/ical-sensor-homeassistant). So i share my ios calendar and then add it to the integration.
It gives me this:

image

And it also automatically creates sensor 0,1,2 etc for the following events.

image

1 Like

See my answer above :+1:

1 Like

so far so good, now i’m stuck on the url. With Apple, the webcal url is specified. but it doesn’t work. I read somewhere that you have to find the right url. do you have an idea for this or how did you solve it.

this is how my url starts: webcal://p14-caldav.icloud.com/published…

Thanks for the help! I was indeed missing some parts and also looking at the wrong part of your code.

I’ve successfully implemented the code, however, I am now facing a bit of a different problem. Unlike in your setup, my button has the state on the left and the icon on the right. All is fine when the marquee is not active, but when the marquee is activated, the state scrolls on the entire width of the button-card (over the icon to the right).

I tried playing with max-width, width, margins and padding with no success. Any way of controlling the width of the marquee?

EDIT:
I think it has to do with the fact that the code is comparing the size of container (e.g. the whole button-card) with the size of the state. However, since in my case, the desired size of the state is never equal to the size of the container, the overlap is unavoidable. Any way of modifying your code to compare the size of state which I would set to a fixed value and the size of the actual contents of the state?