Template to find majority?

Here’s the scenario. I have many weather integrations and I find that none of them are ever 100% accurate to the conditions outside.

I currently use accuweather, pirateweather, openweather, environment canada, and 3 weatherflow stations from neighbours using the public api.

It is currently sunny outside.
4 weather integrations are stating sunny.
3 are stating partly-cloudy

What I’d like to do is a template sensor that uses a “majority rules” type approach. Since more are stating sunny than partly-cloudy I’d like it to output sunny.

Has anybody done something like this (for anything, not necessarily weather) that I can use to get my brain going on how to do the comparison?

EDIT: I know I could do a count and look for one with 4 to represent a majority, but sometimes I’ll see cloudy, partly-cloudy and raining so the majority won’t always be 4+

You could rank the weather conditions. Then, map the current condition for each weather integration to the respective rank (number). Calculate the average and round it. Do a reverse lookup front the number in your list of ranked conditions.

Hmm but with 13 different possible weather states, the average wouldn’t give me the majority.

For example if sunny were ranked as 1, snowy as 10, I could end up with an average of 5 which could be rain.

My first thought was similar in using numbers. I was thinking separate the states into their own sensors. Put those into a group. Then in a template sensor expand the group and count how many match each possible state. I figured I’d have to use a variable so it’ll end up with sunny=4.

But after that I don’t know how to choose the variable with the highest number assigned to it.

I’m pretty sure I’ve got it working. I did testing by manually setting the condition since currently all are showing as sunny.

I’ve now got sensor.weather_conditions with a dynamic icon based on the state and an attribute to show the count of the condition. In the event of a tie it seems to randomly select 1 of the 2 states that it is tied with, haven’t figured out how to write a tie-breaker yet and not sure if I’ll need to really. I may never get a tie in real world use.

