Hi, ich hab das so ähnlich beim ATV/Denon. Wenn das Programm gestartet wird, erscheint oben links das Bild von der Serie oder Film mit einer Zeitleiste.
Das sollte so auch mit einem Radioprogramm oder Streamingdienst (Spotify o.a.) gehen.
type: custom:vertical-stack-in-card
cards:
- type: horizontal-stack
cards:
- type: custom:button-card
entity: media_player.wohnzimmer
triggers_update:
- sensor.now_second
- media_player.wohnzimmer
name: |
[[[
const a = entity?.attributes || {};
const app = (a.app_name || a.source || '').toLowerCase();
const title = a.media_title || a.media_station || '';
if (app.includes('zattoo')) {
// Bei Zattoo liefert tvOS oft keine Titel/Sender-Daten.
return title || 'Zattoo';
}
return title || '—';
]]]
label: |
[[[
const a = entity?.attributes || {};
const app = (a.app_name || a.source || '').toLowerCase();
const type = a.media_content_type || '';
if (app.includes('zattoo')) {
// Klarer Hinweis statt leerem Label
return (a.media_title || a.media_station)
? 'Zattoo'
: 'Zattoo • Kanalinfos nicht verfügbar';
}
const appName = a.app_name || a.source || '';
return appName && type ? `${appName} • ${type}` : (appName || type || '');
]]]
show_label: true
show_icon: false
show_state: false
tap_action:
action: more-info
hold_action:
action: more-info
styles:
card:
- border-radius: 10px
- border: none
- height: 200px
- padding: 16px
- font-family: poppins_regular
- background: |
[[[
const a = entity?.attributes || {};
const art = a.entity_picture || a.media_image_url || a.entity_picture_local || '';
const app = (a.app_name || a.source || '').toLowerCase();
// 1) Wenn echtes Artwork vorhanden: verwende es
if (art) {
return `linear-gradient(to bottom, rgba(0,0,0,0.25), rgba(0,0,0,0.7)), url("${art}") center / cover no-repeat`;
}
// 2) Zattoo aktiv, aber kein Artwork → lokales Platzhalterbild verwenden
if (app.includes('zattoo')) {
const localZ = "/local/pictures/zattoo-card-image.png";
return `linear-gradient(to bottom, rgba(0,0,0,0.25), rgba(0,0,0,0.7)), url("${localZ}") center / cover no-repeat`;
}
// 3) Fallback: lila Verlauf
return 'linear-gradient(130deg, #4F46A3 0%, #413A86 100%)';
]]]
grid:
- grid-template-areas: "\"n\" \"l\" \"progress\""
- grid-template-rows: auto auto auto
- row-gap: 6px
name:
- grid-area: "n"
- color: "#FFFFFF"
- font-size: 18px
- font-weight: 600
- justify-self: start
- white-space: normal
- line-height: 1.25
- overflow: hidden
- display: "-webkit-box"
- "-webkit-line-clamp": 2
- "-webkit-box-orient": vertical
- text-shadow: 0 1px 2px rgba(0,0,0,0.5)
label:
- grid-area: l
- color: "#FFFFFF"
- opacity: 0.9
- font-size: 14px
- justify-self: start
- white-space: normal
- line-height: 1.2
- overflow: hidden
- display: "-webkit-box"
- "-webkit-line-clamp": 1
- "-webkit-box-orient": vertical
- text-shadow: 0 1px 2px rgba(0,0,0,0.45)
custom_fields:
progress:
- grid-area: progress
- align-self: end
- width: 100%
custom_fields:
progress: |
[[[
const a = entity?.attributes || {};
const dur = Number(a.media_duration || 0);
if (dur <= 0) { return ''; } // Live-TV: meist keine Dauer → keine Leiste
let pos = Number(a.media_position || 0);
const upd = Date.parse(a.media_position_updated_at || '');
if (!isNaN(upd)) pos += Math.max(0, (Date.now() - upd) / 1000);
const pct = Math.max(0, Math.min(100, (pos / dur) * 100));
const mmss = s => { s = Math.max(0, Math.floor(s)); const m = Math.floor(s/60); const sec = s % 60; return `${m}:${sec.toString().padStart(2,'0')}`; };
return `
<div class="np-wrap">
<div class="bar"><div class="fill" style="width:${pct}%;"></div></div>
<div class="times"><span>${mmss(pos)}</span><span>${mmss(dur)}</span></div>
</div>
`;
]]]
card_mod:
style: >
:host { flex: 3.2 1 0 !important; min-width: 0; } /* Linke Karte
breiter */
ha-card { font-family: poppins_regular !important; }
.np-wrap { width: 100%; }
.bar {
width: 100%; height: 6px; border-radius: 999px;
background: rgba(255,255,255,0.35);
overflow: hidden; margin-top: 6px;
}
.bar .fill {
height: 100%; border-radius: 999px;
background: rgba(255,255,255,0.95);
transition: width 0.25s linear;
}
.times {
display: flex; justify-content: space-between;
color: #FFFFFF; opacity: 0.9; font-size: 12px; margin-top: 6px;
}
- type: vertical-stack
cards:
- square: false
type: grid
cards:
- type: custom:button-card
entity: media_player.wohnzimmer
icon: mdi:power
show_name: false
tap_action:
action: call-service
service: media_player.toggle
target:
entity_id: media_player.wohnzimmer
styles:
card:
- border-radius: 10px
- padding: 10px
- height: 60px
icon:
- color: "#FFFFFF"
state:
- value: "off"
styles:
card:
- background: "linear-gradient(100deg, #C50D22 0%, #A70C1E 100%)"
- value: idle
styles:
card:
- background: "linear-gradient(100deg, #C50D22 0%, #A70C1E 100%)"
- value: standby
styles:
card:
- background: "linear-gradient(100deg, #C50D22 0%, #A70C1E 100%)"
- value: playing
styles:
card:
- background: "linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)"
- type: custom:button-card
entity: media_player.avr_wohnzimmer_2
show_name: false
icon: mdi:volume-high
show_state: true
state_display: |
[[[
if (!entity || !entity.attributes) return '—';
const v = (entity.attributes.volume_level != null)
? Math.round(entity.attributes.volume_level * 100) + '%'
: '—';
return v;
]]]
styles:
card:
- margin-top: 2px
- border-radius: 10px
- padding: 10px
- height: 60px
- background: |
[[[
if (!entity || entity.attributes.volume_level == null) {
return "linear-gradient(100deg, #9495E0 0%, #8182C3 100%)";
}
const vol = Math.round(entity.attributes.volume_level * 100);
return `linear-gradient(90deg, #4BC67A 0%, #41AA69 ${vol}%, #9495E0 ${vol}%, #8182C3 100%)`;
]]]
grid:
- grid-template-areas: "\"i s\""
- grid-template-columns: 1fr 1fr
- grid-template-rows: 1fr
- align-items: center
icon:
- color: "#FFFFFF"
- width: 27px
- margin-left: "-10px"
state:
- color: "#FFFFFF"
- font-family: poppins_medium
- font-size: 16px
- type: custom:button-card
icon: mdi:volume-mute
tap_action:
action: call-service
service: media_player.volume_mute
data:
is_volume_muted: true
target:
entity_id: media_player.avr_wohnzimmer_2
hold_action:
action: call-service
service: media_player.volume_mute
data:
is_volume_muted: false
target:
entity_id: media_player.avr_wohnzimmer_2
styles:
card:
- margin-top: 2px
- border-radius: 10px
- padding: 10px
- height: 60px
- background: "linear-gradient(100deg, #9495E0 0%, #8182C3 100%)"
icon:
- color: "#FFFFFF"
columns: 1
card_mod:
style: >
:host { flex: 1 1 0 !important; min-width: 0; } /* rechte Spalte
schlanker */
card_mod:
style: >
#root { gap: 12px; align-items: flex-start; } /* Abstand zwischen
links/rechts */
- type: custom:gap-card
height: 10
- square: false
type: grid
cards:
- type: custom:button-card
entity: media_player.wohnzimmer
show_name: false
show_entity_picture: true
entity_picture: /local/logos/netflix.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: Netflix
target:
entity_id: media_player.wohnzimmer
styles:
card:
- border-radius: 10px
- height: 60px
- background: |
[[[
const a = entity?.attributes || {};
const app = (a.app_name || a.source || '').toLowerCase();
const isOn = entity?.state && entity.state !== 'off' && entity.state !== 'idle';
// AVR Quelle prüfen
const avr = states["media_player.avr_wohnzimmer_2"];
const avrSource = (avr?.attributes?.source || "").toLowerCase();
if (isOn && avrSource.includes("xbox")) {
return 'linear-gradient(100deg, #A0A0A0 0%, #707070 100%)'; // Grau wenn AVR Xbox
}
if (isOn && app.includes("netflix")) {
return 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)'; // Grün für Netflix
}
return 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)'; // Standard
]]]
entity_picture:
- width: 80px
- object-fit: contain
- margin-top: 3px
- type: custom:button-card
entity: media_player.wohnzimmer
name: Amazon
show_entity_picture: true
show_name: false
entity_picture: /local/icons/amazon_video.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: Prime Video
target:
entity_id: media_player.wohnzimmer
styles:
card:
- border-radius: 10px
- height: 60px
- background: |
[[[
const a = entity?.attributes || {};
const app = (a.app_name || a.source || '').toLowerCase();
const isOn = entity?.state && entity.state !== 'off' && entity.state !== 'idle';
return (isOn && app.includes('prime video'))
? 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)'
: 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)';
]]]
entity_picture:
- width: 80px
- height: 50px
- object-fit: contain
- margin-top: 5px
name:
- color: "#FFFFFF"
- font-family: poppins_regular
- font-size: 12px
- type: custom:button-card
entity: media_player.wohnzimmer
name: TV-Stream
show_entity_picture: true
show_name: false
entity_picture: /local/icons/zattoo_logo.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: Zattoo | TV Streaming App
target:
entity_id: media_player.wohnzimmer
hold_action:
action: more-info
styles:
card:
- border-radius: 10px
- height: 60px
- background: |
[[[
const a = entity?.attributes || {};
const app = (a.app_name || a.source || '').toLowerCase();
const isOn = entity?.state && entity.state !== 'off' && entity.state !== 'idle';
return (isOn && app.includes('zattoo'))
? 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)' // aktiv
: 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)'; // inaktiv
]]]
entity_picture:
- width: 80px
- height: 40px
- margin-top: "-3px"
- object-fit: contain
name:
- color: "#FFFFFF"
- font-family: poppins_regular
- font-size: 12px
- type: custom:button-card
entity: media_player.wohnzimmer
name: YouTube
show_entity_picture: true
show_name: false
entity_picture: /local/logos/youtube.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: YouTube
target:
entity_id: media_player.wohnzimmer
styles:
card:
- border-radius: 10px
- height: 60px
- background: |
[[[
const a = entity?.attributes || {};
const app = (a.app_name || a.source || '').toLowerCase();
const isOn = entity?.state && entity.state !== 'off' && entity.state !== 'idle';
return (isOn && app.includes('youtube'))
? 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)'
: 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)';
]]]
entity_picture:
- width: 85px
- height: 45px
- object-fit: contain
name:
- color: "#FFFFFF"
- font-family: poppins_regular
- font-size: 12px
columns: 4
- type: custom:gap-card
height: 10
- square: false
type: grid
cards:
- type: custom:button-card
entity: media_player.avr_wohnzimmer_2
show_name: false
show_entity_picture: true
entity_picture: /local/logos/appletv.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: Apple TV
target:
entity_id: media_player.avr_wohnzimmer_2
hold_action:
action: more-info
styles:
card:
- border-radius: 10px
- height: 60px
- padding: 12px
- background: |
[[[
// Aktiv (grün), wenn der AVR an ist und Quelle "Apple TV" gewählt ist
const on = entity && entity.state && entity.state !== 'off' && entity.state !== 'idle';
const src = (entity?.attributes?.source || '').toLowerCase();
return (on && src.includes('apple'))
? 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)'
: 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)';
]]]
entity_picture:
- width: 50px
- object-fit: contain
- margin-top: "-3px"
name:
- color: "#FFFFFF"
- font-family: poppins_regular
- font-size: 12px
- margin-top: "-5px"
- type: custom:button-card
entity: media_player.avr_wohnzimmer_2
show_name: false
show_entity_picture: true
entity_picture: /local/logos/xbox.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: XBox
target:
entity_id: media_player.avr_wohnzimmer_2
hold_action:
action: more-info
styles:
card:
- border-radius: 10px
- height: 60px
- padding: 12px
- background: |
[[[
// Aktiv (grün), wenn der AVR an ist und Quelle "XBox" gewählt ist
const on = entity && entity.state && entity.state !== 'off' && entity.state !== 'idle';
const src = (entity?.attributes?.source || '').toLowerCase();
return (on && src.includes('xbox'))
? 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)'
: 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)';
]]]
entity_picture:
- width: 70px
- object-fit: contain
- type: custom:button-card
entity: media_player.avr_wohnzimmer_2
show_name: false
show_entity_picture: true
entity_picture: /local/icons/phono.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: Phono
target:
entity_id: media_player.avr_wohnzimmer_2
hold_action:
action: more-info
styles:
card:
- border-radius: 10px
- height: 60px
- padding: 12px
- background: |
[[[
// Aktiv (grün), wenn der AVR an ist und Quelle "Phono" gewählt ist
const on = entity && entity.state && entity.state !== 'off' && entity.state !== 'idle';
const src = (entity?.attributes?.source || '').toLowerCase();
return (on && src.includes('phono'))
? 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)'
: 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)';
]]]
entity_picture:
- width: 36px
- object-fit: contain
- type: custom:button-card
entity: media_player.avr_wohnzimmer_2
show_name: false
show_entity_picture: true
entity_picture: /local/logos/denon_heos.png
tap_action:
action: call-service
service: media_player.select_source
data:
source: HEOS Music
target:
entity_id: media_player.avr_wohnzimmer_2
hold_action:
action: more-info
styles:
card:
- border-radius: 10px
- height: 60px
- padding: 12px
- background: |
[[[
// Aktiv (grün), wenn der AVR an ist und Quelle "Heos" gewählt ist
const on = entity && entity.state && entity.state !== 'off' && entity.state !== 'idle';
const src = (entity?.attributes?.source || '').toLowerCase();
return (on && src.includes('heos'))
? 'linear-gradient(100deg, #4BC67A 0%, #41AA69 100%)'
: 'linear-gradient(100deg, #9495E0 0%, #8182C3 100%)';
]]]
entity_picture:
- width: 70px
- object-fit: contain
columns: 4
card_mod:
style: |
ha-card {
border-radius: 25px;
border: none;
padding: 15px;
height: 370px;
background: linear-gradient(150deg, #4F46A3 0%, #413A86 100%);
box-shadow: 3px 8px 8px#ABBCB8;
overflow: visible;
margin-left: 4px;
}