NSPanel Pro UI Design with Lovelace

Entity grid

For quick toggling on/off lights, covers, locks, etc.

grid

type: custom:auto-entities
card:
  type: custom:layout-card
  layout_type: custom:grid-layout
  layout:
    grid-template-columns: repeat(auto-fit,minmax(clamp(100%/(3 + 1) + 0.1%, 100px,100%),1fr))
    grid-template-rows: auto
    align-items: stretch
    box-sizing: border-box
    margin: 0
    padding: 0
    grid-gap: 0.5rem
    width: 100vw
    height: 100vh
  card_mod:
    style: |
      :root{
        height:100%;
      }
      ha-card{
        height:100%;
      }    
card_param: cards
filter:
  include:
    - domain: light
      group: light.areas
      options:
        type: custom:button-card
        color: auto-no-temperature
        entity_id: this.entity_id
        template: nspanel
        show_state: false
    - domain: cover
      area: bedroom
      options:
        type: custom:button-card
        color: auto-no-temperature
        entity_id: this.entity_id
        template: nspanel
    - entity_id: lock.gate
      options:
        type: custom:button-card
        color: auto-no-temperature
        entity_id: this.entity_id
        template: nspanel
  exclude:
    - state: /unavailable|unknown/
    - hidden_by: /user|integration/
sort:
  method: domain
  numeric: false
  reverse: false
  ignore_case: true
card_mod:
  style: |
    :root{
      height:100%;
    }
    ha-card{
      height:100%;
    }

5 Likes

This is awesome! I’m also eagerly waiting for Nextion version, I’ve already started using ESPHome with my non-pro NSPanel, and something like in your first post is exactly what I would like to have :slight_smile:

Unfortunately there is no Nextion Editor for Mac, and it sucks donkey balls anyway to create anything nice looking. So it will be postponed… I personally currently use the stock NSPanel UI / TFT with ESPHome to control it.

If someone is more familiar with the app, I can export assets to the generator.

Very nice design indeed. I’m using the Lovelace integration but the result I have is not as nice as this one.

I really like the look and feel.

1 Like

Sorry for my ignorance but how can I use your great looking UI?

Until now I’ve been using HA just for integrating all my devices into one portal and then link it to Homekit. Since a few days I have a NSPanel Pro and now I feel the need to create an appropriate dashboard.

  1. Create a new dashboard
  2. Paste YAML from from the views you’d like to have into your dashboard.
  3. … It’s a work in progress, not all is ready, will continue to update if more time

edit:
I use Fully Kiosk Browser, to show my Home Assistant Lovelace dashboard. You have to get developer options for the NSPro, explained here:

2 Likes

Do you have that nice weather / time of day animated screen available as well?

edit: almost forgot, it also has slight animations built in :blush: gif is jagged but the animations are smooth and unobtrusive.

video

Moon

  1. Add moon integration and create sensor
    1.1 - integrations/moon
    1.2 - create template sensor for emoji and picture. Rename it to “sensor.moon_phase
template:
- sensor:
    - unique_id: moon_moon
      name: >-
        {{ state_attr('sensor.moon','friendly_name') }}
      icon: >-
        {{ state_attr('sensor.moon','icon') }}
      state: >-
        {{ states('sensor.moon') | replace('_',' ') | capitalize }}
      attributes:
        entity_picture: >-
          {% set moon = states('sensor.moon') %}
          {% set face = '_face' if moon in ['first_quarter','last_quarter','full_moon','new_moon'] else '' %}
            /local/emoji/moon/{{moon}}{{face}}.svg
        emoji: >-
          {% set values = {'first_quarter':'🌓','last_quarter':'🌗','full_moon':'🌕','new_moon':'🌑','waxing_crescent':'🌒','waning_gibbous':'🌖','waxing_gibbous':'🌔','waning_crescent':'🌘'} %}
          {% if moon in ['first_quarter','last_quarter','full_moon','new_moon'] %}
            {% set values = {'first_quarter':'🌛','last_quarter':'🌜','full_moon':'🌝','new_moon':'🌚'} 
            %}
          {% endif %}            
          {% set value = states('sensor.moon') %}  
          {{ values[value] if value in values.keys() else 'off' }}

Weather

Create a template sensor Weather. Rename your weather entity_id! Rename the sensor to “sensor.weather