template:
  - sensor:
    - name: "Weather Conditions"
      icon: >
          {% if is_state("sensor.weather_conditions", "clear_night") %}
            mdi:weather-night
          {% elif is_state("sensor.weather_conditions", "cloudy") %}
            mdi:weather-cloudy
          {% elif is_state("sensor.weather_conditions", "exceptional") %}
            mdi:alert-circle
          {% elif is_state("sensor.weather_conditions", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.weather_conditions", "hail") %}
            mdi:weather-hail
          {% elif is_state("sensor.weather_conditions", "lightning_rainy") %}
            mdi:weather-lightning-rainy
          {% elif is_state("sensor.weather_conditions", "lightning") %}
            mdi:weather-lightning
          {% elif is_state("sensor.weather_conditions", "partly_cloudy") %}
            mdi:weather-partly-cloudy
          {% elif is_state("sensor.weather_conditions", "pouring") %}
            mdi:weather-pouring
          {% elif is_state("sensor.weather_conditions", "rainy") %}
            mdi:weather-rainy
          {% elif is_state("sensor.weather_conditions", "snowy_rainy") %}
            mdi:weather-snowy-rainy
          {% elif is_state("sensor.weather_conditions", "snowy") %}
            mdi:weather-snowy
          {% elif is_state("sensor.weather_conditions", "sunny") %}
            mdi:weather-sunny
          {% elif is_state("sensor.weather_conditions", "windy") %}
            mdi:weather-windy
          {% endif %}
      state: >
        {% set accuweather = {'condition': states('weather.accuweather')} %}
        {% set envirocan = {'condition': states('weather.envirocan')} %}
        {% set openweather = {'condition': states('weather.openweathermap')} %}
        {% set pirateweather = {'condition': states('weather.pirateweather')} %}
        {% set weatherflow1 = {'condition': states('weather.weatherflow_day_based_forecast')} %}
        {% set weatherflow2 = {'condition': states('weather.weatherflow_day_based_forecast_2')} %}
        {% set weatherflow3 = {'condition': states('weather.weatherflow_day_based_forecast_3')} %}
        {% set conditions_list = [accuweather, envirocan, openweather, pirateweather, weatherflow1, weatherflow2, weatherflow3] %}

        {% set clear_night = conditions_list|selectattr('condition','equalto','clear, night')|list|count %}
        {% set cloudy = conditions_list|selectattr('condition','equalto','cloudy')|list|count %}
        {% set exceptional = conditions_list|selectattr('condition','equalto','exceptional')|list|count %}
        {% set fog = conditions_list|selectattr('condition','equalto','fog')|list|count %}
        {% set hail = conditions_list|selectattr('condition','equalto','hail')|list|count %}
        {% set lightning_rainy = conditions_list|selectattr('condition','equalto','lightning_rainy')|list|count %}
        {% set lightning = conditions_list|selectattr('condition','equalto','lightning')|list|count %}
        {% set partly_cloudy = conditions_list|selectattr('condition','equalto','partly cloudy')|list|count %}
        {% set pouring = conditions_list|selectattr('condition','equalto','pouring')|list|count %}
        {% set rainy = conditions_list|selectattr('condition','equalto','rainy')|list|count %}
        {% set snowy_rainy = conditions_list|selectattr('condition','equalto','snowy, rainy')|list|count %}
        {% set snowy = conditions_list|selectattr('condition','equalto','snowy')|list|count %}
        {% set sunny = conditions_list|selectattr('condition','equalto','sunny')|list|count %}
        {% set windy = conditions_list|selectattr('condition','equalto','windy')|list|count %}

        {% set values = {'clear_night': clear_night, 'cloudy': cloudy, 'exceptional': exceptional, 'fog': fog,
                'hail': hail, 'lightning_rainy': lightning_rainy, 'lightning': lightning, 'partly_cloudy': partly_cloudy,
                'pouring': pouring, 'rainy': rainy, 'snowy_rainy': snowy_rainy, 'snowy': snowy, 'sunny': sunny,
                'windy': windy} %}

        {% set max_condition = values|dictsort(false, 'value')|last %}
        {{ max_condition[0] }}
      attributes:
        weather_condition_count: >
          {% set accuweather = {'condition': states('weather.accuweather')} %}
          {% set envirocan = {'condition': states('weather.envirocan')} %}
          {% set openweather = {'condition': states('weather.openweathermap')} %}
          {% set pirateweather = {'condition': states('weather.pirateweather')} %}
          {% set weatherflow1 = {'condition': states('weather.weatherflow_day_based_forecast')} %}
          {% set weatherflow2 = {'condition': states('weather.weatherflow_day_based_forecast_2')} %}
          {% set weatherflow3 = {'condition': states('weather.weatherflow_day_based_forecast_3')} %}
          {% set conditions_list = [accuweather, envirocan, openweather, pirateweather, weatherflow1, weatherflow2, weatherflow3] %}

          {% set clear_night = conditions_list|selectattr('condition','equalto','clear, night')|list|count %}
          {% set cloudy = conditions_list|selectattr('condition','equalto','cloudy')|list|count %}
          {% set exceptional = conditions_list|selectattr('condition','equalto','exceptional')|list|count %}
          {% set fog = conditions_list|selectattr('condition','equalto','fog')|list|count %}
          {% set hail = conditions_list|selectattr('condition','equalto','hail')|list|count %}
          {% set lightning_rainy = conditions_list|selectattr('condition','equalto','lightning_rainy')|list|count %}
          {% set lightning = conditions_list|selectattr('condition','equalto','lightning')|list|count %}
          {% set partly_cloudy = conditions_list|selectattr('condition','equalto','partly cloudy')|list|count %}
          {% set pouring = conditions_list|selectattr('condition','equalto','pouring')|list|count %}
          {% set rainy = conditions_list|selectattr('condition','equalto','rainy')|list|count %}
          {% set snowy_rainy = conditions_list|selectattr('condition','equalto','snowy, rainy')|list|count %}
          {% set snowy = conditions_list|selectattr('condition','equalto','snowy')|list|count %}
          {% set sunny = conditions_list|selectattr('condition','equalto','sunny')|list|count %}
          {% set windy = conditions_list|selectattr('condition','equalto','windy')|list|count %}

          {% set values = {'clear_night': clear_night, 'cloudy': cloudy, 'exceptional': exceptional, 'fog': fog,
                  'hail': hail, 'lightning_rainy': lightning_rainy, 'lightning': lightning, 'partly_cloudy': partly_cloudy,
                  'pouring': pouring, 'rainy': rainy, 'snowy_rainy': snowy_rainy, 'snowy': snowy, 'sunny': sunny,
                  'windy': windy} %}

          {% set max_condition = values|dictsort(false, 'value')|last %}
          {{ max_condition[1] }}

This works by gathering the states from the various weather integrations. Counting how many match each possible weather state. Then it sorts the list and returns the last value.

You can also use a groupby.

template:
  - sensor:
    - name: "Weather Conditions"
      icon: >
        {{ 'mdi:'~ iif( this.state == 'exceptional', 'alert-circle', 
        'weather-'~this.state | replace('_', '-'))  }}
      state: >
        {% set ns = namespace(list = []) %}
        {%- for state in states.weather | groupby("state") %}
          {%- set ns.list = ns.list + [(state[0], state[1] | list | count)] %}
        {%- endfor %}
        {{ (ns.list | sort(attribute=1, reverse=true) | first)[0] }}
      attributes:
        majority_condition_count: "{{states.weather | selectattr('state', 'eq', this.state) | list | count}}"

It’s likely picking the first option alphabetically.

2 Likes

I’m just going to call you the jinja ninja from now on!
WAY cleaner than what I had lol. Thanks!

Thanks so much Didgeridrew, works like a charm