const LUMA_MEDIA_VERSION = '0.2.0'; class LumaMediaCard extends HTMLElement { set hass(hass) { this._hass = hass; this._render(); } setConfig(config) { if (!config.entity) throw new Error('Entity is required'); this._config = { entity: config.entity, name: config.name || '', size: config.size || '1x2', accent_color: config.accent_color || '', icon: config.icon || '', tap_action: config.tap_action || { action: 'more-info' }, hold_action: config.hold_action || { action: 'more-info' }, ...config }; this._render(); } getCardSize() { return 4; } static getStubConfig() { return { entity: '', name: '', size: '1x2' }; } connectedCallback() { if (!this.shadowRoot) this.attachShadow({ mode: 'open' }); this._render(); } _getState() { return this._hass?.states?.[this._config?.entity]; } _isActive() { const s = this._getState(); return s && (s.state === 'playing' || s.state === 'paused'); } _callService(service, data = {}) { if (!this._hass || !this._config) return; this._hass.callService('media_player', service, { entity_id: this._config.entity, ...data }); } _handleClick() { if (!this._hass || !this._config) return; const ev = new Event('hass-more-info', { composed: true }); ev.detail = { entityId: this._config.entity }; this.dispatchEvent(ev); } _formatTime(secs) { if (!secs || secs <= 0) return '0:00'; const m = Math.floor(secs / 60); const s = Math.floor(secs % 60); return `${m}:${s < 10 ? '0' : ''}${s}`; } _render() { if (!this.shadowRoot || !this._config) return; const s = this._getState(); const isPlaying = s?.state === 'playing'; const isPaused = s?.state === 'paused'; const isActive = isPlaying || isPaused; const hasMedia = s?.attributes?.media_title ? true : false; const title = s?.attributes?.media_title || this._config.name || s?.attributes?.friendly_name || ''; const artist = s?.attributes?.media_artist || ''; const entityPicture = s?.attributes?.entity_picture || ''; const volume = Math.round((s?.attributes?.volume_level || 0) * 100); const duration = s?.attributes?.media_duration || 0; const position = s?.attributes?.media_position || 0; const progress = duration > 0 ? Math.min(position / duration, 1) : 0; const artBg = entityPicture ? `background-image: url('${entityPicture}'); background-size: cover; background-position: center;` : `background: rgba(36,40,48,0.9); display: flex; align-items: center; justify-content: center;`; const volFillH = volume > 0 ? `height: ${volume}%` : 'height: 0'; const volAlpha = Math.min(volume / 100 * 0.35, 0.30).toFixed(2); this.shadowRoot.innerHTML = `