diff --git a/auto-entities.js b/auto-entities.js index 699afa5..7f8b4d4 100644 --- a/auto-entities.js +++ b/auto-entities.js @@ -1,4 +1,2 @@ -!function(t){var e={};function r(i){if(e[i])return e[i].exports;var s=e[i]={i:i,l:!1,exports:{}};return t[i].call(s.exports,s,s.exports,r),s.l=!0,s.exports}r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)r.d(i,s,function(e){return t[e]}.bind(null,s));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,r){"use strict";r.r(e);const i=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),s=i.prototype.html;i.prototype.css;function n(t,e,r=null){if((t=new Event(t,{bubbles:!0,cancelable:!1,composed:!0})).detail=e||{},r)r.dispatchEvent(t);else{var i=document.querySelector("home-assistant");(i=(i=(i=(i=(i=(i=(i=(i=(i=(i=(i=i&&i.shadowRoot)&&i.querySelector("home-assistant-main"))&&i.shadowRoot)&&i.querySelector("app-drawer-layout partial-panel-resolver"))&&i.shadowRoot||i)&&i.querySelector("ha-panel-lovelace"))&&i.shadowRoot)&&i.querySelector("hui-root"))&&i.shadowRoot)&&i.querySelector("ha-app-layout #view"))&&i.firstElementChild)&&i.dispatchEvent(t)}}const o="custom:";function a(t,e){const r=document.createElement("hui-error-card");return r.setConfig({type:"error",error:t,origConfig:e}),r}function c(t,e){if(!e||"object"!=typeof e||!e.type)return a(`No ${t} type configured`,e);let r=e.type;if(r=r.startsWith(o)?r.substr(o.length):`hui-${r}-${t}`,customElements.get(r))return function(t,e){const r=document.createElement(t);try{r.setConfig(e)}catch(t){return a(t,e)}return r}(r,e);const i=a(`Custom element doesn't exist: ${r}.`,e);i.style.display="None";const s=setTimeout(()=>{i.style.display=""},2e3);return customElements.whenDefined(r).then(()=>{clearTimeout(s),n("ll-rebuild",{},i)}),i}function u(){return document.querySelector("home-assistant").hass}const l=2;class f extends i{static get version(){return l}static get properties(){return{noHass:{type:Boolean}}}setConfig(t){var e;this._config=t,this.el?this.el.setConfig(t):(this.el=this.create(t),this._hass&&(this.el.hass=this._hass),this.noHass&&(e=this,document.querySelector("home-assistant").provideHass(e)))}set config(t){this.setConfig(t)}set hass(t){this._hass=t,this.el&&(this.el.hass=t)}createRenderRoot(){return this}render(){return s`${this.el}`}}const d=function(t,e){const r=Object.getOwnPropertyDescriptors(e.prototype);for(const[e,i]of Object.entries(r))"constructor"!==e&&Object.defineProperty(t.prototype,e,i);const i=Object.getOwnPropertyDescriptors(e);for(const[e,r]of Object.entries(i))"prototype"!==e&&Object.defineProperty(t,e,r);const s=Object.getPrototypeOf(e),n=Object.getOwnPropertyDescriptors(s.prototype);for(const[e,r]of Object.entries(n))"constructor"!==e&&Object.defineProperty(Object.getPrototypeOf(t).prototype,e,r);const o=Object.getOwnPropertyDescriptors(s);for(const[e,r]of Object.entries(o))"prototype"!==e&&Object.defineProperty(Object.getPrototypeOf(t),e,r)},h=customElements.get("card-maker");if(!h||!h.version||h.version="))return parseFloat(e)>=parseFloat(t.substr(2));if(t.startsWith("<"))return parseFloat(e)"))return parseFloat(e)>parseFloat(t.substr(1));if(t.startsWith("!"))return parseFloat(e)!=parseFloat(t.substr(1));if(t.startsWith("="))return parseFloat(e)==parseFloat(t.substr(1))}return t===e}function j(t,e){return function(r){const i="string"==typeof r?t.states[r]:t.states[r.entity];if(!r)return!1;for(const[s,n]of Object.entries(e))switch(s.split(" ")[0]){case"options":case"sort":break;case"domain":if(!O(n,i.entity_id.split(".")[0]))return!1;break;case"entity_id":if(!O(n,i.entity_id))return!1;break;case"state":if(!O(n,i.state))return!1;break;case"name":if(!i.attributes.friendly_name||!O(n,i.attributes.friendly_name))return!1;break;case"group":if(!(n.startsWith("group.")&&t.states[n]&&t.states[n].attributes.entity_id&&t.states[n].attributes.entity_id.includes(i.entity_id)))return!1;break;case"attributes":for(const[t,e]of Object.entries(n)){let r=t.split(" ")[0],s=i.attributes;for(;r&&s;){let t;[t,r]=r.split(":"),s=s[t]}if(void 0===s||e&&!O(e,s))return!1}break;case"not":if(j(t,n)(r))return!1;break;case"device":let e=!1;for(const t of window.cardToolsData.devices)O(n,t.name)&&w(t).includes(i.entity_id)&&(e=!0);if(!e)return!1;break;case"area":let s=!1;for(const t of window.cardToolsData.areas)O(n,t.name)&&v(t).flatMap(w).includes(i.entity_id)&&(s=!0);if(!s)return!1;break;default:return!1}return!0}}function k(t,e){return"string"==typeof e&&(e={method:e}),function(r,i){const s="string"==typeof r?t.states[r]:t.states[r.entity],n="string"==typeof i?t.states[i]:t.states[i.entity];if(void 0===s||void 0===n)return 0;const[o,a]=e.reverse?[-1,1]:[1,-1];function c(t,r){return e.ignore_case&&t.toLowerCase&&(t=t.toLowerCase()),e.ignore_case&&r.toLowerCase&&(r=r.toLowerCase()),void 0===t&&void 0===r?0:void 0===t?o:void 0===r?a:tr?o:0}switch(e.method){case"domain":return c(s.entity_id.split(".")[0],n.entity_id.split(".")[0]);case"entity_id":return c(s.entity_id,n.entity_id);case"friendly_name":case"name":return c(s.attributes.friendly_name||s.entity_id.split(".")[1],n.attributes.friendly_name||n.entity_id.split(".")[1]);case"state":return c(s.state,n.state);case"attribute":let t=s.attributes,r=n.attributes,i=e.attribute;for(;i;){let e;if([e,i]=i.split(":"),t=t[e],r=r[e],void 0===t&&void 0===r)return 0;if(void 0===t)return o;if(void 0===r)return a}return c(t,r);default:return 0}}}_();customElements.define("auto-entities",class extends i{static get properties(){return{hass:{}}}setConfig(t){if(!t||!t.card)throw new Error("Invalid configuration");this._config?(this._config=t,this.hass=this.hass):(this._config=t,this.cardConfig={entities:[],...t.card},this.entities=[])}async _getEntities(){let t=[];if(this._config.entities&&(t=t.concat(this._config.entities).map(t=>"string"==typeof t?{entity:t}:t)),!this.hass||!this._config.filter)return t;if(this._config.filter.include){const e=Object.keys(this.hass.states).map(t=>new Object({entity:t}));for(const r of this._config.filter.include){if(void 0!==r.type){t.push(r);continue}(r.device||r.area)&&await _();let i=e.filter(j(this.hass,r)).map(t=>new Object({...t,...r.options}));void 0!==r.sort&&(i=i.sort(k(this.hass,r.sort))),t=t.concat(i)}}if(this._config.filter.exclude)for(const e of this._config.filter.exclude)t=t.filter(t=>"string"!=typeof t&&void 0===t.entity||!j(this.hass,e)(t));if(this._config.sort&&(t=t.sort(k(this.hass,this._config.sort))),this._config.unique){function e(t,r){return typeof t==typeof r&&("object"!=typeof t?t===r:!Object.keys(t).some(t=>!Object.keys(r).includes(t))&&Object.keys(t).every(i=>e(t[i],r[i])))}let r=[];for(const i of t)r.some(t=>e(t,i))||r.push(i);t=r}return t}set entities(t){(function(t,e){if(t===e)return!0;if(null==t||null==e)return!1;if(t.length!=e.length)return!1;for(var r=0;rthis._getEntities().then(t=>this.entities=t),0)}createRenderRoot(){return this}render(){return s` - `}getCardSize(){let t=0;return this.querySelector("card-maker")&&this.querySelector("card-maker").getCardSize&&(t=this.querySelector("card-maker").getCardSize()),1===t&&this.entities.length&&(t=this.entities.length),0===t&&this._config.filter&&this._config.filter.include?Object.keys(this._config.filter.include).length:t||1}}),n("ll-rebuild",{})}]); \ No newline at end of file +!function(t){var e={};function i(s){if(e[s])return e[s].exports;var r=e[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=t,i.c=e,i.d=function(t,e,s){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(i.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(s,r,function(e){return t[e]}.bind(null,r));return s},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=0)}([function(t,e,i){"use strict";i.r(e);const s=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),r=s.prototype.html;s.prototype.css;function n(){return document.querySelector("home-assistant").hass}const o=n().callWS({type:"config/area_registry/list"}),a=n().callWS({type:"config/device_registry/list"}),c=n().callWS({type:"config/entity_registry/list"});async function u(){return window.cardToolsData=window.cardToolsData||{areas:await o,devices:await a,entities:await c},window.cardToolsData}function l(t){const e=window.cardToolsData;let i=[];if(!t)return i;for(const s of e.devices)s.area_id===t.area_id&&i.push(s);return i}function f(t){const e=window.cardToolsData;let i=[];if(!t)return i;for(const s of e.entities)s.device_id===t.id&&i.push(s.entity_id);return i}function d(t,e){if("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){if(t.startsWith("<="))return parseFloat(e)<=parseFloat(t.substr(2));if(t.startsWith(">="))return parseFloat(e)>=parseFloat(t.substr(2));if(t.startsWith("<"))return parseFloat(e)"))return parseFloat(e)>parseFloat(t.substr(1));if(t.startsWith("!"))return parseFloat(e)!=parseFloat(t.substr(1));if(t.startsWith("="))return parseFloat(e)==parseFloat(t.substr(1))}return t===e}function h(t,e){return function(i){const s="string"==typeof i?t.states[i]:t.states[i.entity];if(!i)return!1;for(const[r,n]of Object.entries(e))switch(r.split(" ")[0]){case"options":case"sort":break;case"domain":if(!d(n,s.entity_id.split(".")[0]))return!1;break;case"entity_id":if(!d(n,s.entity_id))return!1;break;case"state":if(!d(n,s.state))return!1;break;case"name":if(!s.attributes.friendly_name||!d(n,s.attributes.friendly_name))return!1;break;case"group":if(!(n.startsWith("group.")&&t.states[n]&&t.states[n].attributes.entity_id&&t.states[n].attributes.entity_id.includes(s.entity_id)))return!1;break;case"attributes":for(const[t,e]of Object.entries(n)){let i=t.split(" ")[0],r=s.attributes;for(;i&&r;){let t;[t,i]=i.split(":"),r=r[t]}if(void 0===r||e&&!d(e,r))return!1}break;case"not":if(h(t,n)(i))return!1;break;case"device":if(!window.cardToolsData||!window.cardToolsData.devices)return!1;let e=!1;for(const t of window.cardToolsData.devices)d(n,t.name)&&f(t).includes(s.entity_id)&&(e=!0);if(!e)return!1;break;case"area":if(!window.cardToolsData||!window.cardToolsData.areas)return!1;let r=!1;for(const t of window.cardToolsData.areas)d(n,t.name)&&l(t).flatMap(f).includes(s.entity_id)&&(r=!0);if(!r)return!1;break;default:return!1}return!0}}function y(t,e){return"string"==typeof e&&(e={method:e}),function(i,s){const r="string"==typeof i?t.states[i]:t.states[i.entity],n="string"==typeof s?t.states[s]:t.states[s.entity];if(void 0===r||void 0===n)return 0;const[o,a]=e.reverse?[-1,1]:[1,-1];function c(t,i){return e.ignore_case&&t.toLowerCase&&(t=t.toLowerCase()),e.ignore_case&&i.toLowerCase&&(i=i.toLowerCase()),void 0===t&&void 0===i?0:void 0===t?o:void 0===i?a:ti?o:0}switch(e.method){case"domain":return c(r.entity_id.split(".")[0],n.entity_id.split(".")[0]);case"entity_id":return c(r.entity_id,n.entity_id);case"friendly_name":case"name":return c(r.attributes.friendly_name||r.entity_id.split(".")[1],n.attributes.friendly_name||n.entity_id.split(".")[1]);case"state":return c(r.state,n.state);case"attribute":let t=r.attributes,i=n.attributes,s=e.attribute;for(;s;){let e;if([e,s]=s.split(":"),t=t[e],i=i[e],void 0===t&&void 0===i)return 0;if(void 0===t)return o;if(void 0===i)return a}return c(t,i);default:return 0}}}function p(t,e,i=null){if((t=new Event(t,{bubbles:!0,cancelable:!1,composed:!0})).detail=e||{},i)i.dispatchEvent(t);else{var s=document.querySelector("home-assistant");(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=s&&s.shadowRoot)&&s.querySelector("home-assistant-main"))&&s.shadowRoot)&&s.querySelector("app-drawer-layout partial-panel-resolver"))&&s.shadowRoot||s)&&s.querySelector("ha-panel-lovelace"))&&s.shadowRoot)&&s.querySelector("hui-root"))&&s.shadowRoot)&&s.querySelector("ha-app-layout #view"))&&s.firstElementChild)&&s.dispatchEvent(t)}}u();const g="custom:";function _(t,e){const i=document.createElement("hui-error-card");return i.setConfig({type:"error",error:t,origConfig:e}),i}function m(t,e){if(!e||"object"!=typeof e||!e.type)return _(`No ${t} type configured`,e);let i=e.type;if(i=i.startsWith(g)?i.substr(g.length):`hui-${i}-${t}`,customElements.get(i))return function(t,e){const i=document.createElement(t);try{i.setConfig(e)}catch(t){return _(t,e)}return i}(i,e);const s=_(`Custom element doesn't exist: ${i}.`,e);s.style.display="None";const r=setTimeout(()=>{s.style.display=""},2e3);return customElements.whenDefined(i).then(()=>{clearTimeout(r),p("ll-rebuild",{},s)}),s}customElements.define("auto-entities",class extends s{static get properties(){return{hass:{}}}setConfig(t){if(!t||!t.card)throw new Error("Invalid configuration");this._config?(this._config=t,this.hass=this.hass):(this._config=t,this.hass=n(),this._getEntities(),this.cardConfig={entities:this.entities,...t.card},this.card=function(t){return m("card",t)}(this.cardConfig)),u().then(()=>this._getEntities())}_getEntities(){let t=[];if(this._config.entities&&(t=t.concat(this._config.entities).map(t=>"string"==typeof t?{entity:t}:t)),!this.hass||!this._config.filter)return t;if(this._config.filter.include){const e=Object.keys(this.hass.states).map(t=>new Object({entity:t}));for(const i of this._config.filter.include){if(void 0!==i.type){t.push(i);continue}let s=e.filter(h(this.hass,i)).map(t=>new Object({...t,...i.options}));void 0!==i.sort&&(s=s.sort(y(this.hass,i.sort))),t=t.concat(s)}}if(this._config.filter.exclude)for(const e of this._config.filter.exclude)t=t.filter(t=>"string"!=typeof t&&void 0===t.entity||!h(this.hass,e)(t));if(this._config.sort&&(t=t.sort(y(this.hass,this._config.sort))),this._config.unique){function e(t,i){return typeof t==typeof i&&("object"!=typeof t?t===i:!Object.keys(t).some(t=>!Object.keys(i).includes(t))&&Object.keys(t).every(s=>e(t[s],i[s])))}let i=[];for(const s of t)i.some(t=>e(t,s))||i.push(s);t=i}this.entities=t}set entities(t){(function(t,e){if(t===e)return!0;if(null==t||null==e)return!1;if(t.length!=e.length)return!1;for(var i=0;ithis._getEntities(),0))}createRenderRoot(){return this}render(){return r` + ${this.card}`}getCardSize(){let t=0;return this.card&&this.card.getCardSize&&(t=this.card.getCardSize()),1===t&&this.entities.length&&(t=this.entities.length),0===t&&this._config.filter&&this._config.filter.include&&(t=Object.keys(this._config.filter.include).length),t||1}}),p("ll-rebuild",{})}]); \ No newline at end of file diff --git a/src/filter.js b/src/filter.js index 99392ef..7b858ac 100644 --- a/src/filter.js +++ b/src/filter.js @@ -1,122 +1,126 @@ import { areaByName, areaDevices, deviceByName, deviceEntities } from "card-tools/src/devices"; function match(pattern, value) { - if(typeof(value) === "string" && typeof(pattern) === "string") { - if((pattern.startsWith('/') && pattern.endsWith('/')) || pattern.indexOf('*') !== -1) { - if(!pattern.startsWith('/')) { // Convert globs to regex - pattern = pattern - .replace(/\./g, '\.') - .replace(/\*/g, '.*'); - pattern = `/^${pattern}$/`; - } - let regex = new RegExp(pattern.slice(1,-1)); - return regex.test(value); - } + if(typeof(value) === "string" && typeof(pattern) === "string") { + if((pattern.startsWith('/') && pattern.endsWith('/')) || pattern.indexOf('*') !== -1) { + if(!pattern.startsWith('/')) { // Convert globs to regex + pattern = pattern + .replace(/\./g, '\.') + .replace(/\*/g, '.*'); + pattern = `/^${pattern}$/`; + } + let regex = new RegExp(pattern.slice(1,-1)); + return regex.test(value); } + } - if(typeof(pattern) === "string") { - // Comparisons assume numerical values - if(pattern.startsWith("<=")) return parseFloat(value) <= parseFloat(pattern.substr(2)); - if(pattern.startsWith(">=")) return parseFloat(value) >= parseFloat(pattern.substr(2)); - if(pattern.startsWith("<")) return parseFloat(value) < parseFloat(pattern.substr(1)); - if(pattern.startsWith(">")) return parseFloat(value) > parseFloat(pattern.substr(1)); - if(pattern.startsWith("!")) return parseFloat(value) != parseFloat(pattern.substr(1)); - if(pattern.startsWith("=")) return parseFloat(value) == parseFloat(pattern.substr(1)); - } + if(typeof(pattern) === "string") { + // Comparisons assume numerical values + if(pattern.startsWith("<=")) return parseFloat(value) <= parseFloat(pattern.substr(2)); + if(pattern.startsWith(">=")) return parseFloat(value) >= parseFloat(pattern.substr(2)); + if(pattern.startsWith("<")) return parseFloat(value) < parseFloat(pattern.substr(1)); + if(pattern.startsWith(">")) return parseFloat(value) > parseFloat(pattern.substr(1)); + if(pattern.startsWith("!")) return parseFloat(value) != parseFloat(pattern.substr(1)); + if(pattern.startsWith("=")) return parseFloat(value) == parseFloat(pattern.substr(1)); + } - return pattern === value; + return pattern === value; } export function entity_filter(hass, filter) { - return function(e) { - const entity = typeof(e) === "string" - ? hass.states[e] - : hass.states[e.entity]; - if(!e) return false; - for (const [key, value] of Object.entries(filter)) { - switch(key.split(" ")[0]) { - case "options": - case "sort": - break; + return function(e) { + const entity = typeof(e) === "string" + ? hass.states[e] + : hass.states[e.entity]; + if(!e) return false; + for (const [key, value] of Object.entries(filter)) { + switch(key.split(" ")[0]) { + case "options": + case "sort": + break; - case "domain": - if(!match(value, entity.entity_id.split('.')[0])) - return false; - break; + case "domain": + if(!match(value, entity.entity_id.split('.')[0])) + return false; + break; - case "entity_id": - if(!match(value, entity.entity_id)) - return false; - break; + case "entity_id": + if(!match(value, entity.entity_id)) + return false; + break; - case "state": - if(!match(value, entity.state)) - return false; - break; + case "state": + if(!match(value, entity.state)) + return false; + break; - case "name": - if(!entity.attributes.friendly_name - || !match(value, entity.attributes.friendly_name)) - return false; - break; + case "name": + if(!entity.attributes.friendly_name + || !match(value, entity.attributes.friendly_name)) + return false; + break; - case "group": - if(!value.startsWith("group.") - || !hass.states[value] - || !hass.states[value].attributes.entity_id - || !hass.states[value].attributes.entity_id.includes(entity.entity_id) - ) - return false; - break; + case "group": + if(!value.startsWith("group.") + || !hass.states[value] + || !hass.states[value].attributes.entity_id + || !hass.states[value].attributes.entity_id.includes(entity.entity_id) + ) + return false; + break; - case "attributes": - for(const [k, v] of Object.entries(value)) { - let attr = k.split(" ")[0]; - let entityAttribute = entity.attributes; - while(attr && entityAttribute) { - let step; - [step, attr] = attr.split(":"); - entityAttribute = entityAttribute[step]; - } - if(entityAttribute === undefined - || (v && !match(v, entityAttribute)) - ) - return false; - continue; - } - break; - - case "not": - if(entity_filter(hass,value)(e)) - return false; - break; - - case "device": - let _deviceMatch = false; - for(const d of window.cardToolsData.devices) { - if (match(value, d.name)){ - if(deviceEntities(d).includes(entity.entity_id)) - _deviceMatch = true; - } - } - if(!_deviceMatch) return false; - break; - - case "area": - let _areaMatch = false; - for (const a of window.cardToolsData.areas) { - if(match(value, a.name)) { - if(areaDevices(a).flatMap(deviceEntities).includes(entity.entity_id)) - _areaMatch = true; - } - } - if(!_areaMatch) return false; - break; - - default: - return false; + case "attributes": + for(const [k, v] of Object.entries(value)) { + let attr = k.split(" ")[0]; + let entityAttribute = entity.attributes; + while(attr && entityAttribute) { + let step; + [step, attr] = attr.split(":"); + entityAttribute = entityAttribute[step]; } + if(entityAttribute === undefined + || (v && !match(v, entityAttribute)) + ) + return false; + continue; + } + break; + + case "not": + if(entity_filter(hass,value)(e)) + return false; + break; + + case "device": + if(!window.cardToolsData || !window.cardToolsData.devices) + return false; + let _deviceMatch = false; + for(const d of window.cardToolsData.devices) { + if (match(value, d.name)){ + if(deviceEntities(d).includes(entity.entity_id)) + _deviceMatch = true; + } + } + if(!_deviceMatch) return false; + break; + + case "area": + if(!window.cardToolsData || !window.cardToolsData.areas) + return false; + let _areaMatch = false; + for (const a of window.cardToolsData.areas) { + if(match(value, a.name)) { + if(areaDevices(a).flatMap(deviceEntities).includes(entity.entity_id)) + _areaMatch = true; + } + } + if(!_areaMatch) return false; + break; + + default: + return false; + } } return true; - } -} \ No newline at end of file + } + } \ No newline at end of file diff --git a/src/main.js b/src/main.js index c5730d7..d5d0336 100644 --- a/src/main.js +++ b/src/main.js @@ -1,174 +1,171 @@ import { LitElement, html, css } from "card-tools/src/lit-element"; -import "card-tools/src/card-maker"; import { entity_filter } from "./filter"; import { entity_sorter } from "./sort"; import { getData } from "card-tools/src/devices"; import { fireEvent } from "card-tools/src/event"; +import { createCard } from "card-tools/src/lovelace-element"; +import { hass } from "card-tools/src/hass"; class AutoEntities extends LitElement { - static get properties() { - return { - hass: {}, - }; + static get properties() { + return { + hass: {}, + }; + } + setConfig(config) { + if(!config || !config.card) { + throw new Error("Invalid configuration"); } - setConfig(config) { - if(!config || !config.card) { - throw new Error("Invalid configuration"); - } - if(!this._config) { - this._config = config; - this.cardConfig = {entities: [], ...config.card}; - this.entities = []; - } else { - this._config = config; - this.hass = this.hass; - } + if(!this._config) { + this._config = config; + + this.hass = hass(); + this._getEntities(); + this.cardConfig = {entities: this.entities, ...config.card}; + this.card = createCard(this.cardConfig); + } else { + this._config = config; + this.hass = this.hass; } - async _getEntities() + // Reevaluate all filters once areas have been loaded + getData().then(() => this._getEntities()); + } + + _getEntities() + { + let entities = []; + // Start with any entities added by the `entities` parameter + if(this._config.entities) + entities = entities.concat(this._config.entities) + .map((e) => { + if(typeof(e) === "string") + return {entity: e}; + return e; + }); + + if(!this.hass || !this._config.filter) return entities; + + if(this._config.filter.include) { + const all_entities = Object.keys(this.hass.states) + .map((e) => new Object({entity: e})); + + for(const f of this._config.filter.include) { + if(f.type !== undefined) { + // If the filter has a type, it's a special entry + entities.push(f); + continue; + } + + let add = all_entities.filter(entity_filter(this.hass, f)) + .map((e) => new Object({...e, ...f.options})); + + if(f.sort !== undefined) { + // Sort per filter + add = add.sort(entity_sorter(this.hass, f.sort)); + } + entities = entities.concat(add); + } + } + if(this._config.filter.exclude) { + for(const f of this._config.filter.exclude) { + entities = entities.filter((e) => { + // Don't exclude special entries + if(typeof(e) !== "string" && e.entity === undefined) return true; + return !entity_filter(this.hass,f)(e) + }); + } + } + + if(this._config.sort) { + // Sort everything + entities = entities.sort(entity_sorter(this.hass, this._config.sort)); + } + + if(this._config.unique) { + function compare(a,b) { + if(typeof(a) !== typeof(b)) return false; + if(typeof(a) !== "object") return a===b; + if(Object.keys(a).some((k) => !Object.keys(b).includes(k))) return false; + + return Object.keys(a).every((k) => compare(a[k], b[k])); + } + let newEntities = []; + for(const e of entities) { + if(newEntities.some((i) => compare(i,e))) continue; + newEntities.push(e); + } + entities = newEntities; + } + this.entities = entities; + } + + set entities(ent) { + function compare(a,b) { + if( a === b ) + return true; + if( a == null || b == null) + return false; + if(a.length != b.length) + return false; + for(var i = 0; i < a.length; i++) + if(JSON.stringify(a[i]) !== JSON.stringify(b[i])) + return false; + return true; + } + if(!compare(ent, this._entities)) { - let entities = []; - // Start with any entities added by the `entities` parameter - if(this._config.entities) - entities = entities.concat(this._config.entities) - .map((e) => { - if(typeof(e) === "string") - return {entity: e}; - return e; - }); - - if(!this.hass || !this._config.filter) return entities; - - if(this._config.filter.include) { - const all_entities = Object.keys(this.hass.states) - .map((e) => new Object({entity: e})); - - for(const f of this._config.filter.include) { - if(f.type !== undefined) { - // If the filter has a type, it's a special entry - entities.push(f); - continue; - } - if(f.device || f.area) { - await getData(); - } - - let add = all_entities.filter(entity_filter(this.hass, f)) - .map((e) => new Object({...e, ...f.options})); - - if(f.sort !== undefined) { - // Sort per filter - add = add.sort(entity_sorter(this.hass, f.sort)); - } - entities = entities.concat(add); - } - } - if(this._config.filter.exclude) { - for(const f of this._config.filter.exclude) { - entities = entities.filter((e) => { - // Don't exclude special entries - if(typeof(e) !== "string" && e.entity === undefined) return true; - return !entity_filter(this.hass,f)(e) - }); - } - } - - if(this._config.sort) { - // Sort everything - entities = entities.sort(entity_sorter(this.hass, this._config.sort)); - } - - if(this._config.unique) { - function compare(a,b) { - if(typeof(a) !== typeof(b)) return false; - if(typeof(a) !== "object") return a===b; - if(Object.keys(a).some((k) => !Object.keys(b).includes(k))) return false; - - return Object.keys(a).every((k) => compare(a[k], b[k])); - } - let newEntities = []; - for(const e of entities) { - if(newEntities.some((i) => compare(i,e))) continue; - newEntities.push(e); - } - entities = newEntities; - } - return entities; + this._entities = ent; + this.cardConfig = {...this.cardConfig, entities: this._entities}; + if(ent.length === 0 && this._config.show_empty === false) { + this.style.display = "none"; + this.style.margin = "0"; + } else { + this.style.display = null; + this.style.margin = null; + } } + } + get entities() { + return this._entities; + } - set entities(ent) { - function compare(a,b) { - if( a === b ) - return true; - if( a == null || b == null) - return false; - if(a.length != b.length) - return false; - for(var i = 0; i < a.length; i++) - if(JSON.stringify(a[i]) !== JSON.stringify(b[i])) - return false; - return true; - } - if(!compare(ent, this._entities)) - { - this._entities = ent; - this.cardConfig = {...this.cardConfig, entities: this._entities}; - if(ent.length === 0 && this._config.show_empty === false) { - this.style.display = "none"; - this.style.margin = "0"; - } else { - this.style.display = null; - this.style.margin = null; - } - this.requestUpdate(); - } - } - get entities() { - return this._entities; - } + set cardConfig(cardConfig) { + this._cardConfig = cardConfig; + if(this.card) + this.card.setConfig(cardConfig); + } + get cardConfig() { + return this._cardConfig; + } - set cardConfig(cardConfig) { - this._cardConfig = cardConfig; - if(this.querySelector("card-maker")) - this.querySelector("card-maker").config = cardConfig; - } - get cardConfig() { - return this._cardConfig; + updated(changedProperties) { + if(changedProperties.has("hass") && this.hass) { + this.card.hass = this.hass; + // Run this in a timeout to improve performance + setTimeout(() => this._getEntities(), 0); } + } - firstUpdated() { - this.cardConfig = this._cardConfig; - } + createRenderRoot() { + return this; + } + render() { + return html` + ${this.card}`; + } - updated(changedProperties) { - if(changedProperties.has("hass") && this.hass) { - // Run this in a timeout to improve performance - setTimeout(() => this._getEntities().then((e) => this.entities = e), 0); - } - } - - createRenderRoot() { - return this; - } - - render() { - return html` - `; - } - - getCardSize() { - let len = 0; - if(this.querySelector("card-maker") && this.querySelector("card-maker").getCardSize) - len = this.querySelector("card-maker").getCardSize(); - if(len === 1 && this.entities.length) - len = this.entities.length; - if(len === 0 && this._config.filter && this._config.filter.include) - return Object.keys(this._config.filter.include).length; - return len || 1; - } + getCardSize() { + let len = 0; + if(this.card && this.card.getCardSize) + len = this.card.getCardSize(); + if(len === 1 && this.entities.length) + len = this.entities.length; + if(len === 0 && this._config.filter && this._config.filter.include) + len = Object.keys(this._config.filter.include).length; + return len || 1; + } } customElements.define('auto-entities', AutoEntities); diff --git a/src/sort.js b/src/sort.js index c3e2368..d93a599 100644 --- a/src/sort.js +++ b/src/sort.js @@ -1,66 +1,66 @@ export function entity_sorter(hass, method) { - if(typeof(method) === "string") { - method = {method}; + if(typeof(method) === "string") { + method = {method}; + } + return function(a, b) { + const entityA = typeof(a) === "string" + ? hass.states[a] + : hass.states[a.entity]; + const entityB = typeof(b) === "string" + ? hass.states[b] + : hass.states[b.entity]; + + if(entityA === undefined || entityB === undefined) return 0; + + const [lt, gt] = method.reverse ? [-1, 1] : [1, -1]; + function compare(_a, _b) { + if(method.ignore_case && _a.toLowerCase) _a = _a.toLowerCase(); + if(method.ignore_case && _b.toLowerCase) _b = _b.toLowerCase(); + if(_a === undefined && _b === undefined) return 0; + if(_a === undefined) return lt; + if(_b === undefined) return gt; + if(_a < _b) return gt; + if(_a > _b) return lt; + return 0; } - return function(a, b) { - const entityA = typeof(a) === "string" - ? hass.states[a] - : hass.states[a.entity]; - const entityB = typeof(b) === "string" - ? hass.states[b] - : hass.states[b.entity]; - - if(entityA === undefined || entityB === undefined) return 0; - - const [lt, gt] = method.reverse ? [-1, 1] : [1, -1]; - function compare(_a, _b) { - if(method.ignore_case && _a.toLowerCase) _a = _a.toLowerCase(); - if(method.ignore_case && _b.toLowerCase) _b = _b.toLowerCase(); - if(_a === undefined && _b === undefined) return 0; - if(_a === undefined) return lt; - if(_b === undefined) return gt; - if(_a < _b) return gt; - if(_a > _b) return lt; - return 0; - } - switch(method.method) { - case "domain": - return compare( - entityA.entity_id.split(".")[0], - entityB.entity_id.split(".")[0] - ); - case "entity_id": - return compare( - entityA.entity_id, - entityB.entity_id - ); - case "friendly_name": - case "name": - return compare( - entityA.attributes.friendly_name || entityA.entity_id.split(".")[1], - entityB.attributes.friendly_name || entityB.entity_id.split(".")[1] - ); + switch(method.method) { + case "domain": + return compare( + entityA.entity_id.split(".")[0], + entityB.entity_id.split(".")[0] + ); + case "entity_id": + return compare( + entityA.entity_id, + entityB.entity_id + ); + case "friendly_name": + case "name": + return compare( + entityA.attributes.friendly_name || entityA.entity_id.split(".")[1], + entityB.attributes.friendly_name || entityB.entity_id.split(".")[1] + ); case "state": - return compare( - entityA.state, - entityB.state - ); - case "attribute": - let _a = entityA.attributes; - let _b = entityB.attributes; - let attr = method.attribute; - while(attr) { - let k; - [k, attr] = attr.split(":"); - _a = _a[k]; - _b = _b[k]; - if(_a === undefined && _b === undefined) return 0; - if(_a === undefined) return lt; - if(_b === undefined) return gt; - } - return compare(_a, _b); - default: - return 0; - } - } -} \ No newline at end of file + return compare( + entityA.state, + entityB.state + ); + case "attribute": + let _a = entityA.attributes; + let _b = entityB.attributes; + let attr = method.attribute; + while(attr) { + let k; + [k, attr] = attr.split(":"); + _a = _a[k]; + _b = _b[k]; + if(_a === undefined && _b === undefined) return 0; + if(_a === undefined) return lt; + if(_b === undefined) return gt; + } + return compare(_a, _b); + default: + return 0; + } + } + } \ No newline at end of file