template:
- sensor:
    - unique_id: weather_forecast
      name: Weather Forecast
      icon: >-
        mdi:weather-{{ states('weather.hernes') }}
      state: >-
        {{ states('weather.hernes') | replace('_',' ') | capitalize }}
      attributes:
        forecast_high: >-
          {{ state_attr('weather.hernes','forecast')[0].temperature | int(0) | default(0) }}
        forecast_low: >-
          {{ state_attr('weather.hernes','forecast')[0].templow | int(0) | default(0) }}
        entity_picture: >-
          {%- set elevation = state_attr('sun.sun', 'elevation') | int -%}
          {%- set rising = state_attr('sun.sun', 'rising') -%}
          {%- set azimuth = state_attr('sun.sun', 'azimuth') | int -%}          
          {% set weather = states('weather.hernes') %}
          {% if weather == 'sunny' and elevation <= -12 %}
            {{ state_attr('sensor.moon_phase','entity_picture') }}
          {% else %}      
            {% if weather in ['unknown','unavailable'] %}
              /local/emoji/weather/{{ ['unicorn','sparkles','rainbow'] | random }}.svg
            {% else %}
              /local/emoji/weather/{{weather}}.svg
            {% endif %}   
          {% endif %}
        emoji: >-
          {%- set elevation = state_attr('sun.sun', 'elevation') | int -%}
          {%- set rising = state_attr('sun.sun', 'rising') -%}
          {%- set azimuth = state_attr('sun.sun', 'azimuth') | int -%}
          {% set values = {'cloudy':'☁️','fog':'🌁','lightning-rainy':'⛈️','lightning':'🌩️','partlycloudy':'⛅','rainy':'🌧️','snowy':'❄️','snowy-rainy':'🌨️','sunny':'☀️','windy':'🌬️'} %}
          {% set value = states('weather.hernes') %}  
          {% if values[value] == '☀️' and elevation <= -12 %}
          {{ state_attr('sensor.moon_phase','emoji') }}
          {% else %}      
          {{ values[value] if value in values.keys() else '💯' }}       
          {% endif %}

Solar

Create sensor solar.
This get pictures based on states of weather, moon position, etc.
Rename it to “sensor.solar

template:
- sensor:
    - unique_id: solar
      name: >-
        {%- set elevation = state_attr('sun.sun', 'elevation') | int -%}
        {%- set rising = state_attr('sun.sun', 'rising') -%}
        {%- set azimuth = state_attr('sun.sun', 'azimuth') | int -%}
        {%- if elevation <= -12 -%}
          {%- if (0 <= azimuth <= 10) or (350 <= azimuth <= 360) -%}
            Midnight 
          {%- else -%}
            Night 
          {%- endif -%}
        {%- elif -6 < elevation <= 0 -%}
          {{- 'Sunrise' if rising else 'Sunset' -}}
        {%- elif -12 < elevation <= -6 -%}
          {{- 'Dawn' if rising else 'Dusk' -}}
        {%- else -%}
          {%- if 160 <= azimuth <= 200 -%}
            Noon
          {%- else -%}
            Day
          {%- endif -%}
        {%- endif -%}
      icon: >-
        {%- set elevation = state_attr('sun.sun', 'elevation') | int -%}
        {%- set rising = state_attr('sun.sun', 'rising') -%}
        {%- set azimuth = state_attr('sun.sun', 'azimuth') -%}
        {%- if elevation <= -12 -%}
          {%- if (0 <= azimuth <= 10) or (350 <= azimuth <= 360) -%}
            mdi:moon-waning-crescent 
          {%- else -%}
            mdi:weather-night 
          {%- endif -%}
        {%- elif -6 < elevation <= 0 -%}
          {{- 'mdi:theme-light-dark' if rising else 'mdi:weather-sunset' -}}
        {%- elif -12 < elevation <= -6 -%}
          {{- 'mdi:weather-sunset-up' if rising else 'mdi:weather-sunset-down' -}}
        {%- else -%}
          {%- if 160 <= azimuth <= 200 -%}
            mdi:white-balance-sunny
          {%- else -%}
            mdi:weather-sunny
          {%- endif -%}
        {%- endif -%}
      state: >-
        {%- set elevation = state_attr('sun.sun', 'elevation') -%}
        {%- set rising = state_attr('sun.sun', 'rising') -%}
        {%- set azimuth = state_attr('sun.sun', 'azimuth') -%}
        {%- if elevation <= -12 -%}
          {%- if (0 <= azimuth <= 10) or (350 <= azimuth <= 360) -%}
            🌚 
          {%- else -%}
            🌃 
          {%- endif -%}
        {%- elif -6 < elevation <= 0 -%}
          {{- '🌅' if rising else '🌇' -}}
        {%- elif -12 < elevation <= -6 -%}
          {{- '🌄' if rising else '🌆' -}}
        {%- else -%}
          {%- if 160 < azimuth < 200 -%}
            🌞
          {%- else -%}
            🏙️
          {%- endif -%}
        {%- endif -%}
      attributes:
        entity_picture: >-
          {% set weather = states('weather.hernes') %}
          {% set solar = state_attr('sensor.solar','friendly_name') %}
          {% if solar in ['Midnight','Night'] %}
            {{ state_attr('sensor.moon_phase','entity_picture') }}
          {% elif solar in ['Sunrise','Sunset','Dawn','Dusk'] %}
            /local/emoji/solar/{{solar}}.svg
          {% else %}
            {% if weather == 'sunny' %}
              {% if state_attr('weather.hernes','temperature') >= 0 %}
                /local/emoji/weather/{{weather}}.svg
              {%else%}
                /local/emoji/weather/snowy.svg
              {% endif %}                
            {% else %}
              /local/emoji/weather/{{weather}}.svg
            {% endif %}
          {% endif %}
        weather_picture: >-
          {{ state_attr('sensor.weather','entity_picture') }}
        weather_emoji: >-
          {{ state_attr('sensor.weather','emoji') }}
        solar_picture: >-
          {% set solar = state_attr('sensor.solar','friendly_name') %}
              /local/emoji/solar/{{solar}}.svg
        solar_emoji: >-
          {{ states('sensor.solar') }}
        moon_picture: >-
          {{ state_attr('sensor.moon_phase','entity_picture') }}
        moon_emoji: >-
          {{ state_attr('sensor.moon_phase','emoji') }}
        solar_moon_picture: >-
          {% set solar = state_attr('sensor.solar','friendly_name') %}
          {% if solar in ['Midnight','Night'] %}
            {{ state_attr('sensor.moon_phase','entity_picture') }}
          {% else %}
            /local/emoji/solar/{{solar}}.svg
          {% endif %}

