Skip to content

schumijo/lovelace-ui-minimalist

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

My Home Assistant

At the top of each page we find the chips which allow me to quickly visualize the important information of the page. Then title to separate the different sections and cards to represent and interact with devices, sensors, etc … I used the custom component button-card for all these cards. I drew a lot of inspiration from 7ahang’s work that I found on Behance.

Table of Contents

Installation

  1. Add button_card_templates in ui-lovelace.yaml file.
    button_card_templates: !include lovelace/button_card_templates/button_card_templates.yaml
  2. Add resources in your configuration.yaml file. You will need at least button-card
    lovelace:
      mode: yaml
      resources: !include lovelace/resources/resources.yaml
  3. Add themes in your configuration.yaml file
    frontend: 
      themes: !include configuration/themes.yaml

Design system

Colors

I tried to set up a consistency between the colors used to represent the entities.

Color Type

orange

💡 Light

⚡ Electricity

red

🔥 Heating

Blue

☑️ On/off devices

🏠 Home

green

🌲 Exterior

Chips

Chips

Code template
Template
pilule:
  tap_action:
    action: more-info
  show_icon: false
  show_name: false
  show_state: false
  show_label: true
  size: 80%
  styles:
    img_cell:
      - width: 24px
    card:
      - border-radius: 30px
      - box-shadow: var(--box-shadow)
      - height: 36px
      - width: auto
      - padding-left: 6px
      - padding-right: 6px
    grid:
      - grid-template-areas: '"l"'
    label:
      - justify-self: center
      - padding: 0px 6px
      - font-weight: bold
      - font-size: 14px

Temperature

Chips - Temperature

Code
Example Template
- template: pilule_temperature
  type: 'custom:button-card'
pilule_temperature:
  template: pilule
  tap_action:
      action: navigate
      navigation_path: /lovelace/temperature
  label: |
    [[[
      var inter = states['sensor.fibaro_multisensor_salon_temperature'].state;
      var exter = states['sensor.xiaomi_multisensor_chambre_temperature'].state;
      var icon = '☀️';
      if (states['sensor.dark_sky_icon'].state == 'clear-day'){
        var icon = '☀️';
      } else if(states['sensor.dark_sky_icon'].state == 'clear-night'){
        var icon = '🌙';
      } else if(states['sensor.dark_sky_icon'].state == 'rain'){
        var icon = '🌧️';
      } else if(states['sensor.dark_sky_icon'].state == 'snow'){
        var icon = '❄️';
      } else if(states['sensor.dark_sky_icon'].state == 'sleet'){
        var icon = '❄️';
      } else if(states['sensor.dark_sky_icon'].state == 'wind'){
        var icon = '🌫️';
      } else if(states['sensor.dark_sky_icon'].state == 'fog'){
        var icon = '🌫️';
      } else if(states['sensor.dark_sky_icon'].state == 'cloudy'){
        var icon = '☁️';
      } else if(states['sensor.dark_sky_icon'].state == 'partly-cloudy-day'){
        var icon = '⛅️';
      } else if(states['sensor.dark_sky_icon'].state == 'partly-cloudy-night'){
        var icon = '⛅';
      }
      return icon + ' ' + inter + '° / ' +  exter + '°' ;
    ]]]

Electric consumption

Chips - Consommation

Code
Example Template #1 Template #2
- template: pilule_consommation
  type: 'custom:button-card'
pilule_consommation:
    template: pilule
    tap_action:
      action: navigate
      navigation_path: /lovelace/consommation
    label: |
      [[[
        var price = states['sensor.atome_price_conso_today'].state;
        return '⚡ ' +  price + '€' ;
      ]]]
  pilule_consommation_prix:
    template: pilule
    show_icon: false
    show_state: false
    show_label: true
    tap_action:
      action: navigate
      navigation_path: /lovelace/consommation
    label: |
      [[[
        var price = states['sensor.atome_price_conso_today'].state;
        var conso = states['sensor.atome_daily'].state;
        return '⚡ ' +  price + '€ • ' + conso + 'kWh';
      ]]]

Presence counter

Chips - Present

