Separate row entity elements for Lovelace (#1461)

* Separate row entity elements for Lovelace

* Update

* Update

* Add fan

* Add support for last changed

* Feedback

* Feedback2

* Update

* Update

* Fix timer

* Update

* Lint
This commit is contained in:
c727 2018-07-18 17:25:13 +02:00 committed by Paulus Schoutsen
parent 9ab4158e0a
commit 8c44e243e1
29 changed files with 953 additions and 152 deletions

View File

@ -82,13 +82,12 @@ class StateInfo extends PolymerElement {
},
hass: Object,
stateObj: Object,
inDialog: Boolean,
overrideName: String
inDialog: Boolean
};
}
computeStateName(stateObj) {
return this.overrideName || computeStateName(stateObj);
return computeStateName(stateObj);
}
}

View File

@ -2,20 +2,16 @@ import '@polymer/iron-flex-layout/iron-flex-layout-classes.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import processConfigEntities from '../common/process-config-entities.js';
import '../../../components/ha-card.js';
import '../components/hui-entities-toggle.js';
// just importing this now as shortcut to import correct state-card-*
import '../../../state-summary/state-card-content.js';
import createEntityRowElement from '../common/create-entity-row-element.js';
import processConfigEntities from '../common/process-config-entities.js';
import computeDomain from '../../../common/entity/compute_domain.js';
import { DOMAINS_HIDE_MORE_INFO } from '../../../common/const.js';
import EventsMixin from '../../../mixins/events-mixin.js';
import createEntityRowElement from '../common/create-entity-row-element.js';
import { DOMAINS_HIDE_MORE_INFO } from '../../../common/const.js';
import computeDomain from '../../../common/entity/compute_domain.js';
/*
* @appliesMixin EventsMixin
*/
@ -70,7 +66,7 @@ class HuiEntitiesCard extends EventsMixin(PolymerElement) {
type: Object,
observer: '_hassChanged',
},
_config: Object,
_config: Object
};
}
@ -117,14 +113,13 @@ class HuiEntitiesCard extends EventsMixin(PolymerElement) {
for (const entity of entities) {
const entityId = entity.entity;
const element = createEntityRowElement(entity, this.hass);
const element = createEntityRowElement(entity);
if (entityId && !DOMAINS_HIDE_MORE_INFO.includes(computeDomain(entityId))) {
element.classList.add('state-card-dialog');
element.addEventListener('click', () => this.fire('hass-more-info', { entityId }));
}
this._elements.push({ entityId, element });
element.hass = this.hass;
this._elements.push(element);
const container = document.createElement('div');
container.appendChild(element);
root.appendChild(container);
@ -132,12 +127,9 @@ class HuiEntitiesCard extends EventsMixin(PolymerElement) {
}
_hassChanged(hass) {
for (let i = 0; i < this._elements.length; i++) {
const { entityId, element } = this._elements[i];
const stateObj = hass.states[entityId];
element.stateObj = stateObj;
this._elements.forEach((element) => {
element.hass = hass;
}
});
}
}

View File

@ -1,12 +1,36 @@
import fireEvent from '../../../common/dom/fire_event.js';
import stateCardType from '../../../common/entity/state_card_type.js';
import '../entity-rows/hui-cover-entity-row.js';
import '../entity-rows/hui-input-number-entity-row.js';
import '../entity-rows/hui-input-select-entity-row.js';
import '../entity-rows/hui-input-text-entity-row.js';
import '../entity-rows/hui-lock-entity-row.js';
import '../entity-rows/hui-scene-entity-row.js';
import '../entity-rows/hui-script-entity-row.js';
import '../entity-rows/hui-text-entity-row.js';
import '../entity-rows/hui-timer-entity-row.js';
import '../entity-rows/hui-toggle-entity-row.js';
import createErrorCardConfig from './create-error-card-config.js';
const CUSTOM_TYPE_PREFIX = 'custom:';
const DOMAIN_TO_ELEMENT_TYPE = {
cover: 'cover',
fan: 'toggle',
group: 'toggle',
input_boolean: 'toggle',
input_number: 'input-number',
input_select: 'input-select',
input_text: 'input-text',
light: 'toggle',
lock: 'lock',
scene: 'scene',
script: 'script',
timer: 'timer',
switch: 'toggle'
};
function _createElement(tag, config, stateObj, hass) {
function _createElement(tag, config) {
const element = document.createElement(tag);
try {
if ('setConfig' in element) element.setConfig(config);
@ -17,12 +41,6 @@ function _createElement(tag, config, stateObj, hass) {
return _createErrorElement(err.message, config);
}
element.stateObj = stateObj;
element.hass = hass;
if (config.name) {
element.overrideName = config.name;
}
return element;
}
@ -30,25 +48,19 @@ function _createErrorElement(error, config) {
return _createElement('hui-error-card', createErrorCardConfig(error, config));
}
export default function createEntityRowElement(config, hass) {
export default function createEntityRowElement(config) {
let tag;
if (!config || typeof config !== 'object') {
if (!config || typeof config !== 'object' || !config.entity) {
return _createErrorElement('Invalid config given.', config);
}
const entityId = config.entity;
if (!(entityId in hass.states)) {
return _createErrorElement('Entity not found.', config);
}
const type = config.type || 'default';
const stateObj = hass.states[entityId];
if (type.startsWith(CUSTOM_TYPE_PREFIX)) {
tag = type.substr(CUSTOM_TYPE_PREFIX.length);
if (customElements.get(tag)) {
return _createElement(tag, config, stateObj, hass);
return _createElement(tag, config);
}
const element = _createErrorElement(`Custom element doesn't exist: ${tag}.`, config);
@ -58,7 +70,8 @@ export default function createEntityRowElement(config, hass) {
return element;
}
tag = stateObj ? `state-card-${stateCardType(hass, stateObj)}` : 'state-card-display';
const domain = config.entity.split('.', 1)[0];
tag = `hui-${DOMAIN_TO_ELEMENT_TYPE[domain] || 'text'}-entity-row`;
return _createElement(tag, config, stateObj, hass);
return _createElement(tag, config);
}

View File

@ -0,0 +1,92 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../../../components/entity/state-badge.js';
import '../../../components/ha-relative-time.js';
import computeStateName from '../../../common/entity/compute_state_name.js';
import EventsMixin from '../../../mixins/events-mixin.js';
/*
* @appliesMixin EventsMixin
*/
class HuiGenericEntityRow extends EventsMixin(PolymerElement) {
static get template() {
return html`
<style>
:host {
display: flex;
}
.flex {
margin-left: 16px;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.secondary,
ha-relative-time {
display: block;
color: var(--secondary-text-color);
}
.not-found {
flex: 1;
background-color: yellow;
padding: 8px;
}
</style>
<template is="dom-if" if="[[_stateObj]]">
<state-badge state-obj="[[_stateObj]]"></state-badge>
<div class="flex">
<div class="info">
[[_computeName(config.name, _stateObj)]]
<template is="dom-if" if="[[config.secondary_info]]">
<template is="dom-if" if="[[_equals(config.secondary_info, 'entity-id')]]">
<div class="secondary">
[[_stateObj.entity_id]]
</div>
</template>
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
<ha-relative-time
hass="[[hass]]"
datetime="[[_stateObj.last_changed]]"
></ha-relative-time>
</template>
</template>
</div>
<slot></slot>
</div>
</template>
<template is="dom-if" if="[[!_stateObj]]">
<div class="not-found">
Entity not available: [[config.entity]]
</div>
</template>
`;
}
static get properties() {
return {
hass: Object,
config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, config.entity)'
}
};
}
_equals(a, b) {
return a === b;
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
_computeName(name, stateObj) {
return name || computeStateName(stateObj);
}
}
customElements.define('hui-generic-entity-row', HuiGenericEntityRow);

View File

@ -0,0 +1,56 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../components/hui-generic-entity-row.js';
import '../../../components/ha-cover-controls.js';
import '../../../components/ha-cover-tilt-controls.js';
import CoverEntity from '../../../util/cover-model.js';
class HuiCoverEntityRow extends PolymerElement {
static get template() {
return html`
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<template is="dom-if" if="[[!_entityObj.isTiltOnly]]">
<ha-cover-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-controls>
</template>
<template is="dom-if" if="[[_entityObj.isTiltOnly]]">
<ha-cover-tilt-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-tilt-controls>
</template>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)'
},
_entityObj: {
type: Object,
computed: '_computeEntityObj(hass, _stateObj)'
},
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
_computeEntityObj(hass, stateObj) {
return stateObj ? new CoverEntity(hass, stateObj) : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
}
customElements.define('hui-cover-entity-row', HuiCoverEntityRow);

View File

@ -0,0 +1,147 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-input/paper-input.js';
import '@polymer/paper-slider/paper-slider.js';
import { IronResizableBehavior } from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js';
import '../components/hui-generic-entity-row.js';
class HuiInputNumberEntityRow extends mixinBehaviors([IronResizableBehavior], PolymerElement) {
static get template() {
return html`
<style>
.flex {
display: flex;
align-items: center;
}
.state {
min-width: 45px;
text-align: center;
}
paper-input {
text-align: right;
}
</style>
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
id="input_number_card"
>
<div>
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'slider')]]">
<div class="flex">
<paper-slider
min="[[_min]]"
max="[[_max]]"
value="{{_value}}"
step="[[_step]]"
pin
on-change="_selectedValueChanged"
ignore-bar-touch
></paper-slider>
<div class="state">[[_value]] [[_stateObj.attributes.unit_of_measurement]]</div>
</div>
</template>
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'box')]]">
<paper-input
no-label-float
auto-validate
pattern="[0-9]+([\\.][0-9]+)?"
step="[[_step]]"
min="[[_min]]"
max="[[_max]]"
value="{{_value}}"
type="number"
on-change="_selectedValueChanged"
></paper-input>
</template>
</div>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)',
observer: '_stateObjChanged'
},
_min: {
type: Number,
value: 0
},
_max: {
type: Number,
value: 100
},
_step: Number,
_value: Number
};
}
ready() {
super.ready();
if (typeof ResizeObserver === 'function') {
const ro = new ResizeObserver((entries) => {
entries.forEach(() => {
this._hiddenState();
});
});
ro.observe(this.$.input_number_card);
} else {
this.addEventListener('iron-resize', this._hiddenState);
}
}
_equals(a, b) {
return a === b;
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_hiddenState() {
if (this._stateObj.attributes.mode !== 'slider') return;
const sliderwidth = this.shadowRoot.querySelector('paper-slider').offsetWidth;
const stateElement = this.shadowRoot.querySelector('.state');
if (sliderwidth < 100) {
stateElement.style.display = 'none';
} else if (sliderwidth >= 145) {
stateElement.style.display = 'inline';
}
}
_stateObjChanged(stateObj, oldStateObj) {
this.setProperties({
_min: Number(stateObj.attributes.min),
_max: Number(stateObj.attributes.max),
_step: Number(stateObj.attributes.step),
_value: Number(stateObj.state)
});
if (oldStateObj && stateObj.attributes.mode === 'slider' && oldStateObj.attributes.mode !== 'slider') {
this._hiddenState();
}
}
_selectedValueChanged() {
if (this._value === Number(this._stateObj.state)) return;
this.hass.callService('input_number', 'set_value', {
value: this._value,
entity_id: this._stateObj.entity_id,
});
}
}
customElements.define('hui-input-number-entity-row', HuiInputNumberEntityRow);