Icons

Download the icons:

  1. Get Microsoft Fluent UI Emoji and add to your config/www folder
  2. :sparkles: [easier version] Export them from Figma, which style you like:
    2.1 - 3D emoji
    2.2 - Flat emoji

It now exports SVG icons with correct names, and puts them in appropriate folders, eg. /emoji/solar/noon.svg

Select the menu icon → file → export and all get saved with correct naming the sensor uses:

Layout

Create a new view, add this code and change sensor names:

type: custom:layout-card
layout_type: custom:grid-layout
cards:
  - type: custom:layout-card
    layout_type: custom:grid-layout
    cards:
      - type: markdown
        content: >-
          <img id="main_shadow" src="{{state_attr('sensor.solar','moon_picture') }}" height="48" width="48">
        card_mod:
          style: |
            ha-card{
              background: transparent;
              display:flex;
              align-items: center;
              justify-content: center;
            }
            ha-card>ha-markdown{
              border-radius: 100%;
              background:transparent;
              padding: 0 !important;
              line-height:0;
              display:flex;
              align-items: center;
              justify-content: center;
              overflow:hidden;
              {% set time = now().hour %}
              {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float %}
              opacity:{{brightness}};
              {% set time = now().hour %}
              {% set top = ( (time - 12)*1 ) %}
              transform:scale(5) translateY({{top}}px) translateX(-48px);
              {% set blur = ((((((time/24))-0.5)|abs)*5)|round(0)|int)+5  %}      
              filter:blur({{blur}}px);
            }
        view_layout:
          grid-column: 1
          grid-row: 1
      - type: markdown
        content: >-
          <img id="main" src="{{ state_attr('sensor.solar','solar_moon_picture')}}" height="48" width="48">
        card_mod:
          style: |
            ha-card{
              background: transparent;
              display:flex;
              align-items: center;
              justify-content: center;
              overflow:hidden;
              {% set time = now().hour %} 
              {% set sun = states.sensor.sun_elevation.state | int %}
              {% set sunpos = ((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) * 24 / 100)  | round(0) %}
              {% set left = ((5 - ((time/24*10) | round(0) -5 ) | abs) * 24 ) | int - 240 %}
              {% set top = ( (time - 12)*2 ) %}
              transform: scale(5) translateX(-48px) translateY({{top}}px);

            }
            ha-card>ha-markdown
              background:transparent;
              padding: 0 !important;
              line-height:0;
              display:flex;
              align-items: center;
              justify-content: center;
              overflow:hidden;
            }
        view_layout:
          grid-column: 1
          grid-row: 1
      - type: markdown
        content: >-
          <img id="weatherblur" src="{{ state_attr('sensor.solar','weather_picture')  }}" height="48" width="48">
        card_mod:
          style: |
            :host {
              overflow:hidden;
            }
            ha-card{
              background: transparent;
              display:flex;
              align-items: center;
              justify-content: center;

              {% set sun = states.sensor.sun_elevation.state %}
              transform:scale(2.5) translateY({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0) }}px) translateX({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0)  }}px);
              {% set time = now().hour %}
              {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float %}

            }
            ha-card>ha-markdown{
              transform:scale(0.5) translateY(150%) translateX(-100%);
              background:transparent;
              padding: 0 !important;
              line-height:0;
              display:flex;
              align-items: center;
              justify-content: center;
              overflow:hidden;
            }
        view_layout:
          grid-column: 1
          grid-row: 1
    layout:
      grid-gap: 0
      margin: 0
      padding: 0
      height: 100%
      width: 100%
    card_mod:
      style: |
        ha-card{
         height:100%;
         background:red;
        }
        :root{
         overflow:hidden;
         height:100%;
        }
        layout-card, grid-layout{
          overflow: hidden;
          height:100%;
          transform:scale(3);
        }
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
  - type: markdown
    content: ' '
    card_mod:
      style: |
        :host{
         overflow:hidden;
         width:100%;
         height: 100%;
          padding:0;
          margin:0
        }
        ha-card{
          box-sizing:border-box;
          {% set time = now().hour %}
          {% set hue = (time/24*360 | round(2) ) | int %}
          {% set hue2 = ((time/24*360 | round(2) ) | int) - 45 %}
          {% set hue3 = ((time/24*360 | round(2) ) | int) - 30 %}
          background: radial-gradient( transparent, hsla({{hue3}},100%,50%,0.0), hsla({{hue2}},100%,50%,0.0), hsla({{hue}},100%,50%,0), hsla({{hue}},100%,50%,0));
          background-size: 100% 100%;
          background-repeat: no-repeat;
          background-position: top center;
          backdrop-filter: blur(10px);
          display: flex;
          align-items: end;
          justify-content: left;
          font:var(--h3-font);
          color: var(--accent-text-color);
          border-radius: 100%;
          {% set time = now().hour %} 
          {% set left = ((5 - ((time/24*10) | round(0) -5 ) | abs) * 24 ) | int - 120 %}
          {% set top = ((5 - ((time/24*10) | round(0) -5 ) ) * -24 ) | int + 240 %}
          /*transform: translateX({{ left }}px) translateY({{top}}px);*/
          transform: translateX(-50%) translateY(50%);
          position:relative;
        }
        ha-card .card-header{
            font-size:1rem;
        }
        ha-card > ha-markdown{
         padding:0 !important;
        }
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
  - type: custom:layout-card
    layout_type: custom:grid-layout
    cards:
      - type: markdown
        content: >-
          <img id="weather1" src="{{ state_attr('sensor.solar','weather_picture')  }}" height="48" width="48">
        card_mod:
          style: |
            :host {
              overflow:hidden;
            }
            ha-card{
              background: transparent;
              display:flex;
              align-items: center;
              justify-content: center;

              {% set sun = states.sensor.sun_elevation.state %}
              transform:scale(1.5) translateY({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0) }}px) translateX({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0)  }}px);
              {% set time = now().hour %}
              {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float %}

            }
            ha-card>ha-markdown{
              transform:scale(0.5) translateX(-200%);
              background:transparent;
              padding: 0 !important;
              line-height:0;
              display:flex;
              align-items: center;
              justify-content: center;
              overflow:hidden;
            }
        view_layout:
          grid-column: 1
          grid-row: 1
      - type: markdown
        content: >-
          <img id="weather2" src="{{ state_attr('sensor.solar','weather_picture')  }}" height="48" width="48">
        card_mod:
          style: |
            :host {
              overflow:hidden;
            }
            ha-card{
              background: transparent;
              display:flex;
              align-items: center;
              justify-content: center;

              {% set sun = states.sensor.sun_elevation.state %}
              transform:scale(2.5) translateY({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0) }}px) translateX({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0)  }}px);
              {% set time = now().hour %}
              {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float %}

            }
            ha-card>ha-markdown{
              transform:scale(0.5) translateY(150%) translateX(-100%);
              background:transparent;
              padding: 0 !important;
              line-height:0;
              display:flex;
              align-items: center;
              justify-content: center;
              overflow:hidden;
            }
        view_layout:
          grid-column: 1
          grid-row: 1
      - type: markdown
        content: >-
          <img id="weatherpic" src="{{ state_attr('sensor.solar','weather_picture')  }}" height="48" width="48">
        card_mod:
          style: |
            :host {
              overflow:hidden;
            }
            @keyframes float{
              0%   {transform:translateY(-0.5em)}
              50%  {transform:translateY(0.5em)}
              100% {transform:translateY(-0.5em)}
            }
            ha-card{
              background: transparent;

              display:flex;
              align-items: center;
              justify-content: center;

              {% set sun = states.sensor.sun_elevation.state %}
              transform:scale(2.5) translateY({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0) }}px) translateX({{ ((-((((( -16 - (sun | int ) - 8)) / 32 * 100) | round(0)  ) *24 / 100) ) * 2) | round(0)  }}px);
              {% set time = now().hour %}
              {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float %}

            }
            ha-card>ha-markdown{
              animation: 5s ease-out infinite alternate float;

              background:transparent;
              padding: 0 !important;
              line-height:0;
              display:flex;
              align-items: center;
              justify-content: center;
              overflow:hidden;
            }
            ha-markdown>ha-markdown-element{

            }
        view_layout:
          grid-column: 1
          grid-row: 1
    layout:
      grid-gap: 0
      margin: 0
      padding: 0
      height: 100%
      width: 100%
    card_mod:
      style: |
        ha-card{
         height:100%;
         background:red;
        }
        :root{
         overflow:hidden;
         height:100%;
        }
        layout-card, grid-layout{
          overflow: hidden;
          height:100%;
          transform:scale(3);
        }
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
  - type: markdown
    content: >-
      {% set clock = as_timestamp(now()) %} {{- clock | timestamp_custom("%H")
      }}<span class="minutes">:{{ clock | timestamp_custom("%M") }}</span>
    card_mod:
      style: |
        ha-markdown span{
          color:green !important;
          background:red;
        }
        ha-card > span {
          border: 3px dotted magenta;
          padding: 0px;
          display: unset;
        }
        ha-card{
          background:transparent;
          display: flex;
          align-items: center;
          justify-content: right;
          font: var(--h1-font);
          font-weight: 500;
          letter-spacing:-0.05em;
          {% set time = now().hour %}
          {% set int = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float %}
          font-size: calc(var(--h1-font-size)*3*{{int}} );
          transform: translateX({{ -((((( -16 - (states.sensor.sun_elevation.state | int ) - 8)) / 32 * 100) | round(0)  ) * 24 / 100)  | round(0)}}px);
          mix-blend-mode:difference;
        }
        ha-card > ha-markdown{
          padding:0 !important;
          margin:0 !important;

          {% set hue = (now().hour*15 + ((now().minute/60*100)/100) | round(2)*15 ) | int | abs %}
          {% set hue2 = (((now().hour*15 + ((now().minute/60*100)/100) | round(2)*15 ) | int) - 45) | abs %}
          {% set hue3 = ((now().hour*15 + ((now().minute/60*100)/100) | round(2)*15 ) | int) - 90 | abs %}
          {% set time = now().hour %}
          {% set br1 = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float  %}
          {% set br2 = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 ) | float %}
          {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs) * 10 ) | int + 25 %}
          background: linear-gradient(45deg, hsl({{hue}},100%,{{brightness}}%), hsl({{hue2}},100%,{{brightness}}%));


          -webkit-background-clip: text;
          -webkit-text-fill-color: transparent;
          /*mix-blend-mode:difference;*/
          /*color:hsla({{hue}},100%,50%,{{brightness}});*/
          /*text-shadow: 0 3px 3px hsla({{hue }},100%,50%,0.5), 0 3px 10px hsla({{hue }},100%,50%,1);*/

        }
        ha-card > .card-header, .card-header {
          background-color: var(--background-color-off);
          color: var(--text-color-off);
          font-size:10px;
        }
    view_layout:
      grid-column: 1 / span 5
      grid-row: 2 / 4
  - type: markdown
    content: '{{ (( states(''sensor.calendar_event'') ) ) }}'
    card_mod:
      style: |
        ha-card{
          background: transparent;
          display: flex;
          align-items: flex-end;
          justify-content: right;
          text-align:right;
          font: var(--{{ "h1" if state_attr('sensor.calendar_event','number_days') <= 1 else "card-title" }}-font);
          line-height: 0.8em;
          color: var(--primary-color);
          mix-blend-mode:difference;
        }
        ha-card > ha-markdown{
         padding:0 1rem !important;
        }
    view_layout:
      grid-row: 4 / 5
      grid-column: 4 / 7
  - type: markdown
    content: ' {{ (( states(''sensor.thermal_home_heatindex'') | float ) ) | round(0) }}°C'
    card_mod:
      style: |
        ha-card{
          background: transparent;
          display: flex;
          align-items: center;
          justify-content: right;
          font:var(--h3-font);
          color:var(--accent-text-color);
          mix-blend-mode: lighten;

          {% set hue = (25/(states.sensor.thermal_home_heatindex.state | int)*360) | int %}
          {% set time = now().hour %}
          {% set br1 = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float  %}
          {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs) * 10 ) | int + 25 %}
          color: hsl({{hue}},100%,{{brightness}}%);


          /*mix-blend-mode:difference;*/
          /*color:hsla({{hue}},100%,50%,{{brightness}});*/
          /*text-shadow: 0 3px 3px hsla({{hue }},100%,50%,0.5), 0 3px 10px hsla({{hue }},100%,50%,1);*/

        }
        ha-card > ha-markdown{
         padding:0 1rem !important;
        }
    view_layout:
      grid-row: 1
      grid-column: 3 / 7
  - type: markdown
    content: >-
      {%- macro suffix(d) %}
      {%- set sfx = {1:'st',2:'nd',3:'rd'} %}
      {{- 'th' if 11 <= d <= 13 else sfx.get(d%10, 'th') }}
      {%- endmacro %}
      {% set day = as_timestamp(now()) | timestamp_custom("%d") | int %}
      {{ as_timestamp(now()) | timestamp_custom("%a") }} {{ day }}{{ suffix(day) }}  
    card_mod:
      style: |
        ha-card{
          background:transparent;
          display: flex;
          align-items: center;
          justify-content: left;
          font: var(--h3-font);
          color: var(--accent-color); 
          mix-blend-mode: difference;
        }
        ha-card > ha-markdown{
         padding:0 1rem !important;
        }
    view_layout:
      grid-column: 1 / span 4
      grid-row: 1
  - type: markdown
    content: '{{ (( state_attr(''sensor.calendar_event'',''friendly_name'') ) ) }}'
    card_mod:
      style: |
        ha-card{
          background: transparent;
          display: flex;
          align-items: center;
          justify-content: right;
          text-align:right;
          font: var(--body-font);
          color: var(--accent-text-color);
          mix-blend-mode: difference;
          font-size: calc( 2*{{ "1" if state_attr('sensor.calendar_event','number_days') <= 1 else 1 }}rem);
        }
        ha-card > ha-markdown{
         padding:0 1rem !important;
        }
    view_layout:
      grid-row: 4
      grid-column: 2 / 7
  - type: markdown
    content: >
      {%- set weather = states('weather.hernes') -%}
      {%- if weather in ['unknown','unavailable'] -%}
      {{- ['idks','??'] | random -}}
      {%- else -%}
      {{- (( state_attr('weather.hernes','temperature') | float +
      states('sensor.temperature_balcony') | float ) / 2) | round(0) }}°C  
      {%- endif %} 
    card_mod:
      style: |
        ha-card{
          box-sizing:border-box;
          background: transparent; 
          display: flex;
          align-items: center;
          justify-content: left;
          font:var(--h3-font);
          color: var(--accent-text-color);
          mix-blend-mode: screen;

          {% set wefer = states('sensor.temperature_balcony') | int %} 
          {% set hue = 180-((((wefer)*10)) | int) %} 
          {% if hue <= 0 %}
          {% set hue = 0 %}
          {% elif hue >=360 %}
          {% set hue = 360 - hue %}
          {% else %}
          {% endif %}

          {% set time = now().hour %}
          {% set br1 = ((5 - ((time/24*10) | round(0) -5 ) | abs)/10 + 0.5) | float  %}
          {% set brightness = ((5 - ((time/24*10) | round(0) -5 ) | abs) * 10 ) | int + 25 %}
          color: hsl({{hue}},100%,{{brightness}}%);
        }
        ha-card > ha-markdown{
         padding:0 1rem !important;
        }
    view_layout:
      grid-column: 1 / span 3
      grid-row: 6
  - show_name: true
    show_icon: true
    type: button
    entity: sensor.solar
    tap_action:
      action: navigate
      navigation_path: /dashboard-nspanel/menu
    hold_action:
      action: navigate
      navigation_path: /dashboard-nspanel/screensaver
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
    card_mod:
      style: ha-card{opacity:0}