Code
Example Template
- template: pilule_localisation_present
  type: 'custom:button-card'
pilule_localisation_present:
  tap_action:
    action: navigate
    navigation_path: /lovelace/localisation
  label: |
    [[[
      var personnes_presentes = states['sensor.people_count_present'].state;
      return '🏠 ' +  personnes_presentes;
    ]]]
  template: pilule

Return button

Chips - return

Code
Example Template
- template: return_button
  type: 'custom:button-card
return_button:
  template: pilule
  show_icon: true
  icon: 'mdi:arrow-left'
  size: 80%
  styles:
    grid:
      - grid-template-areas: '"i"'
  tap_action:
    action: navigate
    navigation_path: /lovelace/home

Scene

Scene

Code
Example Template
- entity: sensor.present
  template: scene
  type: 'custom:button-card'
scene:
  size: 20px
  show_label: true
  label: |
    [[[ return (entity.attributes.value )]]]
  styles:
    card:
      - border-radius: 20px
      - box-shadow: var(--box-shadow)
      - padding: 10px 0px 8px 0px 
    grid:
      - grid-template-areas: '"i" "n" "l"'
    name:
      - margin-top: 10px
      - justify-self: center
      - font-weight: bold
      - font-size: 14px
    label:
      - justify-self: center
      - align-self: start
      - font-weight: bolder
      - font-size: 12px
      - filter: opacity(40%)
    icon:
      - color: |
          [[[
             if (states['input_select.localisation_thomas'].state == entity.attributes.friendly_name){
               if (states['input_select.localisation_thomas'].state == 'Present')
                 return 'rgba(var(--couleur-bleu),1)'
               else if (states['input_select.localisation_thomas'].state == 'Absent')
                 return 'rgba(var(--couleur-vert),1)'
               else if (states['input_select.localisation_thomas'].state == 'Nuit')
                 return 'rgba(var(--couleur-bleu),1)'
               else if (states['input_select.localisation_thomas'].state == 'Travail')
                 return 'rgba(var(--couleur-vert),1)'
               else if (states['input_select.localisation_thomas'].state == 'Miléna')
                 return 'rgba(var(--couleur-vert),1)'
               else if (states['input_select.localisation_thomas'].state == 'Parents')
                 return 'rgba(var(--couleur-vert),1)'
             }else{
               return 'rgba(var(--couleur-theme),0.2)'
             }
          ]]]
    img_cell:
      - background-color: |
          [[[
             if (states['input_select.localisation_thomas'].state == entity.attributes.friendly_name){
               if (states['input_select.localisation_thomas'].state == 'Present')
                 return 'rgba(var(--couleur-bleu),0.2)'
               else if (states['input_select.localisation_thomas'].state == 'Absent')
                 return 'rgba(var(--couleur-vert),0.2)'
               else if (states['input_select.localisation_thomas'].state == 'Nuit')
                 return 'rgba(var(--couleur-bleu),0.2)'
               else if (states['input_select.localisation_thomas'].state == 'Travail')
                 return 'rgba(var(--couleur-vert),0.2)'
               else if (states['input_select.localisation_thomas'].state == 'Miléna')
                 return 'rgba(var(--couleur-vert),0.2)'
               else if (states['input_select.localisation_thomas'].state == 'Parents')
                 return 'rgba(var(--couleur-vert),0.2)'
             }else{
               return 'rgba(var(--couleur-theme),0.05)'
             }
          ]]]
      - border-radius: 50%
      - place-self: center
      - width: 42px
      - height: 42px

Title

Title

Code
Example Template
- template: titre
  name: Title
  label: 'Subtitle'
  type: 'custom:button-card'
  
titre:
  tap_action:
    action: none
  show_icon: false
  show_label: true
  show_name: true
  styles:
    card:
      - background-color: rgba(0,0,0,0)
      - box-shadow: none
      - height: auto
      - width: auto
      - margin-top: 12px
      - margin-left: 24px
      - margin-bottom: 0px
    grid:
      - grid-template-areas: '"n" "l"'
      - grid-template-columns: 1fr
      - grid-template-rows: min-content min-content
    name:
      - justify-self: start
      - font-weight: bold
      - font-size: '1.5rem'
    label:
      - justify-self: start
      - font-weight: bold
      - font-size: '1rem'
      - opacity: '0.4'

