diff --git a/src/panels/lovelace/common/compute-card-element.js b/src/panels/lovelace/common/compute-card-element.js new file mode 100644 index 0000000000..9c43f2880f --- /dev/null +++ b/src/panels/lovelace/common/compute-card-element.js @@ -0,0 +1,21 @@ +const CARD_TYPES = [ + 'camera-preview', + 'entities', + 'entity-filter', + 'glance', + 'history-graph', + 'media-control', + 'picture-glance', + 'plant-status', + 'weather-forecast' +]; +const CUSTOM_TYPE_PREFIX = 'custom:'; + +export default function computeCardElement(type) { + if (CARD_TYPES.includes(type)) { + return `hui-${type}-card`; + } else if (type.startsWith(CUSTOM_TYPE_PREFIX)) { + return type.substr(CUSTOM_TYPE_PREFIX.length); + } + return null; +} diff --git a/src/panels/lovelace/hui-entity-filter-card.js b/src/panels/lovelace/hui-entity-filter-card.js index 2ee2b87087..d96256c464 100644 --- a/src/panels/lovelace/hui-entity-filter-card.js +++ b/src/panels/lovelace/hui-entity-filter-card.js @@ -1,55 +1,113 @@ -import { html } from '@polymer/polymer/lib/utils/html-tag.js'; import { PolymerElement } from '@polymer/polymer/polymer-element.js'; +import './hui-camera-preview-card.js'; import './hui-entities-card.js'; +import './hui-entity-filter-card.js'; +import './hui-glance-card'; +import './hui-history-graph-card.js'; +import './hui-media-control-card.js'; +import './hui-picture-glance-card'; +import './hui-plant-status-card.js'; +import './hui-weather-forecast-card'; import computeStateDomain from '../../common/entity/compute_state_domain.js'; +import computeCardElement from './common/compute-card-element.js'; class HuiEntitiesCard extends PolymerElement { - static get template() { - return html` - -`; - } - static get properties() { return { - hass: Object, - config: Object, + hass: { + type: Object, + observer: '_hassChanged' + }, + config: { + type: Object, + observer: '_configChanged' + } }; } getCardSize() { - // +1 for the header - return 1 + this._getEntities(this.hass, this.config.filter).length; + return this.lastChild ? this.lastChild.getCardSize() : 1; } - // Return a list of entities based on a filter. - _getEntities(hass, filter) { - const filters = []; + // Return a list of entities based on filters. + _getEntities(hass, filterList) { + const entities = new Set(); + filterList.forEach((filter) => { + const filters = []; + if (filter.domain) { + filters.push(stateObj => computeStateDomain(stateObj) === filter.domain); + } + if (filter.entity_id) { + filters.push(stateObj => this._filterEntityId(stateObj, filter.entity_id)); + } + if (filter.state) { + filters.push(stateObj => stateObj.state === filter.state); + } - if (filter.domain) { - const domain = filter.domain; - filters.push(stateObj => computeStateDomain(stateObj) === domain); - } - - if (filter.state) { - const state = filter.state; - filters.push(stateObj => stateObj.state === state); - } - - return Object.values(hass.states) - .filter(stateObj => filters.every(filterFunc => filterFunc(stateObj))) - .map(stateObj => stateObj.entity_id); - } - - _computeCardConfig(hass, config) { - return Object.assign({}, config.card_config || {}, { - entities: this._getEntities(hass, config.filter), + Object.values(hass.states).forEach((stateObj) => { + if (filters.every(filterFunc => filterFunc(stateObj))) { + entities.add(stateObj.entity_id); + } + }); }); + return Array.from(entities); + } + + _filterEntityId(stateObj, pattern) { + if (pattern.indexOf('*') === -1) { + return stateObj.entity_id === pattern; + } + const regEx = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`); + return stateObj.entity_id.search(regEx) === 0; + } + + _configChanged(config) { + if (this.lastChild) { + this.removeChild(this.lastChild); + } + let error = null; + let cardConfig; + let tag = config.card ? computeCardElement(config.card) : 'hui-entities-card'; + + if (tag === null) { + error = `Unknown card type encountered: "${config.card}".`; + } else if (!customElements.get(tag)) { + error = `Custom element doesn't exist: "${tag}".`; + } else if (!config.filter || !Array.isArray(config.filter)) { + error = 'No or incorrect filter.'; + } + if (error) { + tag = 'hui-error-card'; + cardConfig = { error }; + } else { + cardConfig = this._computeCardConfig(config); + } + + const element = document.createElement(tag); + element.config = cardConfig; + element.hass = this.hass; + this.appendChild(element); + } + + _hassChanged(hass) { + const element = this.lastChild; + if (element) { + element.hass = hass; + element.config = this._computeCardConfig(this.config); + } + } + + _computeCardConfig(config) { + const cardConfig = Object.assign( + {}, + config, + { entities: this._getEntities(this.hass, config.filter) } + ); + delete cardConfig.card; + delete cardConfig.filter; + return cardConfig; } } customElements.define('hui-entity-filter-card', HuiEntitiesCard); diff --git a/src/panels/lovelace/hui-view.js b/src/panels/lovelace/hui-view.js index cd0534bcc8..b43255e516 100644 --- a/src/panels/lovelace/hui-view.js +++ b/src/panels/lovelace/hui-view.js @@ -14,29 +14,7 @@ import './hui-weather-forecast-card'; import './hui-error-card.js'; import applyThemesOnElement from '../../common/dom/apply_themes_on_element.js'; - -const VALID_TYPES = [ - 'camera-preview', - 'entities', - 'entity-filter', - 'glance', - 'history-graph', - 'media-control', - 'picture', - 'picture-glance', - 'plant-status', - 'weather-forecast' -]; -const CUSTOM_TYPE_PREFIX = 'custom:'; - -function cardElement(type) { - if (VALID_TYPES.includes(type)) { - return `hui-${type}-card`; - } else if (type.startsWith(CUSTOM_TYPE_PREFIX)) { - return type.substr(CUSTOM_TYPE_PREFIX.length); - } - return null; -} +import computeCardElement from './common/compute-card-element.js'; class HUIView extends PolymerElement { static get template() { @@ -123,7 +101,7 @@ class HUIView extends PolymerElement { if (!cardConfig.type) { error = 'Card type not configured.'; } else { - tag = cardElement(cardConfig.type); + tag = computeCardElement(cardConfig.type); if (tag === null) { error = `Unknown card type encountered: "${cardConfig.type}".`; } else if (!customElements.get(tag)) {