View File

@ -0,0 +1,101 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-dropdown-menu/paper-dropdown-menu.js';
import '@polymer/paper-item/paper-item.js';
import '@polymer/paper-listbox/paper-listbox.js';
import '../../../components/entity/state-badge.js';
import computeStateName from '../../../common/entity/compute_state_name.js';
import EventsMixin from '../../../mixins/events-mixin.js';
/*
* @appliesMixin EventsMixin
*/
class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
static get template() {
return html`
<style>
:host {
display: flex;
align-items: center;
}
paper-dropdown-menu {
margin-left: 16px;
flex: 1;
}
.not-found {
flex: 1;
background-color: yellow;
padding: 8px;
}
</style>
<template is="dom-if" if="[[_stateObj]]">
<state-badge state-obj="[[_stateObj]]"></state-badge>
<paper-dropdown-menu on-click="_stopPropagation" selected-item-label="{{_selected}}" label="[[_computeName(_config.name, _stateObj)]]">
<paper-listbox slot="dropdown-content" selected="[[_computeSelected(_stateObj)]]">
<template is="dom-repeat" items="[[_stateObj.attributes.options]]">
<paper-item>[[item]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</template>
<template is="dom-if" if="[[!_stateObj]]">
<div class="not-found">
Entity not available: [[_config.entity]]
</div>
</template>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)'
},
_selected: {
type: String,
observer: '_selectedChanged',
},
};
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
_computeName(name, stateObj) {
return name || computeStateName(stateObj);
}
_computeSelected(stateObj) {
return stateObj.attributes.options.indexOf(stateObj.state);
}
_selectedChanged(option) {
// Selected Option will transition to '' before transitioning to new value
if (option === '' || option === this._stateObj.state) {
return;
}
this.hass.callService('input_select', 'select_option', {
option: option,
entity_id: this._stateObj.entity_id,
});
}
_stopPropagation(ev) {
ev.stopPropagation();
}
}
customElements.define('hui-input-select-entity-row', HuiInputSelectEntityRow);

View File

@ -0,0 +1,67 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-input/paper-input.js';
import '../components/hui-generic-entity-row.js';
class HuiInputTextEntityRow extends PolymerElement {
static get template() {
return html`
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<paper-input
no-label-float
minlength="[[_stateObj.attributes.min]]"
maxlength="[[_stateObj.attributes.max]]"
value="{{_value}}"
auto-validate="[[_stateObj.attributes.pattern]]"
pattern="[[_stateObj.attributes.pattern]]"
type="[[_stateObj.attributes.mode]]"
on-change="_selectedValueChanged"
placeholder="(empty value)"
></paper-input>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)',
observer: '_stateObjChanged',
},
_value: String
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_stateObjChanged(stateObj) {
this._value = stateObj.state;
}
_selectedValueChanged() {
if (this._value === this._stateObj.state) {
return;
}
this.hass.callService('input_text', 'set_value', {
value: this._value,
entity_id: this._stateObj.entity_id,
});
}
}
customElements.define('hui-input-text-entity-row', HuiInputTextEntityRow);

