Custom UI: Weather state card

@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

Cool it’s working ! Thanks a lot, how does i configure it to be with my town ?

Thanks !

Very good job :slight_smile:
But how i can implement it when i want also have implement other custom_ui like name state-card-custom-ui.html ?

And in frontend i have put this:
extra_html_url:
- /local/custom_ui/state-card-custom-ui.html
extra_html_url_es5:
- /local/custom_ui/state-card-custom-ui-es5.html

Ok i add one by one and works perfect.

Hi @Yevgeniy , awesome work!

I have copied your code across as a package and it works except for the rain side of things. I know its forecast for rain on Wednesday but I get this:
image

my icons dont look as good as yours either :thinking:

finally, the wind direction is showing ‘ONO’ when it should be ‘ENE’…not sure whats going on there…?

The icons display will vary depending on the operating system on which the browser is installed. I tested this card on Android, Windows and Linux. Everywhere the icons looked different. This is because these icons are not images, but a text font. I tried to find a solution to integrate the images into the graph, but in this version of Charts.js for the labeling of axes it is allowed to use only text.
Check how the wind direction is shown in the standard weather card. It should be same

OK, no worries.

My entities page shows it as a bearing angle

from another weather provider:
image

I didnt have yweather setup before using this custom ui so dont have it as a standard card

Oh, I’m sorry. ONO is the old name of ENE. It’s the same direction.
Just replace var_DEGREE_TO_TEXT with this:

  var _DEGREE_TO_TEXT = [
    'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
    'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'
  ];

How to have those graphs? I updated your card but i get always the same card, with no graphs…

After changing and saving the html file need to force refresh page by ctrl+F5

Perfect! thank you. Any idea why my precipitation info isn’t showing up?

EDIT: it seems to not show any precipitation data in the entities page… so maybe for some reason there is no data available for my location via yahoo weather?

Ok, got it, but now why i don’t have anymore the hour by hour card when clicking over the card how it was previously?

@sparkydave At the moment, the Yahoo component does not provide information about precipitation. I use Openweathermap to display it, but this is only for hourly forecasts. In the next versions HA I will add precipitations info for daily Openweathermap forecasts. You can try other components. Yahoo weather is just an example.
@maurizio53 ok. I will add it in next update

was that not daily precipitation bar graphs in your screen shot? was that info coming from another weather provider? I use other providers that give rain info so perhaps I can use that data?

In the screenshot I used Openweathermap custom component. You can use any weather component

I changed weather provider from yahoo to openweathermap and now i get this card:

03

Is the output correct? Why i don’t get anymore precipitations graph like when using yahoo?