Custom UI: Weather state card

Easy, just paste it in the editor and then select the code; and click on the little </> icon to set it to code. Preview should show it how you expect.

Thanks a lot - your changes look great!

Can someone paste instructions on how to get this working with openweathermap? Its not clear to me what I’m supposed to do? Use the custom sensor? Or does it work if I already have OpenWeatherMap?

And do I change this somehow to OWM?

input_boolean.weather:
  custom_ui_state_card: custom-weather-card
  config:
    weather: weather.yweather <----weather.openweathermap???
    sun: sun.sun

Yahoo Weather is HORRIBLE. It’s sunny outside and says it’s currently lightning. Absolute garbage

Yes to this (with correct spacing, I can’t get it right in this reply!)

And you need in your configuration.yaml:

weather:
  - platform: openweathermap
    api_key: YOUR_API_KEY

See here for more

1 Like

@Anwen Awesome. Works now and much better. Thanks

1 Like

@Yevgeniy

Awesome work!

Took your most recent code, but no big animated icon shown (double checked, all placed in correct folders)
I see the weather detected as unknown, and the current .svg file appears without name:

In Yweather looks to work fine and the weather state detected correctly “clear-night”:

btw,
I’ve noticed that no “clear” weather condition mapped in the html file:

  var _WEATHER_TO_ICON_DAY = {
    'cloudy': 'cloudy',
    'fog': 'cloudy',
    'hail': 'rainy-7',
    'lightning': 'thunder',
    'lightning-rainy': 'thunder',
    'partlycloudy': 'cloudy-day-3',
    'pouring': 'rainy-6',
    'rainy': 'rainy-5',
    'snowy': 'snowy-6',
    'snowy-rainy': 'rainy-7',
    'sunny': 'day',
    'windy': 'cloudy',
    'windy-variant': 'cloudy-day-3',
    'exceptional': '!!',
  };

What i’m missing?

Thanks!

Thank you for reporting. I added the right condition in the original post.

Excellent, thanks!