View File

@ -0,0 +1,67 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-button/paper-button.js';
import '../components/hui-generic-entity-row.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
/*
* @appliesMixin LocalizeMixin
*/
class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style>
paper-button {
color: var(--primary-color);
font-weight: 500;
margin: 0;
}
</style>
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<paper-button on-click="_callService">
[[_computeButtonTitle(_stateObj.state)]]
</paper-button>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)'
}
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_computeButtonTitle(state) {
return state === 'locked' ?
this.localize('ui.card.lock.unlock') : this.localize('ui.card.lock.lock');
}
_callService(ev) {
ev.stopPropagation();
const stateObj = this._stateObj;
this.hass.callService('lock', stateObj.state === 'locked' ?
'unlock' : 'lock', { entity_id: stateObj.entity_id });
}
}
customElements.define('hui-lock-entity-row', HuiLockEntityRow);

View File

@ -0,0 +1,56 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-button/paper-button.js';
import '../components/hui-generic-entity-row.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
/*
* @appliesMixin LocalizeMixin
*/
class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style>
paper-button {
color: var(--primary-color);
font-weight: 500;
margin: 0;
}
</style>
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<paper-button on-click="_callService">
[[localize('ui.card.scene.activate')]]
</paper-button>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_callService(ev) {
ev.stopPropagation();
this.hass.callService('scene', 'turn_on', { entity_id: this._config.entity });
}
}
customElements.define('hui-scene-entity-row', HuiSceneEntityRow);