layout:
  grid-template-columns: repeat(6, minmax(60px, 1fr))
  grid-template-rows: repeat(6, minmax(60px, 1fr))
  grid-gap: 0.5em
  margin: 0
  padding: 0
  height: 100vh
  min-height: 480px
  overflow: hidden
card_mod:
  style: |
    :host{
      --card-header-font-size:1px;     
    }
    :host > ha-card > .card-header{
     display:none;
    }
    ha-card{
     width:100%;
     height:100vh;
    }
    :root{
     overflow:hidden;
     height:100vh;
    }
    layout-card{
      overflow: hidden;
      height:100vh;
    }

Profit

Enjoy an always dynamic screensaver :tada:

  1. main image based on solar status (day, sunset, sunrise, etc)
    1.1 if night, get actual moon phase
  2. weather conditions as illustrations
  3. time, date and temperature colored based on the state
  4. everything positioned based on time, state, etc, so you never get the same image :slight_smile:

1 Like

And a simpler, bigger screensaver with just images:

2023-04-22_02-35.34

2023-04-22_02-43.21

type: custom:layout-card
layout_type: custom:grid-layout
cards:
  - type: markdown
    content: >-
      <img src="{{state_attr('sensor.solar','moon_picture')}}" width="240px"
      height="240px">
    card_mod:
      style:
        .: |
          ha-card {
          padding:0;
          background: transparent;
          display:flex;
          align-items: center;
          justify-content: center;
          }
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
  - type: markdown
    content: >-
      <img src="{{state_attr('sensor.solar','solar_picture')}}" width="480px"
      height="480px">
    card_mod:
      style:
        .: |
          ha-card {
          padding:0;
          background: transparent;
          display:flex;
          align-items: center;
          justify-content: center;
          }
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
  - type: markdown
    content: >-
      <img src="{{state_attr('sensor.solar','weather_picture')}}" width="480px"
      height="480px">
    card_mod:
      style:
        .: |
          ha-card {
          padding:0;
          background: transparent;
          display:flex;
          align-items: center;
          justify-content: center;
          }
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
  - type: markdown
    content: ' '
    card_mod:
      style: |
        :host{
         overflow:hidden;
         width:100%;
         height: 100%;
          padding:0;
          margin:0
        }
        ha-card{
          box-sizing:border-box;
          {% set time = now().hour %}
          {% set hue = (time/24*360 | round(2) ) | int %}
          {% set hue2 = ((time/24*360 | round(2) ) | int) - 45 %}
          {% set hue3 = ((time/24*360 | round(2) ) | int) - 30 %}
          background: hsla({{hue}},100%,50%,0);
          background-repeat: no-repeat;
          background-position: top center;
          backdrop-filter: blur(30px);
          display: flex;
          align-items: end;
          justify-content: left;
          font:var(--h3-font);
          color: var(--accent-text-color);
          border-radius: 100%;
          {% set time = now().hour %} 
          {% set left = ((5 - ((time/24*10) | round(0) -5 ) | abs) * 24 ) | int - 120 %}
          {% set top = ((5 - ((time/24*10) | round(0) -5 ) ) * -24 ) | int + 240 %}
          /*transform: translateX({{ left }}px) translateY({{top}}px);*/
          transform: translateX(-50%) translateY(50%);
          position:relative;
        }
        ha-card .card-header{
            font-size:1rem;
        }
        ha-card > ha-markdown{
         padding:0 !important;
        }
    view_layout:
      grid-column: 1 / span 6
      grid-row: 1 / span 6
