I would love to have this too, the current options for displaying weather (listing sensors) aren’t that great. Any update?
I would also love this feature!!
this would be awesome to implement!
would like to add this.
is there a update or something on it?
I would love this one too! Is there some progress jet?
Thanks for picking that up! Have very limited time at the moment…
I actually misread the thread and thought you had updated the weather state card to be this animated forecast by default; not the ability to do so
Can you link to the PR please…
What is the source of data? Can you share the config please?
Could something like this be implemented as an iframe that references the weather card generated elsewhere? For instance creating the necessary card with HTML and javascript residing in the /www folder… the using the iframe custom component card ( I saw one made here somewhere…) and the viola animated weather?
Is this possible to implement it as a secondary UI card? As per this manual? https://github.com/andrey-git/home-assistant-custom-ui/blob/master/docs/features.md
well custom cards won’t be merged until the frontend restructure.
here is a modified version to run as state-card
\\HASSIO\config\www\weather_icons\animated\*
: https://www.amcharts.com/free-animated-svg-weather-icons/
\\HASSIO\config\www\custom_ui\custom-weather-card.html
:
<dom-module id='custom-weather-card'>
<template>
<style>
.clear {
clear:both;
}
.card {
margin:1em auto;
padding-left: 1em;
padding-right:1em;
position: relative;
}
iron-icon {
height: 20px;
color: #c8c8c8;
}
.temp {
font-weight: 300;
font-size: 5em;
color:#5b5b5b;
position: absolute;
right: .3em;
}
.variations {
font-weight:300;
color:#8c8c8c;
list-style:none;
margin-left:-2em;
margin-top: 3.5em;
}
.variations.right {
float: right;
margin-left: 0;
margin-right: 1em;
}
.unit {
font-size:.8em;
}
.forecast {
width:100%;
margin:0 auto;
height:9em;
}
.day {
display:block;
width: 25%;
float:left;
text-align:center;
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) {
border-right:none;
margin-right: 0;
}
.highTemp {
font-weight:bold;
}
.lowTemp {
color: #8c8c8c;
}
.icon.bigger {
width: 10em;
height: 10em;
margin-top: -4em;
position: absolute;
}
.icon {
width: 34px;
height: 34px;
display: inline-block;
vertical-align: middle;
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
text-indent: -9999px;
}
</style>
<div class="card">
<span class="city"></span>
<br>
<i class="icon bigger" style="background: none, url(/local/weather_icons/animated/[[nowCond]].svg) no-repeat; background-size: contain;"></i>
<span class="temp">[[roundedTemp]]°</span>
<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>
<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>
<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>
</ul>
<ul class="variations">
<li>[[weatherObj.state]]</li>
<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>
</ul>
</span>
<div class="forecast clear">
<template is="dom-repeat" items="[[forecast]]">
<div class="day"><span class="dayname">[[item.day]]</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>
<template is="dom-if" if="[[item.tempHigh]]">
<br> <span class="highTemp">[[item.tempHigh]]°</span>
</template>
<template is="dom-if" if="[[item.tempLow]]">
<br> <span class="lowTemp">[[item.tempLow]]°</span>
</template>
</div>
</template>
</div>
</div>
</template>
</dom-module>
<script>
(function () {
'use strict';
var _WEATHER_TO_ICON = {
cloudy: 'cloudy',
fog: 'cloudy',
hail: 'rainy-7',
lightning: 'lightning',
'lightning-rainy': 'lightning',
'Zwaar bewolkt': 'cloudy',
partlycloudy: 'cloudy-day-2',
pouring: 'rainy-7',
rainy: 'rainy-5',
snowy: 'snowy-5',
'snowy-rainy': 'snowy-6',
sunny: 'day',
windy: 'cloudy',
'windy-variant': '[]',
exceptional: '!!',
};
var _DEGREE_TEXT = [
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'
];
Polymer({
is: 'custom-weather-card',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
},
weatherObj: {
type: Object,
observer: 'checkRequirements',
computed: 'comptuteWeatherObj(hass, stateObj)',
},
},
comptuteWeatherObj: function (hass, stateObj) {
return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.weather ? hass.states[stateObj.attributes.config.weather] : 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;
}
var tempLow = Math.round(data[i].min_temp * 10) / 10;
var condIcon = _WEATHER_TO_ICON[data[i].condition];
forecast.push({day:day, 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) {
return;
}
this.nowCond = _WEATHER_TO_ICON[this.weatherObj.state];
if (this.weatherObj.attributes.temperature) {
this.roundedTemp = Math.round( this.weatherObj.attributes.temperature * 10) / 10;
}
if (this.weatherObj.attributes.windBearing) {
this.windBearing = this.windBearingToText(this.weatherObj.attributes.windBearing);
}
this.forecast = this.getForecastArray().slice(0, 4);
},
windBearingToText: function (degree) {
return _DEGREE_TEXT[((parseInt(degree) + 5.63) / 11.25) | 0];
},
});
}());
</script>
configuration.yaml
homeassistant:
customize:
input_boolean.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_boolean:
weather:
group:
weather:
name: Weather
entities:
- input_boolean.weather
config:
discovery:
Thank you so much for sharing that state card Eddi89 !!
Following your instructions, I had to correct a few things in the code of the state-card to make it work nicely, so I’ll add here if someone else need:
The lightning icon does not exist in the icon pack linked in the post so replace lightning to thunder for the icon name (not for the variable):
lightning: 'thunder',
'lightning-rainy': 'thunder',
-the ‘data[i].min_temp’ must be replaced by ‘data[i].templow’
-the ‘data[i].tempLow’ must be replaced by ‘data[i].templow’
And then it all work well.
Thanks again!
Thank you so much for this awesome state card, this should be the ha default weather card.
I made a change to add the possibility to change the language, just need to change the name in the mapping function inside the script.
I did also a small layout to improve the information display.
The result is this:
I have no java or html skill so i don’t know if this is the best way to do it, but work so i’m happy with it.
Thank again for sharing this.
I post the code in case someone need it or want to make the same change in a better way:
<dom-module id='custom-weather-card'>
<template>
<style>
.clear {
clear:both;
}
.card {
margin:1em auto;
padding-left: 1em;
padding-right:1em;
position: relative;
}
.iron-icon {
height: 18px;
color: #c8c8c8;
}
.temp {
font-weight: 300;
font-size: 5em;
color:#5b5b5b;
position: absolute;
right: 0em;
}
.variations {
font-weight:300;
color:#8c8c8c;
list-style:none;
margin-left:-2em;
margin-top: 2.5em;
}
.variations.right {
float: right;
margin-left: 0;
margin-right: 1em;
}
.unit {
font-size:.8em;
}
.forecast {
width:100%;
margin:0 auto;
height:9em;
}
.day {
display:block;
width: 25%;
float:left;
text-align:center;
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) {
border-right:none;
margin-right: 0;
}
.highTemp {
font-weight:bold;
}
.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;
color:#5b5b5b;
text-align:center;
position: absolute;
top: 0em;
left: 6em;
}
</style>
<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="weather">[[nowCondIT]]</span>
<br>
<span>
<ul class="variations right">
<template is="dom-if" if="[[weatherObj.attributes.humidity]]">
<li><iron-icon icon="mdi:water-percent"></iron-icon> Umidità [[weatherObj.attributes.humidity]]<span class="unit">%</span></li>
</template>
<template is="dom-if" if="[[weatherObj.attributes.pressure]]">
<li><iron-icon icon="mdi:gauge"></iron-icon> Pressione [[weatherObj.attributes.pressure]]<span class="unit">hPa</span></li>
</template>
</ul>
<ul class="variations">
<template is="dom-if" if="[[weatherObj.attributes.wind_speed]]">
<li><iron-icon icon="mdi:weather-windy"></iron-icon> Vento [[item.windBearing]] [[weatherObj.attributes.wind_speed]]<span class="unit">m/s</span></li>
</template>
<template is="dom-if" if="[[weatherObj.attributes.visibility]]">
<li><iron-icon icon="mdi:weather-fog"></iron-icon> Visibilità [[weatherObj.attributes.visibility]]<span class="unit">m</span></li>
</template>
</ul>
</span>
<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>
<template is="dom-if" if="[[item.tempHigh]]">
<br> <span class="highTemp">[[item.tempHigh]]°</span>
</template>
<template is="dom-if" if="[[item.tempLow]]">
<br> <span class="lowTemp">[[item.tempLow]]°</span>
</template>
</div>
</template>
</div>
</div>
</template>
</dom-module>
<script>
(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: 'Nuvoloso',
fog: 'Nebbia',
hail: 'Grandine',
lightning: 'Temporale',
'lightning-rainy': 'Temporale',
partlycloudy: 'Parzialmente Nuvoloso',
pouring: 'Pioggia Forte',
rainy: 'Pioggia',
snowy: 'Neve',
'snowy-rainy': 'Nevischio',
sunny: 'Soleggiato',
windy: 'Ventoso',
'windy-variant': 'Variabile Ventoso',
exceptional: '!',
};
var _DEGREE_TEXT = [
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SO', 'OSO', 'O', 'ONO', 'NO', 'NNO', 'N',
];
var _DAY_TO_DAY = {
Mon: 'lunedì',
Tue: 'martedì',
Wed : 'mercoledì',
Thu: 'giovedì',
Fri: 'venerdì',
Sat: 'sabato',
Sun: 'domemica',
};
Polymer({
is: 'custom-weather-card',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
},
weatherObj: {
type: Object,
observer: 'checkRequirements',
computed: 'comptuteWeatherObj(hass, stateObj)',
},
},
comptuteWeatherObj: function (hass, stateObj) {
return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.weather ? hass.states[stateObj.attributes.config.weather] : 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;
}
var tempLow = Math.round(data[i].templow * 10) / 10;
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) {
return;
}
this.nowCond = _WEATHER_TO_ICON[this.weatherObj.state];
this.nowCondIT = _WEATHER_TO_NAME[this.weatherObj.state];
if (this.weatherObj.attributes.temperature) {
this.roundedTemp = Math.round( this.weatherObj.attributes.temperature * 10) / 10;
}
if (this.weatherObj.attributes.windBearing) {
this.windBearing = this.windBearingToText(this.weatherObj.attributes.windBearing);
}
this.forecast = this.getForecastArray().slice(0, 4);
},
windBearingToText: function (degree) {
return _DEGREE_TEXT[((parseInt(degree) + 5.63) / 11.25) | 0];
},
});
}());
</script>
I followed all of the config but just getting a blank card. Any ideas what I missed? I’m using Safari on both a Mac and iOS. My other custom_ui files work ok. But I usually have a different es5 version. Could that be it?
Try to remove all unnecessary lines in the weather component
weather:
- platform: yweather
refresh your browser cache
I found out it’s Yahoo Weather which isn’t properly getting my location without setting the WOEID.