View File

@ -0,0 +1,64 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-button/paper-button.js';
import '../components/hui-generic-entity-row.js';
import '../../../components/entity/ha-entity-toggle.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
/*
* @appliesMixin LocalizeMixin
*/
class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style>
paper-button {
color: var(--primary-color);
font-weight: 500;
margin: 0;
}
</style>
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<template is="dom-if" if="[[_stateObj.attributes.can_cancel]]">
<ha-entity-toggle state-obj="[[_stateObj]]" hass="[[hass]]"></ha-entity-toggle>
</template>
<template is="dom-if" if="[[!_stateObj.attributes.can_cancel]]">
<paper-button on-click="_callService">[[localize('ui.card.script.execute')]]</paper-button>
</template>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)'
}
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_callService(ev) {
ev.stopPropagation();
this.hass.callService('script', 'turn_on', { entity_id: this._config.entity });
}
}
customElements.define('hui-script-entity-row', HuiScriptEntityRow);

View File

@ -0,0 +1,53 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../components/hui-generic-entity-row.js';
import computeStateDisplay from '../../../common/entity/compute_state_display.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
/*
* @appliesMixin LocalizeMixin
*/
class HuiTextEntityRow extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<div>
[[_computeState(_stateObj)]]
</div>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)'
}
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_computeState(stateObj) {
return computeStateDisplay(this.localize, stateObj);
}
}
customElements.define('hui-text-entity-row', HuiTextEntityRow);

