diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..1b016bf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "trailingComma": "es5" +} \ No newline at end of file diff --git a/auto-entities.js b/auto-entities.js index 84a74b4..c67d4ec 100644 --- a/auto-entities.js +++ b/auto-entities.js @@ -1,4 +1,4 @@ -function t(t,e,i,n){var s,o=arguments.length,r=o<3?e:null===n?n=Object.getOwnPropertyDescriptor(e,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,e,i,n);else for(var a=t.length-1;a>=0;a--)(s=t[a])&&(r=(o<3?s(r):o>3?s(e,i,r):s(e,i))||r);return o>3&&r&&Object.defineProperty(e,i,r),r}const e=window,i=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,n=Symbol(),s=new WeakMap;let o=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==n)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(i&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=s.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&s.set(e,t))}return t}toString(){return this.cssText}};const r=(t,...e)=>{const i=1===t.length?t[0]:e.reduce(((e,i,n)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[n+1]),t[0]);return new o(i,t,n)},a=i?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new o("string"==typeof t?t:t+"",void 0,n))(e)})(t):t;var l;const d=window,c=d.trustedTypes,h=c?c.emptyScript:"",u=d.reactiveElementPolyfillSupport,v={toAttribute(t,e){switch(e){case Boolean:t=t?h:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},f=(t,e)=>e!==t&&(e==e||t==t),p={attribute:!0,type:String,converter:v,reflect:!1,hasChanged:f};let g=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const n=this._$Ep(i,e);void 0!==n&&(this._$Ev.set(n,i),t.push(n))})),t}static createProperty(t,e=p){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,n=this.getPropertyDescriptor(t,i,e);void 0!==n&&Object.defineProperty(this.prototype,t,n)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(n){const s=this[t];this[e]=n,this.requestUpdate(t,s,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||p}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var t;const n=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,n)=>{i?t.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((i=>{const n=document.createElement("style"),s=e.litNonce;void 0!==s&&n.setAttribute("nonce",s),n.textContent=i.cssText,t.appendChild(n)}))})(n,this.constructor.elementStyles),n}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=p){var n;const s=this.constructor._$Ep(t,i);if(void 0!==s&&!0===i.reflect){const o=(void 0!==(null===(n=i.converter)||void 0===n?void 0:n.toAttribute)?i.converter:v).toAttribute(e,i.type);this._$El=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),this._$El=null}}_$AK(t,e){var i;const n=this.constructor,s=n._$Ev.get(t);if(void 0!==s&&this._$El!==s){const t=n.getPropertyOptions(s),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:v;this._$El=s,this[s]=o.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let n=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||f)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):n=!1),!this.isUpdatePending&&n&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};var _;g.finalized=!0,g.elementProperties=new Map,g.elementStyles=[],g.shadowRootOptions={mode:"open"},null==u||u({ReactiveElement:g}),(null!==(l=d.reactiveElementVersions)&&void 0!==l?l:d.reactiveElementVersions=[]).push("1.5.0");const m=window,y=m.trustedTypes,b=y?y.createPolicy("lit-html",{createHTML:t=>t}):void 0,$=`lit$${(Math.random()+"").slice(9)}$`,w="?"+$,E=`<${w}>`,A=document,C=(t="")=>A.createComment(t),S=t=>null===t||"object"!=typeof t&&"function"!=typeof t,O=Array.isArray,j=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,k=/-->/g,x=/>/g,T=RegExp(">|[ \t\n\f\r](?:([^\\s\"'>=/]+)([ \t\n\f\r]*=[ \t\n\f\r]*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)","g"),U=/'/g,P=/"/g,M=/^(?:script|style|textarea|title)$/i,N=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),I=Symbol.for("lit-noChange"),R=Symbol.for("lit-nothing"),H=new WeakMap,D=A.createTreeWalker(A,129,null,!1),F=(t,e)=>{const i=t.length-1,n=[];let s,o=2===e?"":"",r=j;for(let e=0;e"===l[0]?(r=null!=s?s:j,d=-1):void 0===l[1]?d=-2:(d=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?T:'"'===l[3]?P:U):r===P||r===U?r=T:r===k||r===x?r=j:(r=T,s=void 0);const h=r===T&&t[e+1].startsWith("/>")?" ":"";o+=r===j?i+E:d>=0?(n.push(a),i.slice(0,d)+"$lit$"+i.slice(d)+$+h):i+$+(-2===d?(n.push(void 0),e):h)}const a=o+(t[i]||"")+(2===e?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==b?b.createHTML(a):a,n]};class L{constructor({strings:t,_$litType$:e},i){let n;this.parts=[];let s=0,o=0;const r=t.length-1,a=this.parts,[l,d]=F(t,e);if(this.el=L.createElement(l,i),D.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=D.nextNode())&&a.length0){n.textContent=y?y.emptyScript:"";for(let i=0;iO(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.k(t):this.g(t)}O(t,e=this._$AB){return this._$AA.parentNode.insertBefore(t,e)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}g(t){this._$AH!==R&&S(this._$AH)?this._$AA.nextSibling.data=t:this.T(A.createTextNode(t)),this._$AH=t}$(t){var e;const{values:i,_$litType$:n}=t,s="number"==typeof n?this._$AC(t):(void 0===n.el&&(n.el=L.createElement(n.h,this.options)),n);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===s)this._$AH.p(i);else{const t=new G(s,this),e=t.v(this.options);t.p(i),this.T(e),this._$AH=t}}_$AC(t){let e=H.get(t.strings);return void 0===e&&H.set(t.strings,e=new L(t)),e}k(t){O(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,n=0;for(const s of t)n===e.length?e.push(i=new z(this.O(C()),this.O(C()),this,this.options)):i=e[n],i._$AI(s),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=R}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,n){const s=this.strings;let o=!1;if(void 0===s)t=W(this,t,e,0),o=!S(t)||t!==this._$AH&&t!==I,o&&(this._$AH=t);else{const n=t;let r,a;for(t=s[0],r=0;r{var n,s;const o=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:e;let r=o._$litPart$;if(void 0===r){const t=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:null;o._$litPart$=r=new z(e.insertBefore(C(),t),t,void 0,null!=i?i:{})}return r._$AI(t),r})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return I}}tt.finalized=!0,tt._$litElement$=!0,null===(Q=globalThis.litElementHydrateSupport)||void 0===Q||Q.call(globalThis,{LitElement:tt});const et=globalThis.litElementPolyfillSupport;null==et||et({LitElement:tt}),(null!==(X=globalThis.litElementVersions)&&void 0!==X?X:globalThis.litElementVersions=[]).push("3.2.2");const it=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function nt(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):it(t,e)}function st(t){return nt({...t,state:!0})}var ot;function rt(){return document.querySelector("hc-main")?document.querySelector("hc-main").hass:document.querySelector("home-assistant")?document.querySelector("home-assistant").hass:void 0}null===(ot=window.HTMLSlotElement)||void 0===ot||ot.prototype.assignedElements;const at="lovelace-player-device-id";function lt(){if(!localStorage[at]){const t=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);window.fully&&"function"==typeof fully.getDeviceId?localStorage[at]=fully.getDeviceId():localStorage[at]=`${t()}${t()}-${t()}${t()}`}return localStorage[at]}let dt=lt();const ct=new URLSearchParams(window.location.search);var ht;function ut(t){return!!String(t).includes("{%")||(!!String(t).includes("{{")||void 0)}ct.get("deviceID")&&null!==(ht=ct.get("deviceID"))&&("clear"===ht?localStorage.removeItem(at):localStorage[at]=ht,dt=lt()),window.cardMod_template_cache=window.cardMod_template_cache||{};const vt=window.cardMod_template_cache;async function ft(t,e,i){const n=rt().connection,s=JSON.stringify([e,i]);let o=vt[s];o?(o.callbacks.has(t)||pt(t),t(o.value),o.callbacks.add(t)):(pt(t),t(""),i=Object.assign({user:rt().user.name,browser:dt,hash:location.hash.substr(1)||""},i),vt[s]=o={template:e,variables:i,value:"",callbacks:new Set([t]),unsubscribe:n.subscribeMessage((t=>function(t,e){const i=vt[t];i&&(i.value=e.result,i.callbacks.forEach((t=>t(e.result))))}(s,t)),{type:"render_template",template:e,variables:i})})}async function pt(t){let e;for(const[i,n]of Object.entries(vt))if(n.callbacks.has(t)){n.callbacks.delete(t),0==n.callbacks.size&&(e=n.unsubscribe,delete vt[i]);break}e&&await(await e)()}var gt;const _t=(t,e)=>{if(t===e)return!0;if(typeof t!=typeof e)return!1;if(!(t instanceof Object&&e instanceof Object))return!1;for(const i in t)if(t.hasOwnProperty(i)){if(!e.hasOwnProperty(i))return!1;if(t[i]!==e[i]){if("object"!=typeof t[i])return!1;if(!_t(t[i],e[i]))return!1}}for(const i in e)if(e.hasOwnProperty(i)&&!t.hasOwnProperty(i))return!1;return!0};window.autoEntities_cache=null!==(gt=window.autoEntities_cache)&&void 0!==gt?gt:{};const mt=window.autoEntities_cache;async function yt(t){var e;return mt.areas=null!==(e=mt.areas)&&void 0!==e?e:await t.callWS({type:"config/area_registry/list"}),mt.areas}function bt(){return mt.areas}async function $t(t){var e;return mt.devices=null!==(e=mt.devices)&&void 0!==e?e:await t.callWS({type:"config/device_registry/list"}),mt.devices}function wt(){return mt.devices}async function Et(t){var e;return mt.entities=null!==(e=mt.entities)&&void 0!==e?e:await t.callWS({type:"config/entity_registry/list"}),mt.entities}function At(){return mt.entities}const Ct=/([mhd])\s+ago\s*$/i,St="m ago";function Ot(t,e){if("string"==typeof t&&t.startsWith("$$")&&(t=t.substring(2),e=JSON.stringify(e)),"string"==typeof e&&"string"==typeof t&&(t.startsWith("/")&&t.endsWith("/")||-1!==t.indexOf("*"))){return t.startsWith("/")||(t=`/^${t=t.replace(/\./g,".").replace(/\*/g,".*")}$/`),new RegExp(t.slice(1,-1)).test(e)}if("string"==typeof t){const i=Ct.exec(t);if(i){t=t.replace(i[0],"");e=((new Date).getTime()-new Date(e).getTime())/6e4;const n=i[1];"h"===n?e/=60:"d"===n&&(e=e/60/24)}}if("string"==typeof t){if(t.startsWith("<="))return parseFloat(e)<=parseFloat(t.substring(2));if(t.startsWith(">="))return parseFloat(e)>=parseFloat(t.substring(2));if(t.startsWith("<"))return parseFloat(e)"))return parseFloat(e)>parseFloat(t.substring(1));if(t.startsWith("!"))return parseFloat(e)!=parseFloat(t.substring(1));if(t.startsWith("="))return parseFloat(e)==parseFloat(t.substring(1))}return t===e}const jt={options:async()=>!0,sort:async()=>!0,domain:async(t,e,i)=>Ot(e,i.entity_id.split(".")[0]),entity_id:async(t,e,i)=>Ot(e,i.entity_id),state:async(t,e,i)=>Ot(e,i.state),name:async(t,e,i)=>{var n;return Ot(e,null===(n=i.attributes)||void 0===n?void 0:n.friendly_name)},group:async(t,e,i)=>{var n,s,o;return null===(o=null===(s=null===(n=t.states[e])||void 0===n?void 0:n.attributes)||void 0===s?void 0:s.entity_id)||void 0===o?void 0:o.includes(i.entity_id)},attributes:async(t,e,i)=>{for(const[t,n]of Object.entries(e)){let e=t.split(" ")[0],s=i.attributes;for(const t of e.split(":"))s=s?s[t]:void 0;if(void 0===s||!Ot(n,s))return!1}return!0},not:async(t,e,i)=>!await kt(t,e,i.entity_id),and:async(t,e,i)=>{for(const n of e)if(!await kt(t,n,i.entity_id))return!1;return!0},or:async(t,e,i)=>{for(const n of e)if(await kt(t,n,i.entity_id))return!0;return!1},device:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id));if(!n)return!1;const s=(await $t(t)).find((t=>t.id===n.device_id));return!!s&&(Ot(e,s.name_by_user)||Ot(e,s.name))},device_manufacturer:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id));if(!n)return!1;const s=(await $t(t)).find((t=>t.id===n.device_id));return!!s&&Ot(e,s.manufacturer)},device_model:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id));if(!n)return!1;const s=(await $t(t)).find((t=>t.id===n.device_id));return!!s&&Ot(e,s.model)},area:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id));if(!n)return!1;let s=(await yt(t)).find((t=>t.area_id===n.area_id));if(s)return Ot(e,s.name)||Ot(e,s.area_id);const o=(await $t(t)).find((t=>t.id===n.device_id));return!!o&&(s=(await yt(t)).find((t=>t.area_id===o.area_id)),!!s&&(Ot(e,s.name)||Ot(e,s.area_id)))},entity_category:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id));return!!n&&Ot(e,n.entity_category)},last_changed:async(t,e,i)=>(Ct.test(e)||(e+=St),Ot(e,i.last_changed)),last_updated:async(t,e,i)=>(Ct.test(e)||(e+=St),Ot(e,i.last_updated)),last_triggered:async(t,e,i)=>null!=i.attributes.last_triggered&&(Ct.test(e)||(e+=St),Ot(e,i.attributes.last_triggered)),integration:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id));return!!n&&Ot(e,n.platform)},hidden_by:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id));return!!n&&Ot(e,n.hidden_by)},label:async(t,e,i)=>{const n=(await Et(t)).find((t=>t.entity_id===i.entity_id)),s=await async function(t){var e;return mt.labels=null!==(e=mt.labels)&&void 0!==e?e:await t.callWS({type:"config/label_registry/list"}),mt.labels}(t),o=(t,e)=>{if(Ot(t,e))return!0;const i=s.find((t=>t.label_id===e));if(!i)return!1;return Ot(t,i.name)};if(!n)return!1;if(!n.labels)return!1;const r=n.labels.some((t=>o(e,t)));if(r)return r;const a=(await $t(t)).find((t=>t.id===n.device_id));if(!a)return!1;return a.labels.some((t=>o(e,t)))}};async function kt(t,e,i){var n;if(!t.states[i])return!1;for(let[s,o]of Object.entries(e))if(s=s.trim().split(" ")[0].trim(),!await(null===(n=jt[s])||void 0===n?void 0:n.call(jt,t,o,t.states[i])))return!1;return!0}function xt(t,e,i){var n,s,o,r;const[a,l]=i.reverse?[-1,1]:[1,-1];return i.ignore_case&&(t=null!==(s=null===(n=null==t?void 0:t.toLowerCase)||void 0===n?void 0:n.call(t))&&void 0!==s?s:t,e=null!==(r=null===(o=null==e?void 0:e.toLowerCase)||void 0===o?void 0:o.call(e))&&void 0!==r?r:e),i.numeric&&(isNaN(parseFloat(t))&&isNaN(parseFloat(e))||(t=isNaN(parseFloat(t))?void 0:parseFloat(t),e=isNaN(parseFloat(e))?void 0:parseFloat(e))),void 0===t&&void 0===e?0:void 0===t?a:void 0===e?l:i.numeric?t===e?0:(i.reverse?-1:1)*(t0,domain:(t,e,i)=>{var n,s;return xt(null===(n=null==t?void 0:t.entity_id)||void 0===n?void 0:n.split(".")[0],null===(s=null==e?void 0:e.entity_id)||void 0===s?void 0:s.split(".")[0],i)},entity_id:(t,e,i)=>xt(null==t?void 0:t.entity_id,null==e?void 0:e.entity_id,i),friendly_name:(t,e,i)=>{var n,s,o,r;return xt((null===(n=null==t?void 0:t.attributes)||void 0===n?void 0:n.friendly_name)||(null===(s=null==t?void 0:t.entity_id)||void 0===s?void 0:s.split(".")[1]),(null===(o=null==e?void 0:e.attributes)||void 0===o?void 0:o.friendly_name)||(null===(r=null==e?void 0:e.entity_id)||void 0===r?void 0:r.split(".")[1]),i)},name:(t,e,i)=>{var n,s,o,r;return xt((null===(n=null==t?void 0:t.attributes)||void 0===n?void 0:n.friendly_name)||(null===(s=null==t?void 0:t.entity_id)||void 0===s?void 0:s.split(".")[1]),(null===(o=null==e?void 0:e.attributes)||void 0===o?void 0:o.friendly_name)||(null===(r=null==e?void 0:e.entity_id)||void 0===r?void 0:r.split(".")[1]),i)},device:(t,e,i)=>{var n,s;const o=At().find((e=>e.entity_id===t.entity_id)),r=At().find((t=>t.entity_id===e.entity_id));if(!o||!r)return 0;const a=wt().find((t=>t.id===o.device_id)),l=wt().find((t=>t.id===r.device_id));return a&&l?xt(null!==(n=a.name_by_user)&&void 0!==n?n:a.name,null!==(s=l.name_by_user)&&void 0!==s?s:l.name,i):0},area:(t,e,i)=>{const n=At().find((e=>e.entity_id===t.entity_id)),s=At().find((t=>t.entity_id===e.entity_id));if(!n||!s)return 0;const o=wt().find((t=>t.id===n.device_id)),r=wt().find((t=>t.id===s.device_id));if(!o||!r)return 0;const a=bt().find((t=>t.area_id===o.area_id)),l=bt().find((t=>t.area_id===r.area_id));return a&&l?xt(a.name,l.name,i):0},state:(t,e,i)=>xt(null==t?void 0:t.state,null==e?void 0:e.state,i),attribute:(t,e,i)=>{var n;const[s,o]=(null==i?void 0:i.reverse)?[-1,1]:[1,-1];let r=null==t?void 0:t.attributes,a=null==e?void 0:e.attributes;for(const t of null===(n=null==i?void 0:i.attribute)||void 0===n?void 0:n.split(":")){if(void 0===r&&void 0===a)return 0;if(void 0===r)return s;if(void 0===a)return o;[r,a]=[r[t],a[t]]}return xt(r,a,i)},last_changed:(t,e,i)=>{const[n,s]=(null==i?void 0:i.reverse)?[-1,1]:[1,-1];return null==(null==t?void 0:t.last_changed)&&null==(null==e?void 0:e.last_changed)?0:null==(null==t?void 0:t.last_changed)?n:null==(null==e?void 0:e.last_changed)?s:(i.numeric=!0,xt(new Date(null==t?void 0:t.last_changed).getTime(),new Date(null==e?void 0:e.last_changed).getTime(),i))},last_updated:(t,e,i)=>{const[n,s]=(null==i?void 0:i.reverse)?[-1,1]:[1,-1];return null==(null==t?void 0:t.last_updated)&&null==(null==e?void 0:e.last_updated)?0:null==(null==t?void 0:t.last_updated)?n:null==(null==e?void 0:e.last_updated)?s:(i.numeric=!0,xt(new Date(null==t?void 0:t.last_updated).getTime(),new Date(null==e?void 0:e.last_updated).getTime(),i))},last_triggered:(t,e,i)=>{var n,s,o,r,a,l;const[d,c]=(null==i?void 0:i.reverse)?[-1,1]:[1,-1];return null==(null===(n=null==t?void 0:t.attributes)||void 0===n?void 0:n.last_triggered)&&null==(null===(s=null==e?void 0:e.attributes)||void 0===s?void 0:s.last_triggered)?0:null==(null===(o=null==t?void 0:t.attributes)||void 0===o?void 0:o.last_triggered)?d:null==(null===(r=null==e?void 0:e.attributes)||void 0===r?void 0:r.last_triggered)?c:(i.numeric=!0,xt(new Date(null===(a=null==t?void 0:t.attributes)||void 0===a?void 0:a.last_triggered).getTime(),new Date(null===(l=null==e?void 0:e.attributes)||void 0===l?void 0:l.last_triggered).getTime(),i))}};function Ut(t,e){return function(i,n){var s,o;return null!==(o=null===(s=Tt[e.method])||void 0===s?void 0:s.call(Tt,t.states[i.entity],t.states[n.entity],e))&&void 0!==o?o:0}}var Pt="1.13.0";const Mt=["none","domain","entity_id","state","name","group","area","device","device_manufacturer","device_model","attributes","last_changed","last_updated","last_triggered","entity_category","integration","hidden_by","label"],Nt={type:"select",options:[["domain","Entity Domain"],["entity_id","Entity ID"],["state","Entity State"],["name","Friendly Name"],["group","Member of Group"],["area","In area"],["device","Device"],["label","Label"],["device_manufacturer","Device Manufacturer"],["device_model","Device Model"],["attributes","Attribute"],["last_changed","Last Change"],["last_updated","Last Update"],["last_triggered","Last Trigger"],["entity_category","Entity Category"],["integration","Governing integration"],["hidden_by","Hidden by"]]},It=([t,e],i)=>{var n;return Mt.includes(t)?{type:"grid",name:"",schema:[Object.assign(Object.assign({},Nt),{name:`key_${i}`,label:"Property"}),{name:`value_${i}`,selector:null!==(n={attributes:{object:{}}}[t])&&void 0!==n?n:{text:{}},label:"Value"}]}:{type:"Constant",name:"Some filters are not shown",value:"Please switch to the CODE EDITOR to access all options."}},Rt=[{name:"options",selector:{object:{}}}],Ht=[{name:"data",selector:{object:{}}}],Dt=[{name:"method",label:"Sort method",type:"select",options:[["domain","Entity Domain"],["entity_id","Entity ID"],["friendly_name","Friendly Name"],["state","Entity State"],["last_changed","Last Change"],["last_updated","Last Update"],["last_triggered","Last Trigger"]]},{type:"constant",name:"Sorting options:",value:""},{type:"grid",name:"",schema:[{name:"reverse",type:"boolean",label:"Reverse"},{name:"ignore_case",type:"boolean",label:"Ignore case"},{name:"numeric",type:"boolean",label:"Numeric sort"},{name:"ip",type:"boolean",label:"IP address short"}]}],Ft=[{type:"grid",name:"",schema:[{name:"show_empty",type:"boolean",label:"Show if empty"},{name:"card_param",type:"string",label:"Parameter to populate"}]}];class Lt extends tt{constructor(){super(...arguments),this._selectedTab=0,this._cardGUIMode=!0,this._cardGUIModeAvailable=!0}setConfig(t){this._config=t}connectedCallback(){super.connectedCallback(),(async()=>{var t,e;if(customElements.get("ha-form"))return;const i=await(null===(e=(t=window).loadCardHelpers)||void 0===e?void 0:e.call(t));if(!i)return;const n=await i.createCardElement({type:"entity"});n&&await n.getConfigElement()})()}_handleSwitchTab(t){this._selectedTab=parseInt(t.detail.index,10)}_addFilterGroup(){var t;if(!this._config)return;const e=[...null===(t=this._config.filter)||void 0===t?void 0:t.include];e.push({});const i=Object.assign(Object.assign({},this._config.filter),{include:e});this._config=Object.assign(Object.assign({},this._config),{filter:i}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_deleteFilterGroup(t){var e;if(!this._config)return;const i=[...null===(e=this._config.filter)||void 0===e?void 0:e.include];i.splice(t,1);const n=Object.assign(Object.assign({},this._config.filter),{include:i});this._config=Object.assign(Object.assign({},this._config),{filter:n}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_moveFilterGroup(t,e){var i;if(!this._config)return;const n=[...null===(i=this._config.filter)||void 0===i?void 0:i.include];[n[t],n[t+e]]=[n[t+e],n[t]];const s=Object.assign(Object.assign({},this._config.filter),{include:n});this._config=Object.assign(Object.assign({},this._config),{filter:s}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_addSpecialEntry(){var t;if(!this._config)return;const e=[...null===(t=this._config.filter)||void 0===t?void 0:t.include];e.push({type:""});const i=Object.assign(Object.assign({},this._config.filter),{include:e});this._config=Object.assign(Object.assign({},this._config),{filter:i}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}async _changeSpecialEntry(t,e){var i,n,s,o;if(!this._config)return;const r=null!==(n=Object.assign({},null===(i=e.detail.value)||void 0===i?void 0:i.data))&&void 0!==n?n:{type:""};r.type=null!==(s=r.type)&&void 0!==s?s:"";const a=[...null===(o=this._config.filter)||void 0===o?void 0:o.include];a[t]=r;const l=Object.assign(Object.assign({},this._config.filter),{include:a});this._config=Object.assign(Object.assign({},this._config),{filter:l}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}async _changeGroupOptions(t,e){var i;if(!this._config)return;const n=e.detail.value,s=[...null===(i=this._config.filter)||void 0===i?void 0:i.include];s[t]=Object.assign({},n);const o=Object.assign(Object.assign({},this._config.filter),{include:s});this._config=Object.assign(Object.assign({},this._config),{filter:o}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeFilter(t,e){var i;if(!this._config)return;const n=((t,e)=>{var i;const n={};for(let s=0;s<=t.filter.include.length+1;s++)void 0!==e[`key_${s}`]&&(n[e[`key_${s}`]]=null!==(i=e[`value_${s}`])&&void 0!==i?i:"");return void 0!==e.key_new&&(n[e.key_new]=""),n})(this._config,e.detail.value),s=[...null===(i=this._config.filter)||void 0===i?void 0:i.include];s[t]=Object.assign(Object.assign({},n),{options:s[t].options}),this._config.filter=Object.assign(Object.assign({},this._config.filter),{include:s}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeSortOptions(t){if(!this._config)return;const e=t.detail.value;this._config=Object.assign(Object.assign({},this._config),{sort:e}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeCardOptions(t){if(!this._config)return;const e=t.detail.value;this._config=Object.assign(Object.assign({},this._config),e),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_showEmptyToggle(){if(!this._config)return;const t=!1===this._config.show_empty;this._config=Object.assign(Object.assign({},this._config),{show_empty:t}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeCardParam(t){if(!this._config)return;const e=""===t.target.value||"entities"===t.target.value?void 0:t.target.value;this._config=Object.assign(Object.assign({},this._config),{card_param:e}),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_getCardConfig(){const t=Object.assign({},this._config.card);return t[this._config.card_param||"entities"]=[],t}_handleCardConfigChanged(t){if(t.stopPropagation(),!this._config)return;const e=Object.assign({},t.detail.config);delete e[this._config.card_param||"entities"],this._config=Object.assign(Object.assign({},this._config),{card:e}),this._cardGUIModeAvailable=t.detail.guiModeAvailable,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_deleteCard(t){this._config&&(this._config=Object.assign({},this._config),delete this._config.card,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}})))}_toggleCardMode(t){var e;null===(e=this._cardEditorEl)||void 0===e||e.toggleMode()}_cardGUIModeChanged(t){t.stopPropagation(),this._cardGUIMode=t.detail.guiMode,this._cardGUIModeAvailable=t.detail.guiModeAvailable}render(){return this.hass&&this._config?N` +function t(t,e,i,n){var s,o=arguments.length,r=o<3?e:null===n?n=Object.getOwnPropertyDescriptor(e,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,e,i,n);else for(var a=t.length-1;a>=0;a--)(s=t[a])&&(r=(o<3?s(r):o>3?s(e,i,r):s(e,i))||r);return o>3&&r&&Object.defineProperty(e,i,r),r}const e=window,i=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,n=Symbol(),s=new WeakMap;let o=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==n)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(i&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=s.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&s.set(e,t))}return t}toString(){return this.cssText}};const r=(t,...e)=>{const i=1===t.length?t[0]:e.reduce(((e,i,n)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[n+1]),t[0]);return new o(i,t,n)},a=i?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new o("string"==typeof t?t:t+"",void 0,n))(e)})(t):t;var l;const c=window,d=c.trustedTypes,h=d?d.emptyScript:"",u=c.reactiveElementPolyfillSupport,p={toAttribute(t,e){switch(e){case Boolean:t=t?h:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},v=(t,e)=>e!==t&&(e==e||t==t),g={attribute:!0,type:String,converter:p,reflect:!1,hasChanged:v};let _=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const n=this._$Ep(i,e);void 0!==n&&(this._$Ev.set(n,i),t.push(n))})),t}static createProperty(t,e=g){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,n=this.getPropertyDescriptor(t,i,e);void 0!==n&&Object.defineProperty(this.prototype,t,n)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(n){const s=this[t];this[e]=n,this.requestUpdate(t,s,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||g}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var t;const n=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,n)=>{i?t.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((i=>{const n=document.createElement("style"),s=e.litNonce;void 0!==s&&n.setAttribute("nonce",s),n.textContent=i.cssText,t.appendChild(n)}))})(n,this.constructor.elementStyles),n}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=g){var n;const s=this.constructor._$Ep(t,i);if(void 0!==s&&!0===i.reflect){const o=(void 0!==(null===(n=i.converter)||void 0===n?void 0:n.toAttribute)?i.converter:p).toAttribute(e,i.type);this._$El=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),this._$El=null}}_$AK(t,e){var i;const n=this.constructor,s=n._$Ev.get(t);if(void 0!==s&&this._$El!==s){const t=n.getPropertyOptions(s),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:p;this._$El=s,this[s]=o.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let n=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||v)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):n=!1),!this.isUpdatePending&&n&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};var f;_.finalized=!0,_.elementProperties=new Map,_.elementStyles=[],_.shadowRootOptions={mode:"open"},null==u||u({ReactiveElement:_}),(null!==(l=c.reactiveElementVersions)&&void 0!==l?l:c.reactiveElementVersions=[]).push("1.5.0");const m=window,y=m.trustedTypes,b=y?y.createPolicy("lit-html",{createHTML:t=>t}):void 0,$=`lit$${(Math.random()+"").slice(9)}$`,w="?"+$,E=`<${w}>`,A=document,C=(t="")=>A.createComment(t),S=t=>null===t||"object"!=typeof t&&"function"!=typeof t,P=Array.isArray,k=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,O=/-->/g,x=/>/g,M=RegExp(">|[ \t\n\f\r](?:([^\\s\"'>=/]+)([ \t\n\f\r]*=[ \t\n\f\r]*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)","g"),T=/'/g,U=/"/g,N=/^(?:script|style|textarea|title)$/i,I=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),R=Symbol.for("lit-noChange"),H=Symbol.for("lit-nothing"),D=new WeakMap,j=A.createTreeWalker(A,129,null,!1),F=(t,e)=>{const i=t.length-1,n=[];let s,o=2===e?"":"",r=k;for(let e=0;e"===l[0]?(r=null!=s?s:k,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?M:'"'===l[3]?U:T):r===U||r===T?r=M:r===O||r===x?r=k:(r=M,s=void 0);const h=r===M&&t[e+1].startsWith("/>")?" ":"";o+=r===k?i+E:c>=0?(n.push(a),i.slice(0,c)+"$lit$"+i.slice(c)+$+h):i+$+(-2===c?(n.push(void 0),e):h)}const a=o+(t[i]||"")+(2===e?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==b?b.createHTML(a):a,n]};class L{constructor({strings:t,_$litType$:e},i){let n;this.parts=[];let s=0,o=0;const r=t.length-1,a=this.parts,[l,c]=F(t,e);if(this.el=L.createElement(l,i),j.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=j.nextNode())&&a.length0){n.textContent=y?y.emptyScript:"";for(let i=0;iP(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.k(t):this.g(t)}O(t,e=this._$AB){return this._$AA.parentNode.insertBefore(t,e)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}g(t){this._$AH!==H&&S(this._$AH)?this._$AA.nextSibling.data=t:this.T(A.createTextNode(t)),this._$AH=t}$(t){var e;const{values:i,_$litType$:n}=t,s="number"==typeof n?this._$AC(t):(void 0===n.el&&(n.el=L.createElement(n.h,this.options)),n);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===s)this._$AH.p(i);else{const t=new W(s,this),e=t.v(this.options);t.p(i),this.T(e),this._$AH=t}}_$AC(t){let e=D.get(t.strings);return void 0===e&&D.set(t.strings,e=new L(t)),e}k(t){P(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,n=0;for(const s of t)n===e.length?e.push(i=new z(this.O(C()),this.O(C()),this,this.options)):i=e[n],i._$AI(s),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=H}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,n){const s=this.strings;let o=!1;if(void 0===s)t=G(this,t,e,0),o=!S(t)||t!==this._$AH&&t!==R,o&&(this._$AH=t);else{const n=t;let r,a;for(t=s[0],r=0;r{var n,s;const o=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:e;let r=o._$litPart$;if(void 0===r){const t=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:null;o._$litPart$=r=new z(e.insertBefore(C(),t),t,void 0,null!=i?i:{})}return r._$AI(t),r})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return R}}tt.finalized=!0,tt._$litElement$=!0,null===(Q=globalThis.litElementHydrateSupport)||void 0===Q||Q.call(globalThis,{LitElement:tt});const et=globalThis.litElementPolyfillSupport;null==et||et({LitElement:tt}),(null!==(X=globalThis.litElementVersions)&&void 0!==X?X:globalThis.litElementVersions=[]).push("3.2.2");const it=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function nt(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):it(t,e)}function st(t){return nt({...t,state:!0})}var ot;function rt(){return document.querySelector("hc-main")?document.querySelector("hc-main").hass:document.querySelector("home-assistant")?document.querySelector("home-assistant").hass:void 0}null===(ot=window.HTMLSlotElement)||void 0===ot||ot.prototype.assignedElements;const at="lovelace-player-device-id";function lt(){if(!localStorage[at]){const t=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);window.fully&&"function"==typeof fully.getDeviceId?localStorage[at]=fully.getDeviceId():localStorage[at]=`${t()}${t()}-${t()}${t()}`}return localStorage[at]}let ct=lt();const dt=new URLSearchParams(window.location.search);var ht;function ut(t){return!!String(t).includes("{%")||(!!String(t).includes("{{")||void 0)}dt.get("deviceID")&&null!==(ht=dt.get("deviceID"))&&("clear"===ht?localStorage.removeItem(at):localStorage[at]=ht,ct=lt()),window.cardMod_template_cache=window.cardMod_template_cache||{};const pt=window.cardMod_template_cache;async function vt(t,e,i){const n=rt().connection,s=JSON.stringify([e,i]);let o=pt[s];o?(o.callbacks.has(t)||gt(t),t(o.value),o.callbacks.add(t)):(gt(t),t(""),i={user:rt().user.name,browser:ct,hash:location.hash.substr(1)||"",...i},pt[s]=o={template:e,variables:i,value:"",callbacks:new Set([t]),unsubscribe:n.subscribeMessage((t=>function(t,e){const i=pt[t];i&&(i.value=e.result,i.callbacks.forEach((t=>t(e.result))))}(s,t)),{type:"render_template",template:e,variables:i})})}async function gt(t){let e;for(const[i,n]of Object.entries(pt))if(n.callbacks.has(t)){n.callbacks.delete(t),0==n.callbacks.size&&(e=n.unsubscribe,delete pt[i]);break}e&&await(await e)()}const _t=(t,e)=>{if(t===e)return!0;if(typeof t!=typeof e)return!1;if(!(t instanceof Object&&e instanceof Object))return!1;for(const i in t)if(t.hasOwnProperty(i)){if(!e.hasOwnProperty(i))return!1;if(t[i]!==e[i]){if("object"!=typeof t[i])return!1;if(!_t(t[i],e[i]))return!1}}for(const i in e)if(e.hasOwnProperty(i)&&!t.hasOwnProperty(i))return!1;return!0};function ft(t,e){function i(){let t,e,i=!1;const n=new Promise(((i,n)=>{t=i,e=n}));return{promise:n,resolve:t,reject:e,load:s=>{if(!i){i=!0;try{s().then(t,e)}catch(t){if(i=!1,void 0!==t)throw t}}return n}}}let n=i();return{get_cached:async function(){return await n.promise},get:async function(...s){try{return await n.load((()=>e(...s)))}catch(e){throw void 0!==e&&console.warn(`${t} failed:`,e),n=i(),e}}}}function mt(t,e){return i=>{if(!i)throw void 0;return(async()=>new Map((await i.callWS({type:e})).map((e=>[e[t],e]))))()}}const{get_cached:yt,get:bt}=ft("getDevices",mt("id","config/device_registry/list")),{get_cached:$t,get:wt}=ft("getAreas",mt("area_id","config/area_registry/list")),{get_cached:Et,get:At}=ft("getEntities",mt("entity_id","config/entity_registry/list")),{get_cached:Ct,get:St}=ft("getLabels",mt("label_id","config/label_registry/list")),{get_cached:Pt,get:kt}=ft("getEntityAreas",(async t=>{const[e,i,n]=await Promise.all([At(t),bt(t),wt(t)]);return new Map(Array.from(e.values()).flatMap((t=>{let e=n.get(t.area_id);if(e)return[[t.entity_id,e.area_id]];const s=i.get(t.device_id);return s?(e=n.get(s.area_id),e?[[t.entity_id,e.area_id]]:[]):[]})))})),Ot=/([mhd])\s+ago\s*$/i,xt="m ago";async function Mt(t){let e=[];let i=[];if("string"==typeof t){if(t.startsWith("$$")&&(t=t.substring(2),e.push(JSON.stringify)),t.startsWith("/")&&t.endsWith("/")||-1!==t.indexOf("*")){t.startsWith("/")||(t=`/^${t=t.replace(/\./g,".").replace(/\*/g,".*")}$/`);const e=new RegExp(t.slice(1,-1));i.push([t=>"string"==typeof t,t=>e.test(t)])}const n=Ot.exec(t);if(n){t=t.replace(n[0],"");const i=(new Date).getTime();e.push((t=>{const e=new Date(t).getTime();t=(i-e)/6e4;const s=n[1];"h"===s?t/=60:"d"===s&&(t=t/60/24)}))}if(t.startsWith("<=")){const e=parseFloat(t.substring(2));i.push([()=>!0,t=>parseFloat(t)<=e])}if(t.startsWith(">=")){const e=parseFloat(t.substring(2));i.push([()=>!0,t=>parseFloat(t)>=e])}if(t.startsWith("<")){const e=parseFloat(t.substring(1));i.push([()=>!0,t=>parseFloat(t)")){const e=parseFloat(t.substring(1));i.push([()=>!0,t=>parseFloat(t)>e])}if(t.startsWith("!")){const e=parseFloat(t.substring(1));i.push([()=>!0,t=>parseFloat(t)!=e])}if(t.startsWith("=")){const e=parseFloat(t.substring(1));i.push([()=>!0,t=>parseFloat(t)==e])}}return i.push([()=>!0,e=>t===e]),t=>{const n=function(t){return e.reduce(((t,e)=>e(t)),t)}(t);return(0,i.find((([t,e])=>t(n)))[1])(n)}}const Tt={options:async(t,e)=>t=>!0,sort:async(t,e)=>t=>!0,domain:async(t,e)=>{const[i]=await Promise.all([Mt(e)]);return t=>i(t.entity_id.split(".")[0])},entity_id:async(t,e)=>{const[i]=await Promise.all([Mt(e)]);return t=>i(t.entity_id)},state:async(t,e)=>{const[i]=await Promise.all([Mt(e)]);return t=>i(t.state)},name:async(t,e)=>{const[i]=await Promise.all([Mt(e)]);return t=>{var e;return i(null===(e=t.attributes)||void 0===e?void 0:e.friendly_name)}},group:async(t,e)=>i=>{var n,s,o;return null===(o=null===(s=null===(n=t.states[e])||void 0===n?void 0:n.attributes)||void 0===s?void 0:s.entity_id)||void 0===o?void 0:o.includes(i.entity_id)},attributes:async(t,e)=>{let i=(await Promise.all(Object.entries(e).map((async([t,e])=>{const i=t.split(" ")[0].split(":");return{stepper:t=>i.reduce(((t,e)=>null==t?void 0:t[e]),t),matcher:await Mt(e)}})))).map((({stepper:t,matcher:e})=>i=>{const n=t(i.attributes);return void 0!==n&&e(n)}));return t=>!i.map((e=>e(t))).includes(!1)},not:async(t,e)=>{const[i]=await Promise.all([Ut(t,e)]);return t=>!i(t.entity_id)},and:async(t,e)=>{const i=await Promise.all(e.map((e=>Ut(t,e))));return t=>!i.some((e=>!e(t)))},or:async(t,e)=>{const i=await Promise.all(e.map((e=>Ut(t,e))));return t=>i.some((e=>e(t)))},device:async(t,e)=>{const[i,n,s]=await Promise.all([At(t),bt(t),Mt(e)]);return t=>{const e=i.get(t.entity_id);if(!e)return!1;const o=n.get(e.device_id);return!!o&&(s(o.name_by_user)||s(o.name))}},device_manufacturer:async(t,e)=>{const[i,n,s]=await Promise.all([At(t),bt(t),Mt(e)]);return t=>{const e=i.get(t.entity_id);if(!e)return!1;const o=n.get(e.device_id);return!!o&&s(o.manufacturer)}},device_model:async(t,e)=>{const[i,n,s]=await Promise.all([At(t),bt(t),Mt(e)]);return t=>{const e=i.get(t.entity_id);if(!e)return!1;const o=n.get(e.device_id);return!!o&&s(o.model)}},area:async(t,e)=>{const[i,n,s]=await Promise.all([kt(t),wt(t),Mt(e)]);return t=>{const e=n.get(i.get(t.entity_id));return!!e&&(void 0!==e.name&&s(e.name)||void 0!==e.area_id&&s(e.area_id))}},entity_category:async(t,e)=>{const[i,n]=await Promise.all([At(t),Mt(e)]);return t=>{const e=i.get(t.entity_id);return!!e&&n(e.entity_category)}},last_changed:async(t,e)=>{const[i]=await Promise.all([Mt(e)]);return t=>(Ot.test(e)||(e+=xt),i(t.last_changed))},last_updated:async(t,e)=>{const[i]=await Promise.all([Mt(e)]);return t=>(Ot.test(e)||(e+=xt),i(t.last_updated))},last_triggered:async(t,e)=>{const[i]=await Promise.all([Mt(e)]);return t=>null!=t.attributes.last_triggered&&(Ot.test(e)||(e+=xt),i(t.attributes.last_triggered))},integration:async(t,e)=>{const[i,n]=await Promise.all([At(t),Mt(e)]);return t=>{const e=i.get(t.entity_id);return!!e&&n(e.platform)}},hidden_by:async(t,e)=>{const[i,n]=await Promise.all([At(t),Mt(e)]);return t=>{const e=i.get(t.entity_id);return!!e&&n(e.hidden_by)}},label:async(t,e)=>{const[i,n,s,o]=await Promise.all([At(t),bt(t),St(t),Mt(e)]),r=t=>{if(o(t))return!0;const e=s.get(t);if(!e)return!1;return o(e.name)};return t=>{const e=i.get(t.entity_id);if(!e)return!1;if(!e.labels)return!1;const s=e.labels.some((t=>r(t)));if(s)return s;const o=n.get(e.device_id);if(!o)return!1;return o.labels.some((t=>r(t)))}}};async function Ut(t,e){const i=(await Promise.all(Object.entries(e).map((([e,i])=>{var n;return null===(n=Tt[e.trim().split(" ")[0].trim()])||void 0===n?void 0:n.call(Tt,t,i)})))).filter(Boolean);return e=>{var n;const s=null===(n=null==t?void 0:t.states)||void 0===n?void 0:n[e];return!!s&&!i.some((t=>!t(s)))}}function Nt(t,e,i){var n,s,o,r;const[a,l]=i.reverse?[-1,1]:[1,-1];return i.ignore_case&&(t=null!==(s=null===(n=null==t?void 0:t.toLowerCase)||void 0===n?void 0:n.call(t))&&void 0!==s?s:t,e=null!==(r=null===(o=null==e?void 0:e.toLowerCase)||void 0===o?void 0:o.call(e))&&void 0!==r?r:e),i.numeric&&(isNaN(parseFloat(t))&&isNaN(parseFloat(e))||(t=isNaN(parseFloat(t))?void 0:parseFloat(t),e=isNaN(parseFloat(e))?void 0:parseFloat(e))),void 0===t&&void 0===e?0:void 0===t?a:void 0===e?l:i.numeric?t===e?0:(i.reverse?-1:1)*(t({mapper:async t=>{},sorter:(t,e)=>0}),domain:async t=>({mapper:async t=>{var e;return null===(e=null==t?void 0:t.entity_id)||void 0===e?void 0:e.split(".")[0]},sorter:(e,i)=>Nt(e,i,t)}),entity_id:async t=>({mapper:async t=>null==t?void 0:t.entity_id,sorter:(e,i)=>Nt(e,i,t)}),friendly_name:async t=>({mapper:async t=>{var e,i;return(null===(e=null==t?void 0:t.attributes)||void 0===e?void 0:e.friendly_name)||(null===(i=null==t?void 0:t.entity_id)||void 0===i?void 0:i.split(".")[1])},sorter:(e,i)=>Nt(e,i,t)}),name:async t=>({mapper:async t=>{var e,i;return(null===(e=null==t?void 0:t.attributes)||void 0===e?void 0:e.friendly_name)||(null===(i=null==t?void 0:t.entity_id)||void 0===i?void 0:i.split(".")[1])},sorter:(e,i)=>Nt(e,i,t)}),device:async t=>{const[e,i]=[Et(),yt()];return{mapper:async t=>{var n;const s=(await e).get(t.entity_id),o=s?(await i).get(s.device_id):void 0;return null!==(n=null==o?void 0:o.name_by_user)&&void 0!==n?n:null==o?void 0:o.name},sorter:(e,i)=>e===i===void 0?0:Nt(e,i,t)}},area:async t=>{const[e,i,n]=[Et(),yt(),$t()];return{mapper:async t=>{const s=(await e).get(t.entity_id),o=s?(await i).get(s.device_id):void 0,r=o?(await n).get(o.area_id):void 0;return null==r?void 0:r.name},sorter:(e,i)=>e===i===void 0?0:Nt(e,i,t)}},state:async t=>({mapper:async t=>null==t?void 0:t.state,sorter:(e,i)=>Nt(e,i,t)}),attribute:async t=>({mapper:async t=>t,sorter:(e,i)=>{var n;const[s,o]=(null==t?void 0:t.reverse)?[-1,1]:[1,-1];let r=null==e?void 0:e.attributes,a=null==i?void 0:i.attributes;for(const e of null===(n=null==t?void 0:t.attribute)||void 0===n?void 0:n.split(":")){if(void 0===r&&void 0===a)return 0;if(void 0===r)return s;if(void 0===a)return o;[r,a]=[r[e],a[e]]}return Nt(r,a,t)}}),last_changed:async t=>({mapper:async t=>t,sorter:(e,i)=>{const[n,s]=(null==t?void 0:t.reverse)?[-1,1]:[1,-1];return null==(null==e?void 0:e.last_changed)&&null==(null==i?void 0:i.last_changed)?0:null==(null==e?void 0:e.last_changed)?n:null==(null==i?void 0:i.last_changed)?s:(t.numeric=!0,Nt(new Date(null==e?void 0:e.last_changed).getTime(),new Date(null==i?void 0:i.last_changed).getTime(),t))}}),last_updated:async t=>({mapper:async t=>t,sorter:(e,i)=>{const[n,s]=(null==t?void 0:t.reverse)?[-1,1]:[1,-1];return null==(null==e?void 0:e.last_updated)&&null==(null==i?void 0:i.last_updated)?0:null==(null==e?void 0:e.last_updated)?n:null==(null==i?void 0:i.last_updated)?s:(t.numeric=!0,Nt(new Date(null==e?void 0:e.last_updated).getTime(),new Date(null==i?void 0:i.last_updated).getTime(),t))}}),last_triggered:async t=>({mapper:async t=>t,sorter:(e,i)=>{var n,s,o,r,a,l;const[c,d]=(null==t?void 0:t.reverse)?[-1,1]:[1,-1];return null==(null===(n=null==e?void 0:e.attributes)||void 0===n?void 0:n.last_triggered)&&null==(null===(s=null==i?void 0:i.attributes)||void 0===s?void 0:s.last_triggered)?0:null==(null===(o=null==e?void 0:e.attributes)||void 0===o?void 0:o.last_triggered)?c:null==(null===(r=null==i?void 0:i.attributes)||void 0===r?void 0:r.last_triggered)?d:(t.numeric=!0,Nt(new Date(null===(a=null==e?void 0:e.attributes)||void 0===a?void 0:a.last_triggered).getTime(),new Date(null===(l=null==i?void 0:i.attributes)||void 0===l?void 0:l.last_triggered).getTime(),t))}})};async function Rt(t,e){var i;const{mapper:n,sorter:s}=await(null!==(i=It[e.method])&&void 0!==i?i:It.none)(e);return async t=>{const e=await Promise.all(t.map((async(t,e)=>({i:e,value:await n(t)}))));return e.sort(((t,e)=>s(t.value,e.value))),e.map((e=>t[e.i]))}}var Ht="1.13.0";const Dt=["none","domain","entity_id","state","name","group","area","device","device_manufacturer","device_model","attributes","last_changed","last_updated","last_triggered","entity_category","integration","hidden_by","label"],jt={type:"select",options:[["domain","Entity Domain"],["entity_id","Entity ID"],["state","Entity State"],["name","Friendly Name"],["group","Member of Group"],["area","In area"],["device","Device"],["label","Label"],["device_manufacturer","Device Manufacturer"],["device_model","Device Model"],["attributes","Attribute"],["last_changed","Last Change"],["last_updated","Last Update"],["last_triggered","Last Trigger"],["entity_category","Entity Category"],["integration","Governing integration"],["hidden_by","Hidden by"]]},Ft=([t,e],i)=>{var n;return Dt.includes(t)?{type:"grid",name:"",schema:[{...jt,name:`key_${i}`,label:"Property"},{name:`value_${i}`,selector:null!==(n={attributes:{object:{}}}[t])&&void 0!==n?n:{text:{}},label:"Value"}]}:{type:"Constant",name:"Some filters are not shown",value:"Please switch to the CODE EDITOR to access all options."}},Lt=[{name:"options",selector:{object:{}}}],Gt=[{name:"data",selector:{object:{}}}],Wt=[{name:"method",label:"Sort method",type:"select",options:[["domain","Entity Domain"],["entity_id","Entity ID"],["friendly_name","Friendly Name"],["state","Entity State"],["last_changed","Last Change"],["last_updated","Last Update"],["last_triggered","Last Trigger"]]},{type:"constant",name:"Sorting options:",value:""},{type:"grid",name:"",schema:[{name:"reverse",type:"boolean",label:"Reverse"},{name:"ignore_case",type:"boolean",label:"Ignore case"},{name:"numeric",type:"boolean",label:"Numeric sort"},{name:"ip",type:"boolean",label:"IP address short"}]}],zt=[{type:"grid",name:"",schema:[{name:"show_empty",type:"boolean",label:"Show if empty"},{name:"card_param",type:"string",label:"Parameter to populate"}]}];class Bt extends tt{constructor(){super(...arguments),this._selectedTab=0,this._cardGUIMode=!0,this._cardGUIModeAvailable=!0}setConfig(t){this._config=t}connectedCallback(){super.connectedCallback(),(async()=>{var t,e;if(customElements.get("ha-form"))return;const i=await(null===(e=(t=window).loadCardHelpers)||void 0===e?void 0:e.call(t));if(!i)return;const n=await i.createCardElement({type:"entity"});n&&await n.getConfigElement()})()}_handleSwitchTab(t){this._selectedTab=parseInt(t.detail.index,10)}_addFilterGroup(){var t;if(!this._config)return;const e=[...null===(t=this._config.filter)||void 0===t?void 0:t.include];e.push({});const i={...this._config.filter,include:e};this._config={...this._config,filter:i},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_deleteFilterGroup(t){var e;if(!this._config)return;const i=[...null===(e=this._config.filter)||void 0===e?void 0:e.include];i.splice(t,1);const n={...this._config.filter,include:i};this._config={...this._config,filter:n},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_moveFilterGroup(t,e){var i;if(!this._config)return;const n=[...null===(i=this._config.filter)||void 0===i?void 0:i.include];[n[t],n[t+e]]=[n[t+e],n[t]];const s={...this._config.filter,include:n};this._config={...this._config,filter:s},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_addSpecialEntry(){var t;if(!this._config)return;const e=[...null===(t=this._config.filter)||void 0===t?void 0:t.include];e.push({type:""});const i={...this._config.filter,include:e};this._config={...this._config,filter:i},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}async _changeSpecialEntry(t,e){var i,n,s,o;if(!this._config)return;const r=null!==(n={...null===(i=e.detail.value)||void 0===i?void 0:i.data})&&void 0!==n?n:{type:""};r.type=null!==(s=r.type)&&void 0!==s?s:"";const a=[...null===(o=this._config.filter)||void 0===o?void 0:o.include];a[t]=r;const l={...this._config.filter,include:a};this._config={...this._config,filter:l},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}async _changeGroupOptions(t,e){var i;if(!this._config)return;const n=e.detail.value,s=[...null===(i=this._config.filter)||void 0===i?void 0:i.include];s[t]={...n};const o={...this._config.filter,include:s};this._config={...this._config,filter:o},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeFilter(t,e){var i;if(!this._config)return;const n=((t,e)=>{var i;const n={};for(let s=0;s<=t.filter.include.length+1;s++)void 0!==e[`key_${s}`]&&(n[e[`key_${s}`]]=null!==(i=e[`value_${s}`])&&void 0!==i?i:"");return void 0!==e.key_new&&(n[e.key_new]=""),n})(this._config,e.detail.value),s=[...null===(i=this._config.filter)||void 0===i?void 0:i.include];s[t]={...n,options:s[t].options},this._config.filter={...this._config.filter,include:s},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeSortOptions(t){if(!this._config)return;const e=t.detail.value;this._config={...this._config,sort:e},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeCardOptions(t){if(!this._config)return;const e=t.detail.value;this._config={...this._config,...e},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_showEmptyToggle(){if(!this._config)return;const t=!1===this._config.show_empty;this._config={...this._config,show_empty:t},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_changeCardParam(t){if(!this._config)return;const e=""===t.target.value||"entities"===t.target.value?void 0:t.target.value;this._config={...this._config,card_param:e},this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_getCardConfig(){const t={...this._config.card};return t[this._config.card_param||"entities"]=[],t}_handleCardConfigChanged(t){if(t.stopPropagation(),!this._config)return;const e={...t.detail.config};delete e[this._config.card_param||"entities"],this._config={...this._config,card:e},this._cardGUIModeAvailable=t.detail.guiModeAvailable,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_deleteCard(t){this._config&&(this._config={...this._config},delete this._config.card,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}})))}_toggleCardMode(t){var e;null===(e=this._cardEditorEl)||void 0===e||e.toggleMode()}_cardGUIModeChanged(t){t.stopPropagation(),this._cardGUIMode=t.detail.guiMode,this._cardGUIModeAvailable=t.detail.guiModeAvailable}render(){return this.hass&&this._config?I`
- `:N``}_renderHelp(){return N` + `:I``}_renderHelp(){return I`

Auto entities

@@ -31,15 +31,15 @@ function t(t,e,i,n){var s,o=arguments.length,r=o<3?e:null===n?n=Object.getOwnPro

Not all options are available in the GUI editor.

- `}_renderFilterEditor(){var t;return(null===(t=this._config.filter)||void 0===t?void 0:t.template)||this._config.entities?N` + `}_renderFilterEditor(){var t;return(null===(t=this._config.filter)||void 0===t?void 0:t.template)||this._config.entities?I`

Your filter method is not handled by the GUI editor.

Please switch to the CODE EDITOR to access all options.

- `:N` - ${this._config.filter.include.map(((t,e)=>N` + `:I` + ${this._config.filter.include.map(((t,e)=>I`
- ${void 0===t.type?N` + ${void 0===t.type?I` {const e=Object.assign({},t);return delete e.options,[...Object.entries(e).map(It),Object.assign(Object.assign({},Nt),{name:"key_new",label:"Select property"})]})(t)} - .data=${(t=>{const e=Object.assign({},t);return delete e.options,Object.assign({},...Object.entries(e).map((([t,e],i)=>({[`key_${i}`]:t,[`value_${i}`]:e}))))})(t)} + .schema=${(t=>{const e={...t};return delete e.options,[...Object.entries(e).map(Ft),{...jt,name:"key_new",label:"Select property"}]})(t)} + .data=${(t=>{const e={...t};return delete e.options,Object.assign({},...Object.entries(e).map((([t,e],i)=>({[`key_${i}`]:t,[`value_${i}`]:e}))))})(t)} .computeLabel=${t=>{var e;return null!==(e=t.label)&&void 0!==e?e:t.name}} @value-changed=${t=>this._changeFilter(e,t)} >

Options:

this._changeGroupOptions(e,t)} > - `:N` + `:I` this._changeSpecialEntry(e,t)} > @@ -91,26 +91,26 @@ function t(t,e,i,n){var s,o=arguments.length,r=o<3?e:null===n?n=Object.getOwnPro Add non-filter entry - `}_renderSortEditor(){var t;const e=null!==(t=this._config.sort)&&void 0!==t?t:{method:"none"};return N` + `}_renderSortEditor(){var t;const e=null!==(t=this._config.sort)&&void 0!==t?t:{method:"none"};return I`
{var e;return null!==(e=t.label)&&void 0!==e?e:t.name}} @value-changed=${this._changeSortOptions} >
- `}_renderCardEditor(){var t;const e=Object.assign({},this._config);return e.show_empty=null===(t=e.show_empty)||void 0===t||t,N` + `}_renderCardEditor(){var t;const e={...this._config};return e.show_empty=null===(t=e.show_empty)||void 0===t||t,I`
{var e;return null!==(e=t.label)&&void 0!==e?e:t.name}} .data=${e} @value-changed=${this._changeCardOptions} > - ${this._config.card?N` + ${this._config.card?I`
- `:N` + `:I` (i,n)=>{var s;if(void 0===n){const n=null!==(s=i.originalKey)&&void 0!==s?s:i.key,o=null!=e?{kind:"method",placement:"prototype",key:n,descriptor:e(i.key)}:{...i,key:n};return null!=t&&(o.finisher=function(e){t(e,n)}),o}{const s=i.constructor;void 0!==e&&Object.defineProperty(i,n,e(n)),null==t||t(s,n)}})({descriptor:i=>{const n={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};if(e){const e="symbol"==typeof i?Symbol():"__"+i;n.get=function(){var i,n;return void 0===this[e]&&(this[e]=null!==(n=null===(i=this.renderRoot)||void 0===i?void 0:i.querySelector(t))&&void 0!==n?n:null),this[e]}}return n}})}("hui-card-element-editor")],Lt.prototype,"_cardEditorEl",void 0),customElements.define("auto-entities-editor",Lt),window.customCards=window.customCards||[],window.customCards.push({type:"auto-entities",name:"Auto Entities",preview:!1,description:"Entity Filter on Steroids. Auto Entities allows you to fill other cards with entities automatically, based on a number of attributes."}),window.queueMicrotask=window.queueMicrotask||(t=>window.setTimeout(t,1));const Wt=["section","divider"];class Gt extends tt{constructor(){super(...arguments),this.empty=!1,this._updateCooldown={timer:void 0,rerun:!1},this._renderer=t=>{this._template="string"==typeof t?t.split(/[\s,]+/):t}}static getConfigElement(){return document.createElement("auto-entities-editor")}static getStubConfig(){return{card:{type:"entities"},filter:{include:[],exclude:[]}}}setConfig(t){var e,i;if(!t)throw new Error("No configuration.");if(!(null===(e=t.card)||void 0===e?void 0:e.type))throw new Error("No card type specified.");if(!t.filter&&!t.entities)throw new Error("No filters specified.");t=JSON.parse(JSON.stringify(t)),this._config=t,(null===(i=this._config.filter)||void 0===i?void 0:i.template)&&ut(this._config.filter.template)&&ft(this._renderer,this._config.filter.template,{config:t}),this._cardBuilt=new Promise((t=>this._cardBuiltResolve=t)),queueMicrotask((()=>this.build_else())),queueMicrotask((()=>this.update_all()))}connectedCallback(){var t,e;super.connectedCallback(),(null===(e=null===(t=this._config)||void 0===t?void 0:t.filter)||void 0===e?void 0:e.template)&&ut(this._config.filter.template)&&ft(this._renderer,this._config.filter.template,{config:this._config})}disconnectedCallback(){super.disconnectedCallback(),pt(this._renderer)}async update_all(){if(this.card&&(this.card.hass=this.hass),this.else&&(this.else.hass=this.hass),this._updateCooldown.timer)return void(this._updateCooldown.rerun=!0);this._updateCooldown.rerun=!1,this._updateCooldown.timer=window.setTimeout((()=>{this._updateCooldown.timer=void 0,this._updateCooldown.rerun&&this.update_all()}),500);const t=await this.update_entities();this.update_card(t)}async build_else(){if(void 0===this._config.else)return;const t=await window.loadCardHelpers();this.else=await t.createCardElement(this._config.else),this.else.hass=this.hass}async update_card(t){var e,i,n,s,o,r,a,l,d,c,h,u;if(this._entities&&_t(t,this._entities)&&_t(this._cardConfig,this._config.card))return;const v=(null===(e=this._cardConfig)||void 0===e?void 0:e.type)!==this._config.card.type;this._entities=t,this._cardConfig=JSON.parse(JSON.stringify(this._config.card));const f=Object.assign({[this._config.card_param||"entities"]:t},this._config.card);if(!this.card||v){const t=await window.loadCardHelpers();console.oldError=console.oldError||[];const e=console.error;console.oldError.push(e),console.error=(...t)=>{var i,n,s,o,r,a;3===t.length&&t[2].message&&((null===(n=(i=t[2].message).startsWith)||void 0===n?void 0:n.call(i,"Entities"))||(null===(o=(s=t[2].message).startsWith)||void 0===o?void 0:o.call(s,"Either entities"))||(null===(a=(r=t[2].message).endsWith)||void 0===a?void 0:a.call(r,"entity")))||e(...t)};try{if(this.card=await t.createCardElement(f),"hui-error-card"===this.card.localName){const t=this.card;await customElements.whenDefined("hui-error-card");let e=10;for(;!t._config&&e;)await new Promise((t=>window.setTimeout(t,100))),e--;if((null===(s=null===(n=null===(i=t._config)||void 0===i?void 0:i.error)||void 0===n?void 0:n.startsWith)||void 0===s?void 0:s.call(n,"Entities"))||(null===(a=null===(r=null===(o=t._config)||void 0===o?void 0:o.error)||void 0===r?void 0:r.startsWith)||void 0===a?void 0:a.call(r,"Either entities"))||(null===(c=null===(d=null===(l=t._config)||void 0===l?void 0:l.error)||void 0===d?void 0:d.endsWith)||void 0===c?void 0:c.call(d,"entity")))return this.card=void 0,this._entities=void 0,this._cardConfig=void 0,void(null===(h=this._cardBuiltResolve)||void 0===h||h.call(this))}}finally{console.error=console.oldError.pop()}}else this.card.setConfig(f);null===(u=this._cardBuiltResolve)||void 0===u||u.call(this),this.card.hass=this.hass,this.empty=0===t.length||t.every((t=>Wt.includes(t.type)));const p=this.empty&&!1===this._config.show_empty&&void 0===this._config.else;this.style.display=p?"none":null,this.style.margin=p?"0":null,this.card.requestUpdate&&(await this.updateComplete,this.card.requestUpdate())}async update_entities(){var t,e,i,n,s,o,r,a;const l=t=>t?"string"==typeof t?{entity:t.trim()}:t:null;let d=[...(null===(e=null===(t=this._config)||void 0===t?void 0:t.entities)||void 0===e?void 0:e.map(l))||[]];if(!this.hass)return d;if(this._template&&(d=d.concat(this._template.map(l))),d=d.filter(Boolean),null===(i=this._config.filter)||void 0===i?void 0:i.include){const t=Object.keys(this.hass.states).map(l);for(const e of this._config.filter.include){if(e.type){d.push(e);continue}let i=[];for(const n of t)await kt(this.hass,e,n.entity)&&i.push(JSON.parse(JSON.stringify(Object.assign(Object.assign({},n),e.options)).replace(/this.entity_id/g,n.entity)));if(e.sort&&(await Et(this.hass),await $t(this.hass),await yt(this.hass),i=i.sort(Ut(this.hass,e.sort)),null!==(n=e.sort.count)&&void 0!==n?n:e.sort.first)){const t=null!==(s=e.sort.first)&&void 0!==s?s:0;i=i.slice(t,t+(null!==(o=e.sort.count)&&void 0!==o?o:1/0))}d=d.concat(i)}}if(null===(r=this._config.filter)||void 0===r?void 0:r.exclude)for(const t of this._config.filter.exclude){const e=[];for(const i of d)void 0!==i.entity&&await kt(this.hass,t,i.entity)||e.push(i);d=e}if(this._config.sort&&(d=d.sort(Ut(this.hass,this._config.sort)),this._config.sort.count)){const t=null!==(a=this._config.sort.first)&&void 0!==a?a:0;d=d.slice(t,t+this._config.sort.count)}if(this._config.unique){let t=[];for(const e of d)"entity"===this._config.unique&&e.entity&&t.some((t=>t.entity===e.entity))||t.some((t=>_t(t,e)))||t.push(e);d=t}return d}async updated(t){(t.has("_template")||t.has("hass")&&this.hass)&&queueMicrotask((()=>this.update_all()))}createRenderRoot(){return this}render(){return N`${this.empty&&(!1===this._config.show_empty||this._config.else)?this.else:this.card}`}async getCardSize(){var t,e;let i=0;return await this._cardBuilt,this.card&&this.card.getCardSize&&(i=await this.card.getCardSize()),1===i&&(null===(t=this._entities)||void 0===t?void 0:t.length)&&(i=this._entities.length),0===i&&(null===(e=this._config.filter)||void 0===e?void 0:e.include)&&(i=Object.keys(this._config.filter.include).length),i||5}}t([nt()],Gt.prototype,"_config",void 0),t([nt()],Gt.prototype,"hass",void 0),t([nt()],Gt.prototype,"card",void 0),t([nt()],Gt.prototype,"else",void 0),t([nt()],Gt.prototype,"_template",void 0),t([st()],Gt.prototype,"empty",void 0),customElements.get("auto-entities")||(customElements.define("auto-entities",Gt),console.groupCollapsed(`%cAUTO-ENTITIES ${Pt} IS INSTALLED`,"color: green; font-weight: bold"),console.log("Readme:","https://github.com/thomasloven/lovelace-auto-entities"),console.groupEnd()); + `]}}t([st()],Bt.prototype,"_config",void 0),t([nt()],Bt.prototype,"lovelace",void 0),t([nt()],Bt.prototype,"hass",void 0),t([st()],Bt.prototype,"_selectedTab",void 0),t([st()],Bt.prototype,"_cardGUIMode",void 0),t([st()],Bt.prototype,"_cardGUIModeAvailable",void 0),t([function(t,e){return(({finisher:t,descriptor:e})=>(i,n)=>{var s;if(void 0===n){const n=null!==(s=i.originalKey)&&void 0!==s?s:i.key,o=null!=e?{kind:"method",placement:"prototype",key:n,descriptor:e(i.key)}:{...i,key:n};return null!=t&&(o.finisher=function(e){t(e,n)}),o}{const s=i.constructor;void 0!==e&&Object.defineProperty(i,n,e(n)),null==t||t(s,n)}})({descriptor:i=>{const n={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};if(e){const e="symbol"==typeof i?Symbol():"__"+i;n.get=function(){var i,n;return void 0===this[e]&&(this[e]=null!==(n=null===(i=this.renderRoot)||void 0===i?void 0:i.querySelector(t))&&void 0!==n?n:null),this[e]}}return n}})}("hui-card-element-editor")],Bt.prototype,"_cardEditorEl",void 0),customElements.define("auto-entities-editor",Bt),window.customCards=window.customCards||[],window.customCards.push({type:"auto-entities",name:"Auto Entities",preview:!1,description:"Entity Filter on Steroids. Auto Entities allows you to fill other cards with entities automatically, based on a number of attributes."}),window.queueMicrotask=window.queueMicrotask||(t=>window.setTimeout(t,1));const qt=["section","divider"];class Vt extends tt{constructor(){super(...arguments),this.empty=!1,this._updateCooldown={timer:void 0,rerun:!1},this._renderer=t=>{this._template="string"==typeof t?t.split(/[\s,]+/):t}}static getConfigElement(){return document.createElement("auto-entities-editor")}static getStubConfig(){return{card:{type:"entities"},filter:{include:[],exclude:[]}}}setConfig(t){var e,i;if(!t)throw new Error("No configuration.");if(!(null===(e=t.card)||void 0===e?void 0:e.type))throw new Error("No card type specified.");if(!t.filter&&!t.entities)throw new Error("No filters specified.");t=structuredClone(t),this._config=t,(null===(i=this._config.filter)||void 0===i?void 0:i.template)&&ut(this._config.filter.template)&&vt(this._renderer,this._config.filter.template,{config:t}),this._cardBuilt=new Promise((t=>this._cardBuiltResolve=t)),queueMicrotask((()=>this.build_else())),queueMicrotask((()=>this.update_all()))}connectedCallback(){var t,e;super.connectedCallback(),(null===(e=null===(t=this._config)||void 0===t?void 0:t.filter)||void 0===e?void 0:e.template)&&ut(this._config.filter.template)&&vt(this._renderer,this._config.filter.template,{config:this._config})}disconnectedCallback(){super.disconnectedCallback(),gt(this._renderer)}async update_all(){if(this.card&&(this.card.hass=this.hass),this.else&&(this.else.hass=this.hass),this._updateCooldown.timer)return void(this._updateCooldown.rerun=!0);this._updateCooldown.rerun=!1,this._updateCooldown.timer=window.setTimeout((()=>{this._updateCooldown.timer=void 0,this._updateCooldown.rerun&&this.update_all()}),500);const t=await this.update_entities();this.update_card(t)}async build_else(){if(void 0===this._config.else)return;const t=await window.loadCardHelpers();this.else=await t.createCardElement(this._config.else),this.else.hass=this.hass}async update_card(t){var e,i,n,s,o,r,a,l,c,d,h,u;if(this._entities&&_t(t,this._entities)&&_t(this._cardConfig,this._config.card))return;const p=(null===(e=this._cardConfig)||void 0===e?void 0:e.type)!==this._config.card.type;this._entities=t,this._cardConfig=structuredClone(this._config.card);const v={[this._config.card_param||"entities"]:t,...this._config.card};if(!this.card||p){const t=await window.loadCardHelpers();console.oldError=console.oldError||[];const e=console.error;console.oldError.push(e),console.error=(...t)=>{var i,n,s,o,r,a;3===t.length&&t[2].message&&((null===(n=(i=t[2].message).startsWith)||void 0===n?void 0:n.call(i,"Entities"))||(null===(o=(s=t[2].message).startsWith)||void 0===o?void 0:o.call(s,"Either entities"))||(null===(a=(r=t[2].message).endsWith)||void 0===a?void 0:a.call(r,"entity")))||e(...t)};try{if(this.card=await t.createCardElement(v),"hui-error-card"===this.card.localName){const t=this.card;await customElements.whenDefined("hui-error-card");let e=10;for(;!t._config&&e;)await new Promise((t=>window.setTimeout(t,100))),e--;if((null===(s=null===(n=null===(i=t._config)||void 0===i?void 0:i.error)||void 0===n?void 0:n.startsWith)||void 0===s?void 0:s.call(n,"Entities"))||(null===(a=null===(r=null===(o=t._config)||void 0===o?void 0:o.error)||void 0===r?void 0:r.startsWith)||void 0===a?void 0:a.call(r,"Either entities"))||(null===(d=null===(c=null===(l=t._config)||void 0===l?void 0:l.error)||void 0===c?void 0:c.endsWith)||void 0===d?void 0:d.call(c,"entity")))return this.card=void 0,this._entities=void 0,this._cardConfig=void 0,void(null===(h=this._cardBuiltResolve)||void 0===h||h.call(this))}}finally{console.error=console.oldError.pop()}}else this.card.setConfig(v);null===(u=this._cardBuiltResolve)||void 0===u||u.call(this),this.card.hass=this.hass,this.empty=0===t.length||t.every((t=>qt.includes(t.type)));const g=this.empty&&!1===this._config.show_empty&&void 0===this._config.else;this.style.display=g?"none":null,this.style.margin=g?"0":null,this.card.requestUpdate&&(await this.updateComplete,this.card.requestUpdate())}async update_entities(){var t,e,i,n,s,o,r,a,l,c;const d=t=>t?"string"==typeof t?{entity:t.trim()}:t:null;let[h]=await Promise.all([this._config.sort?Rt(this.hass,this._config.sort):Promise.resolve((t=>Promise.resolve(t)))]);const u=(await Promise.all((null!==(e=null===(t=this._config.filter)||void 0===t?void 0:t.include)&&void 0!==e?e:[]).map((async t=>{if(t.type)return async()=>[t];const e=await Ut(this.hass,t),i=t.sort?await Rt(this.hass,t.sort):t=>t;return async n=>{var s,o,r,a,l;let c=n.filter((t=>e(t.entity)));if(c=await i(c),null!==(o=null===(s=t.sort)||void 0===s?void 0:s.count)&&void 0!==o?o:null===(r=t.sort)||void 0===r?void 0:r.first){const e=null!==(a=t.sort.first)&&void 0!==a?a:0;c=c.slice(e,e+(null!==(l=t.sort.count)&&void 0!==l?l:1/0))}const d=t.options?JSON.stringify(t.options):"{}";return c=c.map((t=>structuredClone({...t,...JSON.parse(d.replace(/this.entity_id/g,t.entity))}))),c}})))).flat(),p=await Promise.all((null!==(n=null===(i=this._config.filter)||void 0===i?void 0:i.exclude)&&void 0!==n?n:[]).map((async t=>{const e=await Ut(this.hass,t);return t=>e(t)})));let v=[...(null===(o=null===(s=this._config)||void 0===s?void 0:s.entities)||void 0===o?void 0:o.map(d))||[]];if(!this.hass)return v;v=v.concat(null!==(l=null===(a=null===(r=this._template)||void 0===r?void 0:r.map)||void 0===a?void 0:a.call(r,d))&&void 0!==l?l:[]),v=v.filter(Boolean);const g=Object.keys(this.hass.states).map(d);if(v=v.concat((await Promise.all(u.map((t=>t(g))))).flat()),v=v.filter((t=>void 0===t.entity||!(t=>p.some((e=>e(t))))(t.entity))),this._config.sort&&(v=await h(v),this._config.sort.count)){const t=null!==(c=this._config.sort.first)&&void 0!==c?c:0;v=v.slice(t,t+this._config.sort.count)}if(this._config.unique){let t=[];for(const e of v)"entity"===this._config.unique&&e.entity&&t.some((t=>t.entity===e.entity))||t.some((t=>_t(t,e)))||t.push(e);v=t}return v}async updated(t){(t.has("_template")||t.has("hass")&&this.hass)&&queueMicrotask((()=>this.update_all()))}createRenderRoot(){return this}render(){return I`${this.empty&&(!1===this._config.show_empty||this._config.else)?this.else:this.card}`}async getCardSize(){var t,e;let i=0;return await this._cardBuilt,this.card&&this.card.getCardSize&&(i=await this.card.getCardSize()),1===i&&(null===(t=this._entities)||void 0===t?void 0:t.length)&&(i=this._entities.length),0===i&&(null===(e=this._config.filter)||void 0===e?void 0:e.include)&&(i=Object.keys(this._config.filter.include).length),i||5}}t([nt()],Vt.prototype,"_config",void 0),t([nt()],Vt.prototype,"hass",void 0),t([nt()],Vt.prototype,"card",void 0),t([nt()],Vt.prototype,"else",void 0),t([nt()],Vt.prototype,"_template",void 0),t([st()],Vt.prototype,"empty",void 0),customElements.get("auto-entities")||(customElements.define("auto-entities",Vt),console.groupCollapsed(`%cAUTO-ENTITIES ${Ht} IS INSTALLED`,"color: green; font-weight: bold"),console.log("Readme:","https://github.com/thomasloven/lovelace-auto-entities"),console.groupEnd()); diff --git a/package-lock.json b/package-lock.json index e647605..2a355ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-terser": "^0.2.1", + "prettier": "3.4.2", "rollup": "^3.8.1", "rollup-plugin-typescript2": "^0.34.1", "tslib": "^2.4.1", @@ -1183,6 +1184,22 @@ "node": ">=8" } }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index 652cfec..96cfb3a 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-terser": "^0.2.1", + "prettier": "3.4.2", "rollup": "^3.8.1", "rollup-plugin-typescript2": "^0.34.1", "tslib": "^2.4.1", diff --git a/rollup.config.js b/rollup.config.js index b067bae..84b9855 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -15,9 +15,12 @@ export default { plugins: [ nodeResolve(), json(), - typescript(), + typescript({ + clean: true, + }), babel({ exclude: "node_modules/**", + babelHelpers: "bundled", }), !dev && terser({ format: { comments: false } }), ], diff --git a/src/editor/auto-entities-editor.ts b/src/editor/auto-entities-editor.ts index 0eea2df..5a0c6ae 100644 --- a/src/editor/auto-entities-editor.ts +++ b/src/editor/auto-entities-editor.ts @@ -1,28 +1,35 @@ -import { LitElement, html, CSSResultArray, css } from "lit"; -import { property, state, query } from "lit/decorators.js"; +import { css, CSSResultArray, html, LitElement } from "lit"; +import { property, query, state } from "lit/decorators.js"; import { AutoEntitiesConfig } from "../types"; import { loadHaForm } from "../helpers"; import { - filterGroupSchema, - filterGroupOptionsSchema, + cardOptionsSchema, filter2form, + filterGroupOptionsSchema, + filterGroupSchema, form2filter, - specialGroupSchema, - cardOptionsSchema, sortSchema, + specialGroupSchema, } from "./schema"; class AutoEntitiesEditor extends LitElement { - @state() _config: AutoEntitiesConfig; + @state() + _config: AutoEntitiesConfig; - @property() lovelace; - @property() hass; + @property() + lovelace; + @property() + hass; - @state() _selectedTab = 0; - @state() _cardGUIMode = true; - @state() _cardGUIModeAvailable = true; + @state() + _selectedTab = 0; + @state() + _cardGUIMode = true; + @state() + _cardGUIModeAvailable = true; - @query("hui-card-element-editor") private _cardEditorEl?; + @query("hui-card-element-editor") + private _cardEditorEl?; setConfig(config) { this._config = config; @@ -258,7 +265,7 @@ class AutoEntitiesEditor extends LitElement { } _renderFilterEditor() { - if (this._config.filter?.template || this._config.entities) + if (this._config.filter?.template || this._config.entities) { return html`

@@ -267,6 +274,7 @@ class AutoEntitiesEditor extends LitElement {

Please switch to the CODE EDITOR to access all options.

`; + } return html` ${this._config.filter.include.map( diff --git a/src/editor/schema.ts b/src/editor/schema.ts index 0015e99..ef42cec 100644 --- a/src/editor/schema.ts +++ b/src/editor/schema.ts @@ -47,12 +47,13 @@ const filterSchema = ([key, value], idx) => { attributes: { object: {} }, }; - if (!GUI_EDITOR_FILTERS.includes(key)) + if (!GUI_EDITOR_FILTERS.includes(key)) { return { type: "Constant", name: "Some filters are not shown", value: "Please switch to the CODE EDITOR to access all options.", }; + } return { type: "grid", @@ -100,8 +101,9 @@ export const filter2form = (group) => { export const form2filter = (config, filter): Object => { const data = {}; for (let i = 0; i <= config.filter.include.length + 1; i++) { - if (filter[`key_${i}`] !== undefined) + if (filter[`key_${i}`] !== undefined) { data[filter[`key_${i}`]] = filter[`value_${i}`] ?? ""; + } } if (filter.key_new !== undefined) { data[filter.key_new] = ""; diff --git a/src/filter.ts b/src/filter.ts index d7c357f..b246e4c 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -1,15 +1,30 @@ -import { HAState, HassObject } from "./types"; -import { getAreas, getDevices, getEntities, getLabels } from "./helpers"; +import { HassObject, HAState } from "./types"; +import { + getAreas, + getDevices, + getEntities, + getEntityAreas, + getLabels, +} from "./helpers"; const ago_suffix_regex = /([mhd])\s+ago\s*$/i; const default_ago_suffix = "m ago"; -function match(pattern: any, value: any) { - if (typeof pattern === "string" && pattern.startsWith("$$")) { - pattern = pattern.substring(2); - value = JSON.stringify(value); +async function match(pattern: any): Promise<(value: any) => boolean> { + let valueTransformers: Array<(x: any) => unknown> = []; + function transformValue(value) { + return valueTransformers.reduce((a, x) => x(a), value); } - if (typeof value === "string" && typeof pattern === "string") { + + let predicates: Array<[(value: any) => boolean, (value: any) => boolean]> = + []; + + if (typeof pattern === "string") { + if (pattern.startsWith("$$")) { + pattern = pattern.substring(2); + valueTransformers.push(JSON.stringify); + } + if ( (pattern.startsWith("/") && pattern.endsWith("/")) || pattern.indexOf("*") !== -1 @@ -19,208 +34,283 @@ function match(pattern: any, value: any) { pattern = pattern.replace(/\./g, ".").replace(/\*/g, ".*"); pattern = `/^${pattern}$/`; } - let regex = new RegExp(pattern.slice(1, -1)); - return regex.test(value); + const regex = new RegExp(pattern.slice(1, -1)); + predicates.push([ + (value) => typeof value === "string", + (value) => regex.test(value), + ]); } - } - if (typeof pattern === "string") { const match = ago_suffix_regex.exec(pattern); if (match) { pattern = pattern.replace(match[0], ""); const now = new Date().getTime(); - const updated = new Date(value).getTime(); - value = (now - updated) / 60000; - const period = match[1]; - if (period === "h") { - value = value / 60; - } else if (period === "d") { - value = value / 60 / 24; - } + valueTransformers.push((value) => { + const updated = new Date(value).getTime(); + value = (now - updated) / 60000; + const period = match[1]; + if (period === "h") { + value = value / 60; + } else if (period === "d") { + value = value / 60 / 24; + } + }); } - } - if (typeof pattern === "string") { // Comparisons assume numerical values - if (pattern.startsWith("<=")) - return parseFloat(value) <= parseFloat(pattern.substring(2)); - if (pattern.startsWith(">=")) - return parseFloat(value) >= parseFloat(pattern.substring(2)); - if (pattern.startsWith("<")) - return parseFloat(value) < parseFloat(pattern.substring(1)); - if (pattern.startsWith(">")) - return parseFloat(value) > parseFloat(pattern.substring(1)); - if (pattern.startsWith("!")) - return parseFloat(value) != parseFloat(pattern.substring(1)); - if (pattern.startsWith("=")) - return parseFloat(value) == parseFloat(pattern.substring(1)); + if (pattern.startsWith("<=")) { + const parameter = parseFloat(pattern.substring(2)); + predicates.push([() => true, (value) => parseFloat(value) <= parameter]); + } + if (pattern.startsWith(">=")) { + const parameter = parseFloat(pattern.substring(2)); + predicates.push([() => true, (value) => parseFloat(value) >= parameter]); + } + if (pattern.startsWith("<")) { + const parameter = parseFloat(pattern.substring(1)); + predicates.push([() => true, (value) => parseFloat(value) < parameter]); + } + if (pattern.startsWith(">")) { + const parameter = parseFloat(pattern.substring(1)); + predicates.push([() => true, (value) => parseFloat(value) > parameter]); + } + if (pattern.startsWith("!")) { + const parameter = parseFloat(pattern.substring(1)); + predicates.push([() => true, (value) => parseFloat(value) != parameter]); + } + if (pattern.startsWith("=")) { + const parameter = parseFloat(pattern.substring(1)); + predicates.push([() => true, (value) => parseFloat(value) == parameter]); + } } - return pattern === value; + predicates.push([() => true, (value) => pattern === value]); + + return (value) => { + const transformedValue = transformValue(value); + const predicate = predicates.find(([valid, predicate]) => + valid(transformedValue) + )[1]; + return predicate(transformedValue); + }; } const FILTERS: Record< string, - (hass: HassObject, value: any, entity: HAState) => Promise + (hass: HassObject, value: any) => Promise<(entity: HAState) => boolean> > = { - options: async () => true, - sort: async () => true, - domain: async (hass, value, entity) => { - return match(value, entity.entity_id.split(".")[0]); + options: async (hass, value) => (entity) => true, + sort: async (hass, value) => (entity) => true, + domain: async (hass, value) => { + const [matcher] = await Promise.all([match(value)]); + return (entity) => matcher(entity.entity_id.split(".")[0]); }, - entity_id: async (hass, value, entity) => { - return match(value, entity.entity_id); + entity_id: async (hass, value) => { + const [matcher] = await Promise.all([match(value)]); + return (entity) => matcher(entity.entity_id); }, - state: async (hass, value, entity) => { - return match(value, entity.state); + state: async (hass, value) => { + const [matcher] = await Promise.all([match(value)]); + return (entity) => matcher(entity.state); }, - name: async (hass, value, entity) => { - return match(value, entity.attributes?.friendly_name); + name: async (hass, value) => { + const [matcher] = await Promise.all([match(value)]); + return (entity) => matcher(entity.attributes?.friendly_name); }, - group: async (hass, value, entity) => { + group: async (hass, value) => (entity) => { return hass.states[value]?.attributes?.entity_id?.includes( entity.entity_id ); }, - attributes: async (hass, value, entity) => { - for (const [k, v] of Object.entries(value as Record)) { - let attr = k.split(" ")[0]; // Remove any suffixes - let obj = entity.attributes; - for (const step of attr.split(":")) { - obj = obj ? obj[step] : undefined; - } - if (obj === undefined || !match(v, obj)) return false; - } - return true; + attributes: async (hass, value: Record) => { + let filters = ( + await Promise.all( + Object.entries(value).map(async ([k, v]) => { + const attr = k.split(" ")[0]; // Remove any suffixes + const steps = attr.split(":"); + const stepper = (obj) => steps.reduce((a, x) => a?.[x], obj); + const matcher = await match(v); + return { stepper, matcher }; + }) + ) + ).map(({ stepper, matcher }) => (entity) => { + const obj = stepper(entity.attributes); + return obj !== undefined && matcher(obj); + }); + return (entity) => !filters.map((filter) => filter(entity)).includes(false); }, - not: async (hass, value, entity) => { - return !(await filter_entity(hass, value, entity.entity_id)); + not: async (hass, value) => { + const [filter] = await Promise.all([filter_entity(hass, value)]); + return (entity) => !filter(entity.entity_id); }, - and: async (hass, value, entity) => { - for (const v of value) { - if (!(await filter_entity(hass, v, entity.entity_id))) return false; - } - return true; + and: async (hass, value) => { + const filters = await Promise.all(value.map((v) => filter_entity(hass, v))); + return (entity) => !filters.some((filter) => !filter(entity)); }, - or: async (hass, value, entity) => { - for (const v of value) { - if (await filter_entity(hass, v, entity.entity_id)) return true; - } - return false; + or: async (hass, value) => { + const filters = await Promise.all(value.map((v) => filter_entity(hass, v))); + return (entity) => filters.some((filter) => filter(entity)); }, - device: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - if (!ent) return false; - const device = (await getDevices(hass)).find((d) => d.id === ent.device_id); - if (!device) return false; - return match(value, device.name_by_user) || match(value, device.name); - }, - device_manufacturer: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - if (!ent) return false; - const device = (await getDevices(hass)).find((d) => d.id === ent.device_id); - if (!device) return false; - return match(value, device.manufacturer); - }, - device_model: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - if (!ent) return false; - const device = (await getDevices(hass)).find((d) => d.id === ent.device_id); - if (!device) return false; - return match(value, device.model); - }, - area: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - if (!ent) return false; - let area = (await getAreas(hass)).find((a) => a.area_id === ent.area_id); - if (area) return match(value, area.name) || match(value, area.area_id); - const device = (await getDevices(hass)).find((d) => d.id === ent.device_id); - if (!device) return false; - area = (await getAreas(hass)).find((a) => a.area_id === device.area_id); - if (!area) return false; - return match(value, area.name) || match(value, area.area_id); - }, - entity_category: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - if (!ent) return false; - return match(value, ent.entity_category); + device: async (hass, value) => { + const [entities, devices, matcher] = await Promise.all([ + getEntities(hass), + getDevices(hass), + match(value), + ]); + return (entity) => { + const ent = entities.get(entity.entity_id); + if (!ent) return false; + const device = devices.get(ent.device_id); + if (!device) return false; + return matcher(device.name_by_user) || matcher(device.name); + }; + }, + device_manufacturer: async (hass, value) => { + const [entities, devices, matcher] = await Promise.all([ + getEntities(hass), + getDevices(hass), + match(value), + ]); + return (entity) => { + const ent = entities.get(entity.entity_id); + if (!ent) return false; + const device = devices.get(ent.device_id); + if (!device) return false; + return matcher(device.manufacturer); + }; + }, + device_model: async (hass, value) => { + const [entities, devices, matcher] = await Promise.all([ + getEntities(hass), + getDevices(hass), + match(value), + ]); + return (entity) => { + const ent = entities.get(entity.entity_id); + if (!ent) return false; + const device = devices.get(ent.device_id); + if (!device) return false; + return matcher(device.model); + }; + }, + area: async (hass, value) => { + const [entityAreas, areas, matcher] = await Promise.all([ + getEntityAreas(hass), + getAreas(hass), + match(value), + ]); + return (entity) => { + const area = areas.get(entityAreas.get(entity.entity_id)); + if (!area) return false; + return ( + (area.name !== undefined && matcher(area.name)) || + (area.area_id !== undefined && matcher(area.area_id)) + ); + }; + }, + entity_category: async (hass, value) => { + const [entities, matcher] = await Promise.all([ + getEntities(hass), + match(value), + ]); + return (entity) => { + const ent = entities.get(entity.entity_id); + if (!ent) return false; + return matcher(ent.entity_category); + }; }, - last_changed: async (hass, value, entity) => { - if (!ago_suffix_regex.test(value)) value = value + default_ago_suffix; + last_changed: async (hass, value) => { + const [matcher] = await Promise.all([match(value)]); + return (entity) => { + if (!ago_suffix_regex.test(value)) value = value + default_ago_suffix; - return match(value, entity.last_changed); + return matcher(entity.last_changed); + }; }, - last_updated: async (hass, value, entity) => { - if (!ago_suffix_regex.test(value)) value = value + default_ago_suffix; + last_updated: async (hass, value) => { + const [matcher] = await Promise.all([match(value)]); + return (entity) => { + if (!ago_suffix_regex.test(value)) value = value + default_ago_suffix; - return match(value, entity.last_updated); + return matcher(entity.last_updated); + }; }, - last_triggered: async (hass, value, entity) => { - if (entity.attributes.last_triggered == null) return false; - if (!ago_suffix_regex.test(value)) value = value + default_ago_suffix; + last_triggered: async (hass, value) => { + const [matcher] = await Promise.all([match(value)]); + return (entity) => { + if (entity.attributes.last_triggered == null) return false; + if (!ago_suffix_regex.test(value)) value = value + default_ago_suffix; - return match(value, entity.attributes.last_triggered); + return matcher(entity.attributes.last_triggered); + }; }, - integration: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - if (!ent) return false; - return match(value, ent.platform); + integration: async (hass, value) => { + const [entities, matcher] = await Promise.all([ + getEntities(hass), + match(value), + ]); + return (entity) => { + const ent = entities.get(entity.entity_id); + if (!ent) return false; + return matcher(ent.platform); + }; }, - hidden_by: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - if (!ent) return false; - return match(value, ent.hidden_by); + hidden_by: async (hass, value) => { + const [entities, matcher] = await Promise.all([ + getEntities(hass), + match(value), + ]); + return (entity) => { + const ent = entities.get(entity.entity_id); + if (!ent) return false; + return matcher(ent.hidden_by); + }; }, - label: async (hass, value, entity) => { - const ent = (await getEntities(hass)).find( - (e) => e.entity_id === entity.entity_id - ); - const labels = await getLabels(hass); - - const match_label = (value, lbl) => { - if (match(value, lbl)) return true; - const label = labels.find((l) => l.label_id === lbl); + label: async (hass, value) => { + const [entities, devices, labels, matcher] = await Promise.all([ + getEntities(hass), + getDevices(hass), + getLabels(hass), + match(value), + ]); + const match_label = (lbl) => { + if (matcher(lbl)) return true; + const label = labels.get(lbl); if (!label) return false; - const result = match(value, label.name); + const result = matcher(label.name); return result; }; + return (entity) => { + const ent = entities.get(entity.entity_id); - if (!ent) return false; - if (!ent.labels) return false; - const entity_match = ent.labels.some((lbl) => match_label(value, lbl)); - if (entity_match) return entity_match; + if (!ent) return false; + if (!ent.labels) return false; + const entity_match = ent.labels.some((lbl) => match_label(lbl)); + if (entity_match) return entity_match; - const device = (await getDevices(hass)).find((d) => d.id === ent.device_id); - if (!device) return false; - const device_match = device.labels.some((lbl) => match_label(value, lbl)); - return device_match; + const device = devices.get(ent.device_id); + if (!device) return false; + const device_match = device.labels.some((lbl) => match_label(lbl)); + return device_match; + }; }, }; export async function filter_entity( hass: HassObject, - filter: Record, - entity_id: string -): Promise { - if (!hass.states[entity_id]) return false; - for (let [k, v] of Object.entries(filter)) { - k = k.trim().split(" ")[0].trim(); - if (!(await FILTERS[k]?.(hass, v, hass.states[entity_id]))) return false; - } - return true; + filter: Record +): Promise<(entity_id: string) => boolean> { + const filters = ( + await Promise.all( + Object.entries(filter).map(([k, v]) => + FILTERS[k.trim().split(" ")[0].trim()]?.(hass, v) + ) + ) + ).filter(Boolean); + return (entity_id: string) => { + const entity = hass?.states?.[entity_id]; + if (!entity) return false; + return !filters.some((filter) => !filter(entity)); + }; } diff --git a/src/helpers.ts b/src/helpers.ts index b93c91e..3517af0 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,3 +1,5 @@ +import type { HassObject } from "./types"; + export const loadHaForm = async () => { if (customElements.get("ha-form")) return; @@ -26,43 +28,136 @@ export const compare_deep = (a: any, b: any) => { return true; }; -(window as any).autoEntities_cache = (window as any).autoEntities_cache ?? {}; -const cache = (window as any).autoEntities_cache; -export async function getAreas(hass) { - cache.areas = - cache.areas ?? (await hass.callWS({ type: "config/area_registry/list" })); - return cache.areas; -} -export function cached_areas() { - return cache.areas; -} -export async function getDevices(hass) { - cache.devices = - cache.devices ?? - (await hass.callWS({ type: "config/device_registry/list" })); - return cache.devices; -} -export function cached_devices() { - return cache.devices; -} -export async function getEntities(hass) { - cache.entities = - cache.entities ?? - (await hass.callWS({ type: "config/entity_registry/list" })); - return cache.entities; -} -export function cached_entities() { - return cache.entities; +function makeCache Promise>( + name: string, + loader: T +): T extends (...args: infer R) => Promise + ? { + get_cached: () => Promise; + get: (...args: R) => Promise; + } + : never; +function makeCache Promise>( + name: string, + loader: T +): { + get_cached: () => Promise>>; + get: (...args: Parameters) => Promise>>; +} { + function refresh() { + let loading = false; + let resolve, reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + const load = (loader: () => Promise) => { + if (!loading) { + loading = true; + try { + loader().then(resolve, reject); + } catch (e) { + loading = false; + if (e !== undefined) throw e; + } + } + return promise; + }; + return { promise, resolve, reject, load }; + } + let cache = refresh>>(); + async function get_cached(): Promise>> { + return await cache.promise; + } + async function get(...args: Parameters): Promise>> { + try { + return await cache.load( + () => loader(...args) as Promise>> + ); + } catch (e) { + if (e !== undefined) console.warn(`${name} failed:`, e); + cache = refresh(); + throw e; + } + } + return { + get_cached, + get, + }; } -export async function getLabels(hass) { - cache.labels = - cache.labels ?? (await hass.callWS({ type: "config/label_registry/list" })); - return cache.labels; + +function hassListLoader>( + key: keyof T, + type: string +): (hass: HassObject) => Promise>> { + return (hass: HassObject) => { + if (!hass) throw undefined; + return (async () => + new Map((await hass.callWS({ type })).map((x: T) => [x[key], x])))(); + }; } +export const { get_cached: cached_devices, get: getDevices } = makeCache( + "getDevices", + hassListLoader<{ + id: string; + area_id?: string | null; + labels: Array>; + }>("id", "config/device_registry/list") +); + +export const { get_cached: cached_areas, get: getAreas } = makeCache( + "getAreas", + hassListLoader<{ + area_id: string; + labels: Array>; + }>("area_id", "config/area_registry/list") +); + +export const { get_cached: cached_entities, get: getEntities } = makeCache( + "getEntities", + hassListLoader<{ + entity_id: string; + device_id?: string | null; + area_id?: string | null; + labels: Array>; + }>("entity_id", "config/entity_registry/list") +); + +export const { get_cached: cached_labels, get: getLabels } = makeCache( + "getLabels", + hassListLoader<{ + label_id: string; + }>("label_id", "config/label_registry/list") +); + +export const { get_cached: cached_area_map, get: getEntityAreas } = makeCache( + "getEntityAreas", + async (hass: HassObject) => { + const [entities, devices, areas] = await Promise.all([ + getEntities(hass), + getDevices(hass), + getAreas(hass), + ]); + return new Map( + Array.from(entities.values()).flatMap((entity) => { + let area = areas.get(entity.area_id); + if (area) return [[entity.entity_id, area.area_id]]; + const device = devices.get(entity.device_id); + if (!device) return []; + area = areas.get(device.area_id); + if (area) return [[entity.entity_id, area.area_id]]; + return []; + }) + ); + } +); + // Debugging helper // (window as any).AutoEntities = { -// getAreas, // getDevices, +// getAreas, // getEntities, +// getLabels, +// getEntityAreas, // }; diff --git a/src/main.ts b/src/main.ts index 9e2664b..e27ae7a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { property, state } from "lit/decorators.js"; import { hasTemplate } from "card-tools/src/templates"; import { bind_template, unbind_template } from "./templates"; @@ -13,7 +13,7 @@ import { } from "./types"; import pjson from "../package.json"; import "./editor/auto-entities-editor"; -import { compare_deep, getAreas, getDevices, getEntities } from "./helpers"; +import { compare_deep } from "./helpers"; window.queueMicrotask = window.queueMicrotask || ((handler) => window.setTimeout(handler, 1)); @@ -21,12 +21,18 @@ window.queueMicrotask = const HIDDEN_TYPES = ["section", "divider"]; class AutoEntities extends LitElement { - @property() _config: AutoEntitiesConfig; - @property() hass: any; - @property() card: LovelaceCard; - @property() else?: LovelaceCard; - @property() _template: string[]; - @state() empty = false; + @property() + _config: AutoEntitiesConfig; + @property() + hass: any; + @property() + card: LovelaceCard; + @property() + else?: LovelaceCard; + @property() + _template: string[]; + @state() + empty = false; _entities: EntityList; _cardConfig; @@ -59,7 +65,7 @@ class AutoEntities extends LitElement { if (!config.filter && !config.entities) { throw new Error("No filters specified."); } - config = JSON.parse(JSON.stringify(config)); + config = structuredClone(config); this._config = config; if ( @@ -132,11 +138,12 @@ class AutoEntities extends LitElement { this._entities && compare_deep(entities, this._entities) && compare_deep(this._cardConfig, this._config.card) - ) + ) { return; + } const newType = this._cardConfig?.type !== this._config.card.type; this._entities = entities; - this._cardConfig = JSON.parse(JSON.stringify(this._config.card)); + this._cardConfig = structuredClone(this._config.card); const cardConfig = { [this._config.card_param || "entities"]: entities, ...this._config.card, @@ -215,69 +222,86 @@ class AutoEntities extends LitElement { return typeof entity === "string" ? { entity: entity.trim() } : entity; }; + let [sort] = await Promise.all([ + this._config.sort + ? get_sorter(this.hass, this._config.sort) + : Promise.resolve((x: EntityList) => Promise.resolve(x)), + ]); + + const includeFilters = ( + await Promise.all( + (this._config.filter?.include ?? []).map( + async ( + filter + ): Promise<(entities: EntityList) => Promise> => { + if (filter.type) return async () => [filter as LovelaceRowConfig]; + + const filterFn = await filter_entity(this.hass, filter); + const sort = filter.sort + ? await get_sorter(this.hass, filter.sort) + : (x) => x; + + return async (entities) => { + let add = entities.filter((entity) => filterFn(entity.entity)); + add = await sort(add); + + if (filter.sort?.count ?? filter.sort?.first) { + const start = filter.sort.first ?? 0; + add = add.slice(start, start + (filter.sort.count ?? Infinity)); + } + + const stringifiedOptions = filter.options + ? JSON.stringify(filter.options) + : "{}"; // Avoid repeating stringify for each entity + add = add.map((entity) => + structuredClone({ + ...entity, + ...JSON.parse( + stringifiedOptions.replace(/this.entity_id/g, entity.entity) + ), + }) + ); + + return add; + }; + } + ) + ) + ).flat(); + const excludeFilters = await Promise.all( + (this._config.filter?.exclude ?? []).map(async (filter) => { + const filterFn = await filter_entity(this.hass, filter); + return (x) => filterFn(x); + }) + ); + const anyExcludeFilter = (x) => excludeFilters.some((filter) => filter(x)); + let entities: EntityList = [...(this._config?.entities?.map(format) || [])]; if (!this.hass) { return entities; } - if (this._template) { - entities = entities.concat(this._template.map(format)); - } + entities = entities.concat(this._template?.map?.(format) ?? []); entities = entities.filter(Boolean); - if (this._config.filter?.include) { - const all_entities = Object.keys(this.hass.states).map(format); - for (const filter of this._config.filter.include) { - if (filter.type) { - entities.push(filter); - continue; - } - - let add: EntityList = []; - for (const entity of all_entities) { - if (await filter_entity(this.hass, filter, entity.entity)) - add.push( - JSON.parse( - JSON.stringify({ ...entity, ...filter.options }).replace( - /this.entity_id/g, - entity.entity - ) - ) - ); - } - - if (filter.sort) { - await getEntities(this.hass); - await getDevices(this.hass); - await getAreas(this.hass); - add = add.sort(get_sorter(this.hass, filter.sort)); - if (filter.sort.count ?? filter.sort.first) { - const start = filter.sort.first ?? 0; - add = add.slice(start, start + (filter.sort.count ?? Infinity)); - } - } - entities = entities.concat(add); - } - } + const all_entities: EntityList = Object.keys(this.hass.states).map(format); + entities = entities.concat( + ( + await Promise.all( + includeFilters.map((includeFilter) => includeFilter(all_entities)) + ) + ).flat() + ); // TODO: Add tests for exclusions - if (this._config.filter?.exclude) { - for (const filter of this._config.filter.exclude) { - const newEntities = []; - for (const entity of entities) { - if ( - entity.entity === undefined || - !(await filter_entity(this.hass, filter, entity.entity)) - ) - newEntities.push(entity); - } - entities = newEntities; - } - } + entities = entities.filter( + (entity) => + entity.entity === undefined || !anyExcludeFilter(entity.entity) + ); if (this._config.sort) { - entities = entities.sort(get_sorter(this.hass, this._config.sort)); + entities = await sort(entities); if (this._config.sort.count) { const start = this._config.sort.first ?? 0; entities = entities.slice(start, start + this._config.sort.count); @@ -291,8 +315,9 @@ class AutoEntities extends LitElement { this._config.unique === "entity" && e.entity && newEntities.some((i) => i.entity === e.entity) - ) + ) { continue; + } if (newEntities.some((i) => compare_deep(i, e))) continue; newEntities.push(e); } @@ -326,8 +351,9 @@ class AutoEntities extends LitElement { await this._cardBuilt; if (this.card && this.card.getCardSize) len = await this.card.getCardSize(); if (len === 1 && this._entities?.length) len = this._entities.length; - if (len === 0 && this._config.filter?.include) + if (len === 0 && this._config.filter?.include) { len = Object.keys(this._config.filter.include).length; + } return len || 5; } } diff --git a/src/sort.ts b/src/sort.ts index 9c7616b..0b9b672 100644 --- a/src/sort.ts +++ b/src/sort.ts @@ -37,130 +37,159 @@ function compare(_a: any, _b: any, method: SortConfig) { ); } -const SORTERS: Record< - string, - (a: HAState, b: HAState, method: SortConfig) => number -> = { - none: () => { - return 0; - }, - domain: (a, b, method) => { - return compare( - a?.entity_id?.split(".")[0], - b?.entity_id?.split(".")[0], - method - ); - }, - entity_id: (a, b, method) => { - return compare(a?.entity_id, b?.entity_id, method); - }, - friendly_name: (a, b, method) => { - return compare( - a?.attributes?.friendly_name || a?.entity_id?.split(".")[1], - b?.attributes?.friendly_name || b?.entity_id?.split(".")[1], - method - ); - }, - name: (a, b, method) => { - return compare( - a?.attributes?.friendly_name || a?.entity_id?.split(".")[1], - b?.attributes?.friendly_name || b?.entity_id?.split(".")[1], - method - ); - }, - device: (a, b, method) => { - const entity_a = cached_entities().find((e) => e.entity_id === a.entity_id); - const entity_b = cached_entities().find((e) => e.entity_id === b.entity_id); - if (!entity_a || !entity_b) return 0; - const device_a = cached_devices().find((d) => d.id === entity_a.device_id); - const device_b = cached_devices().find((d) => d.id === entity_b.device_id); - if (!device_a || !device_b) return 0; - return compare( - device_a.name_by_user ?? device_a.name, - device_b.name_by_user ?? device_b.name, - method - ); - }, - area: (a, b, method) => { - const entity_a = cached_entities().find((e) => e.entity_id === a.entity_id); - const entity_b = cached_entities().find((e) => e.entity_id === b.entity_id); - if (!entity_a || !entity_b) return 0; - const device_a = cached_devices().find((d) => d.id === entity_a.device_id); - const device_b = cached_devices().find((d) => d.id === entity_b.device_id); - if (!device_a || !device_b) return 0; - const area_a = cached_areas().find((a) => a.area_id === device_a.area_id); - const area_b = cached_areas().find((a) => a.area_id === device_b.area_id); - if (!area_a || !area_b) return 0; - return compare(area_a.name, area_b.name, method); - }, - state: (a, b, method) => { - return compare(a?.state, b?.state, method); - }, - attribute: (a, b, method) => { - const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; - let _a = a?.attributes; - let _b = b?.attributes; - for (const step of method?.attribute?.split(":")) { - if (_a === undefined && _b === undefined) return 0; - if (_a === undefined) return lt; - if (_b === undefined) return gt; - [_a, _b] = [_a[step], _b[step]]; - } - return compare(_a, _b, method); - }, - last_changed: (a, b, method) => { - const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; - if (a?.last_changed == null && b?.last_changed == null) return 0; - if (a?.last_changed == null) return lt; - if (b?.last_changed == null) return gt; - method.numeric = true; - return compare( - new Date(a?.last_changed).getTime(), - new Date(b?.last_changed).getTime(), - method - ); - }, - last_updated: (a, b, method) => { - const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; - if (a?.last_updated == null && b?.last_updated == null) return 0; - if (a?.last_updated == null) return lt; - if (b?.last_updated == null) return gt; - method.numeric = true; - return compare( - new Date(a?.last_updated).getTime(), - new Date(b?.last_updated).getTime(), - method - ); +type MapperSorter = { + mapper: (e: HAState) => Promise; + sorter: (a: T, b: T) => number; +}; + +const SORTERS = { + none: async (_method: SortConfig) => ({ + mapper: async (_e: HAState) => undefined, + sorter: (_a, _b): number => 0, + }), + domain: async (method: SortConfig) => ({ + mapper: async (e: HAState) => e?.entity_id?.split(".")[0], + sorter: (a, b): number => compare(a, b, method), + }), + entity_id: async (method: SortConfig) => ({ + mapper: async (e: HAState) => e?.entity_id, + sorter: (a, b): number => compare(a, b, method), + }), + friendly_name: async (method: SortConfig) => ({ + mapper: async (e: HAState) => + e?.attributes?.friendly_name || e?.entity_id?.split(".")[1], + sorter: (a, b): number => compare(a, b, method), + }), + name: async (method: SortConfig) => ({ + mapper: async (e: HAState) => + e?.attributes?.friendly_name || e?.entity_id?.split(".")[1], + sorter: (a, b): number => compare(a, b, method), + }), + device: async (method: SortConfig) => { + const [entities, devices] = [cached_entities(), cached_devices()]; + return { + mapper: async (e: HAState) => { + const entity = (await entities).get(e.entity_id); + const device = entity + ? (await devices).get(entity.device_id) + : undefined; + return device?.name_by_user ?? device?.name; + }, + sorter: (a, b): number => + (a === b) === undefined ? 0 : compare(a, b, method), + }; }, - last_triggered: (a, b, method) => { - const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; - if ( - a?.attributes?.last_triggered == null && - b?.attributes?.last_triggered == null - ) - return 0; - if (a?.attributes?.last_triggered == null) return lt; - if (b?.attributes?.last_triggered == null) return gt; - method.numeric = true; - return compare( - new Date(a?.attributes?.last_triggered).getTime(), - new Date(b?.attributes?.last_triggered).getTime(), - method - ); + area: async (method: SortConfig) => { + const [entities, devices, areas] = [ + cached_entities(), + cached_devices(), + cached_areas(), + ]; + return { + mapper: async (e: HAState) => { + const entity = (await entities).get(e.entity_id); + const device = entity + ? (await devices).get(entity.device_id) + : undefined; + const area = device ? (await areas).get(device.area_id) : undefined; + return area?.name; + }, + sorter: (a, b): number => + (a === b) === undefined ? 0 : compare(a, b, method), + }; }, -}; + state: async (method: SortConfig) => ({ + mapper: async (e: HAState) => e?.state, + sorter: (a, b): number => compare(a, b, method), + }), + attribute: async (method: SortConfig) => ({ + mapper: async (e: HAState) => e, + sorter: (a: HAState, b: HAState): number => { + const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; + let _a = a?.attributes; + let _b = b?.attributes; + for (const step of method?.attribute?.split(":")) { + if (_a === undefined && _b === undefined) return 0; + if (_a === undefined) return lt; + if (_b === undefined) return gt; + [_a, _b] = [_a[step], _b[step]]; + } + return compare(_a, _b, method); + }, + }), + last_changed: async (method: SortConfig) => ({ + mapper: async (e: HAState) => e, + sorter: (a: HAState, b: HAState): number => { + const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; + if (a?.last_changed == null && b?.last_changed == null) return 0; + if (a?.last_changed == null) return lt; + if (b?.last_changed == null) return gt; + method.numeric = true; + return compare( + new Date(a?.last_changed).getTime(), + new Date(b?.last_changed).getTime(), + method + ); + }, + }), + last_updated: async (method: SortConfig) => ({ + mapper: async (e: HAState) => e, + sorter: (a: HAState, b: HAState): number => { + const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; + if (a?.last_updated == null && b?.last_updated == null) return 0; + if (a?.last_updated == null) return lt; + if (b?.last_updated == null) return gt; + method.numeric = true; + return compare( + new Date(a?.last_updated).getTime(), + new Date(b?.last_updated).getTime(), + method + ); + }, + }), + last_triggered: async (method: SortConfig) => ({ + mapper: async (e: HAState) => e, + sorter: (a: HAState, b: HAState): number => { + const [lt, gt] = method?.reverse ? [-1, 1] : [1, -1]; + if ( + a?.attributes?.last_triggered == null && + b?.attributes?.last_triggered == null + ) { + return 0; + } + if (a?.attributes?.last_triggered == null) return lt; + if (b?.attributes?.last_triggered == null) return gt; + method.numeric = true; + return compare( + new Date(a?.attributes?.last_triggered).getTime(), + new Date(b?.attributes?.last_triggered).getTime(), + method + ); + }, + }), +} as const satisfies Record< + string, + (method: SortConfig) => Promise> +>; -export function get_sorter( +export async function get_sorter( hass: HassObject, method: SortConfig -): (a: LovelaceRowConfig, b: LovelaceRowConfig) => number { - return function (a, b) { - return ( - SORTERS[method.method]?.( - hass.states[a.entity], - hass.states[b.entity], - method - ) ?? 0 +): Promise< + (array: Array) => Promise> +> { + const { mapper, sorter } = await (SORTERS[method.method] ?? SORTERS["none"])( + method + ); + const sort = async (data) => { + const mapped = await Promise.all( + data.map(async (x, i) => ({ + i, + value: await mapper(x), + })) ); + mapped.sort((a, b) => sorter(a.value, b.value)); + return mapped.map((v) => data[v.i]); }; + return sort; } diff --git a/tsconfig.json b/tsconfig.json index ea2f28b..073435f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2017", + "target": "es2019", "moduleResolution": "node", "resolveJsonModule": true, "allowSyntheticDefaultImports": true,