var _WEATHER_TO_ICON_DAY = {
'clear-night': 'day',
'cloudy': 'cloudy',
'fog': 'cloudy',

I see you mapped the clear-night to ‘day’ ? Is it typo?

Also, I believe there’s should be mapping for “clear” condition as well like this:

var _WEATHER_TO_ICON_DAY = {
'clear': 'day',
'clear-day': 'day',

var _WEATHER_TO_ICON_NIGHT = {
'clear': 'night',
'clear-night': 'night',

What do you say?

Regarding the icon, it’s visible now (checked in yweather the condition is sunny, so it’s an existed mapping), but the weather condition still appears as Unknown:

No, this is not a typo. In this case, the icon will change when the sun rises or sets, no matter what state is set by Yahoo.

The clear-day class does not exist. Available classes for the Yahoo component you can see here:

This is not a weather condition, it’s an input_text state and this is ok. You can see the weather condition by clicking on the forecast in the weather card

@Yevgeniy, short note to thank you for the card. It’s very nice. And now that it works with openweathermap event better.

ok first off thank you guys for setting this up! especially the few that really took this to something amazing.

I have installed @yevgeniy latest version and I have one question:

I am not getting the animated icons eventhough I had to place them in www folder. is this correct?

they go in www/weather_icons/animated/

yep that’s where I put them. //hassio/config/www/weather_icons/animated

but no icons. (in private browsing on chrome+firefox)

Hmm I had to restart home assistant twice and then in privacy mode I could see the new icons! thanks again.

How to create a weather state card with a graph of the predicted temperature for the next few days?

Since im running on hassbian i changed two lines these below

  <span class="icon bigger" style="background: none, url(/home/homeassistant/.homeassistant/icons/animated/[[nowCond]].svg) no-repeat; background-size: contain;"></span>
      <br> <i class="icon" style="background: none, url(/home/homeassistant/.homeassistant/icons/animated/[[item.condIcon]].svg) no-repeat; background-size: contain;"></i> 

and the icon doesnt change to the animated ones yet the icons are all there

ls -h /home/homeassistant/.homeassistant/icons/animated/
cloudy-day-1.svg  cloudy-night-1.svg  cloudy.svg  rainy-1.svg  rainy-4.svg  rainy-7.svg  snowy-3.svg  snowy-6.svg              weather-sprite.svg
cloudy-day-2.svg  cloudy-night-2.svg  day.svg     rainy-2.svg  rainy-5.svg  snowy-1.svg  snowy-4.svg  thunder.svg              weather_sunset.svg
cloudy-day-3.svg  cloudy-night-3.svg  night.svg   rainy-3.svg  rainy-6.svg  snowy-2.svg  snowy-5.svg  weather_sagittarius.svg  weather.svg

any ideas of whats wrong ?

@toast There are no icons on the card at all? Or it show static icons?
@bmhvanbenthum I’ll try to add a chart to the weather card.

only the regular mdi icons no entity pictures

Have you tried setting up as specified here?

yupp even using the animated icons for other templates works like a charm there, but not on the weather card

For a noob like me, how can i install this on my HA home page ?

Weather-Chart-OWM2

custom-weather-card.html
<dom-module id='custom-weather-card'>
  <template>
    <style>
      .clear {
        clear: both;
      }
      .card {
        margin: 0em auto;
        padding-left: 0em;
        padding-right: 0em;
        position: relative;
      }
      .main {
        font-size: 60px;
        margin-bottom: 36px;
      }
      .main sup {
        font-size: 32px;
      }
      .main span {
        color: var(--paper-item-icon-color);
      }
      .content {
        display: flex;
        justify-content: space-between;
        align-items: center;
        flex-wrap: wrap;
        margin-bottom: 3px;
      }
      .attributes {
        text-align: center;
      }
      iron-icon {
        padding-bottom: 6px;
		color: var(--paper-item-icon-color);
      }
	  
    </style>
    <div class="card">
      <div class="main">
        <div>
          <span>[[getWeatherIcon(weatherObj.state)]]</span>
          [[roundNumber(weatherObj.attributes.temperature)]]<sup>[[getUnit('temperature')]]</sup>
        </div>
      </div>
      <div class="content">
        <template is="dom-if" if="[[weatherObj.attributes.pressure]]">
          <div class="attributes">
            <span><iron-icon icon="mdi:gauge"></iron-icon></span>
            <br>[[roundNumber(weatherObj.attributes.pressure)]] hPa
          </div>
        </template>
        <template is="dom-if" if="[[weatherObj.attributes.humidity]]">
          <div class="attributes">
            <span><iron-icon icon="mdi:water-percent"></iron-icon></span>
            <br>[[roundNumber(weatherObj.attributes.humidity)]] %
          </div>
        </template>
        <template is="dom-if" if="[[weatherObj.attributes.visibility]]">
          <div class="attributes">
            <span><iron-icon icon="mdi:weather-fog"></iron-icon></span>
            <br>[[roundNumber(weatherObj.attributes.visibility)]] km
          </div>
        </template>
        <template is="dom-if" if="[[weatherObj.attributes.wind_bearing]]">
          <div class="attributes">
            <span><iron-icon icon="mdi:[[windBearingToIcon(weatherObj.attributes.wind_bearing)]]"></iron-icon></span>
            <br>[[windBearingToText(weatherObj.attributes.wind_bearing)]]
          </div>
        </template>
        <template is="dom-if" if="[[weatherObj.attributes.wind_speed]]">
          <div class="attributes">
            <span><iron-icon icon="mdi:weather-windy"></iron-icon></span>
            <br>[[roundNumber(weatherObj.attributes.wind_speed)]] m/s
          </div>
        </template>
      </div>
      <template is="dom-if" if="[[weatherObj.attributes.forecast]]">
        <ha-chart-base data="[[chartData]]"></ha-chart-base>
      </template>
    </div>
  </template>
</dom-module>

<script>
(function () {
  'use strict';

  var _WEATHER_TO_ICON = {
    'cloudy': '\u2601\ufe0f',
    'fog': '\uD83C\uDF2B\ufe0f',
    'hail': 'Hail',
    'lightning': '\uD83C\uDF29\ufe0f',
    'lightning-rainy': '\u26c8\ufe0f',
    'partlycloudy': '\u26c5\ufe0f',
    'pouring': '\uD83C\uDF27\ufe0f',
    'rainy': '\uD83C\uDF27\ufe0f',
    'snowy': '\uD83C\uDF28\ufe0f',
    'snowy-rainy': '\uD83C\uDF28\ufe0f',
    'sunny': '\u2600\ufe0f',
    'windy': '\uD83C\uDF2C\ufe0f',
    'windy-variant': '\uD83C\uDF2C\ufe0f',
    'exceptional': '\u2b55\ufe0f',
  };

  var _DEGREE_TO_TEXT = [
    'N', 'NNO', 'NO', 'ONO', 'O', 'OZO', 'ZO', 'ZZO',
    'Z', 'ZZW', 'ZW', 'WZW', 'W', 'WNW', 'NW', 'NNW', 'N'
  ];

  var _DEGREE_TO_ICON = [
    'arrow-down', 'arrow-bottom-left', 'arrow-left', 'arrow-top-left',
    'arrow-up', 'arrow-top-right', 'arrow-right', 'arrow-bottom-right', 'arrow-down'
  ];

  Polymer({
    is: 'custom-weather-card',

    properties: {
      hass: {
        type: Object,
      },
      stateObj: {
        type: Object,
      },
      weatherObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeWeatherObj(hass, stateObj)',
      },
      chartData: {
        type: Object,
      },
    },

    computeWeatherObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.weather ? hass.states[stateObj.attributes.config.weather] : null;
    },

    getUnit(unit) {
      return this.hass.config.core.unit_system[unit] || '';
    },

    roundNumber(number) {
      var rounded = Math.round(number);
      return rounded
    },

    getWeatherIcon(condition) {
      return _WEATHER_TO_ICON[condition];
    },

    windBearingToText: function (degree) {
      return _DEGREE_TO_TEXT[(parseInt((degree + 11.25) / 22.5))];
    },

    windBearingToIcon: function (degree) {
      return _DEGREE_TO_ICON[(parseInt((degree + 22.5) / 45.0))];
    },

    checkRequirements: function () {
      if (!this.weatherObj) {
        return;
      }
      if (!this.weatherObj.attributes) {
        return;
      }
      if (!this.weatherObj.attributes.forecast) {
        return;
      }
      this.drawChart();
    },

    drawChart() {
      var dataArray = [];
      var data = this.weatherObj.attributes.forecast.slice(0,9);
      var i;
      if (!this.weatherObj.attributes.forecast) {
        return [];
      }
      var dateTime = [];
      var tempHigh = [];
      var tempLow = [];
      var precip = [];
      var cond = [];
      for (i = 0; i < data.length; i++) {
        var d = data[i];
        dateTime.push(new Date(d.datetime));
        tempHigh.push(d.temperature);
        tempLow.push(d.templow);
        precip.push(d.precipitation);
        cond.push(_WEATHER_TO_ICON[d.condition]);
      }
      var style = getComputedStyle(document.body);
      var textColor = style.getPropertyValue('--secondary-text-color');
      var iconColor = style.getPropertyValue('--paper-item-icon-color');
      const chartOptions = {
        type: 'bar',
        data: {
          labels: dateTime,
          datasets: [
            {
              label: 'Temperature',
              type: 'line',
              data: tempHigh,
              yAxisID: 'TempAxis',
              borderWidth: 2.0,
              lineTension: 0.5,
              pointRadius: 0.0,
              pointHitRadius: 5.0,
              fill: false,
            },
            {
              label: 'Temperature Low',
              type: 'line',
              data: tempLow,
              yAxisID: 'TempAxis',
              borderWidth: 2.0,
              lineTension: 0.5,
              pointRadius: 0.0,
              pointHitRadius: 5.0,
              fill: false,
            },
            {
              label: 'Precipitations',
              type: 'bar',
              data: precip,
              yAxisID: 'PrecipAxis',
            },
          ]
        },
        options: {
          animation: {
            duration: 300,
            easing: 'linear',
          },
          legend: {
            display: false,
            position: 'bottom',
              labels: {
                boxWidth: 10,
              },
          },
          scales: {
            xAxes: [{
              type: 'time',
              display: false,
              ticks: {
                display: false,
              },
              gridLines: {
                display: false,
              },
            },
            {
              id: 'DateAxis',
              position: 'top',
              type: 'time',
              time: {
                unit: 'day',
                unitStepSize: 1,
              },
              gridLines: {
                display: true,
              },
              ticks: {
                display: true,
                source: 'auto',
                autoSkip: true,
                fontColor: textColor,
                maxRotation: 0,
                callback: function(value) {
                  return new Date(value).toLocaleDateString([], {
                    weekday: 'short'
                  });
                },
              },
            },
            {
              id: 'CondAxis',
              position: 'bottom',
              type: 'category',
              labels: cond,
              gridLines: {
                display: true,
              },
              ticks: {
                display: true,
                fontSize: 20,
                fontColor: iconColor,
                maxRotation: 0,
              },
            }],
            yAxes: [{
              id: 'TempAxis',
              position: 'left',
              gridLines: {
                display: true,
              },
              scaleLabel: {
                display: true,
                labelString: 'Temperature, \xB0C',
                fontColor: textColor,
              },
              ticks: {
                display: true,
                min: 0,
                fontColor: textColor,
              },
            },
            {
              id: 'PrecipAxis',
              position: 'right',
              gridLines: {
                display: false,
              },
              scaleLabel: {
                display: true,
                labelString: 'Precipitations, mm',
                fontColor: textColor,
              },
              ticks: {
                display: true,
                min: 0,
                max: 20,
                fontColor: textColor,
              },
            }],
          },
          tooltips: {
            mode: 'index',
            callbacks: {
              title: function (items, data) {
                const item = items[0];
                const date = data.labels[item.index];
                return new Date(date).toLocaleDateString([], {
                  month: 'long',
                  day: 'numeric',
                  weekday: 'short',
                  hour: '2-digit',
                  minute: '2-digit' 
                });
              }
            }
          },
          layout: {
            padding: {
              top: '0.0',
              bottom: '-2.0',
              right: '-5.0',
              left: '-5.0',
            }
          }
        }
      };
      this.chartData = chartOptions;
    },
  });
}());
</script>
Integration

configuration.yaml:

homeassistant:
  customize:
    input_text.weather:
      custom_ui_state_card: custom-weather-card
      config:
        weather: weather.yweather

frontend:
  extra_html_url:
    - /local/custom_ui/custom-weather-card.html
  extra_html_url_es5:
    - /local/custom_ui/custom-weather-card.html

weather:
  - platform: yweather

input_text:
  weather:
    name: weather 

group:
  weather:
    name: Weather
    entities:
      - input_text.weather

Place custom-weather-card.html to …\config\www\custom_ui.

It also works with input_boolean instead input_text.

7 Likes