View File

@ -0,0 +1,92 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../components/hui-generic-entity-row.js';
import timerTimeRemaining from '../../../common/entity/timer_time_remaining.js';
import secondsToDuration from '../../../common/datetime/seconds_to_duration.js';
class HuiTimerEntityRow extends PolymerElement {
static get template() {
return html`
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<div>
[[_computeDisplay(_stateObj, _timeRemaining)]]
</div>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)',
observer: '_stateObjChanged'
},
_timeRemaining: Number
};
}
disconnectedCallback() {
super.disconnectedCallback();
this._clearInterval();
}
_stateObjChanged(stateObj) {
if (stateObj) {
this._startInterval(stateObj);
} else {
this._clearInterval();
}
}
_clearInterval() {
if (this._updateRemaining) {
clearInterval(this._updateRemaining);
this._updateRemaining = null;
}
}
_startInterval(stateObj) {
this._clearInterval();
this._calculateRemaining(stateObj);
if (stateObj.state === 'active') {
this._updateRemaining = setInterval(() => this._calculateRemaining(this._stateObj), 1000);
}
}
_calculateRemaining(stateObj) {
this._timeRemaining = timerTimeRemaining(stateObj);
}
_computeDisplay(stateObj, time) {
if (stateObj.state === 'idle' || time === 0) return stateObj.state;
let display = secondsToDuration(time);
if (stateObj.state === 'paused') {
display += ' (paused)';
}
return display;
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
}
customElements.define('hui-timer-entity-row', HuiTimerEntityRow);

View File

@ -0,0 +1,37 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../components/hui-generic-entity-row.js';
import '../../../components/entity/ha-entity-toggle.js';
class HuiToggleEntityRow extends PolymerElement {
static get template() {
return html`
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
>
<ha-entity-toggle hass="[[hass]]" state-obj="[[_computeStateObj(hass.states, _config.entity)]]"></ha-entity-toggle>
</hui-generic-entity-row>
`;
}
static get properties() {
return {
hass: Object,
_config: Object
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
}
customElements.define('hui-toggle-entity-row', HuiToggleEntityRow);

View File

@ -37,7 +37,8 @@ class HuiUnusedEntities extends PolymerElement {
const root = this.$.root;
if (root.lastChild) root.removeChild(root.lastChild);
const entities = computeUnusedEntities(this.hass, config);
const entities = computeUnusedEntities(this.hass, config).map(entity =>
({ entity, secondary_info: 'entity-id' }));
const cardConfig = {
type: 'entities',
title: 'Unused entities',

View File

@ -30,12 +30,11 @@ class StateCardClimate extends PolymerElement {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -46,8 +45,7 @@ class StateCardClimate extends PolymerElement {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}
}

View File

@ -38,12 +38,11 @@ class StateCardConfigurator extends LocalizeMixin(PolymerElement) {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -54,8 +53,7 @@ class StateCardConfigurator extends LocalizeMixin(PolymerElement) {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}

View File

@ -29,12 +29,11 @@ class StateCardCover extends PolymerElement {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -49,8 +48,7 @@ class StateCardCover extends PolymerElement {
entityObj: {
type: Object,
computed: 'computeEntityObj(hass, stateObj)',
},
overrideName: String
}
};
}

View File

@ -46,12 +46,11 @@ class StateCardDisplay extends LocalizeMixin(PolymerElement) {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -62,8 +61,7 @@ class StateCardDisplay extends LocalizeMixin(PolymerElement) {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}

View File

@ -51,12 +51,11 @@ class StateCardInputNumber extends mixinBehaviors([
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -105,16 +104,9 @@ class StateCardInputNumber extends mixinBehaviors([
type: Number,
value: 3
},
step: {
type: Number,
},
value: {
type: Number,
},
mode: {
type: String,
},
overrideName: String
step: Number,
value: Number,
mode: String
};
}

View File

@ -59,13 +59,12 @@ class StateCardInputSelect extends PolymerElement {
selectedOption: {
type: String,
observer: 'selectedOptionChanged',
},
overrideName: String
}
};
}
_computeStateName(stateObj) {
return this.overrideName || computeStateName(stateObj);
return computeStateName(stateObj);
}
computeSelected(stateObj) {

View File

@ -25,12 +25,11 @@ class StateCardInputText extends PolymerElement {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -50,8 +49,7 @@ class StateCardInputText extends PolymerElement {
},
pattern: String,
value: String,
overrideName: String
value: String
};
}

View File

@ -38,8 +38,7 @@ class StateCardLock extends LocalizeMixin(PolymerElement) {
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
></state-info>
`;
}
@ -54,8 +53,7 @@ class StateCardLock extends LocalizeMixin(PolymerElement) {
type: Boolean,
value: false,
},
isLocked: Boolean,
overrideName: String
isLocked: Boolean
};
}