Cards

Entity

Anatomy

Anatomy

  1. Dot : Visible when the device is unavailable. Also used on the entity person
  2. Icon : Icon that represents the device
  3. Primary line : Main information
  4. Secondary line : Secondary information
  5. Optionnal part : Possibility to display buttons to launch actions related to the device. Or display a graph to view the history of a sensor

Light

Light

Code
Example Template
- entity: light.example
  name: Lumière
  template: 
    - icon_info_bg
    - light
  type: 'custom:button-card'
  light:
    tap_action:
      action: toggle
    hold_action:
      action: more-info
    label: >-
      [[[ if (entity.state !='unavailable'){
            if (entity.state =='off'){
              var bri = Math.round(entity.attributes.brightness / 2.55);
              return 'Off';  
            }else{
              var bri = Math.round(entity.attributes.brightness / 2.55);
              return (bri ? bri : '0') + '%'; 
            }
          }else{
            return "Indisponible";
          }
      ]]]
    template: 
      - jaune

Outlet

Prise

Code
Example Template
- entity: switch.example
  name: Prise
  template: 
    - icon_info_bg
    - prise_conso
  type: 'custom:button-card'
prise_conso:
  hold_action:
    action: more-info
    entity: sensor.shelly_prise_salon_current_consumption
  label: |-
    [[[ if (entity.state =='on')
       var etat = "On • " + states["sensor.shelly_prise_salon_current_consumption"].state + "W"; 
       else
      var etat = "Off";
    return etat ; ]]]
  template: prise

Movement

Mouvements Fenêtres

Code
Example Template
- entity: binary_sensor.example
  name: Mouvement
  icon: 'mdi:run'
  template: 
    - icon_info_bg
    - mouvement
  type: 'custom:button-card'
mouvement:
  show_last_changed: true
  template: 
    - bleu

Cover

Volets

Code
Example Template
- template: cover_buttons
  variables:
    entity: "cover.example"
    name: "Volets"
  type: 'custom:button-card'
cover:
  tap_action:
    action: more-info
  icon: |
    [[[
        var icon = 'mdi:window-shutter';
        if (entity.attributes.current_position == 0){
          var icon = 'mdi:window-shutter';
        } else
          var icon = 'mdi:window-shutter-open';
        return icon ;
    ]]]
  label: >-
    [[[ 
        if (entity.attributes.power_consumption != '0'){
          return 'Mouvement';
        }else{
          if (entity.attributes.current_position == 0){
            var etat = "Fermé";
          }else{
            var etat = "Ouvert" + ' • ' + (entity.attributes.current_position) + '%' ;
          }
          return etat ;
        }
    ]]]
  state:
    - operator: template
      value: >
        [[[
          return entity.attributes.current_position != 0;
        ]]]
      styles:
        icon:
          - color: 'rgba(var(--couleur-bleu),1)'
        img_cell:
          - background-color: 'rgba(var(--couleur-bleu),0.2)'

####################################################

cover_buttons:
  variables:
    entity: "cover.fibaro_cover_balcon"
    name: "Default name"
  styles:
    card:
      - border-radius: 20px
      - box-shadow: var(--box-shadow)
      - padding: 12px
    grid:
      - grid-template-areas: '"item1" "item2"'
      - grid-template-columns: 1fr
      - grid-template-rows: min-content  min-content
      - row-gap: 12px
  custom_fields:
    item1:
      card:
        entity: '[[[ return variables.entity ]]]'
        name: '[[[ return variables.name ]]]'
        tap_action:
          action: more-info
        template:
          - icon_info
          - cover
        type: 'custom:button-card'
    item2:
      card:
        template: list_items
        type: 'custom:button-card'
        custom_fields:
          item1:
            card:
              icon: 'mdi:arrow-down'
              tap_action:
                action: call-service
                service: cover.close_cover
                service_data:
                  entity_id: '[[[ return variables.entity ]]]'
              type: 'custom:button-card'
              template: widget_icon
          item2:
            card:
              icon: 'mdi:pause'
              tap_action:
                action: call-service
                service: cover.stop_cover
                service_data:
                  entity_id: '[[[ return variables.entity ]]]'
              type: 'custom:button-card'
              template: widget_icon
          item3:
            card:
              icon: 'mdi:arrow-up'
              tap_action:
                action: call-service
                service: cover.open_cover
                service_data:
                  entity_id: '[[[ return variables.entity ]]]'
              type: 'custom:button-card'
              template: widget_icon