layout:
  grid-template-columns: repeat(6, minmax(60px, 1fr))
  grid-template-rows: repeat(6, minmax(60px, 1fr))
  grid-gap: 0.5em
  margin: 0
  padding: 0
  height: 100vh
  min-height: 480px
  overflow: hidden
card_mod:
  style: |
    :host{
      --card-header-font-size:1px;     
    }
    :host > ha-card > .card-header{
     display:none;
    }
    ha-card{
     width:100%;
     height:100vh;
    }
    :root{
     overflow:hidden;
     height:100vh;
    }
    layout-card{
      overflow: hidden;
      height:100vh;
    }

2 Likes

Awesome! Have to try this immediately!

1 Like

I think these are not included in icons? I’ve to check also that other pictures solar svgs etc match, since I assume it is not the same folder structure as with this icon package?

EDIT: I can probably backtrack the icons and rename them under weather/solar etc. names for states and filenames to match. However, if you have those as list or smthing already available, I’d be grateful :slight_smile:

You can export the solar/weather/moon images from Figma as you like - SVG or PNG, either style:

Select the menu icon → file → export and all get saved with correct naming the sensor uses:

And yeah, delete the line :smiley: I use them as placeholders, since my home nickname is “Pea” 🫛…

1 Like

Thanks! I think I got the icons now as they should, having still some problems for setting sensor.solar. No errors or anything on the log, but have to figure out why sensor.solar is still non-existant.

