Custom UI: Weather state card


That’s a good idea :slight_smile:

Made some more updates:

  • If the current temperature value is 0 then it will not be shown on the weather card, added some code to check that and save the temp as string so it will be shown
  • Forecast now supports 5 days
  • Added the fields pressure, visibility, precipation and precipationfc (forecast) which can be used in customize.yaml
  custom_ui_state_card: custom-weather-card
    weather: weather.buienradar
    pressure: sensor.br_pressure_de_bilt
    visibility: sensor.br_visibility_de_bilt
    precipation: sensor.buienradar_precipitation
    precipationfc: sensor.buienradar_precipitation_forecast_average

If your weather component doesn’t have this information (my nearest Buienradar station doesn’t have pressure and visibility available, so I get that from a template sensor), then you can add these.


The HTML code
<dom-module id='custom-weather-card'>
      .clear {
      .card {
        margin:1em auto;
        padding-left: 1em;
        position: relative;
      .iron-icon {
        height: 18px;
        color: #c8c8c8;
      .temp {
        font-weight: 300;
        font-size: 4em;
        position: absolute;
        right: .5em;
      .tempc {
        font-weight: 300;
        font-size: 1.5em;
        vertical-align: super;
        position: absolute;
        right: 0em;
        margin-top: -14px;
        margin-right: 7px;
      .variations {
        margin-top: 3.5em;
      .variations.right {
        float: right;
        margin-left: 0;
        margin-right: 1em;
      .unit {
      .forecast {
        margin:0 auto;
      .day {
        width: 20%;
        color: #5b5b5b;
        border-right:.1em solid #d9d9d9;
        line-height: 2;
        box-sizing: border-box;
      .dayname {
        text-transform: uppercase;

      .forecast .day:first-child {
        margin-left: 0;
      .forecast .day:nth-last-child(2) {
        margin-right: 0;
      .highTemp {
      .lowTemp {
        color: #8c8c8c;
      .icon.bigger {
        width: 10em;
        height: 10em;
        margin-top: -4em;
        position: absolute;
        left: 0em;
      .icon {
        width: 50px;
        height: 50px;
        display: inline-block;
        vertical-align: middle;
        background-size: contain;
        background-position: center center;
        background-repeat: no-repeat;
        text-indent: -9999px;
       .weather {
        font-weight: 300;
        font-size: 1.5em;
        position: absolute;
        top: -0.5em;
        left: 6em;
        word-wrap: break-word;
        width: 30%;

    <div class="card">
      <span class="icon bigger" style="background: none, url(/local/weather_icons/animated/[[nowCond]].svg) no-repeat; background-size: contain;"></span>
      <span class="temp">[[roundedTemp]]</span><span class="tempc">&#176;C</span>
      <span class="weather">[[nowCondIT]]</span>
        <ul class="variations right">
          <template is="dom-if" if="[[weatherObj.attributes.humidity]]">
            <li><iron-icon icon="mdi:water-percent"></iron-icon> [[weatherObj.attributes.humidity]]<span class="unit"> %</span></li>
          <template is="dom-if" if="[[weatherObj.attributes.pressure]]">
            <li><iron-icon icon="mdi:gauge"></iron-icon> [[weatherObj.attributes.pressure]]<span class="unit"> hPa</span></li>
          <template is="dom-if" if="[[pressureObj.state]]"> <!-- if weather station doesn't have pressure info, get it from a separate sensor -->
            <li><iron-icon icon="mdi:gauge"></iron-icon> [[nowPressure]]<span class="unit"> hPa</span></li>
          <template is="dom-if" if="[[precipationfcObj.state]]">
            <li><iron-icon icon="mdi:weather-rainy"></iron-icon> [[nowPrecipationfc]]<span class="unit" > mm/u (> uur)</span></li>

        <ul class="variations">
          <template is="dom-if" if="[[weatherObj.attributes.wind_speed]]">
            <li><iron-icon icon="mdi:weather-windy"></iron-icon> [[windBearing]] [[weatherObj.attributes.wind_speed]]<span class="unit"> m/s</span></li>
          <template is="dom-if" if="[[weatherObj.attributes.visibility]]">
            <li><iron-icon icon="mdi:weather-fog"></iron-icon> [[weatherObj.attributes.visibility]]<span class="unit"> m</span></li>
          <template is="dom-if" if="[[visibilityObj.state]]"> <!-- if weather station doesn't have visibility info, get it from a separate sensor -->
            <li><iron-icon icon="mdi:weather-fog"></iron-icon> [[nowVisibility]]<span class="unit" > m</span></li>
          <template is="dom-if" if="[[precipationObj.state]]">
            <li><iron-icon icon="mdi:weather-pouring"></iron-icon> [[nowPrecipation]]<span class="unit" > mm/u</span></li>

      <div class="forecast clear">
        <template is="dom-repeat" items="[[forecast]]">
        <div class="day"><span class="dayname">[[item.dayIT]]</span>
          <template is="dom-if" if="[[item.condIcon]]">
          <br> <i class="icon" style="background: none, url(/local/weather_icons/animated/[[item.condIcon]].svg) no-repeat; background-size: contain;"></i> 
          <template is="dom-if" if="[[item.tempHigh]]">
          <br> <span class="highTemp">[[item.tempHigh]]&#176;C</span> 
          <template is="dom-if" if="[[item.tempLow]]">
          <br> <span class="lowTemp">[[item.tempLow]]&#176;C</span>

(function () {
  'use strict';

  var _WEATHER_TO_ICON = {
    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: '!!',
  var _WEATHER_TO_NAME = {
    cloudy: 'Bewolkt',
    fog: 'Mist',
    hail: 'Hagel',
    lightning: 'Onweer',
    'lightning-rainy': 'Onweer',
    partlycloudy: 'Gedeeltelijk bewolkt',
    pouring: 'Stortregen',
    rainy: 'Regen',
    snowy: 'Sneeuw',
    'snowy-rainy': 'Natte sneeuw',
    sunny: 'Zonnig',
    windy: 'Winderig',
    'windy-variant': 'Variabele wind',
    exceptional: '!',

  var _DEGREE_TEXT = [
    'N', 'NNO', 'NO', 'ONO', 'O', 'OZO', 'ZO', 'ZZO',
    'Z', 'ZZW', 'ZW', 'WZW', 'W', 'WNW', 'NW', 'NNW', 'N'
  var _DAY_TO_DAY = {
    Mon: 'MA',
    Tue: 'DI',
    Wed: 'WO',
    Thu: 'DO',
    Fri: 'VR',
    Sat: 'ZA',
    Sun: 'ZO',

    is: 'custom-weather-card',

    properties: {
      hass: {
        type: Object,
      stateObj: {
        type: Object,
      weatherObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeWeatherObj(hass, stateObj)',
      pressureObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePressureObj(hass, stateObj)',
      visibilityObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeVisibilityObj(hass, stateObj)',
      precipationObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePrecipationObj(hass, stateObj)',
      precipationfcObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePrecipationfcObj(hass, stateObj)',

    computeWeatherObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && ? hass.states[] : null;
    computePressureObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.pressure ? hass.states[stateObj.attributes.config.pressure] : null;
    computeVisibilityObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.visibility ? hass.states[stateObj.attributes.config.visibility] : null;
    computePrecipationObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.precipation ? hass.states[stateObj.attributes.config.precipation] : null;
    computePrecipationfcObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.precipationfc ? hass.states[stateObj.attributes.config.precipationfc] : null;

    getForecastArray: function () {
      if (!this.weatherObj.attributes.forecast) {
        return [];
      var data = this.weatherObj.attributes.forecast;
      var forecast = [];
      var prevDay = '';
      for (var i = 0; i < data.length; i++) {
        var day = new Date(data[i].datetime).toString().split(' ')[0];
        if (day != prevDay) {
          if (data[i].max_temp) {
            var tempHigh = Math.round(data[i].max_temp * 10) / 10;
          } else {
            var tempHigh = Math.round(data[i].temperature * 10) / 10;
          if (tempHigh == 0) {
            tempHigh = '0'; // otherwise the value 0 will not be shown on the weather card
          var tempLow = Math.round(data[i].templow * 10) / 10;
          if (tempLow == 0) {
            tempLow = '0'; // otherwise the value 0 will not be shown on the weather card
          var condIcon = _WEATHER_TO_ICON[data[i].condition];
          var dayIT = _DAY_TO_DAY[day];
          forecast.push({dayIT:dayIT, tempHigh:tempHigh, tempLow:tempLow, condIcon:condIcon});
          prevDay = day;
        } else {
          if (data[i].max_temp) {
            var tempHigh = Math.round(data[i].max_temp * 10) / 10;
          } else {
            var tempHigh = Math.round(data[i].temperature * 10) / 10;
          var tempLow = Math.round(data[i].tempLow * 10) / 10;
          if (tempLow > forecast[forecast.length-1].tempHigh) {
            forecast[forecast.length-1].tempHigh = tempLow;
          if (tempHigh > forecast[forecast.length-1].tempHigh) {
            forecast[forecast.length-1].tempHigh = tempHigh;
          if (!forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempHigh;
          if (tempHigh < forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempHigh;
          if (tempLow < forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempLow;

      return forecast;

    checkRequirements: function () {
      if (!this.weatherObj) {
      this.nowCond = _WEATHER_TO_ICON[this.weatherObj.state];
      this.nowCondIT = _WEATHER_TO_NAME[this.weatherObj.state];
      if (this.pressureObj != null) {
        this.nowPressure = this.pressureObj.state;
      if (this.visibilityObj != null) {
        this.nowVisibility = this.visibilityObj.state;
      if (this.precipationObj != null) {
        this.nowPrecipation = this.precipationObj.state;
      if (this.precipationfcObj != null) {
        this.nowPrecipationfc = this.precipationfcObj.state;
      if (this.weatherObj.attributes.temperature) {
        this.roundedTemp = Math.round(this.weatherObj.attributes.temperature * 10) / 10;
        if (this.roundedTemp == 0) {
          this.roundedTemp = '0'; // otherwise the value 0 will not be shown on the weather card
      if (this.weatherObj.attributes.wind_bearing) {
        this.windBearing = this.windBearingToText(this.weatherObj.attributes.wind_bearing);
      this.forecast = this.getForecastArray().slice(0, 5);

    windBearingToText: function (degree) {
      // return _DEGREE_TEXT[((parseInt(degree) + 5.63) / 11.25) | 0];
      return _DEGREE_TEXT[(parseInt((degree + 11.25) / 22.5))];


@gerard33 - thank you for the tweaks you’v been making to this (and a big thanks to @Bram_Kragten for starting this off).

I’m having problems implementing your versions for some reason Gerard - I end up with this using your last two HTML code dumps (my only change is the WEATHER_TO_NAME array to give me English translations):

If I use @eddi89’s HTML with the exact same config it shows the temp value and table below. I’ve ruled out cache issues. I’m using the yweather component which is pulling data into the relevant states (apart from your recently added ones).

I did a bit of a compare and can’t see any obvious causes. Any ideas?


Once updated to the latest version cannot make it work. Tried switching from yweather to buienradar, no luck before and after the change.


Found the answer to my own problem.

It seems any of these 4 lines breaks it if there’s no data for them? Of course they were near the bottom :joy:

      this.nowPressure = this.pressureObj.state;
      this.nowVisibility = this.visibilityObj.state;
      this.nowPrecipation = this.precipationObj.state;
      this.nowPrecipationfc = this.precipationfcObj.state;


I will check that. Haven’t tested it without this data so could indeed be the cause of the issue.

@mynameisdaniel you were right, that part of the code didn’t work when there was no data.
I have now added a check for that so it will also work with empty data.
The code is updated in my post above. Can you try if it works now?

@ardeus, can you also try the new code? And if it still doesn’t work share some more details like screenprints and the Chrome console log?


@gerard33 Tried your version and works much better than the original one I was using. Thanks


Works perfectly now. Thanks!

It’s a lovely addition to my tablet interfaces (I still need to tidy up a few things to make colours/sizes more consistent.

Is there any way to override specific values pulled from the weather component to use defined sensor values instead? For example, I’m using Yahoo Weather which is horribly inaccurate for my location (It’s currently 20C, not 16C here…). Dark Sky is better, but only seems to only to today and tomorrow forecasting.

I’d like to use Weather Underground data instead but because it’s not packaged as a Weather component making that happen in this is a bit beyond me. Even if it’s just for today’s forecast and the current conditions, it would be a huge improvement.



still struggling a bit here… sorry. would you please care to post your full code (maybe in a package)?. Hopefully that might alleviate the issues i seem to run into (now had complaints form the validator about the input_boolean/text).
if yes, thanks! :wink:

tis is what i have now:


    name: Weer custom

      custom_ui_state_card: custom-weather-card
        weather: weather.yweather

- platform: openweathermap
  api_key: !secret openweather_key

- platform: yweather
  name: Home

- platform: buienradar
  name: !secret buienradar_station


Is there any way to use the default mdi icons for the weather instead of animated icons? I am also getting the blurry icons and like the consistent look of the mdi icons


I’m not getting blurry icons, but the cloud animation is shaky.

Any idea why this might be happening?


I get this too, but only on my Android tablets, I think it’s the webview browser rendering causing issues or struggling. Works fine on other computers/browsers and my iPhone.


Weird, it happens in Chrome (Windows) but not on my Android phone!


I find I only get the blurry icons when the weather forecast is using the same icon. And if I scroll so the forecast icons aren’t in the picture, the main weather icon becomes clear.


Hi All,

I’m totally noob about custom ui.

Can someone please share both conf of html and yaml configuration?

I’m struggling to make it work without success.


@gerard33, I have updated the code in my setup with the latest edits you have implemented and it works like a charm.
This card definitely needs to be implemented as a standard layout of weather component for Home Assistant.
I have managed to make it work as a secondary UI card and config is hassle free. To change the source of the weather data you simply need to configure weather component.

    - /local/custom_ui/state-card-custom-ui.html
    - /local/custom_ui/custom-weather-card.html
    - /local/custom_ui/state-card-iframe.html
    - /local/custom_ui/state-card-custom-ui-es5.html
    - /local/custom_ui/custom-weather-card.html
    - /local/custom_ui/state-card-iframe.html
      custom_ui_state_card: state-card-custom-ui  
      state_card_mode: badges
      state_card_custom_ui_secondary: custom-weather-card
      friendly_name: ''
        weather: weather.card
  - platform: yweather
    name: card
  - weather:


I’d like to second @Jokerigno 's request - anyone willing to put it all in one place? This looks awesome, but being not an expert, I’m confused on start to finish how to make this happen.


Works geat, but my card shows only 4 days forecast. I’m using yahoo weather.


There should be 5 for Yahoo as well. Did you refresh the page (Ctrl + F5)?


Yes I’ve pressed ctrl+f5. I’m running Hass 0.66.1 in docker.


Then maybe in your region Yahoo only has a 4 day weather forecast? Can you check for that?