Thermostat

Thermostat

Code
Example Template
- entity: climate.example
  template: 
    - icon_info_bg
    - thermostat
  type: 'custom:button-card'
thermostat:
  hold_action:
    action: more-info
    entity: input_boolean.radiateur_arret_force
  label: >-
    [[[ 
        if (entity.state =='off'){
          return 'Off' ;
        }else{
          if (states['light.qubino'].state == 'on'){
            var etat = "Chauffe";
          }else{
            var etat = "Inactif";
          }
          return (entity.attributes.temperature ) + '°' + ' • ' + etat ;
        }
    ]]]
  styles:
    icon:
      - color: |
          [[[
             if (states['light.qubino'].state == 'on')
               return 'rgba(var(--couleur-rouge),1)'
             else
               return 'rgba(var(--couleur-theme),0.2)'
          ]]]
    img_cell:
      - background-color: |
          [[[
             if (states['light.qubino'].state == 'on')
               return 'rgba(var(--couleur-rouge),0.2)'
             else
               return 'rgba(var(--couleur-theme),0.05)'
          ]]]

Water heater

Chauffe-eau

Code
Example Template
- entity: switch.example
  name: Chauffe eau
  template: 
    - icon_info_bg
    - chauffe-eau
  tap_action:
    action: more-info
    entity: sensor.shelly_module_couloir_current_consumption
  type: 'custom:button-card'
chauffe-eau:
  icon: 'mdi:waves'
  tap_action:
    action: more-info
  hold_action:
    action: more-info
    entity: switch.shelly_module_couloir
  label: >-
    [[[ 
        if (entity.state == 'off'){
          return 'Arrêt forcé';
        }else{
          if (states["sensor.shelly_module_couloir_current_consumption"].state > 0){
            var etat = "Chauffe • " + states["sensor.shelly_module_couloir_current_consumption"].state + "W";
          }else{
            var etat = "Inactif";
          }
          return etat ;
        }
    ]]]
  styles:
    icon:
      - color: |
          [[[
             if (states["sensor.shelly_module_couloir_current_consumption"].state > 0)
               return 'rgba(var(--couleur-rouge),1)'
             else
               return 'rgba(var(--couleur-theme),0.2)'
          ]]]
    img_cell:
      - background-color: |
          [[[
             if (states["sensor.shelly_module_couloir_current_consumption"].state > 0)
               return 'rgba(var(--couleur-rouge),0.2)'
             else
               return 'rgba(var(--couleur-theme),0.05)'
          ]]]

Media player

Enceintes

Code
Example Template
- entity: media_player.example
  name: Enceintes
  template: 
    - icon_info_bg
    - media
  type: 'custom:button-card'
