A different take on designing a Lovelace UI

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?

if i remember correctly you just change webal:// to https://

1 Like

@danieljarhult Could you make a list of the integration entity and states that you use for your different card, because I have some errors indicating that there are states that do not correspond to my entities, that would be cool, thanks in advance

  • Julien

I really dont use so many integrations for the cards mentioned above.

Weatherforecast widget.
SMHI weather integration.
For the icons i use this: Weather Icon Pack | Flat | 30 .SVG Icons
+
I use this for displaying upcoming days, just add more and change +1 to +2 etc for day after tomorrow etc

  - platform: template
    sensors:
      daytwo:
        value_template: "{{ ['Måndag','Tisdag','Onsdag','Torsdag','Fredag','Lördag','Söndag'][(now().weekday()+1) % 7] }}"

News widget
I use the integration feedparser.

Calendar/events
ical sensor integration.

4 Likes

Thx,
In any case I have tried some of the card and the rendering is beautiful, I am trying to integrate your calendar card but the problem is that I am using Google calendar and I don’t know how to convert the google calendar attributes for your card, if you could help me to make it compatible I would be infinitely grateful

  • Julien

thank You!!

now looks so:

grafik

Any idee to set also the date?

2 Likes

You did it with iCal ?

1 Like

Great Work;

Just to make sure this Weather forecast custom-button-card goes inside the ui-lovelace.yaml and u are using matthias code?

Hey, thanks @danieljarhult for your Battery Widget, it looks great, I modified your Battery Widget a bit and I think it looks very nice now. I also tried to make it Responsive but it’s not there yet ^^

Widget battery

7 Likes

You have to add the card in ui-lovelace.yml and also add the code related to the functionality in button_card_template.yml and modify entity…

1 Like

Here is the code for the circle.

circle:
  state_display: '[[[ if (entity.state === "on") return " "; ]]]'
  custom_fields:
    circle: >
      [[[
        const width = 4;
        let color = '#717273';
        if (variables.consider_on || entity.entity_id.split('.')[0] === "person") {
          let input = variables.circle_input,
            radius = 20.5,
            circumference = radius * 2 * Math.PI;
          return `
            <svg viewBox="0 0 50 50">
              <circle cx="25" cy="25" r="${radius}" fill="none" stroke="${color}" opacity="0.3" stroke-width="${width}" />
              <circle style="
                  transform: rotate(-90deg);
                  transform-origin: 50% 50%;
                  stroke-dasharray: ${circumference};
                  stroke-dashoffset: ${circumference - input / 100 * circumference};
                "
                id="c_brightness" cx="25" cy="25" r="${radius}" stroke="${color}" opacity="1.0" stroke-width="${width}" fill="none" stroke-linecap="round" />
              <text x="50%" y="54%" fill="${color}" font-size="14" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${input}<tspan font-size="10">%</tspan></text>
            </svg>
          `;
        }
      ]]]
  styles:
    custom_fields:
      circle: &person
        - display: initial
        - width: 90%
        - letter-spacing: 0.03vw
        - margin: -6% -6% 0 0
        - justify-self: end
        - opacity: 1
2 Likes