Custom animated weather card for Lovelace


#252

At the end, i’ve solved in javascript side using:

var weekday = new Array(7);
	weekday[0] =  "Dom";
	weekday[1] = "Lun";
	weekday[2] = "Mar";
	weekday[3] = "Mer";
	weekday[4] = "Gio";
	weekday[5] = "Ven";
	weekday[6] = "Sab";

...

var forecastDate1 = new Date();
    forecastDate1.setDate(forecastDate1.getDate()+1);
	forecastDate1 = weekday[forecastDate1.getDay()];
    var forecastDate2 = new Date();
    forecastDate2.setDate(forecastDate2.getDate()+2);
	forecastDate2 = weekday[forecastDate2.getDay()];
    var forecastDate3 = new Date();
    forecastDate3.setDate(forecastDate3.getDate()+3);
	forecastDate3 = weekday[forecastDate3.getDay()];
    var forecastDate4 = new Date();
    forecastDate4.setDate(forecastDate4.getDate()+4);
	forecastDate4 = weekday[forecastDate4.getDay()];
    var forecastDate5 = new Date();
    forecastDate5.setDate(forecastDate5.getDate()+5);
	forecastDate5 = weekday[forecastDate5.getDay()];

...

<span class="dayname">${(daily.date).toString()}</span>

#253

of course, that is an option too :wink:
just remember to change when the card gets updated.


#254

back.please allow:

made it up to this so far, (and use some external sensors, since OWM doesn’t have visibility, wind bearing and apparent (for which I use my own Jagti windchill sensor).
Now only need to understand why it doesn’t show the days of the week, and has a series of undefined in the bottom line where there should be some forecast?

please have a look with me, to see what I could adapt further:

const entity = hass.states[this.config.entity_weather];
const currentCondition = entity.state;
const humidity = entity.attributes.humidity;
const pressure = entity.attributes.pressure;
const temperature = entity.attributes.temperature;
const visibility = hass.states['sensor.yweather_visibility'].state;
const windBearing = hass.states['sensor.owm_wind_bearing'].state;
const windSpeed = entity.attributes.wind_speed;
const forecast = entity.attributes.forecast.slice(0, 5);
const apparent_temperature = hass.states['sensor.jagti_windchill'].state;


this.content.innerHTML = `
  <span class="icon bigger" style="background: none, url(/local/weather/animated/${weatherIcons[currentCondition]}.svg) no-repeat; background-size: contain;">${currentCondition}</span>
  <ul style="list-style-type:none">
    <li><span class="temp">${temperature}</span><span class="tempc"> ${getUnit('temperature')}</span></li>
    <li><span class="tempa">Feels like ${apparent_temperature}</span><span class="tempc"> ${getUnit('temperature')}</span></li>
  </ul>
  <span>
    <ul class="variations right">
        <li><span class="ha-icon"><ha-icon icon="mdi:water-percent"></ha-icon></span>${humidity}<span class="unit"> %</span></li>
        <li><span class="ha-icon"><ha-icon icon="mdi:gauge"></ha-icon></span>${pressure}<span class="unit"> ${getUnit('air_pressure')}</span></li>
    </ul>
    <ul class="variations">
        <li><span class="ha-icon"><ha-icon icon="mdi:weather-windy"></ha-icon></span>${windBearing} ${windSpeed}<span class="unit"> ${getUnit('length')}/h</span></li>
        <li><span class="ha-icon"><ha-icon icon="mdi:weather-fog"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('length')}</span></li>
    </ul>
  </span>
  <div class="forecast clear">
      ${forecast.map(daily => `
          <div class="day">
              <span class="dayname">${(new Date(daily.datetime)).toLocaleDateString((navigator.language) ? navigator.language : navigator.userLanguage, {weekday: 'short'}).split(' ')[0]}</span>
              <br><i class="icon" style="background: none, url(/local/weather/animated/forecast/${weatherIcons[daily.condition]}.svg) no-repeat; background-size: contain;"></i>
              <br><span class="highTemp">${daily.temperature}${getUnit('temperature')}</span>
              <br><span class="lowTemp">${daily.templow}${getUnit('temperature')}</span>
          </div>`).join('')}
  </div>`;

}

displays like this now:

21


#255