media:
  label: >-
    [[[ if (entity.state =='off'){
          return "Off";
        }else{
          return entity.state;
        }
    ]]]
  icon: |
    [[[
        var application = states["media_player.chromecast_audio_appartement"].attributes.app_name;
        var icon = 'mdi:speaker';
        if (application == 'Oto music'){
          var icon = 'mdi:music-circle';
        } else if(application == 'Spotify'){
          var icon = 'mdi:spotify';
        } else if(application == 'Google Podcasts'){
          var icon = 'mdi:google-podcast';
        } else if(application == 'Plex'){
          var icon = 'mdi:plex';
        }
        return icon ;
    ]]]
  styles:
    icon:
      - color: 'rgba(var(--couleur-theme),0.2)'
    img_cell:
      - background-color: 'rgba(var(--couleur-theme),0.05)'
    card:
      - background-blend-mode: multiply
      - background: >
          [[[
            var image = entity.attributes.entity_picture_local;
            var bg = entity.attributes.entity_picture_local;
            if (image == null){
              var bg = '';
            } else{
              var bg = 'center / cover url(' + image + ') rgba(0, 0, 0, 0.15)';
            }
            return bg;
          ]]]
  state:
    - operator: template
      value: >
        [[[
          return entity.state !='off'
        ]]]
      name: >
          [[[
            return entity.attributes.media_title;
          ]]]
      label: >
          [[[
            return entity.attributes.media_album_name;
          ]]]
      styles:
        label: 
          - color: white
          - filter: opacity(100%)
        img_cell:
          - background-color: 'rgba(var(--couleur-theme),0.0)'
        icon:
          - color: white
        name:
          - color: white
