Muss ich mal paar Stunden finden und es mir anschauen..
Aber danke fΓΌr die Info
1 βGefΓ€llt mirβ
1 βGefΓ€llt mirβ
Hi, das ist kein Theme.
Sind alles Custom Button-Cards.
1 βGefΓ€llt mirβ
Ohhh Das klinkt nach richtig viel Arbeit
Gibt es eine MΓΆglichkeit dein Dashboard zu teilen?
1 βGefΓ€llt mirβ
Er bietet einzelne Bereiche an aber alles Sprengt den Rahmen ![]()
1 βGefΓ€llt mirβ
OK
Das habe ich glaube ΓΌberlesen
mich wΓΌrde speziel der Fahrzeugstatus und Waschmaschine interessieren
Okay, ich geb dir mal die Waschmaschine.
Probiere es aus.
type: custom:button-card
entity: sensor.washer_program_de
triggers_update:
- sensor.time
- sensor.waschmaschine_programm_endzeit
- sensor.waschmaschine_betriebszustand
show_name: false
show_icon: false
show_state: false
variables:
v_program: sensor.washer_program_de
v_mode: sensor.waschmaschine_betriebszustand
v_endtime: sensor.waschmaschine_programm_endzeit
v_status: sensor.waschmaschine_status_de
v_connected: binary_sensor.waschmaschine_konnektivitat
v_idos1: sensor.waschmaschine_niedriger_fullstand_von_i_dos_1
v_idos2: sensor.waschmaschine_niedriger_fullstand_von_i_dos_2
v_power: sensor.hwr_waschmaschine_leistung
v_energy: sensor.hwr_waschmaschine_energiezahler_gesamt
v_start: input_datetime.waschmaschine_startzeit
v_bild: /local/pictures/Waschmaschine.png
v_name: Waschmaschine
v_energy_offset: 134.391
v_bar_gradient: linear-gradient(90deg,#8A0E1C,#E8394A,#FF6B78)
styles:
card:
- width: 443px
- height: 377px
- border-radius: 16px
- padding: 20px
- overflow: hidden
- margin-top: "-11px"
- margin-left: "-10.5px"
- border: 1px solid rgba(67,152,195,0.25)
- background: "linear-gradient(145deg, #1c3a50 0%, #0e2333 40%, #0a1c2b 100%)"
- box-shadow: 0 8px 32px rgba(0,0,0,0.4), 0 0 0 1px rgba(67,152,195,0.12)
grid:
- grid-template-areas: "\"topbar\" \"main\""
- grid-template-columns: 1fr
- grid-template-rows: 0px 1fr
- z-index: "2"
custom_fields:
topbar:
- position: absolute
- top: "0"
- left: "0"
- right: "0"
- bottom: "0"
- pointer-events: none
- z-index: "1"
main:
- z-index: "2"
- width: 100%
- height: 100%
custom_fields:
topbar: |
[[[
const mode = states[variables.v_mode]?.state ?? '';
const active = mode === 'run';
return `
<div style="position:absolute;top:0;left:0;right:0;height:3px;
background:linear-gradient(90deg,transparent,#4398C3,#7EC8E3,#4398C3,transparent);
opacity:${active ? '1' : '0.5'};z-index:3;pointer-events:none;"></div>
<div style="position:absolute;top:-20px;right:-20px;
width:180px;height:180px;border-radius:50%;
background:radial-gradient(circle,rgba(67,152,195,0.18) 0%,transparent 70%);
pointer-events:none;z-index:1;
opacity:${active ? '1' : '0'};transition:opacity 0.4s ease;"></div>`;
]]]
main: |
[[[
// ββββββββββββββββββββββββββββββββββββββββββββββββ
// DATEN LADEN
// ββββββββββββββββββββββββββββββββββββββββββββββββ
const get = (id, fb='β') => states[id]?.state ?? fb;
const inv = ['','unknown','unavailable','none','null'];
const norm = v => String(v ?? '').toLowerCase().trim();
const mode = get(variables.v_mode,'');
const conn = get(variables.v_connected,'off') === 'on';
const active = mode === 'run' && conn;
const prog = get(variables.v_program,'');
const progName = inv.includes(norm(prog)) ? 'Programm AUS' : prog;
const status = conn ? get(variables.v_status,'STANDBY') : 'STANDBY';
const endStr = get(variables.v_endtime,'');
const startStr = get(variables.v_start,'');
const power = parseFloat(get(variables.v_power,'0') || 0).toFixed(1);
const energy = (parseFloat(get(variables.v_energy,'0') || 0) - variables.v_energy_offset).toFixed(2);
// ββββββββββββββββββββββββββββββββββββββββββββββββ
// FORTSCHRITT BERECHNEN
// ββββββββββββββββββββββββββββββββββββββββββββββββ
let pct = 0;
if (active && !inv.includes(norm(endStr)) && !inv.includes(norm(startStr))) {
const start = new Date(startStr).getTime();
const end = new Date(endStr).getTime();
const total = end - start;
pct = total <= 0 ? 0
: Math.min(100, Math.max(0, Math.round(((Date.now()-start)/total)*100)));
}
// ββββββββββββββββββββββββββββββββββββββββββββββββ
// RESTZEIT BERECHNEN
// ββββββββββββββββββββββββββββββββββββββββββββββββ
let timeStr = '--:--';
if (active && !inv.includes(norm(endStr))) {
const end = new Date(endStr);
if (!isNaN(end.getTime())) {
const diff = Math.max(0, Math.floor((end.getTime() - Date.now()) / 1000));
const hh = Math.floor(diff / 3600);
const mm = Math.floor((diff % 3600) / 60);
timeStr = `${String(hh).padStart(2,'0')}:${String(mm).padStart(2,'0')}`;
}
}
// ββββββββββββββββββββββββββββββββββββββββββββββββ
// IDOS-STATUS β Farben fΓΌr OK / NIEDRIG / unbekannt
// ββββββββββββββββββββββββββββββββββββββββββββββββ
const mapIDOS = s =>
s === 'on' ? { t:'NIEDRIG', c:'rgba(195,67,85,1)',
bg:'rgba(195,67,85,0.1)', bd:'rgba(195,67,85,0.25)' }
: s === 'off' ? { t:'OK', c:'rgba(67,195,122,1)',
bg:'rgba(67,195,122,0.1)', bd:'rgba(67,195,122,0.25)' }
: { t:'β', c:'rgba(67,152,195,1)',
bg:'rgba(67,152,195,0.05)', bd:'rgba(67,152,195,0.12)' };
const i1 = mapIDOS(get(variables.v_idos1,'unknown'));
const i2 = mapIDOS(get(variables.v_idos2,'unknown'));
// ββββββββββββββββββββββββββββββββββββββββββββββββ
// BAUSTEINE
// ββββββββββββββββββββββββββββββββββββββββββββββββ
const pill = (dot, bg, bd, tx, lbl, glow) => `
<div style="display:inline-flex;align-items:center;gap:6px;
background:${bg};border:1px solid ${bd};
border-radius:20px;padding:3px 10px 3px 7px;">
<div style="width:8px;height:8px;border-radius:50%;
background:${dot};box-shadow:${glow};flex-shrink:0;"></div>
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;color:${tx};">${lbl}</span>
</div>`;
const statusPill = active
? pill('#43C37A','rgba(67,195,122,0.1)','rgba(67,195,122,0.22)',
'rgba(67,195,122,1)','LΓUFT','0 0 7px rgba(67,195,122,0.7)')
: pill('rgba(67,152,195,1)','rgba(67,152,195,0.05)','rgba(67,152,195,0.12)',
'rgba(67,152,195,1)','STANDBY','none');
const idosPill = d => `
<div style="display:inline-flex;align-items:center;gap:5px;
background:${d.bg};border:1px solid ${d.bd};
border-radius:20px;padding:3px 8px 3px 6px;">
<div style="width:8px;height:8px;border-radius:50%;
background:${d.c};flex-shrink:0;"></div>
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;color:${d.c};">${d.t}</span>
</div>`;
const statTile = (icon, val, unit, label, accent) => `
<div style="flex:1;display:flex;flex-direction:column;align-items:center;
justify-content:center;background:rgba(67,152,195,0.05);
border:1px solid rgba(67,152,195,0.12);border-radius:8px;
padding:10px 6px;gap:5px;text-align:center;">
<div style="display:flex;align-items:center;gap:4px;">
<ha-icon icon="${icon}" style="--mdc-icon-size:20px;color:${accent};
filter:drop-shadow(0 0 4px ${accent}88);"></ha-icon>
<span style="font-family:'EXO 2';font-size:10px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;
color:rgba(67,152,195,1);">${label}</span>
</div>
<div style="display:flex;align-items:baseline;justify-content:center;gap:2px;">
<span style="font-family:'EXO 2';font-size:22px;font-weight:500;
color:#E8F4FC;line-height:1;">${val}</span>
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
color:rgba(126,200,227,1);margin-left:2px;">${unit}</span>
</div>
</div>`;
const barBg = active ? variables.v_bar_gradient : 'rgba(67,152,195,0.12)';
const barGlow = active ? '0 0 8px rgba(232,57,74,0.5)' : 'none';
const pctCol = active ? '#FF6B78' : 'rgba(67,152,195,1)';
// ββββββββββββββββββββββββββββββββββββββββββββββββ
// AUSGABE β Reihenfolge der BlΓΆcke hier bestimmt
// was oben/unten auf der Karte steht
// ββββββββββββββββββββββββββββββββββββββββββββββββ
return `
<div style="width:100%;height:100%;position:relative;">
<!-- BLOCK 1: HEADER (Titel + Status-Pill) -->
<div style="position:absolute;top:0px;left:0;right:0;">
<div style="display:flex;align-items:center;justify-content:space-between;">
<!-- Links: Titel -->
<div style="display:flex;align-items:center;">
<span style="font-family:'EXO 2';font-size:16px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;
color:rgba(126,200,227,1);">
${variables.v_name}
</span>
</div>
<!-- Rechts: Status-Pill -->
${statusPill}
</div>
</div>
<!-- BLOCK 2: BILD + PROGRAMM + RESTZEIT -->
<!-- BildgrΓΆΓe: width Γ€ndern (aktuell 100px) -->
<div style="position:absolute;top:10px;left:0;right:0;">
<div style="display:flex;align-items:center;margin-bottom:10px;position:absolute;top:30px;left:0;right:0;">
<img src="${variables.v_bild}"
style="width:100px;height:100px;object-fit:contain;flex-shrink:0;
filter:drop-shadow(0 4px 14px rgba(67,152,195,0.2))
drop-shadow(0 2px 6px rgba(0,0,0,0.5));">
<div style="flex:1;display:flex;flex-direction:column;gap:5px;">
<!-- Programmname -->
<div>
<div style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;
color:rgba(67,152,195,1);margin-bottom:2px;">Programm</div>
<div style="font-family:'EXO 2';font-size:22px;font-weight:500;
color:#E8F4FC;letter-spacing:0.5px;line-height:1.2;">${progName}</div>
</div>
<!-- Restzeit -->
<div>
<div style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;
color:rgba(67,152,195,1);margin-bottom:2px;">Restzeit</div>
<div style="font-family:'EXO 2';font-size:36px;font-weight:500;
letter-spacing:0.5px;line-height:1.2;
color:${active ? '#E8F4FC' : 'rgba(255,255,255,0.5)'};
${active ? 'text-shadow:0 0 25px rgba(67,152,195,0.3);' : ''}">
${timeStr}
${active ? '<span style="font-size:14px;color:#4398C3;margin-left:4px;font-weight:500;">h</span>' : ''}
</div>
</div>
</div>
</div>
<!-- BLOCK 3: STAT-KACHELN -->
<!-- Reihenfolge der Kacheln hier Γ€nderbar -->
<div style="position:absolute;top:170px;left:0;right:0;">
<div style="display:flex;gap:8px;margin-bottom:10px;">
${statTile('mdi:flash-outline', power, 'W', 'Leistung', '#7EC8E3')}
${statTile('mdi:meter-electric-outline', energy, 'kWh', 'Jahresverbrauch', '#7EC8E3')}
${statTile('mdi:percent-circle-outline', pct, '%', 'Fortschritt', active ? '#FF6B78' : '#4398C3')}
</div>
<!-- BLOCK 4: FORTSCHRITTSBALKEN -->
<div style="position:absolute;top:85px;left:0;right:0;">
<div style="display:flex;align-items:center;
justify-content:space-between;margin-bottom:3px;">
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;color:rgba(67,152,195,1);">
Waschfortschritt
</span>
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
color:${pctCol};">${pct}%</span>
</div>
<div style="height:6px;background:rgba(255,255,255,0.06);
border-radius:3px;overflow:hidden;">
<div style="height:100%;width:${pct}%;background:${barBg};
border-radius:3px;box-shadow:${barGlow};
transition:width 0.5s ease;"></div>
</div>
</div>
<!-- BLOCK 5: FOOTER (Status + iDos 1 + iDos 2) -->
<div style="position:absolute;top:128px;left:0;right:0;">
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
<!-- Betriebsstatus links -->
<div style="display:flex;align-items:center;gap:6px;">
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;color:rgba(67,152,195,1);">
Status:
</span>
<span style="font-family:'EXO 2';font-size:15px;font-weight:500;letter-spacing:0.5px;
color:${active ? '#E8F4FC' : 'rgba(67,152,195,1)'};">
${status}
</span>
</div>
<div style="flex:1;"></div>
<!-- iDos 1 rechts -->
<div style="display:flex;align-items:center;gap:6px;">
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;color:rgba(67,152,195,1);">
iDos 1
</span>
${idosPill(i1)}
</div>
<!-- iDos 2 rechts -->
<div style="display:flex;align-items:center;gap:6px;">
<span style="font-family:'EXO 2';font-size:12px;font-weight:500;
letter-spacing:0.5px;text-transform:uppercase;color:rgba(67,152,195,1);">
iDos 2
</span>
${idosPill(i2)}
</div>
</div>
</div>`;
]]]