Skip to content

Commit

Permalink
add auto-entities.js
Browse files Browse the repository at this point in the history
  • Loading branch information
eifinger committed Mar 21, 2019
1 parent 52ebdc1 commit 90010c6
Showing 1 changed file with 225 additions and 0 deletions.
225 changes: 225 additions & 0 deletions www/auto-entities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
customElements.whenDefined('card-tools').then(() => {
class AutoEntities extends cardTools.LitElement {

setConfig(config) {
if(!config || !config.card)
throw new Error("Invalid configuration");

this._config = config;
this.data = {};

this.entities = this.get_entities() || [];
this.card = cardTools.createCard(Object.assign({entities: this.entities}, config.card));
}


match(pattern, str){
if (typeof(str) === "string" && typeof(pattern) === "string") {
if((pattern.startsWith('/') && pattern.endsWith('/')) || pattern.indexOf('*') !== -1) {
if(pattern[0] !== '/') {
pattern = pattern.replace(/\./g, '\.');
pattern = pattern.replace(/\*/g, '.*');
pattern = `/^${pattern}$/`;
}
var regex = new RegExp(pattern.substr(1).slice(0,-1));
return regex.test(str);
}
}
if(typeof(pattern) === "string") {
if(pattern.indexOf(":") !== -1 && typeof(str) === "object") {
while(pattern.indexOf(":") !== -1)
{
str = str[pattern.split(":")[0]];
pattern = pattern.substr(pattern.indexOf(":")+1, pattern.length);
}
}
if(pattern.startsWith("<=")) return parseFloat(str) <= parseFloat(pattern.substr(2));
if(pattern.startsWith(">=")) return parseFloat(str) >= parseFloat(pattern.substr(2));
if(pattern.startsWith("<")) return parseFloat(str) < parseFloat(pattern.substr(1));
if(pattern.startsWith(">")) return parseFloat(str) > parseFloat(pattern.substr(1));
if(pattern.startsWith("!")) return parseFloat(str) != parseFloat(pattern.substr(1));
if(pattern.startsWith("=")) return parseFloat(str) == parseFloat(pattern.substr(1));
}
return str === pattern;
}

match_filter(hass, entities, filter) {
let retval = [];
let count = -1;
entities.forEach((i) => {
count++;
if(!hass.states) return;
const e = (typeof(i) === "string")?hass.states[i]:hass.states[i.entity];
if(!e) return;

let unmatched = false;
Object.keys(filter).forEach((filterKey) => {
const key = filterKey.split(" ")[0];
const value = filter[filterKey];
switch(key) {
case "options":
break;
case "domain":
if(!this.match(value, e.entity_id.split('.')[0]))
unmatched = true;
break;
case "state":
if(!this.match(value, e.state))
unmatched = true;
break;
case "entity_id":
if(!this.match(value, e.entity_id))
unmatched = true;
break;
case "name":
if(!e.attributes.friendly_name
|| !this.match(value, e.attributes.friendly_name)
)
unmatched = true;
break;
case "area":
let found = false;
this.data.areas.forEach((a) => {
if(found) return;
if(this.match(value, a.name)) {
this.data.devices.forEach((d) => {
if(found) return;
if(d.area_id && d.area_id === a.area_id) {
this.data.entities.forEach((en) => {
if(found) return;
if(en.device_id === d.id && en.entity_id === e.entity_id) {
found = true;
}
});
}
});
}
});
if(!found) unmatched = true;
break;
case "group":
if(!value.startsWith("group.")
|| !hass.states[value]
|| !hass.states[value].attributes.entity_id
|| !hass.states[value].attributes.entity_id.includes(e.entity_id)
)
unmatched = true;
break;
case "attributes":
Object.keys(value).forEach((entityKey) => {
const k = entityKey.split(" ")[0];
const v = value[entityKey];
if(!e.attributes[k]
|| !this.match(v, e.attributes[k])
)
unmatched = true;
});
break;
default:
unmatched = true;
}
});
if(!unmatched) retval.push(count);
});
return retval;
}

get_entities()
{
let entities = [];
if(this._config.entities)
this._config.entities.forEach((e) => entities.push(e));

if(this._hass && this._config.filter) {

if(this._config.filter.include){
this._config.filter.include.forEach((f) => {
const add = this.match_filter(this._hass, Object.keys(this._hass.states), f);
let toAdd = [];
add.forEach((i) => {
toAdd.push(Object.assign({entity: Object.keys(this._hass.states)[i]}, f.options));
});
toAdd.sort((a,b) => {
if (a.entity < b.entity) return -1;
if (a.entity > b.entity) return 1;
return 0;
});
toAdd.forEach((i) => entities.push(i));
});
}

if(this._config.filter.exclude) {
this._config.filter.exclude.forEach((f) => {
const remove = this.match_filter(this._hass, entities, f);
for(var i = remove.length-1; i >= 0; i--)
{
entities.splice(remove[i],1);
}
});
}
}
return entities;
}

createRenderRoot() {
return this;
}
render() {
if(this.entities.length === 0 && this._config.show_empty === false)
return cardTools.LitHtml``;
return cardTools.LitHtml`
<div id="root">${this.card}</div>
`;
}

async get_data(hass) {
try {
this.data.areas = await hass.callWS({type: "config/area_registry/list"});
this.data.devices = await hass.callWS({type: "config/device_registry/list"});
this.data.entities = await hass.callWS({type: "config/entity_registry/list"});
} catch (err) {
}
}

_compare_arrays(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(a[i] !== b[i]) {
return false;
}
}
return true;
}

set hass(hass) {
this._hass = hass;
this.get_data(hass).then(() => {
if(this.card)
{
this.card.hass = this._hass;
}

const oldEntities = this.entities.map((e) => e.entity);
this.entities = this.get_entities() || [];
const newEntities = this.entities.map((e) => e.entity);

if(!this._compare_arrays(oldEntities, newEntities)) {
this.card.setConfig(Object.assign({entities: this.entities}, this._config.card));
this.requestUpdate();
}
});
}

}

customElements.define('auto-entities', AutoEntities);
});

window.setTimeout(() => {
if(customElements.get('card-tools')) return;
customElements.define('auto-entities', class extends HTMLElement{
setConfig() { throw new Error("Can't find card-tools. See https://github.com/thomasloven/lovelace-card-tools");}
});
}, 2000);

0 comments on commit 90010c6

Please sign in to comment.