```  media:
  label: >-
    [[[ if (entity.state =='off'){
          return "Off";
        }else{
          return entity.state;
        }
    ]]]
  icon: |
    [[[
        var application = states["media_player.chromecast_audio_appartement"].attributes.app_name;
        var icon = 'mdi:speaker';
        if (application == 'Oto music'){
          var icon = 'mdi:music-circle';
        } else if(application == 'Spotify'){
          var icon = 'mdi:spotify';
        } else if(application == 'Google Podcasts'){
          var icon = 'mdi:google-podcast';
        } else if(application == 'Plex'){
          var icon = 'mdi:plex';
        }
        return icon ;
    ]]]
  styles:
    icon:
      - color: 'rgba(var(--couleur-theme),0.2)'
    img_cell:
      - background-color: 'rgba(var(--couleur-theme),0.05)'
    card:
      - background-blend-mode: multiply
      - background: >
          [[[
            var image = entity.attributes.entity_picture_local;
            var bg = entity.attributes.entity_picture_local;
            if (image == null){
              var bg = '';
            } else{
              var bg = 'center / cover url(' + image + ') rgba(0, 0, 0, 0.15)';
            }
            return bg;
          ]]]
  state:
    - operator: template
      value: >
        [[[
          return entity.state !='off'
        ]]]
      name: >
          [[[
            return entity.attributes.media_title;
          ]]]
      label: >
          [[[
            return entity.attributes.media_album_name;
          ]]]
      styles:
        label: 
          - color: white
          - filter: opacity(100%)
        img_cell:
          - background-color: 'rgba(var(--couleur-theme),0.0)'
        icon:
          - color: white
        name:
          - color: white

Playstation

Playstation

Code
Example Template
- entity: media_player.example
  template: 
    - icon_info_bg
    - ps4
  type: 'custom:button-card'
ps4:
  label: >-
    [[[ if (entity.state =='unknown'){
          return "Off";
        }else if (entity.state =='standby'){
          return "En veille";
        }else{
          return "On";
        }
    ]]]
  styles:
    icon:
      - color: 'rgba(var(--couleur-theme),0.2)'
    img_cell:
      - background-color: 'rgba(var(--couleur-theme),0.05)'
  state:
    - value: 'idle'
      styles:
        icon:
          - color: 'rgba(var(--couleur-bleu),1)'
        img_cell:
          - background-color: 'rgba(var(--couleur-bleu), 0.2)'
    - value: 'standby'
      styles:
        icon:
          - color: 'rgba(var(--couleur-theme),0.2)'
        img_cell:
          - background-color: 'rgba(var(--couleur-theme),0.05)'
    - operator: template
      value: >
        [[[
          return entity.state !='unknown' 
        ]]]
      name: >
          [[[
            return entity.attributes.media_title;
          ]]]
      label: >
          [[[
            return entity.attributes.friendly_name;
          ]]]
      styles:
        label: 
          - color: white
          - filter: opacity(100%)
        img_cell:
          - background-color: 'none'
        icon:
          - color: white
        name:
          - color: white
        card:
          - background-blend-mode: multiply
          - background: >
              [[[
                var image = entity.attributes.entity_picture;
                return 'center / cover url(' + image + ') rgba(0, 0, 0, 0.15)';
              ]]]

Person

Personne

Information

Dot :

  • At home : Blue
  • Away : Green

Dot icon :

  • At home : Home
  • Away : Walking man
  • Sleeping : Moon

Comment : The sleep state takes over the At home or Away state to display the moon icon

Code
Example Template
- entity: input_select.localisation_thomas
  variables:
    personne: "thomas"
  template: 
    - icon_info_bg
    - personne-thomas
  name: Thomas
  type: 'custom:button-card'
personne:
  tap_action:
    action: more-info
  show_label: true
  label: >
      [[[return entity.state]]]
  styles:
    icon:
      - color: 'rgba(var(--couleur-theme),0.9)'
    custom_fields:
      notification:
        - border-radius: 50%
        - position: absolute
        - left: 38px
        - top: 8px
        - height: 16px
        - width: 16px
        - border: 2px solid var(--card-background-color)
        - font-size: 12px
        - line-height: 14px
  
####################################################
 
personne-thomas:
  template: personne
  hold_action:
    action: more-info
    entity: input_boolean.thomas_nuit
  styles:
    custom_fields:
      notification:
        - background-color: >
            [[[
              if (states['input_select.localisation_thomas'].state == 'Present'){
                return "rgba(var(--couleur-bleu),1)";
              }else{
                return "rgba(var(--couleur-vert),1)";
              }
            ]]]
  custom_fields:
    notification: >
      [[[
        if (states['input_boolean.thomas_nuit'].state == 'on'){
          return `<ha-icon icon="mdi:power-sleep" style="width: 10px; height: 10px; color: white;"></ha-icon>`
        }else{
          if (states['input_select.localisation_thomas'].state == 'Present'){
            return `<ha-icon icon="mdi:home-variant" style="width: 10px; height: 10px; color: white;"></ha-icon>`
          }else{
            return `<ha-icon icon="mdi:walk" style="width: 10px; height: 10px; color: white;"></ha-icon>`
          }
        }
      ]]]

Generic

Generic

Code
Example Template
- entity: sensor.example
  template: 
    - icon_info_bg
    - generique
  type: 'custom:button-card'
generique:
  label: >
      [[[return entity.state + " " + entity.attributes.unit_of_measurement]]]
  styles:
    icon:
      - color: 'rgba(var(--couleur-theme),0.9)'
    grid:
      - grid-template-areas: '"i l" "i n"'
      - grid-template-columns: min-content auto
      - grid-template-rows: min-content min-content
    label:
      - align-self: end
      - justify-self: start
      - font-weight: bold
      - font-size: 14px
      - margin-left: 12px
      - filter: opacity(100%)
    name:
      - justify-self: start
      - align-self: start
      - font-weight: bolder
      - font-size: 12px
      - filter: opacity(40%)
      - margin-left: 12px

Generic + graph

Entity - graph

Code
Example Template
- type: 'custom:button-card'
  template: graph
  variables:
    entity: "sensor.example"
    color: "var(--google-blue-500)"
    name: "Température"
graph:
  variables:
    entity: "sensor.example"
    color: "var(--google-blue-500)"
    name: "Default name"
  styles:
    card:
      - border-radius: 20px
      - box-shadow: var(--box-shadow)
      - padding: 0px
    grid:
      - grid-template-areas: '"item1" "item2"'
      - grid-template-columns: 1fr
      - grid-template-rows: min-content  min-content
  custom_fields:
    item1:
      card:
        entity: '[[[ return variables.entity ]]]'
        name: '[[[ return variables.name ]]]'
        template: 
          - icon_info
          - generique
        styles:
          card:
            - padding: 12px
        type: 'custom:button-card'
    item2:
      card:
        type: 'custom:mini-graph-card'
        entities:
          - entity: '[[[ return variables.entity ]]]'
        line_color: '[[[ return variables.color ]]]'
        show:
          name: false
          icon: false
          legend: false
          state: false
        style: |
          ha-card {
            box-shadow: none;
            border-radius: var(--border-radius);
          }

About

🌻 Lovelace UI • Minimalist

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%