mirror of
https://github.com/Anashost/HA-Animated-cards.git
synced 2026-09-26 04:40:11 +00:00
30 KiB
30 KiB
Home Assistant Animated Climate Collection
{\color{#FF0000}Attention!!}Weather Card V3 available HERE
Whats new:
- 7 Day forecast
- hourly forecast
- many new sensors
- translation support
- time and date
- general improvements
Weather Card
This YouTube Video explains how to do it.
Animated Weather Card
type: custom:button-card
variables:
weather_entity:
temp_entity:
feels_like_entity:
cond_entity:
hum_entity:
wind_entity:
wind_gust_entity:
rain_entity:
sun_entity: sun.sun
cond_clear: sunny, sun
cond_clear_night: clear-night, clear night
cond_cloudy: cloudy, cloud, mostly cloudy, overcast
cond_partlycloudy: partlycloudy, partly-cloudy
cond_rainy: rainy, rain, drizzle, pouring, showers
cond_snowy: snowy, snow, sleet
cond_storm: storm, thunderstorm, lightning
cond_fog: fog, mist, hazy
temptrend_show: true
temptrend_entity: sensor.temptrend_24h
tap_action:
action: none
show_name: false
show_icon: false
show_state: false
styles:
card:
- padding: 0
- border-radius: 20px
- box-shadow: 0 12px 30px -10px rgba(0,0,0,0.3), inset 0 1px 1px rgba(255,255,255,0.2)
- border: none
- color: white
- font-family: >-
-apple-system, BlinkMacSystemFont, "SF Pro Display", "Roboto",
sans-serif
- overflow: hidden
- position: relative
- background: "#1e293b"
- isolation: isolate
grid:
- grid-template-areas: "\"content\""
- grid-template-columns: 1fr
- grid-template-rows: 1fr
custom_fields:
content:
- pointer-events: auto !important
- z-index: 99 !important
extra_styles: >
.bg-container { position:absolute; inset:0; z-index:0; width:100%;
height:100%; transition:background 1.2s ease-in-out; }
.effect-layer { position:absolute; inset:0; width:100%; height:100%;
pointer-events:none; z-index:1; overflow:hidden; }
@keyframes rain-fall { from { background-position:0 -200px; } to {
background-position:0 0; } }
@keyframes snowflakes-sliding { 0% { background-position:0% 0%; } 100% {
background-position:0% 100%; } }
@keyframes celestial-breathe { 0% { transform:scale(0.96); opacity:0.95; }
100% { transform:scale(1.04); opacity:1; } }
@keyframes aura-pulse { 0% { transform:scale(1); opacity:0.3; } 100% {
transform:scale(1.4); opacity:0.6; } }
@keyframes cloud-drift-1 { 0% { transform:translateX(450px); opacity:0; } 15%
{ opacity:0.9; } 75% { transform:translateX(180px); opacity:0.9; } 100% {
transform:translateX(110px); opacity:0; } }
@keyframes cloud-drift-2 { 0% { transform:translateX(400px); opacity:0; } 20%
{ opacity:0.5; } 70% { transform:translateX(200px); opacity:0.5; } 100% {
transform:translateX(130px); opacity:0; } }
@keyframes fog-drift { 0% { transform:translateX(-50%) skewX(-15deg); } 100% {
transform:translateX(0%) skewX(-15deg); } }
@keyframes storm-strike { 0%,21%,23%,56%,81%,100% { opacity:0; } 20% {
opacity:1; top:10%; left:10%; transform:scale(1) rotate(-10deg);
filter:brightness(2); } 22% { opacity:0.5; top:10%; left:10%;
transform:scale(1); } 55% { opacity:1; top:10%; left:40%; transform:scale(1.5)
rotate(0deg); } 80% { opacity:1; top:30%; left:70%; transform:scale(0.8)
rotate(10deg); } }
@keyframes room-flash { 0%,21%,23%,56%,81%,100% { opacity:0; } 20% {
opacity:0.4; } 22% { opacity:0.1; } 55% { opacity:0.6; } 80% { opacity:0.3; }
}
@keyframes shine { 0%,85%,100% { opacity:0.2; transform:scale(1);
box-shadow:none; } 92% { opacity:1; transform:scale(1.4); box-shadow:0 0 5px
1px rgba(255,255,255,0.9); } }
.celestial-shared { position:absolute; border-radius:50%; }
.sun-core-day { width:100px; height:100px; top:-20px; right:-20px;
background:radial-gradient(circle,#fff9c4 0%,#ffca28 60%,#ff8f00 100%);
box-shadow:0 0 40px rgba(255,193,7,0.8); animation:celestial-breathe 5s
infinite alternate ease-in-out; }
.sun-aura-day { width:160px; height:160px; top:-50px; right:-50px;
background:radial-gradient(circle,rgba(255,213,79,0.6) 0%,transparent 70%);
filter:blur(10px); animation:aura-pulse 6s infinite alternate ease-in-out; }
.sun-core-twilight { width:100px; height:100px; top:-10px; right:-10px;
background:radial-gradient(circle,#fff3e0 0%,#ffcc80 60%,#ffb74d 100%);
box-shadow:0 0 50px rgba(255,183,77,0.7); animation:celestial-breathe 6s
infinite alternate ease-in-out; }
.sun-aura-twilight { width:160px; height:160px; top:-40px; right:-40px;
background:radial-gradient(circle,rgba(255,183,77,0.5) 0%,transparent 70%);
filter:blur(12px); animation:aura-pulse 7s infinite alternate ease-in-out; }
.moon-core-night { width:90px; height:90px; top:-15px; right:-15px;
background:radial-gradient(circle,#ffffff 0%,#e0e5db 50%,#9ca3af 100%);
box-shadow:0 0 30px rgba(200,220,255,0.6), inset -10px -10px 20px
rgba(0,0,0,0.3); animation:celestial-breathe 6s infinite alternate
ease-in-out; }
.moon-aura-night { width:150px; height:150px; top:-45px; right:-45px;
background:radial-gradient(circle,rgba(200,220,255,0.3) 0%,transparent 70%);
filter:blur(10px); animation:aura-pulse 8s infinite alternate ease-in-out; }
.stars { position:absolute; inset:0; overflow:hidden; pointer-events:none; }
.star-layer { position:absolute; width:1.5px; height:1.5px;
background:transparent; border-radius:50%; box-shadow:12px 45px
rgba(255,255,255,0.8),88px 12px rgba(255,255,255,0.5),145px 80px #fff,210px
25px rgba(255,255,255,0.6),280px 95px rgba(255,255,255,0.9),340px 15px
rgba(255,255,255,0.4),415px 70px #fff,490px 30px rgba(255,255,255,0.7),560px
110px rgba(255,255,255,0.8),65px 120px rgba(255,255,255,0.5),180px 135px
#fff,310px 50px rgba(255,255,255,0.6),450px 140px rgba(255,255,255,0.9); }
.star-layer::after { content:''; position:absolute; width:1px; height:1px;
background:transparent; border-radius:50%; box-shadow:35px 20px
rgba(255,255,255,0.7),105px 65px rgba(255,255,255,0.9),160px 15px
rgba(255,255,255,0.4),235px 115px #fff,295px 35px rgba(255,255,255,0.8),370px
125px rgba(255,255,255,0.5),425px 45px rgba(255,255,255,0.6),485px 95px
#fff,530px 15px rgba(255,255,255,0.9),80px 100px rgba(255,255,255,0.4),195px
60px rgba(255,255,255,0.7),260px 130px #fff,390px 85px
rgba(255,255,255,0.5),510px 125px rgba(255,255,255,0.8); }
.t-star { position:absolute; width:1.5px; height:1.5px; background:white;
border-radius:50%; opacity:0.2; }
.ts-1 { top:18%; left:22%; animation:shine 4.2s infinite; }
.ts-2 { top:35%; left:68%; animation:shine 5.5s infinite 1.8s; }
.ts-3 { top:12%; left:48%; animation:shine 3.6s infinite 2.5s; }
.css-cloud { position:absolute; background:rgba(255,255,255,0.8);
border-radius:50px; filter:drop-shadow(0 8px 16px rgba(0,0,0,0.15)); }
.css-cloud::before, .css-cloud::after { content:''; position:absolute;
background:inherit; border-radius:50%; }
.cloud-twilight { background:rgba(255,220,210,0.85); filter:drop-shadow(0 8px
16px rgba(100,0,0,0.1)); }
.cloud-night { background:rgba(200,210,225,0.4); filter:drop-shadow(0 5px 10px
rgba(0,0,0,0.4)); }
.cloud-shape-1 { width:140px; height:45px; top:40px; animation:cloud-drift-1
16s linear infinite; z-index:2; }
.cloud-shape-1::before { width:70px; height:70px; top:-35px; left:25px; }
.cloud-shape-1::after { width:50px; height:50px; top:-20px; right:20px; }
.cloud-shape-2 { width:105px; height:34px; top:65px; animation:cloud-drift-2
22s linear infinite -10s; z-index:1; }
.cloud-shape-2::before { width:52px; height:52px; top:-26px; left:19px; }
.cloud-shape-2::after { width:37px; height:37px; top:-15px; right:15px; }
.rain-1 { background-image:radial-gradient(ellipse at 30px
20px,rgba(255,255,255,0.6) 1px,transparent 3px),radial-gradient(ellipse at
80px 100px,rgba(255,255,255,0.6) 1px,transparent 3px),radial-gradient(ellipse
at 150px 60px,rgba(255,255,255,0.6) 1px,transparent
3px),radial-gradient(ellipse at 250px 150px,rgba(255,255,255,0.6)
1px,transparent 3px); background-size:300px 200px; animation:rain-fall 0.6s
linear infinite; opacity:0.8; }
.rain-2 { background-image:radial-gradient(ellipse at 10px
50px,rgba(255,255,255,0.4) 1px,transparent 2px),radial-gradient(ellipse at
60px 120px,rgba(255,255,255,0.4) 1px,transparent 2px),radial-gradient(ellipse
at 120px 30px,rgba(255,255,255,0.4) 1px,transparent
2px),radial-gradient(ellipse at 200px 90px,rgba(255,255,255,0.4)
1px,transparent 2px); background-size:300px 180px; animation:rain-fall 1.2s
linear infinite; opacity:0.5; }
.snow-1 { height:200%; background-image:radial-gradient(circle at 20%
10%,rgba(255,255,255,0.4) 0%,transparent 6px),radial-gradient(circle at 80%
30%,rgba(255,255,255,0.4) 0%,transparent 8px),radial-gradient(circle at 40%
50%,rgba(255,255,255,0.4) 0%,transparent 6px),radial-gradient(circle at 70%
75%,rgba(255,255,255,0.4) 0%,transparent 7px),radial-gradient(circle at 10%
90%,rgba(255,255,255,0.4) 0%,transparent 5px),radial-gradient(circle at 60%
15%,rgba(255,255,255,0.4) 0%,transparent 6px); background-size:100% 50%;
animation:snowflakes-sliding 2s linear infinite; opacity:0.8; }
.snow-2 { height:200%; background-image:radial-gradient(circle at 15%
5%,rgba(255,255,255,0.3) 0%,transparent 4px),radial-gradient(circle at 55%
25%,rgba(255,255,255,0.3) 0%,transparent 3px),radial-gradient(circle at 85%
45%,rgba(255,255,255,0.3) 0%,transparent 4px),radial-gradient(circle at 25%
65%,rgba(255,255,255,0.3) 0%,transparent 3px),radial-gradient(circle at 65%
85%,rgba(255,255,255,0.3) 0%,transparent 4px); background-size:100% 50%;
animation:snowflakes-sliding 1s linear infinite; opacity:0.6; }
.fog-band { position:absolute; width:200%; height:40px;
background:linear-gradient(to
right,transparent,rgba(255,255,255,0.7),transparent); filter:blur(10px); }
.fog-1 { top:40px; animation:fog-drift 18s linear infinite alternate; }
.fog-2 { top:100px; opacity:0.6; animation:fog-drift 28s linear infinite
alternate-reverse; width:250%; }
.storm-flash { background:white; animation:room-flash 7s steps(1) infinite;
mix-blend-mode:overlay; }
.storm-bolt { width:80px; height:140px; background:#e0f2fe;
clip-path:polygon(55% 0%,80% 0%,50% 45%,85% 45%,20% 100%,40% 55%,10% 55%);
filter:drop-shadow(0 0 15px rgba(255,255,255,0.9)); animation:storm-strike 6s
steps(1) infinite; }
.card-content { position:relative; z-index:5; padding:12px 16px; display:flex;
flex-direction:column; width:100%; box-sizing:border-box; }
.top-row { display:flex; justify-content:space-between; align-items:center;
width:100%; }
.glass-metric { pointer-events:auto; display:flex; align-items:center;
background:rgba(0,0,0,0.15); backdrop-filter:blur(20px) saturate(180%);
-webkit-backdrop-filter:blur(20px) saturate(180%); border:1px solid
rgba(255,255,255,0.2); border-radius:12px; padding:4px 8px; font-size:11px;
font-weight:600; color:white; text-shadow:0 1px 2px rgba(0,0,0,0.4);
box-shadow:0 2px 10px rgba(0,0,0,0.05); letter-spacing:0.2px;
white-space:nowrap; transition:all 0.2s ease; cursor:pointer; }
.glass-metric:hover, .glass-metric:active { background:rgba(0,0,0,0.25);
transform:translateY(-1px); }
.glass-metric ha-icon { opacity:0.9; width:14px; height:14px;
margin-right:4px; filter:drop-shadow(0 1px 1px rgba(0,0,0,0.3)); }
.hover-text-block { pointer-events:auto; display:inline-flex;
align-items:center; cursor:pointer; border-radius:8px; padding:2px 6px;
margin-left:-6px; transition:background 0.2s ease; }
.hover-text-block:hover, .hover-text-block:active {
background:rgba(255,255,255,0.15); }
.temptrend-container { width:100%; margin-top:2px; padding-top:8px;
border-top:0px solid rgba(255,255,255,0.1); }
.temptrend-bar { width:100%; height:6px; border-radius:4px; opacity:0.9;
box-shadow:inset 0 1px 2px rgba(0,0,0,0.4); }
custom_fields:
content: |
[[[
const wE = variables.weather_entity, tE = variables.temp_entity, cE = variables.cond_entity;
const wV = wE && states[wE], tV = tE && states[tE], cV = cE && states[cE];
if (!wV && !tV && !cV) {
return `
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;
padding:24px;text-align:center;box-sizing:border-box;width:100%;min-height:140px;
border-radius:20px;background:#1e293b;">
<ha-icon icon="mdi:cog" style="color:#38bdf8;width:36px;height:36px;
margin-bottom:12px;filter:drop-shadow(0 2px 4px rgba(0,0,0,0.4));"></ha-icon>
<span style="font-size:16px;font-weight:600;margin-bottom:6px;color:white;">
Setup Required
</span>
<span style="font-size:12px;color:rgba(255,255,255,0.7);line-height:1.4;">
Provide a valid <b>weather_entity</b>,<br>or both <b>temp_entity</b> and <b>cond_entity</b>.
</span>
</div>`;
}
const wS = wV ? states[wE].state : null;
const tS = tV ? states[tE].state : null;
const cS = cV ? states[cE].state : null;
const clk = e => e ? `ontouchstart="this.sX=event.touches[0].clientX;this.sY=event.touches[0].clientY;"
ontouchend="if(Math.abs(event.changedTouches[0].clientX-this.sX)<15&&Math.abs(event.changedTouches[0].clientY-this.sY)<15)this.dispatchEvent(new CustomEvent('hass-more-info',{bubbles:true,composed:true,detail:{entityId:'${e}'}}));"
onclick="this.dispatchEvent(new CustomEvent('hass-more-info',{bubbles:true,composed:true,detail:{entityId:'${e}'}}));"` : '';
const wTemp = v => `
<span style="font-size:38px;font-weight:400;line-height:1;letter-spacing:-1px;
text-shadow:0 1px 3px rgba(0,0,0,0.3);display:flex;align-items:center;">${v}</span>`;
const wCond = v => `
<span style="font-size:19px;font-weight:600;color:rgba(255,255,255,0.98);
text-shadow:0 1px 3px rgba(0,0,0,0.3);letter-spacing:0.3px;">${v}</span>`;
const tBdg = `
<div style="display:flex;align-items:center;justify-content:center;
background:rgba(239,68,68,0.15);padding:4px 12px;border-radius:12px;
border:1px solid rgba(239,68,68,0.3);height:32px;box-sizing:border-box;">
<ha-icon icon="mdi:thermometer-off" style="width:20px;height:20px;
margin-right:6px;color:#fca5a5;display:block;"></ha-icon>
<span style="font-size:20px;font-weight:600;color:#fca5a5;
line-height:1;display:block;">-</span>
</div>`;
let tH = tBdg;
if (tV) {
if (tS === 'unavailable') tH = wTemp('-');
else if (tS !== 'unknown') tH = wTemp(Math.round(tS) + '°');
} else if (wV) {
if (wS === 'unavailable') tH = wTemp('-');
else if (states[wE].attributes?.temperature !== undefined)
tH = wTemp(Math.round(states[wE].attributes.temperature) + '°');
}
const cBdg = `
<div style="display:flex;align-items:center;justify-content:center;
background:rgba(239,68,68,0.15);padding:4px 10px;border-radius:10px;
border:1px solid rgba(239,68,68,0.3);height:26px;box-sizing:border-box;">
<ha-icon icon="mdi:cloud-question" style="width:16px;height:16px;
margin-right:6px;color:#fca5a5;display:block;"></ha-icon>
<span style="font-size:16px;font-weight:600;color:#fca5a5;
line-height:1;display:block;">-</span>
</div>`;
let rC = '', cH = cBdg;
if (cV) {
if (cS === 'unavailable') cH = wCond('-');
else if (cS !== 'unknown') rC = cS.toLowerCase();
} else if (wV) {
if (wS === 'unavailable') cH = wCond('-');
else if (wS !== 'unknown') rC = wS.toLowerCase();
}
if (rC && !isNaN(Number(rC))) {
rC = '';
cH = cBdg;
} else if (rC) {
const fmtC = rC.replace(/[-_]/g, ' ').replace(/partlycloudy/gi, 'partly cloudy')
.split(' ').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
cH = wCond(fmtC);
}
const fE = variables.feels_like_entity, fV = fE && states[fE];
const fS = fV ? states[fE].state : null;
let fH = '';
if (fV) {
if (fS === 'unavailable') fH = `
<div class="hover-text-block" style="margin-left:2px;">
<span style="font-size:14px;font-weight:500;opacity:0.95;">Feels like -</span>
</div>`;
else if (fS !== 'unknown') fH = `
<div class="hover-text-block" style="margin-left:2px;" ${clk(fE)}>
<span style="font-size:14px;font-weight:500;opacity:0.95;">Feels like ${Math.round(fS)}°</span>
</div>`;
} else if (wV) {
if (wS === 'unavailable') fH = `
<div class="hover-text-block" style="margin-left:2px;">
<span style="font-size:14px;font-weight:500;opacity:0.95;">Feels like -</span>
</div>`;
else if (states[wE].attributes?.apparent_temperature !== undefined) fH = `
<div class="hover-text-block" style="margin-left:2px;" ${clk(wE)}>
<span style="font-size:14px;font-weight:500;opacity:0.95;">
Feels like ${Math.round(states[wE].attributes.apparent_temperature)}°
</span>
</div>`;
}
const bM = (e, a, uA, ic, p) => {
const eV = e && states[e], eS = eV ? states[e].state : null;
if (eV) {
if (eS === 'unavailable') return `<div class="glass-metric"><ha-icon icon="${ic}"></ha-icon> -</div>`;
if (eS !== 'unknown') {
const u = p ? '%' : ' ' + (states[e].attributes?.unit_of_measurement || '');
return `<div class="glass-metric" ${clk(e)}><ha-icon icon="${ic}"></ha-icon> ${Math.round(eS)}${u}</div>`;
}
} else if (wV) {
if (wS === 'unavailable') return `<div class="glass-metric"><ha-icon icon="${ic}"></ha-icon> -</div>`;
const val = states[wE].attributes?.[a];
if (val !== undefined) {
const u = p ? '%' : ' ' + (states[wE].attributes?.[uA] || '');
return `<div class="glass-metric" ${clk(wE)}><ha-icon icon="${ic}"></ha-icon> ${Math.round(val)}${u}</div>`;
}
}
return '';
};
const mL = [
[variables.hum_entity, 'humidity', '', 'mdi:water-percent', true],
[variables.wind_entity, 'wind_speed', 'wind_speed_unit', 'mdi:weather-windy', false],
[variables.wind_gust_entity, 'wind_gust', 'wind_speed_unit', 'mdi:weather-windy-variant', false],
[variables.rain_entity, 'precipitation', 'precipitation_unit', 'mdi:weather-pouring', false]
];
const mH = mL.map(m => bM(...m)).join('');
let cK = 'unknown';
const cM = {
'clear': variables.cond_clear, 'clear-night': variables.cond_clear_night,
'cloudy': variables.cond_cloudy, 'partlycloudy': variables.cond_partlycloudy,
'rainy': variables.cond_rainy, 'snowy': variables.cond_snowy,
'storm': variables.cond_storm, 'fog': variables.cond_fog
};
if (rC) {
for (const [k, v] of Object.entries(cM)) {
if (v?.split(',').map(s => s.trim().toLowerCase()).includes(rC)) { cK = k; break; }
}
}
let tD = 'day';
const sE = variables.sun_entity, el = sE && states[sE]?.attributes?.elevation;
if (el !== undefined) {
tD = el < -4 ? 'night' : (el <= 10 ? (new Date().getHours() < 12 ? 'sunrise' : 'sunset') : 'day');
} else if (cK.includes('night')) {
tD = 'night';
}
if (tD !== 'night' && cK.includes('night')) cK = cK.replace('-night', '').replace('night', 'clear');
else if (tD === 'night' && cK === 'sunny') cK = 'clear-night';
const bG = {
sunny: { day: ['#29b6f6', '#0288d1'], twilight: ['#7986cb', '#e1bee7', '#ffe0b2'], night: ['#080c16', '#162032'] },
partly: { day: ['#4fc3f7', '#1976d2'], twilight: ['#5c6bc0', '#ce93d8', '#ffccbc'], night: ['#111827', '#1e293b'] },
cloudy: { day: ['#607d8b', '#455a64'], twilight: ['#455a64', '#78909c', '#ffebee'], night: ['#1f2937', '#374151'] },
rain: { day: ['#2c3e50', '#4ca1af'], twilight: ['#3949ab', '#7e57c2', '#ffe0b2'], night: ['#0f172a', '#1e293b'] },
snow: { day: ['#94a3b8', '#64748b'], twilight: ['#8b5cf6', '#d8b4fe', '#94a3b8'], night: ['#1e293b', '#0f172a'] },
storm: { day: ['#64748b', '#334155'], twilight: ['#2b285b', '#1e1b4b', '#0f172a'], night: ['#0f172a', '#1e293b'] },
fog: { day: ['#9ca3af', '#6b7280'], twilight: ['#8b5cf6', '#c084fc', '#9ca3af'], night: ['#1f2937', '#111827'] },
unknown: { day: ['#475569', '#334155'], twilight: ['#334155', '#1e293b', '#0f172a'], night: ['#0f172a', '#020617'] }
};
const sh = {
rain: { day: 'inset 0 0 20px rgba(0,0,0,0.3)', night: 'inset 0 0 30px rgba(0,0,0,0.6)' },
snow: { day: 'inset 0 0 50px rgba(200,220,255,0.2)', twilight: 'inset 0 0 30px rgba(200,220,255,0.2)', night: 'inset 0 0 40px rgba(200,220,255,0.1)' },
storm: { day: 'inset 0 0 50px rgba(0,0,0,0.7)', twilight: 'inset 0 0 50px rgba(0,0,0,0.8)', night: 'inset 0 0 50px rgba(0,0,0,0.9)' }
};
let cat = 'sunny', eL = '';
const sH = tD === 'night' ? `<div class="stars"><div class="star-layer"></div>
<div class="t-star ts-1"></div><div class="t-star ts-2"></div><div class="t-star ts-3"></div></div>` : '';
const cC = tD === 'night' ? 'cloud-night' : (tD !== 'day' ? 'cloud-twilight' : '');
const sC = tD !== 'day' ? 'twilight' : 'day';
if (cK === 'unknown') {
cat = 'unknown';
} else if (cK.includes('storm') || cK.includes('thunder')) {
cat = 'storm';
eL = `<div class="effect-layer storm-flash"></div><div class="effect-layer storm-bolt"></div>`;
} else if (cK.includes('snow') || cK.includes('hail')) {
cat = 'snow';
eL = `<div class="effect-layer snow-1"></div><div class="effect-layer snow-2"></div>`;
} else if (cK.includes('rain') || cK.includes('pour')) {
cat = 'rain';
eL = `<div class="effect-layer rain-1"></div><div class="effect-layer rain-2"></div>`;
} else if (cK.includes('partly')) {
cat = 'partly';
eL = tD === 'night'
? `${sH}<div class="effect-layer"><div class="moon-aura-night celestial-shared"></div>
<div class="moon-core-night celestial-shared"></div><div class="css-cloud ${cC} cloud-shape-1"></div></div>`
: `<div class="effect-layer"><div class="sun-aura-${sC} celestial-shared"></div>
<div class="sun-core-${sC} celestial-shared"></div><div class="css-cloud ${cC} cloud-shape-1"></div></div>`;
} else if (cK.includes('cloud')) {
cat = 'cloudy';
eL = `${sH}<div class="effect-layer"><div class="css-cloud ${cC} cloud-shape-1"></div>
<div class="css-cloud ${cC} cloud-shape-2"></div></div>`;
} else if (cK.includes('fog')) {
cat = 'fog';
eL = `${sH}<div class="effect-layer"><div class="fog-band fog-1"></div><div class="fog-band fog-2"></div></div>`;
} else {
cat = 'sunny';
eL = tD === 'night'
? `${sH}<div class="effect-layer"><div class="moon-aura-night celestial-shared"></div>
<div class="moon-core-night celestial-shared"></div></div>`
: `<div class="effect-layer"><div class="sun-aura-${sC} celestial-shared"></div>
<div class="sun-core-${sC} celestial-shared"></div></div>`;
}
const tK = tD === 'night' ? 'night' : (tD !== 'day' ? 'twilight' : 'day');
const aC = (bG[cat] || bG.sunny)[tK].join(', ');
const aS = sh[cat]?.[tK] || 'none';
const bgS = `background:linear-gradient(to bottom, ${aC});box-shadow:${aS};`;
let trH = '';
if (variables.temptrend_show && variables.temptrend_entity) {
const trE = variables.temptrend_entity;
const hs = states[trE]?.attributes?.history?.map(Number);
if (hs?.length > 1) {
const isF = (tV && states[tE].attributes?.unit_of_measurement?.includes('F')) ||
(wV && states[wE].attributes?.temperature_unit?.includes('F'));
const minSp = isF ? 16.2 : 9;
const mn = Math.min(...hs), mx = Math.max(...hs);
let sp = mx - mn, bs = mn;
if (sp < minSp) { bs = ((mx + mn) / 2) - (minSp / 2); sp = minSp; }
const st = hs.map((t, i) => {
const p = Math.max(0, Math.min(1, (t - bs) / (sp || 1)));
const sg = Math.min(4, Math.floor(p * 5)), lP = (p * 5) - sg;
let r = 0, g = 0, b = 0;
if (sg === 0) { g = Math.round(50 + lP * 205); b = 255; }
else if (sg === 1) { g = 255; b = Math.round(255 - lP * 255); }
else if (sg === 2) { r = Math.round(lP * 255); g = 255; }
else if (sg === 3) { r = 255; g = Math.round(255 - lP * 127); }
else { r = 255; g = Math.round(128 - lP * 128); }
return `rgb(${r},${g},${b}) ${(i / (hs.length - 1)) * 100}%`;
});
trH = `
<div class="temptrend-container">
<div class="temptrend-bar" style="background:linear-gradient(to right, ${st.join(', ')});"></div>
</div>`;
}
}
return `
<div class="bg-container" style="${bgS}"></div>
${eL}
<div class="card-content">
<div class="top-row">
<div style="display:flex;flex-direction:column;align-items:flex-start;width:100%;">
<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
<div class="hover-text-block" style="display:flex;align-items:center;" ${clk(tE || wE)}>
${tH}
</div>
${fH}
</div>
<div class="hover-text-block" style="margin-top:4px;" ${clk(cE || wE)}>
${cH}
</div>
<div style="display:flex;gap:6px;margin-top:8px;flex-wrap:wrap;">
${mH}
</div>
</div>
</div>
${trH}
</div>
`;
]]]
Template (updated August 29th 2026)
-
whats new: fallback to last known history value if sensor is unavailable or when the weather integration goes unavailable temporarily.
-
Replace
sensor.openweathermap_temperaturewith your ownweather.xxorsensor.xx temperature, in both places.
- trigger:
- platform: time_pattern
minutes: "/1"
- platform: homeassistant
event: start
condition: >
{{ (now().timestamp() - state_attr('sensor.temptrend_24h', 'last_update') | default(0, true) | float(0)) >= 3600 }}
sensor:
- name: "Temptrend 24h"
unique_id: temptrend_24h
state: >
{% set target = 'sensor.openweathermap_temperature' %}
{% set raw_val = state_attr(target, 'temperature') if target.startswith('weather.') else states(target) %}
{% if is_number(raw_val) %}
{{ raw_val | float | round(1) }}
{% else %}
{{ this.state | default(0, true) }}
{% endif %}
attributes:
last_update: "{{ now().timestamp() }}"
history: >
{% set target = 'sensor.openweathermap_temperature' %}
{% set raw_val = state_attr(target, 'temperature') if target.startswith('weather.') else states(target) %}
{% set past = state_attr('sensor.temptrend_24h', 'history') %}
{% if is_number(raw_val) %}
{% set current = raw_val | float | round(1) %}
{% else %}
{% set current = past[-1] if past is not none and past | length > 0 else 0 %}
{% endif %}
{% if past is none or past | length == 0 %}
{{ [current] * 24 }}
{% else %}
{{ (past + [current])[-24:] }}
{% endif %}
Note
More Climate Cards are coming soon. They're currently available to members for Testing, Patreon and YouTube.