button_card_templates: variable_template: variables: dashboardversion: 1.3.2 var_assistsat_entity: |- [[[ return localStorage.getItem("view_assist_sensor") ]]] var_dashboard: |- [[[ try { return hass.states[variables.var_assistsat_entity].attributes.dashboard || '/view-assist'; } catch { return '/view-assist'; } ]]] var_mic_switch: |- [[[ try { var micdevice = hass.states[variables.var_assistsat_entity].attributes.mute_switch; return `${micdevice}`; } catch { return ""} ]]] var_wake_switch: |- [[[ try { var micdevice = hass.states[variables.var_assistsat_entity].attributes.mic_device; if (micdevice.startsWith("assist_satellite.")) { var wakebutton = micdevice.replace("assist_satellite.", "button.") + "_wake"; if (wakebutton in hass.states) { return `${wakebutton}`; } } return ""; } catch { return ""; } ]]] var_mediaplayer_device: |- [[[ try { var mediadevice = hass.states[variables.var_assistsat_entity].attributes.mediaplayer_device; return `${mediadevice}`; } catch { return "";} ]]] var_mediaplayer_mute: |- [[[ try { var mediadevice = hass.states[variables.var_assistsat_entity].attributes.mediaplayer_device; var mediaplayerstate = hass.states[mediadevice].attributes.is_volume_muted; return `${mediaplayerstate}`; } catch { return "";} ]]] var_assistsat_time_format: |- [[[ if (variables.var_assistsat_entity_use_24_hour_time) { return '%H:%M'; } else { return '%l:%M'; } ]]] var_current_time: |- [[[ return ``; ]]] var_date_short: |- [[[ return ``; ]]] var_date_long: |- [[[ return ``; ]]] var_assistsat_entity_font_style: | [[[ try { return states[variables.var_assistsat_entity].attributes.font_style; } catch { return "Roboto"} ]]] var_assistsat_entity_weather_entity: | [[[ try { return states[variables.var_assistsat_entity].attributes.weather_entity; } catch { return ""} ]]] var_assistsat_entity_responsive_status_icons_size: | [[[ try { const baseSize = states[variables.var_assistsat_entity].attributes.status_icons_size || "6vw"; if (window.viewAssistResponsive?.orientation === "portrait") { const numericValue = parseFloat(baseSize); const unit = baseSize.replace(numericValue.toString(), ''); return `${numericValue * 1.5}${unit}`; } return baseSize; } catch { return window.viewAssistResponsive?.orientation === "portrait" ? "9vw" : "6vw"; } ]]] var_assistsat_entity_use_24_hour_time: | [[[ try { return states[variables.var_assistsat_entity].attributes.use_24_hour_time; } catch { return false} ]]] var_title: |- [[[ try { return states[variables.var_assistsat_entity].attributes.title; } catch { return ""} ]]] var_message: |- [[[ try { return states[variables.var_assistsat_entity].attributes.message; } catch { return ""} ]]] var_image: |- [[[ try { return states[variables.var_assistsat_entity].attributes.image; } catch { return ""} ]]] var_font_size: |- [[[ try { return states[variables.var_assistsat_entity].attributes.message_font_size; } catch { return ""} ]]] var_weather_temperature: |- [[[ try { return (states[variables.var_assistsat_entity_weather_entity].attributes.temperature + '°'); } catch { return ""} ]]] var_weather_icon: |- [[[ const weatherIconsDay = { "clear-night": "mdi:weather-night", "cloudy": "mdi:weather-cloudy", "dust": "mdi:weather-dust", "fog": "mdi:weather-fog", "hail": "mdi:weather-hail", "hazy": "mdi:weather-hazy", "hurricane": "mdi:weather-hurricane", "lightning": "mdi:weather-lightning", "lightning-rainy": "mdi:weather-lightning-rainy", "partlycloudy": "mdi:weather-partly-cloudy", "partly-lightning": "mdi:weather-partly-lightning", "partly-rainy": "mdi:weather-partly-rainy", "partly-snowy": "mdi:weather-partly-snowy", "partly-snowy-rainy": "mdi:weather-partly-snowy-rainy", "pouring": "mdi:weather-pouring", "rainy": "mdi:weather-rainy", "snowy": "mdi:weather-snowy", "snowy-heavy": "mdi:weather-snowy-heavy", "snowy-rainy": "mdi:weather-snowy-rainy", "sunny": "mdi:weather-sunny", "tornado": "mdi:weather-tornado", "windy": "mdi:weather-windy", "windy-variant": "mdi:weather-windy-variant", }; const weatherIconsNight = { ...weatherIconsDay, clear: "mdi:weather-night", sunny: "mdi:weather-night", partlycloudy: "mdi:weather-night-partly-cloudy", }; try { var condition = states[variables.var_assistsat_entity_weather_entity].state; if (states['sun.sun'] === 'above_horizon') { var weather_icon = weatherIconsDay[condition]; } else { var weather_icon = weatherIconsDay[condition]; } if (typeof(weather_icon) === 'undefined') { var weather_icon = "mdi:help" } return `${weather_icon}`; } catch { return ""} ]]] var_current_view: |- [[[ try { const pathname = window.location.pathname; const match = pathname.match(/\/view-assist\/([^\/]+)/); return match && match[1] ? match[1] : ""; } catch { return ""; } ]]] responsive_base: variables: var_orientation_sensor: |- [[[ try { const orientationSensor = hass.states[variables.var_assistsat_entity]?.attributes?.orientation_sensor; if (orientationSensor && orientationSensor in hass.states) { const state = hass.states[orientationSensor].state; if (state === "portrait" || state === "landscape") { return state; } } return ""; } catch { return ""; } ]]] var_orientation: value: |- [[[ const sensorOrientation = variables.var_orientation_sensor; const windowOrientation = window.innerHeight > window.innerWidth ? "portrait" : "landscape"; const currentOrientation = sensorOrientation || windowOrientation; if (!window.viewAssistResponsive) { window.viewAssistResponsive = { orientation: currentOrientation, sensor_available: !!sensorOrientation, lastUpdate: Date.now() }; } else { const oldOrientation = window.viewAssistResponsive.orientation; window.viewAssistResponsive.sensor_available = !!sensorOrientation; if (oldOrientation !== currentOrientation) { window.viewAssistResponsive.orientation = currentOrientation; window.viewAssistResponsive.lastUpdate = Date.now(); window.dispatchEvent(new CustomEvent('view-assist-responsive-change', { detail: window.viewAssistResponsive, bubbles: true })); } } return window.viewAssistResponsive.orientation; ]]] force_eval: true body_template: template: - variable_template - status_icons_content - responsive_base show_state: false show_icon: false show_name: false tap_action: action: call-service service: view_assist.set_state service_data: entity_id: >- [[[ try { return variables.var_assistsat_entity } catch { return "" }]]] mode: hold double_tap_action: action: call-service service: view_assist.set_state service_data: entity_id: >- [[[ try { return variables.var_assistsat_entity } catch { return "" } ]]] mode: normal hold_action: action: call-service service: switch.toggle service_data: entity_id: '[[[ try { return variables.var_mic_switch } catch { return "" } ]]]' styles: grid: - grid-template-areas: | "title status" "message message" "assist assist" - grid-template-rows: min-content 1fr - grid-template-columns: 1fr 1fr - row-gap: .5rem card: - min-height: |- [[[ try { const screenMode = hass.states[variables.var_assistsat_entity]?.attributes?.screen_mode; if (screenMode === 'no_hide' || screenMode === 'hide_sidebar') { return 'calc(100vh - var(--header-height))'; } return '100vh'; } catch { return '100vh'; } ]]] - max-height: |- [[[ try { const screenMode = hass.states[variables.var_assistsat_entity]?.attributes?.screen_mode; if (screenMode === 'no_hide' || screenMode === 'hide_sidebar') { return 'calc(100vh - var(--header-height))'; } return '100vh'; } catch { return '100vh'; } ]]] - height: |- [[[ try { const screenMode = hass.states[variables.var_assistsat_entity]?.attributes?.screen_mode; if (screenMode === 'no_hide' || screenMode === 'hide_sidebar') { return 'calc(100vh - var(--header-height))'; } return '100vh'; } catch { return '100vh'; } ]]] - aspect-ratio: |- [[[ if (!window.viewAssistResponsive) return "16 / 9"; return window.viewAssistResponsive.orientation === "portrait" ? "9 / 16" : "16 / 9"; ]]] - background: | [[[ if (variables.background != null) { return `center / cover no-repeat url(${variables.background})` } else if (variables.var_background != null) { return `center / cover no-repeat url(${variables.var_background})` } else { return `center / cover no-repeat ${variables.background_color}` } ]]] - background-size: cover - border-radius: 0px - overflow: hidden - color: white - font-family: |- [[[ return `"${variables.var_assistsat_entity_font_style}", sans-serif`; ]]] - font-weight: 300 - position: relative - box-sizing: border-box custom_fields: title: - position: absolute - justify-self: start - align-self: start - z-index: 1 - font-size: |- [[[ return window.viewAssistResponsive?.orientation === "portrait" ? "220%" : "200%"; ]]] - font-weight: 400 - width: max-content - margin-left: 2% - margin-top: '-4%' status: - position: absolute - justify-self: end - align-self: end - justify-content: right - top: 0vh - right: 0vw - z-index: 1 status_icons_content: custom_fields: title: '[[[ return variables.var_title ]]]' status: card: type: custom:layout-card layout_type: custom:horizontal-layout cards: - type: custom:layout-card layout_type: grid-layout layout: margin: 0 card_margin: 0 place-content: end grid-template-columns: |- [[[ const baseIconSize = variables.var_assistsat_entity_responsive_status_icons_size; return `repeat(auto-fit, minmax(${baseIconSize}, ${baseIconSize}))`; ]]] cards: |- [[[{ const buttonList = []; try { const vaEntity = variables.var_assistsat_entity; if (!vaEntity || !hass.states[vaEntity]) { return []; } const statusIcons = hass.states[vaEntity].attributes.status_icons || []; const menuItems = hass.states[vaEntity].attributes.menu_items || []; const menuActive = hass.states[vaEntity].attributes.menu_active || false; const currentView = variables.var_current_view; const availableTemplates = new Set(); try { const lovelace = document.querySelector("home-assistant") ?.shadowRoot?.querySelector("home-assistant-main") ?.shadowRoot?.querySelector("ha-drawer partial-panel-resolver ha-panel-lovelace") ?.lovelace; if (lovelace?.config?.button_card_templates) { Object.keys(lovelace.config.button_card_templates).forEach(template => { availableTemplates.add(template); }); } } catch (e) { } function isCurrentView(item) { if (!currentView) return false; if (typeof item === 'string' && !item.includes(':')) { if (item === currentView) return true; const homePath = hass.states[vaEntity]?.attributes?.home_screen || "/view-assist/clock"; const homeView = homePath.split('/').pop() || "clock"; const templateViewMap = { "home": homeView }; if (templateViewMap[item] === currentView) return true; return false; } if (typeof item === 'string' && item.includes(':')) { const parts = item.split('|'); const typeAndTarget = parts[0]; const [type, target] = typeAndTarget.split(':'); if (type === 'view') { const viewName = target.includes('/') ? target.split('/').pop() : target; return viewName === currentView; } } return false; } const addedItems = new Set(); function addIconToList(icon, isDynamicItem = false) { if (icon === "menu") return; if (isCurrentView(icon)) { return; } let buttonConfig; if (isDynamicItem) { const type = icon.split(':')[0]; buttonConfig = { type: "custom:button-card", template: `dynamic_${type}_item`, variables: { menu_item: icon, entity_id: vaEntity } }; } else { if (availableTemplates.size > 0 && !availableTemplates.has(icon)) { buttonConfig = { type: "custom:button-card", template: "icon_template", icon: "mdi:alert-circle-outline", name: icon, styles: { card: [{ "border": "2px dashed rgba(255, 152, 0, 0.8)" }], icon: [{ "color": "rgba(255, 152, 0, 0.9)" }], name: [{ "font-size": "0.7em", "color": "rgba(255, 152, 0, 0.9)", "padding-top": "4px" }] }, show_name: true, tap_action: { action: "none" } }; } else { buttonConfig = { type: "custom:button-card", template: icon }; } } const key = icon; if (addedItems.has(key)) return; addedItems.add(key); buttonList.push(buttonConfig); } if (menuActive) { const reversedMenuItems = [...menuItems].reverse(); reversedMenuItems.forEach(item => { if (item !== "menu") { if (item.includes(':')) { addIconToList(item, true); } else { addIconToList(item, false); } } }); } statusIcons.forEach(icon => { if (icon !== "menu") { if (icon.includes(':')) { addIconToList(icon, true); } else { addIconToList(icon, false); } } }); if (hass.states[vaEntity].attributes.menu_config === "menu_enabled_button_visible" && !addedItems.has("menu")) { buttonList.push({ type: "custom:button-card", template: "menu" }); } } catch (e) { } return buttonList; }]]] status_icons_overlay: template: - variable_template - responsive_base - status_icons_content show_state: false show_icon: false show_name: false triggers_update: all styles: card: null custom_fields: status: - position: fixed - top: |- [[[ try { const screenMode = hass.states[variables.var_assistsat_entity]?.attributes?.screen_mode; if (screenMode === 'no_hide' || screenMode === 'hide_sidebar') { return 'calc(0vh + var(--header-height))'; } return '0vh'; } catch { return '0vh'; } ]]] - right: 0vw - z-index: 4 icon_template: template: variable_template color_type: card show_name: false size: 90% padding: 0px styles: card: - background-color: transparent - border-width: 0px - aspect-ratio: 1 / 1 icon: - display: grid - color: white dynamic_view_item: template: icon_template icon: |- [[[ const parts = variables.menu_item.split('|'); const icon = parts.length > 1 ? parts[1] : 'view-dashboard'; return `mdi:${icon}`; ]]] tap_action: action: call-service service: view_assist.navigate service_data: device: '[[[ return variables.entity_id; ]]]' path: |- [[[ const parts = variables.menu_item.split('|'); const typeAndTarget = parts[0]; const target = typeAndTarget.split(':')[1]; const base = variables.var_dashboard || '/view-assist'; return target.startsWith('/') ? target : `${base}/${target}`; ]]] dynamic_entity_item: template: icon_template state_display: none entity: |- [[[ const parts = variables.menu_item.split('|'); const typeAndTarget = parts[0]; return typeAndTarget.split(':')[1]; ]]] icon: |- [[[ const parts = variables.menu_item.split('|'); const typeAndTarget = parts[0]; const entityId = typeAndTarget.split(':')[1]; const iconOptions = parts.length > 1 ? parts[1].split(',') : ['help-circle']; let icon = iconOptions[0]; if (iconOptions.length > 1 && hass.states[entityId]) { const state = hass.states[entityId].state; if (state === 'off' && iconOptions[1]) { icon = iconOptions[1]; } } return `mdi:${icon}`; ]]] tap_action: action: toggle entity: |- [[[ const parts = variables.menu_item.split('|'); const typeAndTarget = parts[0]; return typeAndTarget.split(':')[1]; ]]] hold_action: action: more-info entity: |- [[[ const parts = variables.menu_item.split('|'); const typeAndTarget = parts[0]; return typeAndTarget.split(':')[1]; ]]] dynamic_service_item: template: icon_template icon: |- [[[ const parts = variables.menu_item.split('|'); const icon = parts.length > 1 ? parts[1] : 'cog'; return `mdi:${icon}`; ]]] tap_action: action: call-service service: |- [[[ const parts = variables.menu_item.split('|'); const typeAndTarget = parts[0]; return typeAndTarget.split(':')[1]; ]]] service_data: {} mediaplayer: type: custom:button-card template: icon_template icon: mdi:volume-off tap_action: action: call-service service: media_player.volume_mute service_data: entity_id: '[[[ return variables.var_mediaplayer_device ]]]' is_volume_muted: false mic: type: custom:button-card template: icon_template icon: mdi:microphone-off tap_action: action: call-service service: homeassistant.turn_off service_data: entity_id: '[[[ return variables.var_mic_switch ]]]' hold: type: custom:button-card template: icon_template icon: mdi:hand-back-left tap_action: action: call-service service: view_assist.set_state service_data: mode: normal entity_id: '[[[ return variables.var_assistsat_entity ]]]' cycle: type: custom:button-card template: icon_template icon: mdi:sync tap_action: action: call-service service: view_assist.set_state service_data: mode: normal entity_id: '[[[ return variables.var_assistsat_entity ]]]' dnd: type: custom:button-card template: icon_template icon: mdi:minus-circle tap_action: action: call-service service: view_assist.set_state service_data: do_not_disturb: false entity_id: '[[[ return variables.var_assistsat_entity ]]]' weather: type: custom:button-card template: icon_template icon: mdi:weather-sunny tap_action: action: call-service service: view_assist.navigate service_data: device: '[[[ return variables.var_assistsat_entity ]]]' path: '[[[ return `${variables.var_dashboard}/weather` ]]]' home: type: custom:button-card template: icon_template icon: mdi:home tap_action: action: call-service service: view_assist.navigate service_data: device: '[[[ return variables.var_assistsat_entity ]]]' path: home menu: type: custom:button-card template: icon_template icon: mdi:menu tap_action: action: call-service service: view_assist.toggle_menu service_data: entity_id: '[[[ return variables.var_assistsat_entity ]]]' show: >- [[[ return !states[variables.var_assistsat_entity].attributes.menu_active ]]] hold_action: action: call-service service: view_assist.set_state service_data: entity_id: '[[[ return variables.var_assistsat_entity ]]]' mode: hold camera: type: custom:button-card template: icon_template icon: mdi:cctv tap_action: action: call-service service: view_assist.navigate service_data: device: '[[[ return variables.var_assistsat_entity ]]]' path: '[[[ return `${variables.var_dashboard}/camera` ]]]' music: type: custom:button-card template: icon_template icon: mdi:music tap_action: action: call-service service: view_assist.navigate service_data: device: '[[[ return variables.var_assistsat_entity ]]]' path: '[[[ return `${variables.var_dashboard}/music` ]]]' wake: type: custom:button-card template: icon_template icon: mdi:button-pointer tap_action: action: call-service service: button.press service_data: entity_id: "[[[ return variables.var_wake_switch ]]]" views: - title: Home