Hi(ciao)
if you want to change the language displayed on the card with the language set in HA, then change this string :
<span class=“dayname”>${(new Date(daily.datetime)).toLocaleDateString((navigator.language) ? navigator.language : navigator.userLanguage, {weekday: ‘short’}).split(’ ')[0]}</span>

to:
<span class="dayname">${(daily.date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}</span>


#256

I thanks for replying.
it is not as much the language, but the 5 following days id like to change. Right now it shows 3 times monday, and twice tuesday, instead of mo-friday…

also, I think at the bottom should be somekind of summary forecast, but this obviously doesn’t work with Open Weather Map…

the animated card for regular HA does it like this:

33

but both systems show the native weather card as:

36 50

so it is to do with OWM, but the animated Lovelace card makes it even worse…


#257

Immagine
this is mine, i’ve changed that code, anyway, i write down my js:

class DarkSkyWeatherCard extends HTMLElement {
  set hass(hass) {
    if (!this.content) {
      const card = document.createElement('ha-card');
      const link = document.createElement('link');
      link.type = 'text/css';
      link.rel = 'stylesheet';
      link.href = '/local/custom_ui/dark-sky-weather-card.css';
      card.appendChild(link);
      this.content = document.createElement('div');
      this.content.className = 'card';
      card.appendChild(this.content);
      this.appendChild(card);
    }

    const getUnit = function (measure) {
      const lengthUnit = hass.config.unit_system.length;
      switch (measure) {
        case 'air_pressure':
          return lengthUnit === 'km' ? 'hPa' : 'mbar';
        case 'length':
          return lengthUnit;
        case 'precipitation':
          return lengthUnit === 'km' ? 'mm' : 'in';
        default:
          return hass.config.unit_system[measure] || '';
      }
    };

    const transformDayNight = {
      "below_horizon": "night",
      "above_horizon": "day",
    }
    const sunLocation = transformDayNight[hass.states[this.config.entity_sun].state];
    const weatherIcons = {
      'clear-day': 'day',
      'clear-night': 'night',
      'rain': 'rainy-5',
      'snow': 'snowy-6',
      'sleet': 'rainy-6',
      'wind': 'cloudy',
      'fog': 'cloudy',
      'cloudy': 'cloudy',
      'partly-cloudy-day': 'cloudy-day-3',
      'partly-cloudy-night': 'cloudy-night-3',
      'hail': 'rainy-7',
      'lightning': 'thunder',
      'thunderstorm': 'thunder',
      'windy-variant': `cloudy-${sunLocation}-3`,
      'exceptional': '!!',
    }

    const windDirections = [
      'N',
      'NNE',
      'NE',
      'ENE',
      'E',
      'ESE',
      'SE',
      'SSE',
      'S',
      'SSW',
      'SW',
      'WSW',
      'W',
      'WNW',
      'NW',
      'NNW',
      'N'
    ];
    
    var forecastDate1 = new Date();
    forecastDate1.setDate(forecastDate1.getDate()+1);
    var forecastDate2 = new Date();
    forecastDate2.setDate(forecastDate2.getDate()+2);
    var forecastDate3 = new Date();
    forecastDate3.setDate(forecastDate3.getDate()+3);
    var forecastDate4 = new Date();
    forecastDate4.setDate(forecastDate4.getDate()+4);
    var forecastDate5 = new Date();
    forecastDate5.setDate(forecastDate5.getDate()+5);
    
    
    const currentConditions = hass.states[this.config.entity_current_conditions].state;
    const humidity = hass.states[this.config.entity_humidity].state;
    const pressure = Math.round(hass.states[this.config.entity_pressure].state);
    const temperature = Math.round(hass.states[this.config.entity_temperature].state);
    const visibility = hass.states[this.config.entity_visibility].state;
    const windBearing = windDirections[(Math.round((hass.states[this.config.entity_wind_bearing].state / 360) * 16))];
    const windSpeed = Math.round(hass.states[this.config.entity_wind_speed].state);
    const forecast1 = {
      date: forecastDate1,
      condition: this.config.entity_forecast_icon_1,
      temphigh: this.config.entity_forecast_high_temp_1,
      templow: this.config.entity_forecast_low_temp_1,
    };
    const forecast2 = {
      date: forecastDate2,
      condition: this.config.entity_forecast_icon_2,
      temphigh: this.config.entity_forecast_high_temp_2,
      templow: this.config.entity_forecast_low_temp_2,
    };
    const forecast3 = {
      date: forecastDate3,
      condition: this.config.entity_forecast_icon_3,
      temphigh: this.config.entity_forecast_high_temp_3,
      templow: this.config.entity_forecast_low_temp_3,
    };
    const forecast4 = {
      date: forecastDate4,
      condition: this.config.entity_forecast_icon_4,
      temphigh: this.config.entity_forecast_high_temp_4,
      templow: this.config.entity_forecast_low_temp_4,
    };
    const forecast5 = {
      date: forecastDate5,
      condition: this.config.entity_forecast_icon_5,
      temphigh: this.config.entity_forecast_high_temp_5,
      templow: this.config.entity_forecast_low_temp_5,
    };

    const forecast = [forecast1, forecast2, forecast3, forecast4, forecast5];
    const sun = hass.states[this.config.entity_sun];
    const sunraise = sun.attributes.next_rising;
    const sunset = sun.attributes.next_setting;

    this.content.innerHTML = `
      <span class="icon bigger" style="background: none, url(/local/weather_icons/animated/${weatherIcons[currentConditions]}.svg) no-repeat; background-size: contain;">${currentConditions}</span>
      <span class="temp">${temperature}</span><span class="tempc"> ${getUnit('temperature')}</span>
      <span>
        <ul class="variations right">
            <li><span class="ha-icon"><ha-icon icon="mdi:water-percent"></ha-icon></span>${humidity}<span class="unit"> %</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:gauge"></ha-icon></span>${pressure}<span class="unit"> ${getUnit('air_pressure')}</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-sunset-up"></ha-icon></span>${new Date(sunraise).toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'})}</li>  
        </ul>
        <ul class="variations">
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-windy"></ha-icon></span>${windBearing} ${windSpeed}<span class="unit"> ${getUnit('length')}/h</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-fog"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('length')}</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-sunset-down"></ha-icon></span>${new Date(sunset).toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'})}</li>  
        </ul>
      </span>
      <div class="forecast clear">
          ${forecast.map(daily => `
              <div class="day">
                  <span class="dayname">${(daily.date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}</span>
                  <br><i class="icon" style="background: none, url(/local/icons/weather_icons/animated/${weatherIcons[hass.states[daily.condition].state]}.svg) no-repeat; background-size: contain;"></i>
                  <br><span class="highTemp">${Math.round(hass.states[daily.temphigh].state)}${getUnit('temperature')}</span>
                  <br><span class="lowTemp">${Math.round(hass.states[daily.templow].state)}${getUnit('temperature')}</span>
              </div>`).join('')}
      </div>
      <br><span class="unit">${hass.states[this.config.entity_daily_summary].state}</span></br>`;
  }

  setConfig(config) {
    if (!config.entity_current_conditions || 
    		!config.entity_humidity ||
    		!config.entity_pressure ||
     		!config.entity_temperature ||
    		!config.entity_visibility ||
    		!config.entity_wind_bearing ||
        !config.entity_wind_speed ||
        !config.entity_sun) {
      throw new Error('Please define entities');
    }
    this.config = config;
  }

  // @TODO: This requires more intelligent logic
  getCardSize() {
    return 3;
  }
}

customElements.define('dark-sky-weather-card', DarkSkyWeatherCard);

#258

Ciao @g.nigro

How did you get forecast text in italian?

Thanks


#259

hi, this is my sensors config( i’ve set language: it ):

- platform: darksky
  api_key: xxxxxxxxxxxxxxxxxxxxxxxx
  language: it
  forecast:
    - 1
    - 2
    - 3
    - 4
    - 5
  monitored_conditions:
    - icon
    - summary
    - nearest_storm_distance
    - nearest_storm_bearing
    - humidity
    - temperature
    - temperature_high
    - temperature_low
    - apparent_temperature
    - apparent_temperature_high
    - apparent_temperature_low
    - wind_speed
    - wind_bearing
    - precip_type
    - precip_probability
    - precip_accumulation
    - precip_intensity
    - precip_intensity_max
    - uv_index
    - daily_summary
    - pressure
    - visibility 
    - cloud_cover
    #- moon_phase
  update_interval:
    minutes: 10

#260

So simple, that I haven’t think of it.

Thanks


#261

One more thing.
Even If I have now set language to IT the days are in english.

I’ve looked at dark-sky-weather-card css and js but I haven’t found nothing…

38

Where can I change it?

Thanks again


#262

look at this


#263

@g.nigro

This is my js

class DarkSkyWeatherCard extends HTMLElement {
  set hass(hass) {
    if (!this.content) {
      const card = document.createElement('ha-card');
      const link = document.createElement('link');
      link.type = 'text/css';
      link.rel = 'stylesheet';
      link.href = '/local/custom_ui/dark-sky-weather-card.css';
      card.appendChild(link);
      this.content = document.createElement('div');
      this.content.className = 'card';
      card.appendChild(this.content);
      this.appendChild(card);
    }

    const getUnit = function (measure) {
      const lengthUnit = hass.config.unit_system.length;
      switch (measure) {
        case 'air_pressure':
          return lengthUnit === 'km' ? 'hPa' : 'mbar';
        case 'length':
          return lengthUnit;
        case 'precipitation':
          return lengthUnit === 'km' ? 'mm' : 'in';
        default:
          return hass.config.unit_system[measure] || '';
      }
    };

    const transformDayNight = {
      "below_horizon": "night",
      "above_horizon": "day",
    }
    const sunLocation = transformDayNight[hass.states[this.config.entity_sun].state];
    const weatherIcons = {
      'clear-day': 'day',
      'clear-night': 'night',
      'rain': 'rainy-5',
      'snow': 'snowy-6',
      'sleet': 'rainy-6',
      'wind': 'cloudy',
      'fog': 'cloudy',
      'cloudy': 'cloudy',
      'partly-cloudy-day': 'cloudy-day-3',
      'partly-cloudy-night': 'cloudy-night-3',
      'hail': 'rainy-7',
      'lightning': 'thunder',
      'thunderstorm': 'thunder',
      'windy-variant': `cloudy-${sunLocation}-3`,
      'exceptional': '!!',
    }

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

    var forecastDate1 = new Date();
    forecastDate1.setDate(forecastDate1.getDate()+1);
    var forecastDate2 = new Date();
    forecastDate2.setDate(forecastDate2.getDate()+2);
    var forecastDate3 = new Date();
    forecastDate3.setDate(forecastDate3.getDate()+3);
    var forecastDate4 = new Date();
    forecastDate4.setDate(forecastDate4.getDate()+4);
    var forecastDate5 = new Date();
    forecastDate5.setDate(forecastDate5.getDate()+5);


    const currentConditions = hass.states[this.config.entity_current_conditions].state;
    const humidity = hass.states[this.config.entity_humidity].state;
    const pressure = Math.round(hass.states[this.config.entity_pressure].state);
    const temperature = Math.round(hass.states[this.config.entity_temperature].state);
    const feelstemperature = Math.round(hass.states[this.config.entity_feelstemp].state);
    const visibility = hass.states[this.config.entity_visibility].state;
    const windBearing = windDirections[(Math.round((hass.states[this.config.entity_wind_bearing].state / 360) * 16))];
    const windSpeed = Math.round(hass.states[this.config.entity_wind_speed].state);
    var sunSetOrRiseA = hass.states[this.config.entity_sunup].state;
    var sunSetOrRiseIconA = "mdi:weather-sunset-up";
    var sunSetOrRiseB = hass.states[this.config.entity_sundown].state;
    var sunSetOrRiseIconB = "mdi:weather-sunset-down";
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
        sunSetOrRiseA = hass.states[this.config.entity_sundown].state;
        sunSetOrRiseB = hass.states[this.config.entity_sunup].state;
        sunSetOrRiseIconA = "mdi:weather-sunset-down";
        sunSetOrRiseIconB = "mdi:weather-sunset-up";
    }
    const forecast1 = { date: forecastDate1,
    				   condition: this.config.entity_forecast_icon_1,
    				   temphigh: this.config.entity_forecast_high_temp_1,
    				   templow:  this.config.entity_forecast_low_temp_1,
               pop: this.config.entity_forecast_pop_1, };
    const forecast2 = { date: forecastDate2,
    				   condition: this.config.entity_forecast_icon_2,
    				   temphigh: this.config.entity_forecast_high_temp_2,
    				   templow:  this.config.entity_forecast_low_temp_2,
               pop: this.config.entity_forecast_pop_2, };
    const forecast3 = { date: forecastDate3,
    				   condition: this.config.entity_forecast_icon_3,
    				   temphigh: this.config.entity_forecast_high_temp_3,
    				   templow:  this.config.entity_forecast_low_temp_3,
               pop: this.config.entity_forecast_pop_3, };
    const forecast4 = { date: forecastDate4,
    				   condition: this.config.entity_forecast_icon_4,
    				   temphigh: this.config.entity_forecast_high_temp_4,
    				   templow:  this.config.entity_forecast_low_temp_4,
               pop: this.config.entity_forecast_pop_4, };
    const forecast5 = { date: forecastDate5,
    				   condition: this.config.entity_forecast_icon_5,
    				   temphigh: this.config.entity_forecast_high_temp_5,
    				   templow:  this.config.entity_forecast_low_temp_5,
               pop: this.config.entity_forecast_pop_5, };

    const forecast = [forecast1,forecast2,forecast3,forecast4,forecast5];

    this.content.innerHTML = `
      <div>
        <span class="icon bigger" style="background: none, url(/local/icons/weather_icons/animated/${weatherIcons[currentConditions]}.svg) no-repeat; background-size: contain;">${currentConditions}</span>
        <div class="divtemp">
          <span class="temp">${temperature}<span class="tempc"> ${getUnit('temperature')}</span></span>
          <span class="feelslike">Feels like<span class="feelstemp">${feelstemperature}<span class="feelstempc">${getUnit('temperature')}</span></span></span>
        </div>
      </div>
      <span class="tdaysum">Oggi: ${hass.states[this.config.entity_today_summary].state} <br>Previsione: ${hass.states[this.config.entity_daily_summary].state}</span>
      <span>
        <ul class="variations right">
            <li><span class="ha-icon"><ha-icon icon="mdi:water-percent"></ha-icon></span>${humidity}<span class="unit"> %</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:gauge"></ha-icon></span>${pressure}<span class="unit"> ${getUnit('air_pressure')}</span></li>
  <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconB}></ha-icon></span>${sunSetOrRiseB}</li>
        </ul>
        <ul class="variations">
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-windy"></ha-icon></span>${windBearing} ${windSpeed}<span class="unit"> ${getUnit('length')}/h</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:eye"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('length')}</span></li>
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconA}></ha-icon></span>${sunSetOrRiseA}</li>
        </ul>
      </span>
      <div class="forecast clear">
          ${forecast.map(daily => `
              <div class="day">
                  <span class="dayname">${(daily.date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}</span>
                  <br><i class="icon" style="background: none, url(/local/icons/weather_icons/animated/${weatherIcons[hass.states[daily.condition].state]}.svg) no-repeat; background-size: contain;"></i>
                  <br><span class="highTemp">${Math.round(hass.states[daily.temphigh].state)}${getUnit('temperature')}</span>
                  <br><span class="lowTemp">${Math.round(hass.states[daily.templow].state)}${getUnit('temperature')}</span>
                  <br><span class="highTemp">${Math.round(hass.states[daily.pop].state)}%</span>
              </div>`).join('')}
      </div>
      <span class="threedaysum">
          ${(forecast[0].date).toString().split(' ')[0]}: ${hass.states[this.config.entity_forecast_sum_1].state}<br>
          ${(forecast[1].date).toString().split(' ')[0]}: ${hass.states[this.config.entity_forecast_sum_2].state}<br>
          ${(forecast[2].date).toString().split(' ')[0]}: ${hass.states[this.config.entity_forecast_sum_3].state}<br>
      </span>
      <br></br>`;
  }

  setConfig(config) {
    if (!config.entity_current_conditions ||
    		!config.entity_humidity ||
    		!config.entity_pressure ||
     		!config.entity_temperature ||
    		!config.entity_visibility ||
    		!config.entity_wind_bearing ||
    		!config.entity_wind_speed) {
      throw new Error('Please define entities');
    }
    this.config = config;
  }

  // @TODO: This requires more intelligent logic
  getCardSize() {
    return 3;
  }
}

customElements.define('dark-sky-weather-card', DarkSkyWeatherCard);

And this is my card:

40

Can you help me find where to have the changes to get also these days in italian?

Thanks


#264

sorry, can’t help , some posts above i’ve included my js,
i’m not a programmer… only a normal user ( with a good attitude to copy others code :wink: )


#265

Hi, is there a way to just have the forecast displayed, not the current values?
Background: I have a netatmo station and can display this info via other cards and those infos take up some space on the animated weather card. Something like this: (just a quick mockup):
wetter


#266

HI All,

what I am trying to find out, coming from legacy HA, is how this new animated card for Lovelace can show the state of the weather component in the header.

I thought this line:

  <span class="icon bigger" style="background: none, url(/local/weather/animated/${weatherIcons[currentCondition]}.svg) no-repeat; background-size: contain;"> ${currentCondition}</span>

should take care of that, but it doesn’t. Not sure why the ${currentCondition is there, should it popup hovering the icon (it doesn’t in my setup) ?

Anyways, as @arsaboo shows in his initial post of this thread, this is what I am referring to:

image

mine is now showing like this, and Id love to have the state back in…

30

btw I am using weather Buienradar, and was severely helped by @jusdwy in this thread about the dark sky card Custom Dark Sky Animated Weather Card

Please have a look how to show the entity.state, and for reference sake below is my js file.

class WeatherCard extends HTMLElement {
  set hass(hass) {
    if (!this.content) {
      const card = document.createElement('ha-card');
      const link = document.createElement('link');
      card.header = hass.states[this.config.entity_weather].attributes.friendly_name;
      link.type = 'text/css';
      link.rel = 'stylesheet';
      link.href = '/local/lovelace/weather-card.css';
      card.appendChild(link);
      this.content = document.createElement('div');
      this.content.className = 'card';
      card.appendChild(this.content);
      this.appendChild(card);
    }

    const getUnit = function (measure) {
      const lengthUnit = hass.config.unit_system.length;
      switch (measure) {
        case 'air_pressure':
          return lengthUnit === 'km' ? 'hPa' : 'inHg';
        case 'length':
          return lengthUnit;
        case 'visibility':
          return lengthUnit === 'km' ? 'm' : 'in';
        case 'precipitation':
          return lengthUnit === 'km' ? 'mm' : 'in';
        default:
          return hass.config.unit_system[measure] || '';
      }
    };

    const transformDayNight = {
      "below_horizon": "night",
      "above_horizon": "day",
    }
    const sunLocation = transformDayNight[hass.states[this.config.entity_sun].state];
    const weatherIcons = {
      'clear-night': `${sunLocation}`,
      'cloudy': 'cloudy',
      'fog': 'cloudy',
      'hail': 'rainy-7',
      'lightning': 'thunder',
      'lightning-rainy': 'thunder',
      'partlycloudy': `cloudy-${sunLocation}-3`,
      'pouring': 'rainy-6',
      'rainy': 'rainy-5',
      'snowy': 'snowy-6',
      'snowy-rainy': 'rainy-7',
      'sunny': `${sunLocation}`,
      'windy': 'cloudy',
      'windy-variant': `cloudy-${sunLocation}-3`,
      'exceptional': '!!',
    }

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

    const entity = hass.states[this.config.entity_weather];
    const currentCondition = entity.state;
    const humidity = entity.attributes.humidity;
    const pressure = entity.attributes.pressure;
    const temperature = entity.attributes.temperature;
    const visibility = entity.attributes.visibility;
    const windBearing = windDirections[(parseInt((entity.attributes.wind_bearing + 11.25) / 22.5))];
    const windSpeed = entity.attributes.wind_speed;
    const forecast = entity.attributes.forecast.slice(0, 5);
    const apparent_temperature = hass.states[this.config.entity_apparent_temperature].state;
//    const sunrise = hass.states[this.config.entity_sun].attributes.next_rising;
//    const sunset = hass.states[this.config.entity_sun].attributes.next_setting;

    var sunSetOrRiseA = new Date(hass.states[this.config.entity_sun].attributes.next_setting);
    var sunSetOrRiseIconA = "mdi:weather-sunset-down";
    var sunSetOrRiseB = new Date(hass.states[this.config.entity_sun].attributes.next_rising);
    var sunSetOrRiseIconB = "mdi:weather-sunset-up";
    // A == sunset   B == sunrise
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
        // sun has risen, but hasn't set
        // sunset is today (no date displayed). sunrise is tomorrow (display date)
        // next is sunset == A
        
        sunSetOrRiseA = sunSetOrRiseA.toLocaleTimeString();
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        sunSetOrRiseB = sunSetOrRiseB.toDateString().substr(0,3) + " " + sunSetOrRiseB.toLocaleTimeString();
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
    } else {
        // next is sunrise == B
        var ss = sunSetOrRiseA;
        if ( new Date().getDate() != sunSetOrRiseB.getDate() ) {
            // sun hasn't risen, and it's not same day
            // so display dates for both
            sunSetOrRiseA = sunSetOrRiseB.toDateString().substr(0,3) + " " + sunSetOrRiseB.toLocaleTimeString();
            sunSetOrRiseB = ss.toDateString().substr(0,3) + " " + ss.toLocaleTimeString();
        } else {
            // sun hasn't risen, but it's the same day
            // since rise and set are today, no dates displayed
            sunSetOrRiseA = sunSetOrRiseB.toLocaleTimeString();
            sunSetOrRiseB = ss.toLocaleTimeString();
        }
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
        sunSetOrRiseIconA = "mdi:weather-sunset-up";
        sunSetOrRiseIconB = "mdi:weather-sunset-down";     
    }
    this.content.innerHTML = `
      <span class="icon bigger" style="background: none, url(/local/weather/animated/${weatherIcons[currentCondition]}.svg) no-repeat; background-size: contain;"> ${currentCondition}</span>
      <ul style="list-style-type:none">
        <li><span class="temp">${temperature}</span><span class="tempc"> ${getUnit('temperature')}</span></li>
        <li><span class="tempa">Feels like ${apparent_temperature}</span><span class="unitc"> ${getUnit('temperature')}</span></li>
      </ul>
      <span>
        <ul class="variations right">
            <li><span class="ha-icon"><ha-icon icon="mdi:water-percent"></ha-icon></span>${humidity}<span class="unit"> %</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:gauge"></ha-icon></span>${pressure}<span class="unit"> ${getUnit('air_pressure')}</span></li>
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconB}></ha-icon></span>${sunSetOrRiseB}</li>
        </ul>
        <ul class="variations">
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-windy"></ha-icon></span>${windBearing} ${windSpeed}<span class="unit"> ${getUnit('length')}/h</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-fog"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('visibility')}</span></li>
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconA}></ha-icon></span>${sunSetOrRiseA}</li>
        </ul>
      </span>
      <div class="forecast clear">
          ${forecast.map(daily => `
              <div class="day">
                  <span class="dayname">${(new Date(daily.datetime)).toLocaleDateString((navigator.language) ? navigator.language : navigator.userLanguage, {weekday: 'short'}).split(' ')[0]}</span>
                  <br><i class="icon" style="background: none, url(/local/weather/animated/forecast/${weatherIcons[daily.condition]}.svg) no-repeat; background-size: contain;"></i>
                  <br><span class="highTemp">${daily.temperature}${getUnit('temperature')}</span>
                  <br><span class="lowTemp">${daily.templow}${getUnit('temperature')}</span>
              </div>`).join('')}
      </div>`;
  }

  setConfig(config) {
    if (!config.entity_weather || !config.entity_sun) {
      throw new Error('Please define entities');
    }
    this.config = config;
  }

  // @TODO: This requires more intelligent logic
  getCardSize() {
    return 3;
  }
}

customElements.define('weather-card', WeatherCard);

//            <li><span class="ha-icon"><ha-icon icon="mdi:weather-sunset-up"></ha-icon></span>${sunrise}<span class="unitc"> ${getUnit('sunrise')}</span></li>
//            <li><span class="ha-icon"><ha-icon icon="mdi:weather-sunset-down"></ha-icon></span>${sunset}<span class="unitc"> ${getUnit('sunset')}</span></li>

—edit----

been hacking the css and main .js file a bit further and now have this:

04

well enough for today…


#267

Dear arsaboo,
I followed your instructions but when running it told me that
custom element doesn't exist: weather-card
What’s wrong???
I have no experiment on Lovelace
Many thanks
Minh Chau


#268

Dear arsaboo
Many thanks for your instructions.
I followed but fail.
I don’t know reason why for I am newbee.
Can you or anyone check for me?
I take screenshots of all my configurations layout and failure error message attached in this thread
Many thanks for your attentions
Minh Chau


#269

There is breaking change for dark sky.

  • Darksky: Expose missing conditions for day 0 forecast and append _0 to the day 0 attributes: precip_accumulation , temperature_high , temperature_low , apparent_temperature_high , apparent_temperature_low , precip_intensity_max , moon_phase . (@leppa - #18312) (sensor.darksky docs) (breaking change)

Setting darksky: https://www.home-assistant.io/components/sensor.darksky/

Also in ui-lovelace.yaml for darksky change the existing sensor.dark_sky_overnight_low_temperature to sensor.dark_sky_overnight_low_temperature_1 to 5 accordingly.


#270

Hi Marius,

since I’m also using buienradar (and somehow not so far fromyour location apparently) I’ve tried your js code.
I’m however getting the following error:
71:87 Uncaught TypeError: Cannot read property 'state' of undefined
Any Idea what could be wrong?
Or maybe share your latest js?
Thanks!


#271

sure, here you go.

be ware, I am using an extra sensor for wind_chill, or apparent temperature, set in the config, use the CC sun.sun by @pnbruckner and use card-modder to have a background image:

  - type: custom:card-modder
    card:
      type: custom:weather-card
      entity_weather: weather.woensdrecht
      entity_sun: sun.sun
      entity_apparent_temperature: sensor.jagti_windchill
    style:
      background-image: url("/local/lovelace/images/weather-background-day.png")
      background-size: 100% 400px
      --primary-text-color: 'rgb(76,156,241)'
      --secondary-text-color: red
      --paper-item-icon-color: 'rgb(76,156,241)' #small variation icons

js:

class WeatherCard extends HTMLElement {
  set hass(hass) {
    if (!this.content) {
      const card = document.createElement('ha-card');
      const link = document.createElement('link');
      card.header = hass.states[this.config.entity_weather].attributes.friendly_name;
      link.type = 'text/css';
      link.rel = 'stylesheet';
      link.href = '/local/lovelace/resources/weather-card.css';
      card.appendChild(link);
      this.content = document.createElement('div');
      this.content.className = 'card';
      card.appendChild(this.content);
      this.appendChild(card);
    }

    const getUnit = function (measure) {
      const lengthUnit = hass.config.unit_system.length;
      switch (measure) {
        case 'air_pressure':
          return lengthUnit === 'km' ? 'hPa' : 'inHg';
        case 'length':
          return lengthUnit;
        case 'visibility':
          return lengthUnit === 'km' ? 'm' : 'in';
        case 'precipitation':
          return lengthUnit === 'km' ? 'mm' : 'in';
        default:
          return hass.config.unit_system[measure] || '';
      }
    };

    const transformDayNight = {
      "below_horizon": "night",
      "above_horizon": "day",
    }
    const sunLocation = transformDayNight[hass.states[this.config.entity_sun].state];
    const weatherIcons = {
      'clear-night': `${sunLocation}`,
      'cloudy': 'cloudy',
      'fog': 'cloudy',
      'hail': 'rainy-7',
      'lightning': 'thunder',
      'lightning-rainy': 'thunder',
      'partlycloudy': `cloudy-${sunLocation}-3`,
      'pouring': 'rainy-6',
      'rainy': 'rainy-5',
      'snowy': 'snowy-6',
      'snowy-rainy': 'rainy-7',
      'sunny': `${sunLocation}`,
      'windy': 'cloudy',
      'windy-variant': `cloudy-${sunLocation}-3`,
      'exceptional': '!!',
    }

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

    const entity = hass.states[this.config.entity_weather];
    const currentCondition = entity.state;
    const humidity = entity.attributes.humidity;
    const pressure = entity.attributes.pressure;
    const temperature = entity.attributes.temperature;
    const visibility = entity.attributes.visibility;
    const windBearing = windDirections[(parseInt((entity.attributes.wind_bearing + 11.25) / 22.5))];
    const windSpeed = entity.attributes.wind_speed;
    const forecast = entity.attributes.forecast.slice(0, 5);
    const apparent_temperature = hass.states[this.config.entity_apparent_temperature].state;
//    const sunrise = hass.states[this.config.entity_sun].attributes.next_rising;
//    const sunset = hass.states[this.config.entity_sun].attributes.next_setting;

    var sunSetOrRiseA = new Date(hass.states[this.config.entity_sun].attributes.next_setting);
    var sunSetOrRiseIconA = "mdi:weather-sunset-down";
    var sunSetOrRiseB = new Date(hass.states[this.config.entity_sun].attributes.next_rising);
    var sunSetOrRiseIconB = "mdi:weather-sunset-up";
    // A == sunset   B == sunrise
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
        // sun has risen, but hasn't set
        // sunset is today (no date displayed). sunrise is tomorrow (display date)
        // next is sunset == A
        
        sunSetOrRiseA = sunSetOrRiseA.toLocaleTimeString();
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        sunSetOrRiseB = sunSetOrRiseB.toDateString().substr(0,3) + " " + sunSetOrRiseB.toLocaleTimeString();
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
    } else {
        // next is sunrise == B
        var ss = sunSetOrRiseA;
        if ( new Date().getDate() != sunSetOrRiseB.getDate() ) {
            // sun hasn't risen, and it's not same day
            // so display dates for both
            sunSetOrRiseA = sunSetOrRiseB.toDateString().substr(0,3) + " " + sunSetOrRiseB.toLocaleTimeString();
            sunSetOrRiseB = ss.toDateString().substr(0,3) + " " + ss.toLocaleTimeString();
        } else {
            // sun hasn't risen, but it's the same day
            // since rise and set are today, no dates displayed
            sunSetOrRiseA = sunSetOrRiseB.toLocaleTimeString();
            sunSetOrRiseB = ss.toLocaleTimeString();
        }
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
        sunSetOrRiseIconA = "mdi:weather-sunset-up";
        sunSetOrRiseIconB = "mdi:weather-sunset-down";
    }

// 'background-image: url(\"/local/tiles/family/name_' + state + '.png\")';
    this.content.innerHTML = `
      <span class="icon bigger" style="background: none, url(/local/weather/animated/${weatherIcons[currentCondition]}.svg) no-repeat; background-size: contain;"> </span>
      <ul style="list-style-type:none">
        <li><span class="weather">${currentCondition}</span></li>
        <li><span class="temp">${temperature}</span><span class="tempc"> ${getUnit('temperature')}</span></li>
        <li><span class="tempa">Feels like ${apparent_temperature}</span><span class="unitc"> ${getUnit('temperature')}</span></li>
      </ul>
      <span>
        <ul class="variations right">
            <li><span class="ha-icon"><ha-icon icon="mdi:water-percent"></ha-icon></span>${humidity}<span class="unit"> %</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:gauge"></ha-icon></span>${pressure}<span class="unit"> ${getUnit('air_pressure')}</span></li>
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconB}></ha-icon></span>${sunSetOrRiseB}</li>
        </ul>
        <ul class="variations">
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-windy"></ha-icon></span>${windBearing} ${windSpeed}<span class="unit"> ${getUnit('length')}/h</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-fog"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('visibility')}</span></li>
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconA}></ha-icon></span>${sunSetOrRiseA}</li>
        </ul>
      </span>
      <div class="forecast clear">
          ${forecast.map(daily => `
              <div class="day">
                  <span class="dayname">${(new Date(daily.datetime)).toLocaleDateString((navigator.language) ? navigator.language : navigator.userLanguage, {weekday: 'short'}).split(' ')[0]}</span>
                  <br><i class="icon" style="background: none, url(/local/weather/animated/forecast/${weatherIcons[daily.condition]}.svg);"></i>
                  <br><span class="highTemp">${daily.temperature}${getUnit('temperature')}</span>
                  <br><span class="lowTemp">${daily.templow}${getUnit('temperature')}</span>
              </div>`).join('')}
      </div>`;
  }

  setConfig(config) {
    if (!config.entity_weather || !config.entity_sun) {
      throw new Error('Please define entities');
    }
    this.config = config;
  }

  // @TODO: This requires more intelligent logic
  getCardSize() {
    return 3;
  }
}

customElements.define('weather-card', WeatherCard);

//            <li><span class="ha-icon"><ha-icon icon="mdi:weather-sunset-up"></ha-icon></span>${sunrise}<span class="unitc"> ${getUnit('sunrise')}</span></li>
//            <li><span class="ha-icon"><ha-icon icon="mdi:weather-sunset-down"></ha-icon></span>${sunset}<span class="unitc"> ${getUnit('sunset')}</span></li>

and my css file, of which you should set the file-path in the .js file correctly:

.clear {
    clear: both;
  }

  .card {
    margin: auto;
    padding-top: 2em;
    padding-bottom: 1em;
    padding-left: 1em;
    padding-right:1em;
    position: relative;
  }

  .ha-icon {
    height: 18px;
    margin-right: 5px;
    color: var(--paper-item-icon-color);
  }

  .temp {
    font-weight: 300;
    font-size: 4em;
    color: var(--primary-text-color);
    position: absolute;
    right: 1em;
  }

  .tempa {
    position: absolute;
    color: var(--primary-text-color);
    right: 1em;
    font-weight: 300;
    margin-left: 0;
    margin-right: 2.5em;
    margin-top: 2.7em;
    font-size: .9em;
  }

  .unitc {
    position: absolute;
    color: var(--primary-text-color);
    font-weight: 300;
    right: .6em;
    vertical-align: super;
    margin-left: 0;
    margin-right: 1.7em;
    margin-top: 2.7em;
    font-size: .9em;
  }

  .tempc {
    font-weight: 300;
    font-size: 1.5em;
    vertical-align: super;
    color: var(--primary-text-color);
    position: absolute;
    right: 1em;
    margin-top: -14px;
    margin-right: 7px;
  }

  .variations {
    display: inline-block;
    font-weight: 300;
    color: var(--primary-text-color);
    list-style: none;
    margin-left: -2em;
    margin-top: 4.5em;
  }

  .variations.right {
    position: absolute;
    right: 1em;
    margin-left: 0;
    margin-right: 1em;
  }

  .unit {
    font-size: .8em;
  }

  .forecast {
    width: 100%;
    margin: 0 auto;
    height: 9em;
  }

  .day {
    display: block;
    width: 20%;
    float: left;
    text-align: center;
    color: var(--primary-text-color);
    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(1) {
    border-right: none;
    margin-right: 0;
  }

  .highTemp {
    font-weight: bold;
  }

  .lowTemp {
    color: var(--secondary-text-color);
  }

  .icon.bigger {
    width: 10em;
    height: 10em;
    margin-top: -3.5em;
    position: absolute;
    left: 0em;
  }

  .icon {
    width: 50px;
    height: 50px;
    margin-right: 5px;
    display: inline-block;
    vertical-align: middle;
    background-size: contain;
    background-position: center center;
    background-repeat: no-repeat;
    text-indent: -9999px;
  }

  .weather {
    font-weight: 400;
    font-size: 2em;
    color: var(--secondary-text-color);
    text-align: left;
    position: absolute;
    top: -1.5em;
    right: 0em;
    text-transform: capitalize;
    word-wrap: break-word;
    width: 30%;
  }

template sensor

  jagti_windchill:
    value_template: >
      {% set temp = states('sensor.owm_temperature')|float %}
      {% set wind = states('sensor.owm_wind_speed')|float %}
      {{(13.12 +0.6215*temp  + 0.3965*(temp - 28.676)*(wind**0.16)) | round(2) }}
    unit_of_measurement: '°C'
    device_class: temperature
    friendly_name: Jag/Ti Wchill