Fully local weather template entity

using several public services like Buienradar, Knmi, and a few, I decided to make my local weather station the single truth...

documentation Template - Home Assistant

however, the challenge starts with the first required option, for condition... how to make a condition outputting only those required values based on the entities my Ecowitt weather station provides.

Also, since I dont have a cloud_coverage entity nor a visibility entity, I had to find a way to template those out of my available entities on the station.

I did allow myself to add a local lux sensor (Hue outdoor) and of course the sun entity to help in day/night calculations..

Im sharing my below template, hoping you could have a look, and maybe suggest optimizations to any computation, or other thoughts you might have

Weather station weather template
template:

##########################################################################################
# Weather components
##########################################################################################

  - weather:
      - unique_id: ws_5500_weer_station_weather_template
        name: Weer station
        condition: >
          {% set temp = states('sensor.ws_5500_outdoor_temperature') | float %}
          {% set humidity = states('sensor.ws_5500_humidity') | float %}
          {% set wind = states('sensor.ws_5500_wind_speed') | float %}
          {% set gust = states('sensor.ws_5500_wind_gust') | float %}
          {% set rain = states('sensor.ws_5500_rain_rate') | float %}
          {% set uv = states('sensor.ws_5500_uv_index') | float %}
          {% set rad = states('sensor.ws_5500_solar_radiation') | float %}
          {% set lux = states('sensor.achtertuin_buiten_sensor_illuminance') | float %}
          {% set is_night = is_state('sun.sun', 'below_horizon') %}
          {% set elevation = state_attr('sun.sun', 'elevation') | float %}

          {# --- DAYTIME CLEAR-SKY RATIO --- #}
          {% if elevation > 0 %}
            {% set clear_sky = 1000 * (elevation / 90) %}
            {% set cloud_ratio = rad / clear_sky %}
          {% else %}
            {% set cloud_ratio = 0 %}
          {% endif %}

          {# --- EXTREME CONDITIONS FIRST --- #}
          {% if rain > 20 %}
            pouring
          {% elif rain > 0 and temp < 1 %}
            snowy-rainy
          {% elif rain > 0 %}
            rainy
          {% elif temp < 0 and humidity > 80 %}
            snowy
          {% elif humidity > 95 and elevation > 5 and rad < 80 and lux < 500 %}
            fog
          {% elif gust > 60 %}
            windy-variant
          {% elif wind > 30 %}
            windy

          {# --- DAYTIME CLOUDINESS: radiation + lux --- #}
          {% elif not is_night and elevation > 0 %}
            {# strong sun: high ratio AND high lux #}
            {% if cloud_ratio > 0.8 and lux > 20000 %}
              sunny
            {# thin clouds / haze: good ratio but lower lux #}
            {% elif cloud_ratio > 0.6 and lux > 10000 %}
              sunny
            {% elif cloud_ratio > 0.4 %}
              partlycloudy
            {% else %}
              cloudy
            {% endif %}

          {# --- NIGHTTIME CLOUDINESS VIA LUX + HUMIDITY --- #}
          {% else %}
            {% if lux > 10 %}
              clear-night
            {% elif lux > 5 %}
              clear-night
            {% elif lux > 2 %}
              partlycloudy
            {% elif lux > 0.5 %}
              cloudy
            {% else %}
              {% if humidity > 95 %}
                fog
              {% else %}
                cloudy
              {% endif %}
            {% endif %}
          {% endif %}
        temperature: >
          {{states('sensor.ws_5500_outdoor_temperature')|float(0)}}
        apparent_temperature: >
          {{states('sensor.ws_5500_feels_like_temperature')|float(0)}}
        cloud_coverage: >
          {% set rad = states('sensor.ws_5500_solar_radiation') | float %}
          {% set lux = states('sensor.achtertuin_buiten_sensor_illuminance') | float %}
          {% set humidity = states('sensor.ws_5500_humidity') | float %}
          {% set elevation = state_attr('sun.sun', 'elevation') | float %}

          {# ----------------------------- #}
          {#   DAYTIME CLOUD COVER (0-100) #}
          {# ----------------------------- #}

          {% if elevation > 0 %}
            {# theoretical clear-sky radiation #}
            {% set clear_sky = 1000 * (elevation / 90) %}
            {% set ratio = rad / clear_sky %}

            {# convert to cloud coverage percentage #}
            {% set clouds = (1 - ratio) * 100 %}

            {{ [0, [clouds, 100] | min] | max | round(0) }}

          {# ----------------------------- #}
          {#   NIGHTTIME CLOUD COVER (0-100) #}
          {# ----------------------------- #}

          {% else %}
            {# Generic nighttime lux thresholds #}
            {% if lux > 10 %}
              10
            {% elif lux > 5 %}
              20
            {% elif lux > 2 %}
              50
            {% elif lux > 0.5 %}
              80
            {% else %}
              {% if humidity > 95 %}
                95
              {% else %}
                90
              {% endif %}
            {% endif %}
          {% endif %}
        dew_point: >
          {{states('sensor.ws_5500_dewpoint')|float(0)}}
        humidity: >
          {{states('sensor.ws_5500_humidity')|float(0)}}
#         precipitation: >
#           {{states('sensor.ws_5500_event_rain_rate')|float(0)}}
        pressure: >
          {{states('sensor.ws_5500_relative_pressure')|float(0)}}
        uv_index: >
          {{states('sensor.ws_5500_uv_index')|float(0)}}
        visibility: >
          {% set humidity = states('sensor.ws_5500_humidity') | float %}
          {% set rad = states('sensor.ws_5500_solar_radiation') | float %}
          {% set elevation = state_attr('sun.sun', 'elevation') | float %}
          {% set lux = states('sensor.achtertuin_buiten_sensor_illuminance') | float %}

          {% if elevation <= 0 %}
            {# --- NIGHT: lux + humidity based visibility (km) --- #}
            {% set base = 15 %}

            {% if lux < 0.5 %}
              {% set base = base - 8 %}
            {% elif lux < 2 %}
              {% set base = base - 4 %}
            {% endif %}

            {% if humidity > 95 %}
              {% set base = base - 6 %}
            {% elif humidity > 90 %}
              {% set base = base - 3 %}
            {% endif %}

            {{ [1, base] | max | round(1) }}

          {% else %}
            {# --- DAY: radiation + lux + humidity based visibility (km) --- #}
            {% set base = 20 %}

            {# humidity penalties #}
            {% if humidity > 95 %}
              {% set base = base - 12 %}
            {% elif humidity > 90 %}
              {% set base = base - 8 %}
            {% elif humidity > 80 %}
              {% set base = base - 4 %}
            {% endif %}

            {# radiation penalties #}
            {% if rad < 50 %}
              {% set base = base - 10 %}
            {% elif rad < 150 %}
              {% set base = base - 5 %}
            {% endif %}

            {# lux penalties (dim daylight = haze/clouds) #}
            {% if lux < 5000 %}
              {% set base = base - 5 %}
            {% elif lux < 15000 %}
              {% set base = base - 2 %}
            {% endif %}

            {{ [1, base] | max | round(1) }}
          {% endif %}
        wind_speed: >
          {{states('sensor.ws_5500_wind_speed')|int(-5)}}
        wind_bearing: >
          {{states('sensor.ws_5500_wind_direction')|int(-5)}}
#         forecast_daily: "{{state_attr('sensor.buienradar_voorspelling_per_dag','voorspelling')}}"

I did add the buienradar forecast at first (Ive left it in comments) but decided I have those already on the actual weather.buienradar, and, since this is fully local entity, I left that out. resulting in this

and all attributes in details

Ecowitt device and entities:

in a second round to make those templates use the same setters for all options, I rebuilt as trigger based template, and first create those as variables:

  - triggers:
      - trigger: time_pattern
        minutes: '/2'
    actions:
      - variables:
          temp: >
            {{states('sensor.ws_5500_outdoor_temperature') | float(-10)}}
          humidity: >
            {{states('sensor.ws_5500_humidity') | float(-10)}}
          wind: >
            {{states('sensor.ws_5500_wind_speed') | float(-10) }}
          gust: >
            {{states('sensor.ws_5500_wind_gust') | float(-10) }}
          rain: >
            {{states('sensor.ws_5500_rain_rate') | float(-10) }}
          uv: >
            {{states('sensor.ws_5500_uv_index') | float(-10) }}
          rad: >
            {{states('sensor.ws_5500_solar_radiation') | float(-10) }}
          lux: >
            {{states('sensor.achtertuin_buiten_sensor_illuminance') | float(-10) }}
          is_night: >
            {{is_state('sun.sun', 'below_horizon') }}
          elevation: >
            {{state_attr('sun.sun', 'elevation') | float(0) }}

which we can simply reuse in any of the following templates below it.

maybe can even reduce further, but this gets the thing done now

Trigger based weather template
  - triggers:
      - trigger: time_pattern
        minutes: '/2'
    actions:
      - variables:
          temp: >
            {{states('sensor.ws_5500_outdoor_temperature') | float(-10)}}
          humidity: >
            {{states('sensor.ws_5500_humidity') | float(-10)}}
          wind: >
            {{states('sensor.ws_5500_wind_speed') | float(-10) }}
          gust: >
            {{states('sensor.ws_5500_wind_gust') | float(-10) }}
          rain: >
            {{states('sensor.ws_5500_rain_rate') | float(-10) }}
          uv: >
            {{states('sensor.ws_5500_uv_index') | float(-10) }}
          rad: >
            {{states('sensor.ws_5500_solar_radiation') | float(-10) }}
          lux: >
            {{states('sensor.achtertuin_buiten_sensor_illuminance') | float(-10) }}
          is_night: >
            {{is_state('sun.sun', 'below_horizon') }}
          elevation: >
            {{state_attr('sun.sun', 'elevation') | float(0) }}
    weather:
      - unique_id: ws_5500_weer_station_weather_trigger_template
        name: Weer station
        condition: >
          {# --- DAYTIME CLEAR-SKY RATIO --- #}
          {% if elevation > 0 %}
            {% set clear_sky = 1000 * (elevation / 90) %}
            {% set cloud_ratio = rad / clear_sky %}
          {% else %}
            {% set cloud_ratio = 0 %}
          {% endif %}

          {# --- EXTREME CONDITIONS FIRST --- #}
          {% if rain > 20 %}
            pouring
          {% elif rain > 0 and temp < 1 %}
            snowy-rainy
          {% elif rain > 0 %}
            rainy
          {% elif temp < 0 and humidity > 80 %}
            snowy
          {% elif humidity > 95 and elevation > 5 and rad < 80 and lux < 500 %}
            fog
          {% elif gust > 60 %}
            windy-variant
          {% elif wind > 30 %}
            windy

          {# --- DAYTIME CLOUDINESS: radiation + lux --- #}
          {% elif not is_night and elevation > 0 %}
            {# strong sun: high ratio AND high lux #}
            {% if cloud_ratio > 0.8 and lux > 20000 %}
              sunny
            {# thin clouds / haze: good ratio but lower lux #}
            {% elif cloud_ratio > 0.6 and lux > 10000 %}
              sunny
            {% elif cloud_ratio > 0.4 %}
              partlycloudy
            {% else %}
              cloudy
            {% endif %}

          {# --- NIGHTTIME CLOUDINESS VIA LUX + HUMIDITY --- #}
          {% else %}
            {% if lux > 10 %}
              clear-night
            {% elif lux > 5 %}
              clear-night
            {% elif lux > 2 %}
              partlycloudy
            {% elif lux > 0.5 %}
              cloudy
            {% else %}
              {% if humidity > 95 %}
                fog
              {% else %}
                cloudy
              {% endif %}
            {% endif %}
          {% endif %}
        temperature: >
          {{temp}}
        apparent_temperature: >
          {{states('sensor.ws_5500_feels_like_temperature')|float(0)}}
        cloud_coverage: >
          {# ----------------------------- #}
          {#   DAYTIME CLOUD COVER (0-100) #}
          {# ----------------------------- #}

          {% if elevation > 0 %}
            {# theoretical clear-sky radiation #}
            {% set clear_sky = 1000 * (elevation / 90) %}
            {% set ratio = rad / clear_sky %}

            {# convert to cloud coverage percentage #}
            {% set clouds = (1 - ratio) * 100 %}

            {{ [0, [clouds, 100] | min] | max | round(0) }}

          {# ----------------------------- #}
          {#   NIGHTTIME CLOUD COVER (0-100) #}
          {# ----------------------------- #}

          {% else %}
            {# Generic nighttime lux thresholds #}
            {% if lux > 10 %}
              10
            {% elif lux > 5 %}
              20
            {% elif lux > 2 %}
              50
            {% elif lux > 0.5 %}
              80
            {% else %}
              {% if humidity > 95 %}
                95
              {% else %}
                90
              {% endif %}
            {% endif %}
          {% endif %}
        dew_point: >
          {{states('sensor.ws_5500_dewpoint')|float(0)}}
        humidity: >
          {{humidity}}
#         precipitation: >
#           {{states('sensor.ws_5500_event_rain_rate')|float(0)}}
        pressure: >
          {{states('sensor.ws_5500_relative_pressure')|float(0)}}
        uv_index: >
          {{states('sensor.ws_5500_uv_index')|float(0)}}
        visibility: >
          {% if elevation <= 0 %}
            {# --- NIGHT: lux + humidity based visibility (km) --- #}
            {% set base = 15 %}

            {% if lux < 0.5 %}
              {% set base = base - 8 %}
            {% elif lux < 2 %}
              {% set base = base - 4 %}
            {% endif %}

            {% if humidity > 95 %}
              {% set base = base - 6 %}
            {% elif humidity > 90 %}
              {% set base = base - 3 %}
            {% endif %}

            {{ [1, base] | max | round(1) }}

          {% else %}
            {# --- DAY: radiation + lux + humidity based visibility (km) --- #}
            {% set base = 20 %}

            {# humidity penalties #}
            {% if humidity > 95 %}
              {% set base = base - 12 %}
            {% elif humidity > 90 %}
              {% set base = base - 8 %}
            {% elif humidity > 80 %}
              {% set base = base - 4 %}
            {% endif %}

            {# radiation penalties #}
            {% if rad < 50 %}
              {% set base = base - 10 %}
            {% elif rad < 150 %}
              {% set base = base - 5 %}
            {% endif %}

            {# lux penalties (dim daylight = haze/clouds) #}
            {% if lux < 5000 %}
              {% set base = base - 5 %}
            {% elif lux < 15000 %}
              {% set base = base - 2 %}
            {% endif %}

            {{ [1, base] | max | round(1) }}
          {% endif %}
        wind_speed: >
          {{wind}}
        wind_bearing: >
          {{states('sensor.ws_5500_wind_direction')|int(-5)}}

also added my ancient icon mapper to differentiate between day and night

        icon: >
          {% set nightly = {
            'clear-night': 'night',
            'clear': 'night',
            'sunny': 'night',
            'partlycloudy': 'night-partly-cloudy',
            'partly-cloudy': 'night-partly-cloudy',
            'cloudy': 'night-partly-cloudy'
          } %}

          {% set condition = states('weather.weer_station') %}

          {# Normalize daytime partlycloudy → partly-cloudy #}
          {% set day = 'partly-cloudy' if condition == 'partlycloudy' else condition %}

          {# Pick night override if available #}
          {% set weather = nightly[condition]
             if is_night and condition in nightly
             else day %}

could maybe be more detailed, or use the animated icons, but for now I wanted to stay as close to core as possible, no custom stuff, only templates to existing material