diff --git a/src/common/const.js b/src/common/const.js
index ddd57cda75..f9ee04ec30 100644
--- a/src/common/const.js
+++ b/src/common/const.js
@@ -60,6 +60,14 @@ export const DOMAINS_MORE_INFO_NO_HISTORY = [
'scene',
];
+/** States that we consider "on". */
+export const STATES_ON = [
+ 'home',
+ 'locked',
+ 'on',
+ 'open',
+];
+
/** States that we consider "off". */
export const STATES_OFF = [
'closed',
diff --git a/src/panels/lovelace/hui-picture-glance-card.js b/src/panels/lovelace/hui-picture-glance-card.js
new file mode 100644
index 0000000000..5b6dbf217d
--- /dev/null
+++ b/src/panels/lovelace/hui-picture-glance-card.js
@@ -0,0 +1,188 @@
+import { html } from '@polymer/polymer/lib/utils/html-tag.js';
+import { PolymerElement } from '@polymer/polymer/polymer-element.js';
+import '@polymer/paper-icon-button/paper-icon-button.js';
+
+import '../../components/ha-card.js';
+
+import { STATES_ON } from '../../common/const.js';
+import computeDomain from '../../common/entity/compute_domain.js';
+import computeStateDisplay from '../../common/entity/compute_state_display.js';
+import computeStateName from '../../common/entity/compute_state_name.js';
+import stateIcon from '../../common/entity/state_icon.js';
+
+import EventsMixin from '../../mixins/events-mixin.js';
+import LocalizeMixin from '../../mixins/localize-mixin.js';
+
+const DOMAINS_FORCE_DIALOG = ['binary_sensor', 'device_tracker', 'sensor'];
+
+/*
+ * @appliesMixin EventsMixin
+ * @appliesMixin LocalizeMixin
+ */
+class HuiPictureGlanceCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
+ static get template() {
+ return html`
+
+
+
+
+
+
+ [[_error]]
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ config: {
+ type: Object,
+ observer: '_configChanged'
+ },
+ _entitiesDialog: Array,
+ _entitiesService: Array,
+ _error: String
+ };
+ }
+
+ getCardSize() {
+ return 3;
+ }
+
+ _configChanged(config) {
+ let dialog = [];
+ let service = [];
+ let _error = null;
+ if (config && config.entities && Array.isArray(config.entities) && config.image) {
+ if (config.force_dialog) {
+ dialog = config.entities;
+ } else {
+ dialog = config.entities
+ .filter(entity => DOMAINS_FORCE_DIALOG.includes(computeDomain(entity)));
+ service = config.entities.filter(entity => !dialog.includes(entity));
+ }
+ } else {
+ _error = 'Error in card configuration.';
+ }
+ this.setProperties({
+ _entitiesDialog: dialog,
+ _entitiesService: service,
+ _error
+ });
+ }
+
+ _showEntity(entityId, states) {
+ return entityId in states;
+ }
+
+ _computeIcon(entityId, states) {
+ return stateIcon(states[entityId]);
+ }
+
+ _computeClass(entityId, states) {
+ return STATES_ON.includes(states[entityId].state) ? 'state-on' : '';
+ }
+
+ _computeTooltip(entityId, states) {
+ return `${computeStateName(states[entityId])}: ${computeStateDisplay(this.localize, states[entityId])}`;
+ }
+
+ _openDialog(ev) {
+ this.fire('hass-more-info', { entityId: ev.model.item });
+ }
+
+ _callService(ev) {
+ const entityId = ev.model.item;
+ const domain = computeDomain(entityId);
+ const isOn = STATES_ON.includes(this.hass.states[entityId].state);
+ let service;
+ switch (domain) {
+ case 'lock':
+ service = isOn ? 'unlock' : 'lock';
+ break;
+ case 'cover':
+ service = isOn ? 'close' : 'open';
+ break;
+ case 'scene':
+ service = 'turn_on';
+ break;
+ default:
+ service = isOn ? 'turn_off' : 'turn_on';
+ }
+ this.hass.callService(domain, service, { entity_id: entityId });
+ }
+}
+
+customElements.define('hui-picture-glance-card', HuiPictureGlanceCard);
diff --git a/src/panels/lovelace/hui-view.js b/src/panels/lovelace/hui-view.js
index b0a03ef743..09facc3659 100644
--- a/src/panels/lovelace/hui-view.js
+++ b/src/panels/lovelace/hui-view.js
@@ -7,6 +7,7 @@ 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 './hui-error-card.js';
@@ -20,6 +21,7 @@ const VALID_TYPES = [
'glance',
'history-graph',
'media-control',
+ 'picture-glance',
'plant-status',
'weather-forecast'
];