Did you add it to template: ? It might have different name and entity_id at first, since it changes name based on state as well. I use it all over my dashboards as a regular sensor to display the emoji or name:

Thanks!

Yeah, sensor’s id was not sensor.solar, found it in entities as “sensor.day” and gave it manually entity_id sensor.solar.

There is still lot for me to do:

  • exported icons from Figma (not familiar with that previously). Exported svgs came out with filenames such as [email protected]: batch renamed them to xxx.svg
  • attributes “Solar picture”, “Solar emoji” and “Solar moon picture” seem to be currently the only attributes working, rest (Weather and Moon pictures and emojis) are still “Unknown”.
  • I might read the template wrongly, but can’t see anything that points to moon(s) -directory for png/svg-resources? With solar and weather there are some explicit references such as /local/emoji/solar etc.
  • With developer tools I manually set missing attributes to see is otherwise the layout card would show me something, but itis basically empty without most of graphics (one icon was visible, as was date, time, temperatures are as ‘raw’ unformatted strings).
  • I’m quite sure that for example the theme/themes I’m using is causing issues as well when it comes to UI, transparency etc.
  • With my skills I probably need much more than this one weekend to get good hold of your fantastic work to adjust it for my enviroment :smile:

Forgot some steps that i had implemented and took for granted :flushed:
Updated the main post for all steps.

