//const const Debug_Prefix = "Animated Background DEBUG: "; const Log_Prefix = "Animated Background: " //globals var Root; var Panel_Holder; var Hui; var Lovelace; var Animated_Config; var Haobj = null; var View; var Debug_Mode = false; var Loaded = false; var View_Loaded = false; var Meme_Remover = null; var Meme_Count = 0; //state tracking variables let Previous_State; let Previous_Entity; let Previous_Url; let Previous_Config; function STATUS_MESSAGE(message, force) { if (!Debug_Mode) { console.log(Log_Prefix + message); } else { if (force) { console.log(Debug_Prefix + message); } } } function DEBUG_MESSAGE(message, object, only_if_view_not_loaded) { if (Debug_Mode) { if (only_if_view_not_loaded && View_Loaded) { return; } console.log(Debug_Prefix + message); if (object) { console.log(object); } } } function randomIntFromInterval(min, max) { // min and max included return Math.floor(Math.random() * (max - min + 1) + min); } //reset all DOM variables function getVars() { Root = document.querySelector("home-assistant"); Root = Root && Root.shadowRoot; Root = Root && Root.querySelector("home-assistant-main"); Root = Root && Root.shadowRoot; Root = Root && Root.querySelector("app-drawer-layout partial-panel-resolver, ha-drawer partial-panel-resolver"); Root = (Root && Root.shadowRoot) || Root; Root = Root && Root.querySelector("ha-panel-lovelace"); if (Root) { Panel_Holder = Root.shadowRoot; } Root = Root && Root.shadowRoot; Root = Root && Root.querySelector("hui-root"); Hui = Root; if (Root) { Lovelace = Root.lovelace; if (Lovelace) { Animated_Config = Lovelace.config.animated_background; } View = Root.shadowRoot.getElementById("view"); } } //Mutation observer to set the background of views to transparent each time a new tab is selected var View_Observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.addedNodes.length > 0) { if (!currentConfig() && View_Loaded) { DEBUG_MESSAGE("No configuration found for this view"); } View_Loaded = false; clearMemes(); renderBackgroundHTML(); } }); }); //Mutation observer to refresh video on HA refresh var Hui_Observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.addedNodes.length > 0) { DEBUG_MESSAGE("Proof that this observer is not useless"); renderBackgroundHTML(); } }); }); //Mutation observer to reload on dashboard change var Panel_Observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.removedNodes.length > 0) { if (mutation.removedNodes[0].nodeName.toLowerCase() == "hui-editor") { restart(); } } }); }); //Current known support: iphone, ipad (if set to mobile site option), windows, macintosh, android function deviceIncluded(element, index, array) { return navigator.userAgent.toLowerCase().includes(element.toLowerCase()); } //return the currently selected lovelace view function currentViewPath() { return window.location.pathname.split('/')[2]; } //return group config by name if it exists function getGroupConfig(name) { var return_config = null; if (name == "none") { return { enabled: false, reason: "current group is set to 'none'" }; } if (Animated_Config.groups) { Animated_Config.groups.forEach(group => { if (group.name) { if (group.name == name) { if (group.config) { return_config = group.config; } } } }) } return return_config; } //return the current view configuration or null if none is found function currentConfig() { var current_view_path = currentViewPath(); var return_config = null; if (current_view_path == undefined) { return return_config; } if (Animated_Config) { if (Animated_Config.entity || Animated_Config.default_url) { return_config = Animated_Config; } if (Animated_Config.views) { Animated_Config.views.forEach(view => { if (view.path == current_view_path) { if (view.config) { return_config = view.config; } else { STATUS_MESSAGE("Error, defined view has no config", true); } } }); } var current_view_path = currentViewPath(); var current_view_config = Lovelace.config.views[Lovelace.current_view]; if (Lovelace && current_view_path) { for (var i = 0; Lovelace.config.views.length > i; i++) { if (Lovelace.config.views[i].path == current_view_path) { current_view_config = Lovelace.config.views[i]; } else { if (i.toString() == current_view_path.toString()) { current_view_config = Lovelace.config.views[i]; } } } if (current_view_config) { var potential_config = getGroupConfig(current_view_config.animated_background); if (potential_config) { return_config = potential_config; } } } if (return_config) { if (return_config.entity) { var current_state = getEntityState(return_config.entity); var current_url = return_config.state_url[current_state]; if (current_url) { if (current_url == "none") { return_config = { enabled: false, reason: "current state('" + current_state + "') state_url is set to 'none'", entity: return_config.entity, default_url: return_config.default_url, state_url: return_config.state_url }; } } } } } return return_config; } //logic for checking if enabled in configuration function enabled() { var temp_enabled = false; if (Animated_Config) { if (Animated_Config.default_url || Animated_Config.entity || Animated_Config.views || Animated_Config.groups) { temp_enabled = true; } } if (temp_enabled == false) { return false; } var current_config = currentConfig(); if (!Haobj) { return false; } if (!current_config) { return false; } //Root configuration exceptions if (Animated_Config.excluded_devices) { if (Animated_Config.excluded_devices.some(deviceIncluded)) { if (temp_enabled) { DEBUG_MESSAGE("Current device is excluded", null, true); temp_enabled = false; } } } if (Animated_Config.excluded_users) { if (Animated_Config.excluded_users.map(username => username.toLowerCase()).includes(Haobj.user.name.toLowerCase())) { if (temp_enabled) { DEBUG_MESSAGE("Current user: " + Haobj.user.name + " is excluded", null, true); temp_enabled = false; } } } if (Animated_Config.included_users) { if (Animated_Config.included_users.map(username => username.toLowerCase()).includes(Haobj.user.name.toLowerCase())) { temp_enabled = true; } else { if (temp_enabled) { DEBUG_MESSAGE("Current user: " + Haobj.user.name + " is not included", null, true); temp_enabled = false; } } } if (Animated_Config.included_devices) { if (Animated_Config.included_devices.some(deviceIncluded)) { temp_enabled = true; } else { if (temp_enabled) { DEBUG_MESSAGE("Current device is not included", null, true); temp_enabled = false; } } } //Current config overrides (only does anything if curre_config and Animated_Config are different) if (current_config.excluded_devices) { if (current_config.excluded_devices.some(deviceIncluded)) { if (temp_enabled) { DEBUG_MESSAGE("Current device is excluded", null, true); temp_enabled = false; } } } if (current_config.excluded_users) { if (current_config.excluded_users.map(username => username.toLowerCase()).includes(Haobj.user.name.toLowerCase())) { if (temp_enabled) { DEBUG_MESSAGE("Current user: " + Haobj.user.name + " is excluded", null, true); temp_enabled = false; } } } if (current_config.included_users) { if (current_config.included_users.map(username => username.toLowerCase()).includes(Haobj.user.name.toLowerCase())) { temp_enabled = true; } else { if (temp_enabled) { DEBUG_MESSAGE("Current user: " + Haobj.user.name + " is not included", null, true); temp_enabled = false; } } } if (current_config.included_devices) { if (current_config.included_devices.some(deviceIncluded)) { temp_enabled = true; } else { if (temp_enabled) { DEBUG_MESSAGE("Current device is not included", null, true); temp_enabled = false; } } } if (current_config.enabled == false) { temp_enabled = false; } if (current_config.enabled == true) { temp_enabled = true; } return temp_enabled; } //returns selected entity's current state if it is available function getEntityState(entity) { var return_state = null; if (Haobj) { if (Haobj.states[entity]) { return_state = Haobj.states[entity].state; } } return return_state; } //main render function function renderBackgroundHTML() { var current_config = currentConfig(); var state_url = ""; var temp_enabled = true; //rerender background if entity has changed (to avoid no background refresh if the new entity happens to have the same state) if (current_config && current_config.entity && Previous_Entity != current_config.entity) { Previous_State = null; } if (current_config != Previous_Config) { Previous_State = null; } //get state of config object if (current_config) { if (current_config.entity && current_config.state_url) { Previous_Entity = current_config.entity; var current_state = getEntityState(current_config.entity); if (current_config.state_url[current_state]) { if (Previous_State != current_state) { View_Loaded = false; DEBUG_MESSAGE("Configured entity " + current_config.entity + " is now " + current_state, true); if (current_config.state_url) { var url = current_config.state_url[current_state]; if (Array.isArray(url)) { state_url = url[randomIntFromInterval(0, url.length - 1)]; } else { state_url = current_config.state_url[current_state]; } } Previous_State = current_state; } } else { DEBUG_MESSAGE("No state_url found for the current state '" + current_state + "'. Attempting to set default_url") Previous_State = current_state; Previous_Url = null; var url = current_config.default_url; if (url) { if (Array.isArray(url)) { state_url = url[randomIntFromInterval(0, url.length - 1)]; } else { state_url = url; } } else { if (!current_config.reason) { DEBUG_MESSAGE("No default_url found, restoring lovelace theme") } temp_enabled = false; } } } else { var url = current_config.default_url; if (url) { if (Array.isArray(url)) { state_url = url[randomIntFromInterval(0, url.length - 1)]; } else { state_url = url; } } else { if (!current_config.reason) { DEBUG_MESSAGE("No default_url found, restoring lovelace theme") } temp_enabled = false; } } } else { temp_enabled = false; } if (temp_enabled) { temp_enabled = enabled(); } processDefaultBackground(temp_enabled); if (!temp_enabled || !current_config) { return; } Previous_Config = current_config; var html_to_render; if (state_url != "" && Hui) { var bg = Hui.shadowRoot.getElementById("background-iframe"); var video_type = urlIsVideo(state_url); var doc_body; if (video_type) { doc_body = `` } else { doc_body = `` } var source_doc = ` ${doc_body} `; if (!bg) { if (!current_config.entity) { STATUS_MESSAGE("Applying default background", true); } var style = document.createElement("style"); style.innerHTML = ` .bg-video{ min-width: 100vw; min-height: 100vh; } .bg-wrap{ position: fixed; right: 0; top: 0; min-width: 100vw; min-height: 100vh; z-index: -10; }`; var div = document.createElement("div"); div.id = "background-video"; div.className = "bg-wrap" div.innerHTML = `