diff --git a/src/panels/lovelace/cards/hui-picture-entity-card.js b/src/panels/lovelace/cards/hui-picture-entity-card.js index 82e91cfb0e..e2b0a18d5b 100644 --- a/src/panels/lovelace/cards/hui-picture-entity-card.js +++ b/src/panels/lovelace/cards/hui-picture-entity-card.js @@ -2,8 +2,8 @@ import { html } from '@polymer/polymer/lib/utils/html-tag.js'; import { PolymerElement } from '@polymer/polymer/polymer-element.js'; import '../../../components/ha-card.js'; +import '../components/hui-image.js'; -import { STATES_OFF } from '../../../common/const.js'; import computeDomain from '../../../common/entity/compute_domain.js'; import computeStateDisplay from '../../../common/entity/compute_state_display.js'; import computeStateDomain from '../../../common/entity/compute_state_domain.js'; @@ -22,14 +22,10 @@ class HuiPictureEntityCard extends LocalizeMixin(PolymerElement) { return html` - +
@@ -97,12 +99,7 @@ class HuiPictureEntityCard extends LocalizeMixin(PolymerElement) { _updateState(hass, entityId, config) { const state = entityId in hass.states ? hass.states[entityId].state : OFFLINE; - const stateImg = config.state_image && - (config.state_image[state] || config.state_image.default); - this.$.image.src = stateImg || config.image; - this.$.image.style.filter = stateImg || (!STATES_OFF.includes(state) && state !== OFFLINE) ? - '' : 'grayscale(100%)'; this.$.title.innerText = config.title || (state === OFFLINE ? entityId : computeStateName(hass.states[entityId])); this.$.state.innerText = state === OFFLINE ? @@ -137,6 +134,10 @@ class HuiPictureEntityCard extends LocalizeMixin(PolymerElement) { toggleEntity(this.hass, entityId); } } + + _getStateObj() { + return this.hass && this.hass.states[this._config.entity]; + } } customElements.define('hui-picture-entity-card', HuiPictureEntityCard); diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.js b/src/panels/lovelace/cards/hui-picture-glance-card.js index 2220667c59..3955a9b693 100644 --- a/src/panels/lovelace/cards/hui-picture-glance-card.js +++ b/src/panels/lovelace/cards/hui-picture-glance-card.js @@ -3,6 +3,7 @@ import { PolymerElement } from '@polymer/polymer/polymer-element.js'; import '@polymer/paper-icon-button/paper-icon-button.js'; import '../../../components/ha-card.js'; +import '../components/hui-image.js'; import { STATES_OFF } from '../../../common/const.js'; import canToggleState from '../../../common/entity/can_toggle_state.js'; @@ -27,11 +28,6 @@ class HuiPictureGlanceCard extends LocalizeMixin(EventsMixin(PolymerElement)) { min-height: 48px; overflow: hidden; } - img { - display: block; - width: 100%; - height: auto; - } .box { @apply --paper-font-common-nowrap; position: absolute; @@ -61,7 +57,11 @@ class HuiPictureGlanceCard extends LocalizeMixin(EventsMixin(PolymerElement)) { - +
[[_config.title]]
@@ -114,7 +114,8 @@ class HuiPictureGlanceCard extends LocalizeMixin(EventsMixin(PolymerElement)) { } setConfig(config) { - if (!config || !config.entities || !Array.isArray(config.entities) || !config.image) { + if (!config || !config.entities || !Array.isArray(config.entities) || + !(config.image || config.camera_image)) { throw new Error('Invalid card configuration'); } diff --git a/src/panels/lovelace/common/is-valid-object.js b/src/panels/lovelace/common/is-valid-object.js new file mode 100644 index 0000000000..3d76c19f74 --- /dev/null +++ b/src/panels/lovelace/common/is-valid-object.js @@ -0,0 +1,4 @@ +// Check if given obj is a JS object and optionally contains all required keys +export default function isValidObject(obj, requiredKeys = []) { + return obj && typeof obj === 'object' && !Array.isArray(obj) && requiredKeys.every(k => k in obj); +} diff --git a/src/panels/lovelace/components/hui-image.js b/src/panels/lovelace/components/hui-image.js new file mode 100644 index 0000000000..9ec876a68b --- /dev/null +++ b/src/panels/lovelace/components/hui-image.js @@ -0,0 +1,143 @@ +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { PolymerElement } from '@polymer/polymer/polymer-element.js'; +import '@polymer/paper-toggle-button/paper-toggle-button.js'; + +import { STATES_OFF } from '../../../common/const.js'; +import LocalizeMixin from '../../../mixins/localize-mixin.js'; +import isValidObject from '../common/is-valid-object'; + +const UPDATE_INTERVAL = 10000; + +/* + * @appliesMixin LocalizeMixin + */ +class HuiImage extends LocalizeMixin(PolymerElement) { + static get template() { + return html` + + + + +`; + } + + static get properties() { + return { + hass: Object, + state: { + type: Object, + value: null, + observer: '_stateChanged' + }, + image: String, + stateImage: Object, + cameraImage: String, + _error: { + type: Boolean, + value: false + }, + _imageClass: String, + _imageSrc: String + }; + } + + static get observers() { + return ['_configChanged(image, stateImage, cameraImage)']; + } + + connectedCallback() { + super.connectedCallback(); + if (this.cameraImage) { + this.timer = setInterval(() => this._updateCameraImageSrc(), UPDATE_INTERVAL); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + clearInterval(this.timer); + } + + _configChanged(image, stateImage, cameraImage) { + if (cameraImage) { + this._updateCameraImageSrc(); + } else if (image && !stateImage) { + this._imageSrc = image; + } + } + + _onImageError() { + this.setProperties({ + _imageSrc: null, + _error: true + }); + } + + _onImageLoad() { + this._error = false; + } + + _stateChanged(state) { + if (this.cameraImage) { + return; + } + + if (!this.stateImage) { + this._imageClass = (!isValidObject(state, ['state']) || STATES_OFF.includes(state.state)) ? 'state-off' : ''; + return; + } + + const stateImg = isValidObject(state, ['state']) ? this.stateImage[state.state] : this.stateImage.offline; + + this.setProperties({ + _imageSrc: stateImg || this.stateImage.default || this.image, + _imageClass: '' + }); + } + + _updateCameraImageSrc() { + this.hass.connection.sendMessagePromise({ + type: 'camera_thumbnail', + entity_id: this.cameraImage, + }).then((resp) => { + if (resp.success) { + this.setProperties({ + _imageSrc: `data:${resp.result.content_type};base64, ${resp.result.content}`, + _error: false + }); + } else { + this.setProperties({ + _imageSrc: null, + _error: true + }); + } + }); + } +} + +customElements.define('hui-image', HuiImage);