View File

@ -53,12 +53,11 @@ class StateCardMediaPlayer extends LocalizeMixin(PolymerElement) {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -73,8 +72,7 @@ class StateCardMediaPlayer extends LocalizeMixin(PolymerElement) {
playerObj: {
type: Object,
computed: 'computePlayerObj(hass, stateObj)',
},
overrideName: String
}
};
}

View File

@ -32,12 +32,11 @@ class StateCardScene extends LocalizeMixin(PolymerElement) {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -48,8 +47,7 @@ class StateCardScene extends LocalizeMixin(PolymerElement) {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}

View File

@ -43,12 +43,11 @@ class StateCardScript extends LocalizeMixin(PolymerElement) {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -59,8 +58,7 @@ class StateCardScript extends LocalizeMixin(PolymerElement) {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}

View File

@ -7,12 +7,8 @@ import '../components/entity/state-info.js';
import timerTimeRemaining from '../common/entity/timer_time_remaining.js';
import secondsToDuration from '../common/datetime/seconds_to_duration.js';
import LocalizeMixin from '../mixins/localize-mixin.js';
/*
* @appliesMixin LocalizeMixin
*/
class StateCardTimer extends LocalizeMixin(PolymerElement) {
class StateCardTimer extends PolymerElement {
static get template() {
return html`
<style include="iron-flex iron-flex-alignment"></style>
@ -36,12 +32,11 @@ class StateCardTimer extends LocalizeMixin(PolymerElement) {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -56,8 +51,7 @@ class StateCardTimer extends LocalizeMixin(PolymerElement) {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}

View File

@ -25,12 +25,11 @@ class StateCardToggle extends PolymerElement {
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
override-name="[[overrideName]]">
</state-info>
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
@ -41,8 +40,7 @@ class StateCardToggle extends PolymerElement {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}
}

View File

@ -40,8 +40,7 @@ class StateCardWeblink extends PolymerElement {
inDialog: {
type: Boolean,
value: false,
},
overrideName: String
}
};
}
@ -51,7 +50,7 @@ class StateCardWeblink extends PolymerElement {
}
_computeStateName(stateObj) {
return this.overrideName || computeStateName(stateObj);
return computeStateName(stateObj);
}
onTap(ev) {