exported icons from Figma (not familiar with that previously). Exported svgs came out with filenames such as [email protected]: batch renamed them to xxx.svg

Export now names and puts them in correct folders, no manual labor needed :slight_smile:

attributes “Solar picture”, “Solar emoji” and “Solar moon picture” seem to be currently the only attributes working, rest (Weather and Moon pictures and emojis) are still “Unknown”.

Weather and Moon sensor configs added. :full_moon_with_face:

With developer tools I manually set missing attributes to see is otherwise the layout card would show me something, but itis basically empty without most of graphics (one icon was visible, as was date, time, temperatures are as ‘raw’ unformatted strings).

You need these front-end tools:

  1. card-mod
  2. layout-card

And rename all the entities in the sample code to match your own, these are mine and won’t correspond to yours :slight_smile:

I’m quite sure that for example the theme/themes I’m using is causing issues as well when it comes to UI, transparency etc.

I’m using theme variables (–color, --font, etc), reset paddings/margins with card-mod, so all themes should be supported. You can get your own unique flavor this way :slight_smile:

With my skills I probably need much more than this one weekend to get good hold of your fantastic work to adjust it for my enviroment :smile:

Have fun :smiley: Hope I made it a bit easier now.

2 Likes

Sure did! Thanks again!

Now all the attributes of the three sensors are as they should.

Also the “simpler and bigger screensaver with just images” is working most excellent!

Also the main - more detailed screensaver - is one step closer to be working for me. Instead of just those strings and numeric values from different sensors there is also some of the graphics (almost) visible, behind multiple dark layers (layout cards). But just as graphics being behind those card backgrounds, also the fontstyle of those strings and numeric values are not yet following the styles compared to your screenshots. One thing to notice is that atleast the colours of the strings are not all just white. So all in all, I must still have some missing declarations/definitions somewhere. I’m quite sure that I have not missed single entity nor made mistake on c&p.

Will keep you updated if I notice something and/or when I solve this for me :smiley:
simple
complex

1 Like

I tried with all themes, even light and they work, get colors and fonts based on theme… maybe you are using a mushroom theme that uses custom layout? Perhaps try setting the theme to “no theme” or some basic “dark” one that is supplied.

Tested themes working:

I’ll double check. However, same thing was with all my devices, so maybe it is not theme. Also, as stated, big composite picture one worked just fine.

Do you have card-mod installed? This is the way to inject custom CSS and make it look like it is.