+
+
+
-
`;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+
+ columns: {
+ type: Number,
+ value: 2,
+ },
+
+ states: Object,
+ panelVisible: Boolean,
+
+ viewVisible: {
+ type: Boolean,
+ value: false,
+ },
+
+ orderedGroupEntities: Array,
+
+ cards: Object,
+ };
+ }
+
+ static get observers() {
+ return [
+ 'updateCards(columns, states, panelVisible, viewVisible, orderedGroupEntities)',
+ ];
+ }
+
+ updateCards(
+ columns,
+ states,
+ panelVisible,
+ viewVisible,
+ orderedGroupEntities
+ ) {
+ if (!panelVisible || !viewVisible) {
+ if (this.$.main.parentNode) {
+ this.$.main._parentNode = this.$.main.parentNode;
+ this.$.main.parentNode.removeChild(this.$.main);
+ }
+ return;
+ } else if (!this.$.main.parentNode && this.$.main._parentNode) {
+ this.$.main._parentNode.appendChild(this.$.main);
+ }
+ this._debouncer = Debouncer.debounce(
+ this._debouncer,
+ timeOut.after(10),
+ () => {
+ // Things might have changed since it got scheduled.
+ if (this.panelVisible && this.viewVisible) {
+ this.cards = this.computeCards(columns, states, orderedGroupEntities);
+ }
+ }
+ );
+ }
+
+ emptyCards() {
+ return {
+ demo: false,
+ badges: [],
+ columns: [],
+ };
+ }
+
+ computeCards(columns, states, orderedGroupEntities) {
+ const hass = this.hass;
+
+ const cards = this.emptyCards();
+
+ const entityCount = [];
+ for (let i = 0; i < columns; i++) {
+ cards.columns.push([]);
+ entityCount.push(0);
}
- static get properties() {
- return {
- hass: Object,
+ // Find column with < 5 entities, else column with lowest count
+ function getIndex(size) {
+ let minIndex = 0;
+ for (let i = 0; i < entityCount.length; i++) {
+ if (entityCount[i] < 5) {
+ minIndex = i;
+ break;
+ }
+ if (entityCount[i] < entityCount[minIndex]) {
+ minIndex = i;
+ }
+ }
- columns: {
- type: Number,
- value: 2,
- },
+ entityCount[minIndex] += size;
- states: Object,
- panelVisible: Boolean,
-
- viewVisible: {
- type: Boolean,
- value: false,
- },
-
- orderedGroupEntities: Array,
-
- cards: Object,
- };
+ return minIndex;
}
- static get observers() {
- return [
- 'updateCards(columns, states, panelVisible, viewVisible, orderedGroupEntities)',
- ];
- }
+ function addEntitiesCard(name, entities, groupEntity) {
+ if (entities.length === 0) return;
- updateCards(
- columns,
- states,
- panelVisible,
- viewVisible,
- orderedGroupEntities
- ) {
- if (!panelVisible || !viewVisible) {
- if (this.$.main.parentNode) {
- this.$.main._parentNode = this.$.main.parentNode;
- this.$.main.parentNode.removeChild(this.$.main);
- }
- return;
- } else if (!this.$.main.parentNode && this.$.main._parentNode) {
- this.$.main._parentNode.appendChild(this.$.main);
- }
- this._debouncer = Debouncer.debounce(
- this._debouncer,
- timeOut.after(10),
- () => {
- // Things might have changed since it got scheduled.
- if (this.panelVisible && this.viewVisible) {
- this.cards = this.computeCards(columns, states, orderedGroupEntities);
- }
- }
- );
- }
+ const owncard = [];
+ const other = [];
- emptyCards() {
- return {
- demo: false,
- badges: [],
- columns: [],
- };
- }
+ let size = 0;
- computeCards(columns, states, orderedGroupEntities) {
- const hass = this.hass;
+ entities.forEach((entity) => {
+ const domain = computeStateDomain(entity);
- const cards = this.emptyCards();
-
- const entityCount = [];
- for (let i = 0; i < columns; i++) {
- cards.columns.push([]);
- entityCount.push(0);
- }
-
- // Find column with < 5 entities, else column with lowest count
- function getIndex(size) {
- let minIndex = 0;
- for (let i = 0; i < entityCount.length; i++) {
- if (entityCount[i] < 5) {
- minIndex = i;
- break;
- }
- if (entityCount[i] < entityCount[minIndex]) {
- minIndex = i;
- }
- }
-
- entityCount[minIndex] += size;
-
- return minIndex;
- }
-
- function addEntitiesCard(name, entities, groupEntity) {
- if (entities.length === 0) return;
-
- const owncard = [];
- const other = [];
-
- let size = 0;
-
- entities.forEach((entity) => {
- const domain = computeStateDomain(entity);
-
- if (domain in DOMAINS_WITH_CARD) {
- owncard.push(entity);
- size += DOMAINS_WITH_CARD[domain];
- } else {
- other.push(entity);
- size++;
- }
- });
-
- // Add 1 to the size if we're rendering entities card
- size += other.length > 0;
-
- const curIndex = getIndex(size);
-
- if (other.length > 0) {
- cards.columns[curIndex].push({
- hass: hass,
- cardType: 'entities',
- states: other,
- groupEntity: groupEntity || false,
- });
- }
-
- owncard.forEach((entity) => {
- cards.columns[curIndex].push({
- hass: hass,
- cardType: computeStateDomain(entity),
- stateObj: entity,
- });
- });
- }
-
- const splitted = splitByGroups(states);
- if (orderedGroupEntities) {
- splitted.groups.sort((gr1, gr2) => orderedGroupEntities[gr1.entity_id] -
- orderedGroupEntities[gr2.entity_id]);
- } else {
- splitted.groups.sort((gr1, gr2) => gr1.attributes.order - gr2.attributes.order);
- }
-
- const badgesColl = {};
- const beforeGroupColl = {};
- const afterGroupedColl = {};
-
- Object.keys(splitted.ungrouped).forEach((key) => {
- const state = splitted.ungrouped[key];
- const domain = computeStateDomain(state);
-
- if (domain === 'a') {
- cards.demo = true;
- return;
- }
-
- const priority = getPriority(domain);
- let coll;
-
- if (priority < 0) {
- coll = beforeGroupColl;
- } else if (priority < 10) {
- coll = badgesColl;
+ if (domain in DOMAINS_WITH_CARD) {
+ owncard.push(entity);
+ size += DOMAINS_WITH_CARD[domain];
} else {
- coll = afterGroupedColl;
+ other.push(entity);
+ size++;
}
-
- if (!(domain in coll)) {
- coll[domain] = {
- domain: domain,
- priority: priority,
- states: [],
- };
- }
-
- coll[domain].states.push(state);
});
- if (orderedGroupEntities) {
- Object.keys(badgesColl)
- .map(key => badgesColl[key])
- .forEach((domain) => {
- cards.badges.push.apply(cards.badges, domain.states);
- });
+ // Add 1 to the size if we're rendering entities card
+ size += other.length > 0;
- cards.badges.sort((e1, e2) => orderedGroupEntities[e1.entity_id] -
- orderedGroupEntities[e2.entity_id]);
+ const curIndex = getIndex(size);
+
+ if (other.length > 0) {
+ cards.columns[curIndex].push({
+ hass: hass,
+ cardType: 'entities',
+ states: other,
+ groupEntity: groupEntity || false,
+ });
+ }
+
+ owncard.forEach((entity) => {
+ cards.columns[curIndex].push({
+ hass: hass,
+ cardType: computeStateDomain(entity),
+ stateObj: entity,
+ });
+ });
+ }
+
+ const splitted = splitByGroups(states);
+ if (orderedGroupEntities) {
+ splitted.groups.sort((gr1, gr2) => orderedGroupEntities[gr1.entity_id] -
+ orderedGroupEntities[gr2.entity_id]);
+ } else {
+ splitted.groups.sort((gr1, gr2) => gr1.attributes.order - gr2.attributes.order);
+ }
+
+ const badgesColl = {};
+ const beforeGroupColl = {};
+ const afterGroupedColl = {};
+
+ Object.keys(splitted.ungrouped).forEach((key) => {
+ const state = splitted.ungrouped[key];
+ const domain = computeStateDomain(state);
+
+ if (domain === 'a') {
+ cards.demo = true;
+ return;
+ }
+
+ const priority = getPriority(domain);
+ let coll;
+
+ if (priority < 0) {
+ coll = beforeGroupColl;
+ } else if (priority < 10) {
+ coll = badgesColl;
} else {
- iterateDomainSorted(badgesColl, (domain) => {
+ coll = afterGroupedColl;
+ }
+
+ if (!(domain in coll)) {
+ coll[domain] = {
+ domain: domain,
+ priority: priority,
+ states: [],
+ };
+ }
+
+ coll[domain].states.push(state);
+ });
+
+ if (orderedGroupEntities) {
+ Object.keys(badgesColl)
+ .map(key => badgesColl[key])
+ .forEach((domain) => {
cards.badges.push.apply(cards.badges, domain.states);
});
- }
- iterateDomainSorted(beforeGroupColl, (domain) => {
- addEntitiesCard(domain.domain, domain.states);
+ cards.badges.sort((e1, e2) => orderedGroupEntities[e1.entity_id] -
+ orderedGroupEntities[e2.entity_id]);
+ } else {
+ iterateDomainSorted(badgesColl, (domain) => {
+ cards.badges.push.apply(cards.badges, domain.states);
});
-
- splitted.groups.forEach((groupState) => {
- const entities = getGroupEntities(states, groupState);
- addEntitiesCard(
- groupState.entity_id,
- Object.keys(entities).map(key => entities[key]),
- groupState
- );
- });
-
- iterateDomainSorted(afterGroupedColl, (domain) => {
- addEntitiesCard(domain.domain, domain.states);
- });
-
- // Remove empty columns
- cards.columns = cards.columns.filter(val => val.length > 0);
-
- return cards;
}
+
+ iterateDomainSorted(beforeGroupColl, (domain) => {
+ addEntitiesCard(domain.domain, domain.states);
+ });
+
+ splitted.groups.forEach((groupState) => {
+ const entities = getGroupEntities(states, groupState);
+ addEntitiesCard(
+ groupState.entity_id,
+ Object.keys(entities).map(key => entities[key]),
+ groupState
+ );
+ });
+
+ iterateDomainSorted(afterGroupedColl, (domain) => {
+ addEntitiesCard(domain.domain, domain.states);
+ });
+
+ // Remove empty columns
+ cards.columns = cards.columns.filter(val => val.length > 0);
+
+ return cards;
}
- customElements.define('ha-cards', HaCards);
}
+customElements.define('ha-cards', HaCards);
diff --git a/src/data/ha-state-history-data.js b/src/data/ha-state-history-data.js
index 55ad02613b..4fd571df6a 100644
--- a/src/data/ha-state-history-data.js
+++ b/src/data/ha-state-history-data.js
@@ -8,381 +8,379 @@ import computeStateDomain from '../common/entity/compute_state_domain.js';
import computeStateDisplay from '../common/entity/compute_state_display.js';
import LocalizeMixin from '../mixins/localize-mixin.js';
-{
- const RECENT_THRESHOLD = 60000; // 1 minute
- const RECENT_CACHE = {};
- const DOMAINS_USE_LAST_UPDATED = ['thermostat', 'climate'];
- const LINE_ATTRIBUTES_TO_KEEP = ['temperature', 'current_temperature', 'target_temp_low', 'target_temp_high'];
- const stateHistoryCache = {};
+const RECENT_THRESHOLD = 60000; // 1 minute
+const RECENT_CACHE = {};
+const DOMAINS_USE_LAST_UPDATED = ['thermostat', 'climate'];
+const LINE_ATTRIBUTES_TO_KEEP = ['temperature', 'current_temperature', 'target_temp_low', 'target_temp_high'];
+const stateHistoryCache = {};
- function computeHistory(stateHistory, localize, language) {
- const lineChartDevices = {};
- const timelineDevices = [];
- if (!stateHistory) {
- return { line: [], timeline: [] };
- }
-
- stateHistory.forEach((stateInfo) => {
- if (stateInfo.length === 0) {
- return;
- }
-
- const stateWithUnit = stateInfo.find(state => 'unit_of_measurement' in state.attributes);
-
- const unit = stateWithUnit ?
- stateWithUnit.attributes.unit_of_measurement : false;
-
- if (!unit) {
- timelineDevices.push({
- name: computeStateName(stateInfo[0]),
- entity_id: stateInfo[0].entity_id,
- data: stateInfo
- .map(state => ({
- state_localize: computeStateDisplay(localize, state, language),
- state: state.state,
- last_changed: state.last_changed,
- }))
- .filter((element, index, arr) => {
- if (index === 0) return true;
- return element.state !== arr[index - 1].state;
- })
- });
- } else if (unit in lineChartDevices) {
- lineChartDevices[unit].push(stateInfo);
- } else {
- lineChartDevices[unit] = [stateInfo];
- }
- });
-
- const unitStates = Object.keys(lineChartDevices).map(unit => ({
- unit: unit,
- identifier: lineChartDevices[unit].map(states => states[0].entity_id).join(''),
- data: lineChartDevices[unit].map((states) => {
- const last = states[states.length - 1];
- const domain = computeStateDomain(last);
- return {
- domain: domain,
- name: computeStateName(last),
- entity_id: last.entity_id,
- states: states.map((state) => {
- const result = {
- state: state.state,
- last_changed: state.last_changed,
- };
- if (DOMAINS_USE_LAST_UPDATED.includes(domain)) {
- result.last_changed = state.last_updated;
- }
- LINE_ATTRIBUTES_TO_KEEP.forEach((attr) => {
- if (attr in state.attributes) {
- result.attributes = result.attributes || {};
- result.attributes[attr] = state.attributes[attr];
- }
- });
- return result;
- }).filter((element, index, arr) => {
- // Remove data point if it is equal to previous point and next point.
- if (index === 0 || index === (arr.length - 1)) return true;
- function compare(obj1, obj2) {
- if (obj1.state !== obj2.state) return false;
- if (!obj1.attributes && !obj2.attributes) return true;
- if (!obj1.attributes || !obj2.attributes) return false;
- return LINE_ATTRIBUTES_TO_KEEP.every(attr =>
- obj1.attributes[attr] === obj2.attributes[attr]);
- }
- return !compare(element, arr[index - 1]) || !compare(element, arr[index + 1]);
- })
- };
- }),
- }));
-
- return { line: unitStates, timeline: timelineDevices };
+function computeHistory(stateHistory, localize, language) {
+ const lineChartDevices = {};
+ const timelineDevices = [];
+ if (!stateHistory) {
+ return { line: [], timeline: [] };
}
- /*
- * @appliesMixin LocalizeMixin
- */
- class HaStateHistoryData extends LocalizeMixin(PolymerElement) {
- static get properties() {
+ stateHistory.forEach((stateInfo) => {
+ if (stateInfo.length === 0) {
+ return;
+ }
+
+ const stateWithUnit = stateInfo.find(state => 'unit_of_measurement' in state.attributes);
+
+ const unit = stateWithUnit ?
+ stateWithUnit.attributes.unit_of_measurement : false;
+
+ if (!unit) {
+ timelineDevices.push({
+ name: computeStateName(stateInfo[0]),
+ entity_id: stateInfo[0].entity_id,
+ data: stateInfo
+ .map(state => ({
+ state_localize: computeStateDisplay(localize, state, language),
+ state: state.state,
+ last_changed: state.last_changed,
+ }))
+ .filter((element, index, arr) => {
+ if (index === 0) return true;
+ return element.state !== arr[index - 1].state;
+ })
+ });
+ } else if (unit in lineChartDevices) {
+ lineChartDevices[unit].push(stateInfo);
+ } else {
+ lineChartDevices[unit] = [stateInfo];
+ }
+ });
+
+ const unitStates = Object.keys(lineChartDevices).map(unit => ({
+ unit: unit,
+ identifier: lineChartDevices[unit].map(states => states[0].entity_id).join(''),
+ data: lineChartDevices[unit].map((states) => {
+ const last = states[states.length - 1];
+ const domain = computeStateDomain(last);
return {
- hass: {
- type: Object,
- observer: 'hassChanged',
- },
-
- filterType: String,
-
- cacheConfig: Object,
-
- startTime: Date,
- endTime: Date,
-
- entityId: String,
-
- isLoading: {
- type: Boolean,
- value: true,
- readOnly: true,
- notify: true,
- },
-
- data: {
- type: Object,
- value: null,
- readOnly: true,
- notify: true,
- },
+ domain: domain,
+ name: computeStateName(last),
+ entity_id: last.entity_id,
+ states: states.map((state) => {
+ const result = {
+ state: state.state,
+ last_changed: state.last_changed,
+ };
+ if (DOMAINS_USE_LAST_UPDATED.includes(domain)) {
+ result.last_changed = state.last_updated;
+ }
+ LINE_ATTRIBUTES_TO_KEEP.forEach((attr) => {
+ if (attr in state.attributes) {
+ result.attributes = result.attributes || {};
+ result.attributes[attr] = state.attributes[attr];
+ }
+ });
+ return result;
+ }).filter((element, index, arr) => {
+ // Remove data point if it is equal to previous point and next point.
+ if (index === 0 || index === (arr.length - 1)) return true;
+ function compare(obj1, obj2) {
+ if (obj1.state !== obj2.state) return false;
+ if (!obj1.attributes && !obj2.attributes) return true;
+ if (!obj1.attributes || !obj2.attributes) return false;
+ return LINE_ATTRIBUTES_TO_KEEP.every(attr =>
+ obj1.attributes[attr] === obj2.attributes[attr]);
+ }
+ return !compare(element, arr[index - 1]) || !compare(element, arr[index + 1]);
+ })
};
- }
+ }),
+ }));
- static get observers() {
- return [
- 'filterChangedDebouncer(filterType, entityId, startTime, endTime, cacheConfig, localize, language)',
- ];
- }
+ return { line: unitStates, timeline: timelineDevices };
+}
- connectedCallback() {
- super.connectedCallback();
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HaStateHistoryData extends LocalizeMixin(PolymerElement) {
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: 'hassChanged',
+ },
+
+ filterType: String,
+
+ cacheConfig: Object,
+
+ startTime: Date,
+ endTime: Date,
+
+ entityId: String,
+
+ isLoading: {
+ type: Boolean,
+ value: true,
+ readOnly: true,
+ notify: true,
+ },
+
+ data: {
+ type: Object,
+ value: null,
+ readOnly: true,
+ notify: true,
+ },
+ };
+ }
+
+ static get observers() {
+ return [
+ 'filterChangedDebouncer(filterType, entityId, startTime, endTime, cacheConfig, localize, language)',
+ ];
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.filterChangedDebouncer(
+ this.filterType, this.entityId, this.startTime, this.endTime,
+ this.cacheConfig, this.localize, this.language
+ );
+ }
+
+ disconnectedCallback() {
+ if (this._refreshTimeoutId) {
+ window.clearInterval(this._refreshTimeoutId);
+ this._refreshTimeoutId = null;
+ }
+ super.disconnectedCallback();
+ }
+
+ hassChanged(newHass, oldHass) {
+ if (!oldHass && !this._madeFirstCall) {
this.filterChangedDebouncer(
this.filterType, this.entityId, this.startTime, this.endTime,
this.cacheConfig, this.localize, this.language
);
}
-
- disconnectedCallback() {
- if (this._refreshTimeoutId) {
- window.clearInterval(this._refreshTimeoutId);
- this._refreshTimeoutId = null;
- }
- super.disconnectedCallback();
- }
-
- hassChanged(newHass, oldHass) {
- if (!oldHass && !this._madeFirstCall) {
- this.filterChangedDebouncer(
- this.filterType, this.entityId, this.startTime, this.endTime,
- this.cacheConfig, this.localize, this.language
- );
- }
- }
-
- filterChangedDebouncer(...args) {
- this._debounceFilterChanged = Debouncer.debounce(
- this._debounceFilterChanged,
- timeOut.after(0),
- () => {
- this.filterChanged(...args);
- }
- );
- }
-
- filterChanged(filterType, entityId, startTime, endTime, cacheConfig, localize, language) {
- if (!this.hass) return;
- if (cacheConfig && !cacheConfig.cacheKey) return;
- if (!localize || !language) return;
- this._madeFirstCall = true;
- let data;
-
- if (filterType === 'date') {
- if (!startTime || !endTime) return;
- data = this.getDate(startTime, endTime, localize, language);
- } else if (filterType === 'recent-entity') {
- if (!entityId) return;
- if (cacheConfig) {
- data = this.getRecentWithCacheRefresh(entityId, cacheConfig, localize, language);
- } else {
- data = this.getRecent(entityId, startTime, endTime, localize, language);
- }
- } else {
- return;
- }
- this._setIsLoading(true);
-
- data.then((stateHistory) => {
- this._setData(stateHistory);
- this._setIsLoading(false);
- });
- }
-
- getEmptyCache(language) {
- return {
- prom: Promise.resolve({ line: [], timeline: [] }),
- language: language,
- data: { line: [], timeline: [] },
- };
- }
-
- getRecentWithCacheRefresh(entityId, cacheConfig, localize, language) {
- if (this._refreshTimeoutId) {
- window.clearInterval(this._refreshTimeoutId);
- this._refreshTimeoutId = null;
- }
- if (cacheConfig.refresh) {
- this._refreshTimeoutId = window.setInterval(() => {
- this.getRecentWithCache(entityId, cacheConfig, localize, language)
- .then((stateHistory) => {
- this._setData(Object.assign({}, stateHistory));
- });
- }, cacheConfig.refresh * 1000);
- }
- return this.getRecentWithCache(entityId, cacheConfig, localize, language);
- }
-
- mergeLine(historyLines, cacheLines) {
- historyLines.forEach((line) => {
- const unit = line.unit;
- const oldLine = cacheLines.find(cacheLine => cacheLine.unit === unit);
- if (oldLine) {
- line.data.forEach((entity) => {
- const oldEntity =
- oldLine.data.find(cacheEntity => entity.entity_id === cacheEntity.entity_id);
- if (oldEntity) {
- oldEntity.states = oldEntity.states.concat(entity.states);
- } else {
- oldLine.data.push(entity);
- }
- });
- } else {
- cacheLines.push(line);
- }
- });
- }
-
- mergeTimeline(historyTimelines, cacheTimelines) {
- historyTimelines.forEach((timeline) => {
- const oldTimeline =
- cacheTimelines.find(cacheTimeline => cacheTimeline.entity_id === timeline.entity_id);
- if (oldTimeline) {
- oldTimeline.data = oldTimeline.data.concat(timeline.data);
- } else {
- cacheTimelines.push(timeline);
- }
- });
- }
-
- pruneArray(originalStartTime, arr) {
- if (arr.length === 0) return arr;
- const changedAfterStartTime = arr.findIndex((state) => {
- const lastChanged = new Date(state.last_changed);
- return lastChanged > originalStartTime;
- });
- if (changedAfterStartTime === 0) {
- // If all changes happened after originalStartTime then we are done.
- return arr;
- }
-
- // If all changes happened at or before originalStartTime. Use last index.
- const updateIndex = changedAfterStartTime === -1 ? arr.length - 1 : changedAfterStartTime - 1;
- arr[updateIndex].last_changed = originalStartTime;
- return arr.slice(updateIndex);
- }
-
- pruneStartTime(originalStartTime, cacheData) {
- cacheData.line.forEach((line) => {
- line.data.forEach((entity) => {
- entity.states = this.pruneArray(originalStartTime, entity.states);
- });
- });
-
- cacheData.timeline.forEach((timeline) => {
- timeline.data = this.pruneArray(originalStartTime, timeline.data);
- });
- }
-
- getRecentWithCache(entityId, cacheConfig, localize, language) {
- const cacheKey = cacheConfig.cacheKey;
- const endTime = new Date();
- const originalStartTime = new Date(endTime);
- originalStartTime.setHours(originalStartTime.getHours() - cacheConfig.hoursToShow);
- let startTime = originalStartTime;
- let appendingToCache = false;
- let cache = stateHistoryCache[cacheKey];
- if (cache && startTime >= cache.startTime && startTime <= cache.endTime
- && cache.language === language) {
- startTime = cache.endTime;
- appendingToCache = true;
- if (endTime <= cache.endTime) {
- return cache.prom;
- }
- } else {
- cache = stateHistoryCache[cacheKey] = this.getEmptyCache(language);
- }
- // Use Promise.all in order to make sure the old and the new fetches have both completed.
- const prom = Promise.all([cache.prom,
- this.fetchRecent(entityId, startTime, endTime, appendingToCache)])
- // Use only data from the new fetch. Old fetch is already stored in cache.data
- .then(oldAndNew => oldAndNew[1])
- // Convert data into format state-history-chart-* understands.
- .then(stateHistory => computeHistory(stateHistory, localize, language))
- // Merge old and new.
- .then((stateHistory) => {
- this.mergeLine(stateHistory.line, cache.data.line);
- this.mergeTimeline(stateHistory.timeline, cache.data.timeline);
- if (appendingToCache) {
- this.pruneStartTime(originalStartTime, cache.data);
- }
- return cache.data;
- })
- .catch((err) => {
- /* eslint-disable no-console */
- console.error(err);
- stateHistoryCache[cacheKey] = undefined;
- });
- cache.prom = prom;
- cache.startTime = originalStartTime;
- cache.endTime = endTime;
- return prom;
- }
-
- getRecent(entityId, startTime, endTime, localize, language) {
- const cacheKey = entityId;
- const cache = RECENT_CACHE[cacheKey];
-
- if (cache && Date.now() - cache.created < RECENT_THRESHOLD && cache.language === language) {
- return cache.data;
- }
-
- const prom = this.fetchRecent(entityId, startTime, endTime).then(
- stateHistory => computeHistory(stateHistory, localize, language),
- () => {
- RECENT_CACHE[entityId] = false;
- return null;
- }
- );
-
- RECENT_CACHE[cacheKey] = {
- created: Date.now(),
- language: language,
- data: prom,
- };
- return prom;
- }
-
- fetchRecent(entityId, startTime, endTime, skipInitialState = false) {
- let url = 'history/period';
- if (startTime) {
- url += '/' + startTime.toISOString();
- }
- url += '?filter_entity_id=' + entityId;
- if (endTime) {
- url += '&end_time=' + endTime.toISOString();
- }
- if (skipInitialState) {
- url += '&skip_initial_state';
- }
-
- return this.hass.callApi('GET', url);
- }
-
- getDate(startTime, endTime, localize, language) {
- const filter = startTime.toISOString() + '?end_time=' + endTime.toISOString();
-
- const prom = this.hass.callApi('GET', 'history/period/' + filter).then(
- stateHistory => computeHistory(stateHistory, localize, language),
- () => null
- );
-
- return prom;
- }
}
- customElements.define('ha-state-history-data', HaStateHistoryData);
+
+ filterChangedDebouncer(...args) {
+ this._debounceFilterChanged = Debouncer.debounce(
+ this._debounceFilterChanged,
+ timeOut.after(0),
+ () => {
+ this.filterChanged(...args);
+ }
+ );
+ }
+
+ filterChanged(filterType, entityId, startTime, endTime, cacheConfig, localize, language) {
+ if (!this.hass) return;
+ if (cacheConfig && !cacheConfig.cacheKey) return;
+ if (!localize || !language) return;
+ this._madeFirstCall = true;
+ let data;
+
+ if (filterType === 'date') {
+ if (!startTime || !endTime) return;
+ data = this.getDate(startTime, endTime, localize, language);
+ } else if (filterType === 'recent-entity') {
+ if (!entityId) return;
+ if (cacheConfig) {
+ data = this.getRecentWithCacheRefresh(entityId, cacheConfig, localize, language);
+ } else {
+ data = this.getRecent(entityId, startTime, endTime, localize, language);
+ }
+ } else {
+ return;
+ }
+ this._setIsLoading(true);
+
+ data.then((stateHistory) => {
+ this._setData(stateHistory);
+ this._setIsLoading(false);
+ });
+ }
+
+ getEmptyCache(language) {
+ return {
+ prom: Promise.resolve({ line: [], timeline: [] }),
+ language: language,
+ data: { line: [], timeline: [] },
+ };
+ }
+
+ getRecentWithCacheRefresh(entityId, cacheConfig, localize, language) {
+ if (this._refreshTimeoutId) {
+ window.clearInterval(this._refreshTimeoutId);
+ this._refreshTimeoutId = null;
+ }
+ if (cacheConfig.refresh) {
+ this._refreshTimeoutId = window.setInterval(() => {
+ this.getRecentWithCache(entityId, cacheConfig, localize, language)
+ .then((stateHistory) => {
+ this._setData(Object.assign({}, stateHistory));
+ });
+ }, cacheConfig.refresh * 1000);
+ }
+ return this.getRecentWithCache(entityId, cacheConfig, localize, language);
+ }
+
+ mergeLine(historyLines, cacheLines) {
+ historyLines.forEach((line) => {
+ const unit = line.unit;
+ const oldLine = cacheLines.find(cacheLine => cacheLine.unit === unit);
+ if (oldLine) {
+ line.data.forEach((entity) => {
+ const oldEntity =
+ oldLine.data.find(cacheEntity => entity.entity_id === cacheEntity.entity_id);
+ if (oldEntity) {
+ oldEntity.states = oldEntity.states.concat(entity.states);
+ } else {
+ oldLine.data.push(entity);
+ }
+ });
+ } else {
+ cacheLines.push(line);
+ }
+ });
+ }
+
+ mergeTimeline(historyTimelines, cacheTimelines) {
+ historyTimelines.forEach((timeline) => {
+ const oldTimeline =
+ cacheTimelines.find(cacheTimeline => cacheTimeline.entity_id === timeline.entity_id);
+ if (oldTimeline) {
+ oldTimeline.data = oldTimeline.data.concat(timeline.data);
+ } else {
+ cacheTimelines.push(timeline);
+ }
+ });
+ }
+
+ pruneArray(originalStartTime, arr) {
+ if (arr.length === 0) return arr;
+ const changedAfterStartTime = arr.findIndex((state) => {
+ const lastChanged = new Date(state.last_changed);
+ return lastChanged > originalStartTime;
+ });
+ if (changedAfterStartTime === 0) {
+ // If all changes happened after originalStartTime then we are done.
+ return arr;
+ }
+
+ // If all changes happened at or before originalStartTime. Use last index.
+ const updateIndex = changedAfterStartTime === -1 ? arr.length - 1 : changedAfterStartTime - 1;
+ arr[updateIndex].last_changed = originalStartTime;
+ return arr.slice(updateIndex);
+ }
+
+ pruneStartTime(originalStartTime, cacheData) {
+ cacheData.line.forEach((line) => {
+ line.data.forEach((entity) => {
+ entity.states = this.pruneArray(originalStartTime, entity.states);
+ });
+ });
+
+ cacheData.timeline.forEach((timeline) => {
+ timeline.data = this.pruneArray(originalStartTime, timeline.data);
+ });
+ }
+
+ getRecentWithCache(entityId, cacheConfig, localize, language) {
+ const cacheKey = cacheConfig.cacheKey;
+ const endTime = new Date();
+ const originalStartTime = new Date(endTime);
+ originalStartTime.setHours(originalStartTime.getHours() - cacheConfig.hoursToShow);
+ let startTime = originalStartTime;
+ let appendingToCache = false;
+ let cache = stateHistoryCache[cacheKey];
+ if (cache && startTime >= cache.startTime && startTime <= cache.endTime
+ && cache.language === language) {
+ startTime = cache.endTime;
+ appendingToCache = true;
+ if (endTime <= cache.endTime) {
+ return cache.prom;
+ }
+ } else {
+ cache = stateHistoryCache[cacheKey] = this.getEmptyCache(language);
+ }
+ // Use Promise.all in order to make sure the old and the new fetches have both completed.
+ const prom = Promise.all([cache.prom,
+ this.fetchRecent(entityId, startTime, endTime, appendingToCache)])
+ // Use only data from the new fetch. Old fetch is already stored in cache.data
+ .then(oldAndNew => oldAndNew[1])
+ // Convert data into format state-history-chart-* understands.
+ .then(stateHistory => computeHistory(stateHistory, localize, language))
+ // Merge old and new.
+ .then((stateHistory) => {
+ this.mergeLine(stateHistory.line, cache.data.line);
+ this.mergeTimeline(stateHistory.timeline, cache.data.timeline);
+ if (appendingToCache) {
+ this.pruneStartTime(originalStartTime, cache.data);
+ }
+ return cache.data;
+ })
+ .catch((err) => {
+ /* eslint-disable no-console */
+ console.error(err);
+ stateHistoryCache[cacheKey] = undefined;
+ });
+ cache.prom = prom;
+ cache.startTime = originalStartTime;
+ cache.endTime = endTime;
+ return prom;
+ }
+
+ getRecent(entityId, startTime, endTime, localize, language) {
+ const cacheKey = entityId;
+ const cache = RECENT_CACHE[cacheKey];
+
+ if (cache && Date.now() - cache.created < RECENT_THRESHOLD && cache.language === language) {
+ return cache.data;
+ }
+
+ const prom = this.fetchRecent(entityId, startTime, endTime).then(
+ stateHistory => computeHistory(stateHistory, localize, language),
+ () => {
+ RECENT_CACHE[entityId] = false;
+ return null;
+ }
+ );
+
+ RECENT_CACHE[cacheKey] = {
+ created: Date.now(),
+ language: language,
+ data: prom,
+ };
+ return prom;
+ }
+
+ fetchRecent(entityId, startTime, endTime, skipInitialState = false) {
+ let url = 'history/period';
+ if (startTime) {
+ url += '/' + startTime.toISOString();
+ }
+ url += '?filter_entity_id=' + entityId;
+ if (endTime) {
+ url += '&end_time=' + endTime.toISOString();
+ }
+ if (skipInitialState) {
+ url += '&skip_initial_state';
+ }
+
+ return this.hass.callApi('GET', url);
+ }
+
+ getDate(startTime, endTime, localize, language) {
+ const filter = startTime.toISOString() + '?end_time=' + endTime.toISOString();
+
+ const prom = this.hass.callApi('GET', 'history/period/' + filter).then(
+ stateHistory => computeHistory(stateHistory, localize, language),
+ () => null
+ );
+
+ return prom;
+ }
}
+customElements.define('ha-state-history-data', HaStateHistoryData);
diff --git a/src/dialogs/more-info/controls/more-info-cover.js b/src/dialogs/more-info/controls/more-info-cover.js
index d75ba2a9ed..c9e96977f2 100644
--- a/src/dialogs/more-info/controls/more-info-cover.js
+++ b/src/dialogs/more-info/controls/more-info-cover.js
@@ -12,106 +12,104 @@ import featureClassNames from '../../../common/entity/feature_class_names';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
-{
- const FEATURE_CLASS_NAMES = {
- 128: 'has-set_tilt_position',
- };
- class MoreInfoCover extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+ [invisible] {
+ visibility: hidden !important;
+ }
+
+
`;
- }
+ }
- static get properties() {
- return {
- hass: Object,
- stateObj: {
- type: Object,
- observer: 'stateObjChanged',
- },
- entityObj: {
- type: Object,
- computed: 'computeEntityObj(hass, stateObj)',
- },
- coverPositionSliderValue: Number,
- coverTiltPositionSliderValue: Number
- };
- }
+ static get properties() {
+ return {
+ hass: Object,
+ stateObj: {
+ type: Object,
+ observer: 'stateObjChanged',
+ },
+ entityObj: {
+ type: Object,
+ computed: 'computeEntityObj(hass, stateObj)',
+ },
+ coverPositionSliderValue: Number,
+ coverTiltPositionSliderValue: Number
+ };
+ }
- computeEntityObj(hass, stateObj) {
- return new CoverEntity(hass, stateObj);
- }
+ computeEntityObj(hass, stateObj) {
+ return new CoverEntity(hass, stateObj);
+ }
- stateObjChanged(newVal) {
- if (newVal) {
- this.setProperties({
- coverPositionSliderValue: newVal.attributes.current_position,
- coverTiltPositionSliderValue: newVal.attributes.current_tilt_position,
- });
- }
- }
-
- computeClassNames(stateObj) {
- var classes = [
- attributeClassNames(stateObj, ['current_position', 'current_tilt_position']),
- featureClassNames(stateObj, FEATURE_CLASS_NAMES),
- ];
- return classes.join(' ');
- }
-
- coverPositionSliderChanged(ev) {
- this.entityObj.setCoverPosition(ev.target.value);
- }
-
- coverTiltPositionSliderChanged(ev) {
- this.entityObj.setCoverTiltPosition(ev.target.value);
+ stateObjChanged(newVal) {
+ if (newVal) {
+ this.setProperties({
+ coverPositionSliderValue: newVal.attributes.current_position,
+ coverTiltPositionSliderValue: newVal.attributes.current_tilt_position,
+ });
}
}
- customElements.define('more-info-cover', MoreInfoCover);
+ computeClassNames(stateObj) {
+ var classes = [
+ attributeClassNames(stateObj, ['current_position', 'current_tilt_position']),
+ featureClassNames(stateObj, FEATURE_CLASS_NAMES),
+ ];
+ return classes.join(' ');
+ }
+
+ coverPositionSliderChanged(ev) {
+ this.entityObj.setCoverPosition(ev.target.value);
+ }
+
+ coverTiltPositionSliderChanged(ev) {
+ this.entityObj.setCoverTiltPosition(ev.target.value);
+ }
}
+
+customElements.define('more-info-cover', MoreInfoCover);
diff --git a/src/dialogs/more-info/controls/more-info-light.js b/src/dialogs/more-info/controls/more-info-light.js
index d3ee5b5d07..f31edc7c2c 100644
--- a/src/dialogs/more-info/controls/more-info-light.js
+++ b/src/dialogs/more-info/controls/more-info-light.js
@@ -14,253 +14,251 @@ import featureClassNames from '../../../common/entity/feature_class_names';
import EventsMixin from '../../../mixins/events-mixin.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
-{
- const FEATURE_CLASS_NAMES = {
- 1: 'has-brightness',
- 2: 'has-color_temp',
- 4: 'has-effect_list',
- 16: 'has-color',
- 128: 'has-white_value',
- };
- /*
- * @appliesMixin EventsMixin
- */
- class MoreInfoLight extends LocalizeMixin(EventsMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
+
+ paper-item {
+ cursor: pointer;
+ }
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
- }
+ }
- static get properties() {
- return {
- hass: {
- type: Object,
- },
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ },
- stateObj: {
- type: Object,
- observer: 'stateObjChanged',
- },
+ stateObj: {
+ type: Object,
+ observer: 'stateObjChanged',
+ },
- effectIndex: {
- type: Number,
- value: -1,
- observer: 'effectChanged',
- },
+ effectIndex: {
+ type: Number,
+ value: -1,
+ observer: 'effectChanged',
+ },
- brightnessSliderValue: {
- type: Number,
- value: 0,
- },
+ brightnessSliderValue: {
+ type: Number,
+ value: 0,
+ },
- ctSliderValue: {
- type: Number,
- value: 0,
- },
+ ctSliderValue: {
+ type: Number,
+ value: 0,
+ },
- wvSliderValue: {
- type: Number,
- value: 0,
- },
+ wvSliderValue: {
+ type: Number,
+ value: 0,
+ },
- colorPickerColor: {
- type: Object,
- }
- };
- }
-
- stateObjChanged(newVal, oldVal) {
- const props = {
- brightnessSliderValue: 0
- };
-
- if (newVal && newVal.state === 'on') {
- props.brightnessSliderValue = newVal.attributes.brightness;
- props.ctSliderValue = newVal.attributes.color_temp;
- props.wvSliderValue = newVal.attributes.white_value;
- if (newVal.attributes.hs_color) {
- props.colorPickerColor = {
- h: newVal.attributes.hs_color[0],
- s: newVal.attributes.hs_color[1] / 100,
- };
- }
- if (newVal.attributes.effect_list) {
- props.effectIndex = newVal.attributes.effect_list.indexOf(newVal.attributes.effect);
- } else {
- props.effectIndex = -1;
- }
+ colorPickerColor: {
+ type: Object,
}
+ };
+ }
- this.setProperties(props);
+ stateObjChanged(newVal, oldVal) {
+ const props = {
+ brightnessSliderValue: 0
+ };
- if (oldVal) {
- setTimeout(() => {
- this.fire('iron-resize');
- }, 500);
+ if (newVal && newVal.state === 'on') {
+ props.brightnessSliderValue = newVal.attributes.brightness;
+ props.ctSliderValue = newVal.attributes.color_temp;
+ props.wvSliderValue = newVal.attributes.white_value;
+ if (newVal.attributes.hs_color) {
+ props.colorPickerColor = {
+ h: newVal.attributes.hs_color[0],
+ s: newVal.attributes.hs_color[1] / 100,
+ };
}
- }
-
- computeClassNames(stateObj) {
- const classes = [featureClassNames(stateObj, FEATURE_CLASS_NAMES)];
- if (stateObj && stateObj.state === 'on') {
- classes.push('is-on');
- }
- if (stateObj && stateObj.state === 'unavailable') {
- classes.push('is-unavailable');
- }
- return classes.join(' ');
- }
-
- effectChanged(effectIndex) {
- var effectInput;
- // Selected Option will transition to '' before transitioning to new value
- if (effectIndex === '' || effectIndex === -1) return;
-
- effectInput = this.stateObj.attributes.effect_list[effectIndex];
- if (effectInput === this.stateObj.attributes.effect) return;
-
- this.hass.callService('light', 'turn_on', {
- entity_id: this.stateObj.entity_id,
- effect: effectInput,
- });
- }
-
- brightnessSliderChanged(ev) {
- var bri = parseInt(ev.target.value, 10);
-
- if (isNaN(bri)) return;
-
- if (bri === 0) {
- this.hass.callService('light', 'turn_off', {
- entity_id: this.stateObj.entity_id,
- });
+ if (newVal.attributes.effect_list) {
+ props.effectIndex = newVal.attributes.effect_list.indexOf(newVal.attributes.effect);
} else {
- this.hass.callService('light', 'turn_on', {
- entity_id: this.stateObj.entity_id,
- brightness: bri,
- });
+ props.effectIndex = -1;
}
}
- ctSliderChanged(ev) {
- var ct = parseInt(ev.target.value, 10);
+ this.setProperties(props);
- if (isNaN(ct)) return;
-
- this.hass.callService('light', 'turn_on', {
- entity_id: this.stateObj.entity_id,
- color_temp: ct,
- });
- }
-
- wvSliderChanged(ev) {
- var wv = parseInt(ev.target.value, 10);
-
- if (isNaN(wv)) return;
-
- this.hass.callService('light', 'turn_on', {
- entity_id: this.stateObj.entity_id,
- white_value: wv,
- });
- }
-
- serviceChangeColor(hass, entityId, color) {
- hass.callService('light', 'turn_on', {
- entity_id: entityId,
- hs_color: [color.h, color.s * 100],
- });
- }
-
- /**
- * Called when a new color has been picked.
- * should be throttled with the 'throttle=' attribute of the color picker
- */
- colorPicked(ev) {
- this.serviceChangeColor(this.hass, this.stateObj.entity_id, ev.detail.hs);
+ if (oldVal) {
+ setTimeout(() => {
+ this.fire('iron-resize');
+ }, 500);
}
}
- customElements.define('more-info-light', MoreInfoLight);
+ computeClassNames(stateObj) {
+ const classes = [featureClassNames(stateObj, FEATURE_CLASS_NAMES)];
+ if (stateObj && stateObj.state === 'on') {
+ classes.push('is-on');
+ }
+ if (stateObj && stateObj.state === 'unavailable') {
+ classes.push('is-unavailable');
+ }
+ return classes.join(' ');
+ }
+
+ effectChanged(effectIndex) {
+ var effectInput;
+ // Selected Option will transition to '' before transitioning to new value
+ if (effectIndex === '' || effectIndex === -1) return;
+
+ effectInput = this.stateObj.attributes.effect_list[effectIndex];
+ if (effectInput === this.stateObj.attributes.effect) return;
+
+ this.hass.callService('light', 'turn_on', {
+ entity_id: this.stateObj.entity_id,
+ effect: effectInput,
+ });
+ }
+
+ brightnessSliderChanged(ev) {
+ var bri = parseInt(ev.target.value, 10);
+
+ if (isNaN(bri)) return;
+
+ if (bri === 0) {
+ this.hass.callService('light', 'turn_off', {
+ entity_id: this.stateObj.entity_id,
+ });
+ } else {
+ this.hass.callService('light', 'turn_on', {
+ entity_id: this.stateObj.entity_id,
+ brightness: bri,
+ });
+ }
+ }
+
+ ctSliderChanged(ev) {
+ var ct = parseInt(ev.target.value, 10);
+
+ if (isNaN(ct)) return;
+
+ this.hass.callService('light', 'turn_on', {
+ entity_id: this.stateObj.entity_id,
+ color_temp: ct,
+ });
+ }
+
+ wvSliderChanged(ev) {
+ var wv = parseInt(ev.target.value, 10);
+
+ if (isNaN(wv)) return;
+
+ this.hass.callService('light', 'turn_on', {
+ entity_id: this.stateObj.entity_id,
+ white_value: wv,
+ });
+ }
+
+ serviceChangeColor(hass, entityId, color) {
+ hass.callService('light', 'turn_on', {
+ entity_id: entityId,
+ hs_color: [color.h, color.s * 100],
+ });
+ }
+
+ /**
+ * Called when a new color has been picked.
+ * should be throttled with the 'throttle=' attribute of the color picker
+ */
+ colorPicked(ev) {
+ this.serviceChangeColor(this.hass, this.stateObj.entity_id, ev.detail.hs);
+ }
}
+
+customElements.define('more-info-light', MoreInfoLight);
diff --git a/src/dialogs/more-info/controls/more-info-media_player.js b/src/dialogs/more-info/controls/more-info-media_player.js
index 70638514c5..0263626886 100644
--- a/src/dialogs/more-info/controls/more-info-media_player.js
+++ b/src/dialogs/more-info/controls/more-info-media_player.js
@@ -16,277 +16,275 @@ import isComponentLoaded from '../../../common/config/is_component_loaded.js';
import EventsMixin from '../../../mixins/events-mixin.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
-{
- /*
- * @appliesMixin EventsMixin
- */
- class MoreInfoMediaPlayer extends LocalizeMixin(EventsMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
+
+ paper-item {
+ cursor: pointer;
+ }
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
- [[item]]
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ [[item]]
+
+
+
+
+
+
+
`;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ stateObj: Object,
+ playerObj: {
+ type: Object,
+ computed: 'computePlayerObj(hass, stateObj)',
+ observer: 'playerObjChanged',
+ },
+
+ sourceIndex: {
+ type: Number,
+ value: 0,
+ observer: 'handleSourceChanged',
+ },
+
+ ttsLoaded: {
+ type: Boolean,
+ computed: 'computeTTSLoaded(hass)',
+ },
+
+ ttsMessage: {
+ type: String,
+ value: '',
+ },
+
+ };
+ }
+
+ computePlayerObj(hass, stateObj) {
+ return new HassMediaPlayerEntity(hass, stateObj);
+ }
+
+ playerObjChanged(newVal, oldVal) {
+ if (newVal && newVal.sourceList !== undefined) {
+ this.sourceIndex = newVal.sourceList.indexOf(newVal.source);
}
- static get properties() {
- return {
- hass: Object,
- stateObj: Object,
- playerObj: {
- type: Object,
- computed: 'computePlayerObj(hass, stateObj)',
- observer: 'playerObjChanged',
- },
-
- sourceIndex: {
- type: Number,
- value: 0,
- observer: 'handleSourceChanged',
- },
-
- ttsLoaded: {
- type: Boolean,
- computed: 'computeTTSLoaded(hass)',
- },
-
- ttsMessage: {
- type: String,
- value: '',
- },
-
- };
- }
-
- computePlayerObj(hass, stateObj) {
- return new HassMediaPlayerEntity(hass, stateObj);
- }
-
- playerObjChanged(newVal, oldVal) {
- if (newVal && newVal.sourceList !== undefined) {
- this.sourceIndex = newVal.sourceList.indexOf(newVal.source);
- }
-
- if (oldVal) {
- setTimeout(() => {
- this.fire('iron-resize');
- }, 500);
- }
- }
-
- computeClassNames(stateObj) {
- return attributeClassNames(stateObj, ['volume_level']);
- }
-
- computeMuteVolumeIcon(playerObj) {
- return playerObj.isMuted ? 'hass:volume-off' : 'hass:volume-high';
- }
-
- computeHideVolumeButtons(playerObj) {
- return !playerObj.supportsVolumeButtons || playerObj.isOff;
- }
-
- computeShowPlaybackControls(playerObj) {
- return !playerObj.isOff && playerObj.hasMediaControl;
- }
-
- computePlaybackControlIcon(playerObj) {
- if (playerObj.isPlaying) {
- return playerObj.supportsPause ? 'hass:pause' : 'hass:stop';
- }
- return playerObj.supportsPlay ? 'hass:play' : null;
- }
-
- computeHidePowerButton(playerObj) {
- return playerObj.isOff ? !playerObj.supportsTurnOn : !playerObj.supportsTurnOff;
- }
-
- computeHideSelectSource(playerObj) {
- return playerObj.isOff || !playerObj.supportsSelectSource || !playerObj.sourceList;
- }
-
- computeHideTTS(ttsLoaded, playerObj) {
- return !ttsLoaded || !playerObj.supportsPlayMedia;
- }
-
- computeTTSLoaded(hass) {
- return isComponentLoaded(hass, 'tts');
- }
-
- handleTogglePower() {
- this.playerObj.togglePower();
- }
-
- handlePrevious() {
- this.playerObj.previousTrack();
- }
-
- handlePlaybackControl() {
- this.playerObj.mediaPlayPause();
- }
-
- handleNext() {
- this.playerObj.nextTrack();
- }
-
- handleSourceChanged(sourceIndex, sourceIndexOld) {
- // Selected Option will transition to '' before transitioning to new value
- if (!this.playerObj
- || !this.playerObj.supportsSelectSource
- || this.playerObj.sourceList === undefined
- || sourceIndex < 0
- || sourceIndex >= this.playerObj.sourceList
- || sourceIndexOld === undefined
- ) {
- return;
- }
-
- const sourceInput = this.playerObj.sourceList[sourceIndex];
-
- if (sourceInput === this.playerObj.source) {
- return;
- }
-
- this.playerObj.selectSource(sourceInput);
- }
-
- handleVolumeTap() {
- if (!this.playerObj.supportsVolumeMute) {
- return;
- }
- this.playerObj.volumeMute(!this.playerObj.isMuted);
- }
-
- handleVolumeUp() {
- const obj = this.$.volumeUp;
- this.handleVolumeWorker('volume_up', obj, true);
- }
-
- handleVolumeDown() {
- const obj = this.$.volumeDown;
- this.handleVolumeWorker('volume_down', obj, true);
- }
-
- handleVolumeWorker(service, obj, force) {
- if (force || (obj !== undefined && obj.pointerDown)) {
- this.playerObj.callService(service);
- setTimeout(() => this.handleVolumeWorker(service, obj, false), 500);
- }
- }
-
- volumeSliderChanged(ev) {
- const volPercentage = parseFloat(ev.target.value);
- const volume = volPercentage > 0 ? volPercentage / 100 : 0;
- this.playerObj.setVolume(volume);
- }
-
- ttsCheckForEnter(ev) {
- if (ev.keyCode === 13) this.sendTTS();
- }
-
- sendTTS() {
- const services = this.hass.config.services.tts;
- const serviceKeys = Object.keys(services).sort();
- let service;
- let i;
-
- for (i = 0; i < serviceKeys.length; i++) {
- if (serviceKeys[i].indexOf('_say') !== -1) {
- service = serviceKeys[i];
- break;
- }
- }
-
- if (!service) {
- return;
- }
-
- this.hass.callService('tts', service, {
- entity_id: this.stateObj.entity_id,
- message: this.ttsMessage,
- });
- this.ttsMessage = '';
- this.$.ttsInput.focus();
+ if (oldVal) {
+ setTimeout(() => {
+ this.fire('iron-resize');
+ }, 500);
}
}
- customElements.define('more-info-media_player', MoreInfoMediaPlayer);
+ computeClassNames(stateObj) {
+ return attributeClassNames(stateObj, ['volume_level']);
+ }
+
+ computeMuteVolumeIcon(playerObj) {
+ return playerObj.isMuted ? 'hass:volume-off' : 'hass:volume-high';
+ }
+
+ computeHideVolumeButtons(playerObj) {
+ return !playerObj.supportsVolumeButtons || playerObj.isOff;
+ }
+
+ computeShowPlaybackControls(playerObj) {
+ return !playerObj.isOff && playerObj.hasMediaControl;
+ }
+
+ computePlaybackControlIcon(playerObj) {
+ if (playerObj.isPlaying) {
+ return playerObj.supportsPause ? 'hass:pause' : 'hass:stop';
+ }
+ return playerObj.supportsPlay ? 'hass:play' : null;
+ }
+
+ computeHidePowerButton(playerObj) {
+ return playerObj.isOff ? !playerObj.supportsTurnOn : !playerObj.supportsTurnOff;
+ }
+
+ computeHideSelectSource(playerObj) {
+ return playerObj.isOff || !playerObj.supportsSelectSource || !playerObj.sourceList;
+ }
+
+ computeHideTTS(ttsLoaded, playerObj) {
+ return !ttsLoaded || !playerObj.supportsPlayMedia;
+ }
+
+ computeTTSLoaded(hass) {
+ return isComponentLoaded(hass, 'tts');
+ }
+
+ handleTogglePower() {
+ this.playerObj.togglePower();
+ }
+
+ handlePrevious() {
+ this.playerObj.previousTrack();
+ }
+
+ handlePlaybackControl() {
+ this.playerObj.mediaPlayPause();
+ }
+
+ handleNext() {
+ this.playerObj.nextTrack();
+ }
+
+ handleSourceChanged(sourceIndex, sourceIndexOld) {
+ // Selected Option will transition to '' before transitioning to new value
+ if (!this.playerObj
+ || !this.playerObj.supportsSelectSource
+ || this.playerObj.sourceList === undefined
+ || sourceIndex < 0
+ || sourceIndex >= this.playerObj.sourceList
+ || sourceIndexOld === undefined
+ ) {
+ return;
+ }
+
+ const sourceInput = this.playerObj.sourceList[sourceIndex];
+
+ if (sourceInput === this.playerObj.source) {
+ return;
+ }
+
+ this.playerObj.selectSource(sourceInput);
+ }
+
+ handleVolumeTap() {
+ if (!this.playerObj.supportsVolumeMute) {
+ return;
+ }
+ this.playerObj.volumeMute(!this.playerObj.isMuted);
+ }
+
+ handleVolumeUp() {
+ const obj = this.$.volumeUp;
+ this.handleVolumeWorker('volume_up', obj, true);
+ }
+
+ handleVolumeDown() {
+ const obj = this.$.volumeDown;
+ this.handleVolumeWorker('volume_down', obj, true);
+ }
+
+ handleVolumeWorker(service, obj, force) {
+ if (force || (obj !== undefined && obj.pointerDown)) {
+ this.playerObj.callService(service);
+ setTimeout(() => this.handleVolumeWorker(service, obj, false), 500);
+ }
+ }
+
+ volumeSliderChanged(ev) {
+ const volPercentage = parseFloat(ev.target.value);
+ const volume = volPercentage > 0 ? volPercentage / 100 : 0;
+ this.playerObj.setVolume(volume);
+ }
+
+ ttsCheckForEnter(ev) {
+ if (ev.keyCode === 13) this.sendTTS();
+ }
+
+ sendTTS() {
+ const services = this.hass.config.services.tts;
+ const serviceKeys = Object.keys(services).sort();
+ let service;
+ let i;
+
+ for (i = 0; i < serviceKeys.length; i++) {
+ if (serviceKeys[i].indexOf('_say') !== -1) {
+ service = serviceKeys[i];
+ break;
+ }
+ }
+
+ if (!service) {
+ return;
+ }
+
+ this.hass.callService('tts', service, {
+ entity_id: this.stateObj.entity_id,
+ message: this.ttsMessage,
+ });
+ this.ttsMessage = '';
+ this.$.ttsInput.focus();
+ }
}
+
+customElements.define('more-info-media_player', MoreInfoMediaPlayer);
diff --git a/src/dialogs/more-info/more-info-controls.js b/src/dialogs/more-info/more-info-controls.js
index 173a9f7c67..49709e3a95 100644
--- a/src/dialogs/more-info/more-info-controls.js
+++ b/src/dialogs/more-info/more-info-controls.js
@@ -17,147 +17,145 @@ import isComponentLoaded from '../../common/config/is_component_loaded.js';
import { DOMAINS_MORE_INFO_NO_HISTORY } from '../../common/const.js';
import EventsMixin from '../../mixins/events-mixin.js';
-{
- const DOMAINS_NO_INFO = [
- 'camera',
- 'configurator',
- 'history_graph',
- ];
- /*
- * @appliesMixin EventsMixin
- */
- class MoreInfoControls extends EventsMixin(PolymerElement) {
- static get template() {
- return html`
-
- state-card-content {
- display: block;
- padding: 16px;
- }
-
- state-history-charts {
- max-width: 100%;
- margin-bottom: 16px;
- }
-
- @media all and (min-width: 451px) and (min-height: 501px) {
- .main-title {
- pointer-events: auto;
- cursor: default;
- }
- }
-
- :host([domain=camera]) paper-dialog-scrollable {
- margin: 0 -24px -5px;
- }
-
-
-
-
- [[_computeStateName(stateObj)]]
-
-
-
-
-
-
-
+
+
+ [[_computeStateName(stateObj)]]
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
`;
- }
+ }
- static get properties() {
- return {
- hass: Object,
+ static get properties() {
+ return {
+ hass: Object,
- stateObj: {
- type: Object,
- observer: '_stateObjChanged',
+ stateObj: {
+ type: Object,
+ observer: '_stateObjChanged',
+ },
+
+ dialogElement: Object,
+ canConfigure: Boolean,
+
+ domain: {
+ type: String,
+ reflectToAttribute: true,
+ computed: '_computeDomain(stateObj)',
+ },
+
+ _stateHistory: Object,
+ _stateHistoryLoading: Boolean,
+
+ large: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ },
+
+ _cacheConfig: {
+ type: Object,
+ value: {
+ refresh: 60,
+ cacheKey: null,
+ hoursToShow: 24,
},
+ },
+ };
+ }
- dialogElement: Object,
- canConfigure: Boolean,
+ enlarge() {
+ this.large = !this.large;
+ }
- domain: {
- type: String,
- reflectToAttribute: true,
- computed: '_computeDomain(stateObj)',
- },
+ _computeShowStateInfo(stateObj) {
+ return !stateObj || !DOMAINS_NO_INFO.includes(computeStateDomain(stateObj));
+ }
- _stateHistory: Object,
- _stateHistoryLoading: Boolean,
+ _computeShowHistoryComponent(hass, stateObj) {
+ return hass && stateObj &&
+ isComponentLoaded(hass, 'history') &&
+ !DOMAINS_MORE_INFO_NO_HISTORY.includes(computeStateDomain(stateObj));
+ }
- large: {
- type: Boolean,
- value: false,
- notify: true,
- },
+ _computeDomain(stateObj) {
+ return stateObj ? computeStateDomain(stateObj) : '';
+ }
- _cacheConfig: {
- type: Object,
- value: {
- refresh: 60,
- cacheKey: null,
- hoursToShow: 24,
- },
- },
- };
+ _computeStateName(stateObj) {
+ return stateObj ? computeStateName(stateObj) : '';
+ }
+
+ _stateObjChanged(newVal) {
+ if (!newVal) {
+ return;
}
- enlarge() {
- this.large = !this.large;
- }
-
- _computeShowStateInfo(stateObj) {
- return !stateObj || !DOMAINS_NO_INFO.includes(computeStateDomain(stateObj));
- }
-
- _computeShowHistoryComponent(hass, stateObj) {
- return hass && stateObj &&
- isComponentLoaded(hass, 'history') &&
- !DOMAINS_MORE_INFO_NO_HISTORY.includes(computeStateDomain(stateObj));
- }
-
- _computeDomain(stateObj) {
- return stateObj ? computeStateDomain(stateObj) : '';
- }
-
- _computeStateName(stateObj) {
- return stateObj ? computeStateName(stateObj) : '';
- }
-
- _stateObjChanged(newVal) {
- if (!newVal) {
- return;
- }
-
- if (this._cacheConfig.cacheKey !== `more_info.${newVal.entity_id}`) {
- this._cacheConfig = Object.assign(
- {}, this._cacheConfig,
- { cacheKey: `more_info.${newVal.entity_id}` }
- );
- }
- }
-
- _gotoSettings() {
- this.fire('more-info-page', { page: 'settings' });
+ if (this._cacheConfig.cacheKey !== `more_info.${newVal.entity_id}`) {
+ this._cacheConfig = Object.assign(
+ {}, this._cacheConfig,
+ { cacheKey: `more_info.${newVal.entity_id}` }
+ );
}
}
- customElements.define('more-info-controls', MoreInfoControls);
+
+ _gotoSettings() {
+ this.fire('more-info-page', { page: 'settings' });
+ }
}
+customElements.define('more-info-controls', MoreInfoControls);
diff --git a/src/layouts/home-assistant-main.js b/src/layouts/home-assistant-main.js
index cf9a100115..58aea5f00c 100644
--- a/src/layouts/home-assistant-main.js
+++ b/src/layouts/home-assistant-main.js
@@ -17,117 +17,115 @@ import(/* webpackChunkName: "ha-sidebar" */ '../components/ha-sidebar.js');
import(/* webpackChunkName: "more-info-dialog" */ '../dialogs/ha-more-info-dialog.js');
import(/* webpackChunkName: "voice-command-dialog" */ '../dialogs/ha-voice-command-dialog.js');
-{
- const NON_SWIPABLE_PANELS = ['kiosk', 'map'];
+const NON_SWIPABLE_PANELS = ['kiosk', 'map'];
- class HomeAssistantMain extends NavigateMixin(EventsMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
-
-
-
-
-
+class HomeAssistantMain extends NavigateMixin(EventsMixin(PolymerElement)) {
+ static get template() {
+ return html`
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
-
+
-
-
+
+
`;
- }
+ }
- static get properties() {
- return {
- hass: Object,
- narrow: Boolean,
- route: {
- type: Object,
- observer: '_routeChanged',
- },
- statesRouteTail: Object,
- dockedSidebar: {
- type: Boolean,
- computed: 'computeDockedSidebar(hass)',
- },
- };
- }
+ static get properties() {
+ return {
+ hass: Object,
+ narrow: Boolean,
+ route: {
+ type: Object,
+ observer: '_routeChanged',
+ },
+ statesRouteTail: Object,
+ dockedSidebar: {
+ type: Boolean,
+ computed: 'computeDockedSidebar(hass)',
+ },
+ };
+ }
- ready() {
- super.ready();
- this.addEventListener('hass-open-menu', () => this.handleOpenMenu());
- this.addEventListener('hass-close-menu', () => this.handleCloseMenu());
- this.addEventListener('hass-start-voice', ev => this.handleStartVoice(ev));
- }
+ ready() {
+ super.ready();
+ this.addEventListener('hass-open-menu', () => this.handleOpenMenu());
+ this.addEventListener('hass-close-menu', () => this.handleCloseMenu());
+ this.addEventListener('hass-start-voice', ev => this.handleStartVoice(ev));
+ }
- _routeChanged() {
- if (this.narrow) {
- this.$.drawer.close();
- }
- }
-
- handleStartVoice(ev) {
- ev.stopPropagation();
- this.$.voiceDialog.opened = true;
- }
-
- handleOpenMenu() {
- if (this.narrow) {
- this.$.drawer.open();
- } else {
- this.fire('hass-dock-sidebar', { dock: true });
- }
- }
-
- handleCloseMenu() {
+ _routeChanged() {
+ if (this.narrow) {
this.$.drawer.close();
- if (this.dockedSidebar) {
- this.fire('hass-dock-sidebar', { dock: false });
- }
- }
-
- connectedCallback() {
- super.connectedCallback();
- window.removeInitMsg();
- if (document.location.pathname === '/') {
- this.navigate('/states', true);
- }
- }
-
- computeForceNarrow(narrow, dockedSidebar) {
- return narrow || !dockedSidebar;
- }
-
- computeDockedSidebar(hass) {
- return hass.dockedSidebar;
- }
-
- _computeDisableSwipe(hass) {
- return NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1;
}
}
- customElements.define('home-assistant-main', HomeAssistantMain);
+ handleStartVoice(ev) {
+ ev.stopPropagation();
+ this.$.voiceDialog.opened = true;
+ }
+
+ handleOpenMenu() {
+ if (this.narrow) {
+ this.$.drawer.open();
+ } else {
+ this.fire('hass-dock-sidebar', { dock: true });
+ }
+ }
+
+ handleCloseMenu() {
+ this.$.drawer.close();
+ if (this.dockedSidebar) {
+ this.fire('hass-dock-sidebar', { dock: false });
+ }
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ window.removeInitMsg();
+ if (document.location.pathname === '/') {
+ this.navigate('/states', true);
+ }
+ }
+
+ computeForceNarrow(narrow, dockedSidebar) {
+ return narrow || !dockedSidebar;
+ }
+
+ computeDockedSidebar(hass) {
+ return hass.dockedSidebar;
+ }
+
+ _computeDisableSwipe(hass) {
+ return NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1;
+ }
}
+
+customElements.define('home-assistant-main', HomeAssistantMain);
diff --git a/src/layouts/partial-cards.js b/src/layouts/partial-cards.js
index 362d468c72..af00f6c82e 100644
--- a/src/layouts/partial-cards.js
+++ b/src/layouts/partial-cards.js
@@ -24,330 +24,328 @@ import computeLocationName from '../common/config/location_name.js';
import NavigateMixin from '../mixins/navigate-mixin.js';
import EventsMixin from '../mixins/events-mixin.js';
-{
- const DEFAULT_VIEW_ENTITY_ID = 'group.default_view';
- const ALWAYS_SHOW_DOMAIN = ['persistent_notification', 'configurator'];
+const DEFAULT_VIEW_ENTITY_ID = 'group.default_view';
+const ALWAYS_SHOW_DOMAIN = ['persistent_notification', 'configurator'];
- /*
- * @appliesMixin EventsMixin
- * @appliesMixin NavigateMixin
- */
- class PartialCards extends EventsMixin(NavigateMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
-
-
-
-
- [[computeTitle(views, defaultView, locationName)]]
-
-
+ paper-tabs {
+ margin-left: 12px;
+ --paper-tabs-selection-bar-color: var(--text-primary-color, #FFF);
+ text-transform: uppercase;
+ }
+
+
+
+
+
+
+ [[computeTitle(views, defaultView, locationName)]]
+
+
-
-
-
-
- Home
+
+
+
+
+ Home
+
+
+
+
-
-
-
-
-
- [[_computeStateName(defaultView)]]
-
+
+ [[_computeStateName(defaultView)]]
+
+
+
+
+
+
+
+
+
+ [[_computeStateName(item)]]
-
-
-
-
-
-
- [[_computeStateName(item)]]
-
-
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
-
+
+
`;
- }
+ }
- static get properties() {
- return {
+ static get properties() {
+ return {
- hass: {
- type: Object,
- value: null,
- observer: 'hassChanged'
- },
+ hass: {
+ type: Object,
+ value: null,
+ observer: 'hassChanged'
+ },
- narrow: {
- type: Boolean,
- value: false,
- },
+ narrow: {
+ type: Boolean,
+ value: false,
+ },
- showMenu: {
- type: Boolean,
- observer: 'handleWindowChange',
- },
+ showMenu: {
+ type: Boolean,
+ observer: 'handleWindowChange',
+ },
- panelVisible: {
- type: Boolean,
- value: false,
- },
+ panelVisible: {
+ type: Boolean,
+ value: false,
+ },
- route: Object,
- routeData: Object,
- routeMatch: Boolean,
+ route: Object,
+ routeData: Object,
+ routeMatch: Boolean,
- _columns: {
- type: Number,
- value: 1,
- },
+ _columns: {
+ type: Number,
+ value: 1,
+ },
- locationName: {
- type: String,
- value: '',
- computed: '_computeLocationName(hass)',
- },
+ locationName: {
+ type: String,
+ value: '',
+ computed: '_computeLocationName(hass)',
+ },
- currentView: {
- type: String,
- computed: '_computeCurrentView(hass, routeMatch, routeData)',
- },
+ currentView: {
+ type: String,
+ computed: '_computeCurrentView(hass, routeMatch, routeData)',
+ },
- views: {
- type: Array,
- },
+ views: {
+ type: Array,
+ },
- defaultView: {
- type: Object,
- },
+ defaultView: {
+ type: Object,
+ },
- viewStates: {
- type: Object,
- computed: 'computeViewStates(currentView, hass, defaultView)',
- },
+ viewStates: {
+ type: Object,
+ computed: 'computeViewStates(currentView, hass, defaultView)',
+ },
- orderedGroupEntities: {
- type: Array,
- computed: 'computeOrderedGroupEntities(currentView, hass, defaultView)',
- },
+ orderedGroupEntities: {
+ type: Array,
+ computed: 'computeOrderedGroupEntities(currentView, hass, defaultView)',
+ },
- showTabs: {
- type: Boolean,
- value: false,
- },
- };
- }
+ showTabs: {
+ type: Boolean,
+ value: false,
+ },
+ };
+ }
- ready() {
- this.handleWindowChange = this.handleWindowChange.bind(this);
- this.mqls = [300, 600, 900, 1200].map(width => matchMedia(`(min-width: ${width}px)`));
- super.ready();
- }
+ ready() {
+ this.handleWindowChange = this.handleWindowChange.bind(this);
+ this.mqls = [300, 600, 900, 1200].map(width => matchMedia(`(min-width: ${width}px)`));
+ super.ready();
+ }
- connectedCallback() {
- super.connectedCallback();
- this.mqls.forEach(mql => mql.addListener(this.handleWindowChange));
- }
+ connectedCallback() {
+ super.connectedCallback();
+ this.mqls.forEach(mql => mql.addListener(this.handleWindowChange));
+ }
- disconnectedCallback() {
- super.disconnectedCallback();
- this.mqls.forEach(mql => mql.removeListener(this.handleWindowChange));
- }
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this.mqls.forEach(mql => mql.removeListener(this.handleWindowChange));
+ }
- handleWindowChange() {
- const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
- // Do -1 column if the menu is docked and open
- this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
- }
+ handleWindowChange() {
+ const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
+ // Do -1 column if the menu is docked and open
+ this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
+ }
- areTabsHidden(views, showTabs) {
- return !views || !views.length || !showTabs;
- }
+ areTabsHidden(views, showTabs) {
+ return !views || !views.length || !showTabs;
+ }
- /**
- * Scroll to a specific y coordinate.
- *
- * Copied from paper-scroll-header-panel.
- *
- * @method scroll
- * @param {number} top The coordinate to scroll to, along the y-axis.
- * @param {boolean} smooth true if the scroll position should be smoothly adjusted.
- */
- scrollToTop() {
- // the scroll event will trigger _updateScrollState directly,
- // However, _updateScrollState relies on the previous `scrollTop` to update the states.
- // Calling _updateScrollState will ensure that the states are synced correctly.
- var top = 0;
- var scroller = this.$.layout.header.scrollTarget;
- var easingFn = function easeOutQuad(t, b, c, d) {
- /* eslint-disable no-param-reassign, space-infix-ops, no-mixed-operators */
- t /= d;
- return -c * t*(t-2) + b;
- /* eslint-enable no-param-reassign, space-infix-ops, no-mixed-operators */
- };
- var animationId = Math.random();
- var duration = 200;
- var startTime = Date.now();
- var currentScrollTop = scroller.scrollTop;
- var deltaScrollTop = top - currentScrollTop;
- this._currentAnimationId = animationId;
- (function updateFrame() {
- var now = Date.now();
- var elapsedTime = now - startTime;
- if (elapsedTime > duration) {
- scroller.scrollTop = top;
- } else if (this._currentAnimationId === animationId) {
- scroller.scrollTop = easingFn(elapsedTime, currentScrollTop, deltaScrollTop, duration);
- requestAnimationFrame(updateFrame.bind(this));
- }
- }).call(this);
- }
-
- handleViewSelected(ev) {
- const view = ev.detail.item.getAttribute('data-entity') || null;
-
- if (view !== this.currentView) {
- let path = '/states';
- if (view) {
- path += '/' + view;
- }
- this.navigate(path);
+ /**
+ * Scroll to a specific y coordinate.
+ *
+ * Copied from paper-scroll-header-panel.
+ *
+ * @method scroll
+ * @param {number} top The coordinate to scroll to, along the y-axis.
+ * @param {boolean} smooth true if the scroll position should be smoothly adjusted.
+ */
+ scrollToTop() {
+ // the scroll event will trigger _updateScrollState directly,
+ // However, _updateScrollState relies on the previous `scrollTop` to update the states.
+ // Calling _updateScrollState will ensure that the states are synced correctly.
+ var top = 0;
+ var scroller = this.$.layout.header.scrollTarget;
+ var easingFn = function easeOutQuad(t, b, c, d) {
+ /* eslint-disable no-param-reassign, space-infix-ops, no-mixed-operators */
+ t /= d;
+ return -c * t*(t-2) + b;
+ /* eslint-enable no-param-reassign, space-infix-ops, no-mixed-operators */
+ };
+ var animationId = Math.random();
+ var duration = 200;
+ var startTime = Date.now();
+ var currentScrollTop = scroller.scrollTop;
+ var deltaScrollTop = top - currentScrollTop;
+ this._currentAnimationId = animationId;
+ (function updateFrame() {
+ var now = Date.now();
+ var elapsedTime = now - startTime;
+ if (elapsedTime > duration) {
+ scroller.scrollTop = top;
+ } else if (this._currentAnimationId === animationId) {
+ scroller.scrollTop = easingFn(elapsedTime, currentScrollTop, deltaScrollTop, duration);
+ requestAnimationFrame(updateFrame.bind(this));
}
- }
+ }).call(this);
+ }
- _computeCurrentView(hass, routeMatch, routeData) {
- if (!routeMatch) return '';
- if (!hass.states[routeData.view] || !hass.states[routeData.view].attributes.view) {
- return '';
+ handleViewSelected(ev) {
+ const view = ev.detail.item.getAttribute('data-entity') || null;
+
+ if (view !== this.currentView) {
+ let path = '/states';
+ if (view) {
+ path += '/' + view;
}
- return routeData.view;
- }
-
- computeTitle(views, defaultView, locationName) {
- return (views && views.length > 0 && !defaultView && locationName === 'Home') || !locationName ? 'Home Assistant' : locationName;
- }
-
- _computeStateName(stateObj) {
- return computeStateName(stateObj);
- }
-
- _computeLocationName(hass) {
- return computeLocationName(hass);
- }
-
- hassChanged(hass) {
- if (!hass) return;
- const views = extractViews(hass.states);
- let defaultView = null;
- // If default view present, it's in first index.
- if (views.length > 0 && views[0].entity_id === DEFAULT_VIEW_ENTITY_ID) {
- defaultView = views.shift();
- }
-
- this.setProperties({ views, defaultView });
- }
-
- isView(currentView, defaultView) {
- return (currentView || defaultView) &&
- this.hass.states[currentView || DEFAULT_VIEW_ENTITY_ID];
- }
-
- _defaultViewFilter(hass, entityId) {
- // Filter out hidden
- return !hass.states[entityId].attributes.hidden;
- }
-
- _computeDefaultViewStates(hass, entityIds) {
- const states = {};
- entityIds.filter(this._defaultViewFilter.bind(null, hass)).forEach((entityId) => {
- states[entityId] = hass.states[entityId];
- });
- return states;
- }
-
- /*
- Compute the states to show for current view.
-
- Will make sure we always show entities from ALWAYS_SHOW_DOMAINS domains.
- */
- computeViewStates(currentView, hass, defaultView) {
- const entityIds = Object.keys(hass.states);
-
- // If we base off all entities, only have to filter out hidden
- if (!this.isView(currentView, defaultView)) {
- return this._computeDefaultViewStates(hass, entityIds);
- }
-
- let states;
- if (currentView) {
- states = getViewEntities(hass.states, hass.states[currentView]);
- } else {
- states = getViewEntities(hass.states, hass.states[DEFAULT_VIEW_ENTITY_ID]);
- }
-
- // Make sure certain domains are always shown.
- entityIds.forEach((entityId) => {
- const state = hass.states[entityId];
-
- if (ALWAYS_SHOW_DOMAIN.includes(computeStateDomain(state))) {
- states[entityId] = state;
- }
- });
-
- return states;
- }
-
- /*
- Compute the ordered list of groups for this view
- */
- computeOrderedGroupEntities(currentView, hass, defaultView) {
- if (!this.isView(currentView, defaultView)) {
- return null;
- }
-
- var orderedGroupEntities = {};
- var entitiesList = hass.states[currentView || DEFAULT_VIEW_ENTITY_ID].attributes.entity_id;
-
- for (var i = 0; i < entitiesList.length; i++) {
- orderedGroupEntities[entitiesList[i]] = i;
- }
-
- return orderedGroupEntities;
+ this.navigate(path);
}
}
- customElements.define('partial-cards', PartialCards);
+ _computeCurrentView(hass, routeMatch, routeData) {
+ if (!routeMatch) return '';
+ if (!hass.states[routeData.view] || !hass.states[routeData.view].attributes.view) {
+ return '';
+ }
+ return routeData.view;
+ }
+
+ computeTitle(views, defaultView, locationName) {
+ return (views && views.length > 0 && !defaultView && locationName === 'Home') || !locationName ? 'Home Assistant' : locationName;
+ }
+
+ _computeStateName(stateObj) {
+ return computeStateName(stateObj);
+ }
+
+ _computeLocationName(hass) {
+ return computeLocationName(hass);
+ }
+
+ hassChanged(hass) {
+ if (!hass) return;
+ const views = extractViews(hass.states);
+ let defaultView = null;
+ // If default view present, it's in first index.
+ if (views.length > 0 && views[0].entity_id === DEFAULT_VIEW_ENTITY_ID) {
+ defaultView = views.shift();
+ }
+
+ this.setProperties({ views, defaultView });
+ }
+
+ isView(currentView, defaultView) {
+ return (currentView || defaultView) &&
+ this.hass.states[currentView || DEFAULT_VIEW_ENTITY_ID];
+ }
+
+ _defaultViewFilter(hass, entityId) {
+ // Filter out hidden
+ return !hass.states[entityId].attributes.hidden;
+ }
+
+ _computeDefaultViewStates(hass, entityIds) {
+ const states = {};
+ entityIds.filter(this._defaultViewFilter.bind(null, hass)).forEach((entityId) => {
+ states[entityId] = hass.states[entityId];
+ });
+ return states;
+ }
+
+ /*
+ Compute the states to show for current view.
+
+ Will make sure we always show entities from ALWAYS_SHOW_DOMAINS domains.
+ */
+ computeViewStates(currentView, hass, defaultView) {
+ const entityIds = Object.keys(hass.states);
+
+ // If we base off all entities, only have to filter out hidden
+ if (!this.isView(currentView, defaultView)) {
+ return this._computeDefaultViewStates(hass, entityIds);
+ }
+
+ let states;
+ if (currentView) {
+ states = getViewEntities(hass.states, hass.states[currentView]);
+ } else {
+ states = getViewEntities(hass.states, hass.states[DEFAULT_VIEW_ENTITY_ID]);
+ }
+
+ // Make sure certain domains are always shown.
+ entityIds.forEach((entityId) => {
+ const state = hass.states[entityId];
+
+ if (ALWAYS_SHOW_DOMAIN.includes(computeStateDomain(state))) {
+ states[entityId] = state;
+ }
+ });
+
+ return states;
+ }
+
+ /*
+ Compute the ordered list of groups for this view
+ */
+ computeOrderedGroupEntities(currentView, hass, defaultView) {
+ if (!this.isView(currentView, defaultView)) {
+ return null;
+ }
+
+ var orderedGroupEntities = {};
+ var entitiesList = hass.states[currentView || DEFAULT_VIEW_ENTITY_ID].attributes.entity_id;
+
+ for (var i = 0; i < entitiesList.length; i++) {
+ orderedGroupEntities[entitiesList[i]] = i;
+ }
+
+ return orderedGroupEntities;
+ }
}
+
+customElements.define('partial-cards', PartialCards);
diff --git a/src/panels/config/cloud/ha-config-cloud.js b/src/panels/config/cloud/ha-config-cloud.js
index 7778634c17..c17435e9e0 100644
--- a/src/panels/config/cloud/ha-config-cloud.js
+++ b/src/panels/config/cloud/ha-config-cloud.js
@@ -12,100 +12,98 @@ import './ha-config-cloud-login.js';
import './ha-config-cloud-register.js';
import NavigateMixin from '../../../mixins/navigate-mixin.js';
-{
- const LOGGED_IN_URLS = [
- '/cloud/account',
- ];
- const NOT_LOGGED_IN_URLS = [
- '/cloud/login',
- '/cloud/register',
- '/cloud/forgot-password',
- ];
+const LOGGED_IN_URLS = [
+ '/cloud/account',
+];
+const NOT_LOGGED_IN_URLS = [
+ '/cloud/login',
+ '/cloud/register',
+ '/cloud/forgot-password',
+];
- /*
- * @appliesMixin NavigateMixin
- */
- class HaConfigCloud extends NavigateMixin(PolymerElement) {
- static get template() {
- return html`
-
+/*
+ * @appliesMixin NavigateMixin
+ */
+class HaConfigCloud extends NavigateMixin(PolymerElement) {
+ static get template() {
+ return html`
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
`;
- }
-
- static get properties() {
- return {
- hass: Object,
- isWide: Boolean,
- loadingAccount: {
- type: Boolean,
- value: false
- },
- account: {
- type: Object,
- },
- _flashMessage: {
- type: String,
- value: '',
- },
-
- route: Object,
-
- _routeData: Object,
- _routeTail: Object,
- _loginEmail: String,
- };
- }
-
- static get observers() {
- return [
- '_checkRoute(route, account)'
- ];
- }
-
- ready() {
- super.ready();
- this.addEventListener('cloud-done', (ev) => {
- this._flashMessage = ev.detail.flashMessage;
- this.navigate('/config/cloud/login');
- });
- }
-
- _checkRoute(route) {
- if (!route || route.path.substr(0, 6) !== '/cloud') return;
-
- this._debouncer = Debouncer.debounce(
- this._debouncer,
- timeOut.after(0),
- () => {
- if (!this.account && !NOT_LOGGED_IN_URLS.includes(route.path)) {
- this.navigate('/config/cloud/login', true);
- } else if (this.account && !LOGGED_IN_URLS.includes(route.path)) {
- this.navigate('/config/cloud/account', true);
- }
- }
- );
- }
-
- _equals(a, b) {
- return a === b;
- }
}
- customElements.define('ha-config-cloud', HaConfigCloud);
+ static get properties() {
+ return {
+ hass: Object,
+ isWide: Boolean,
+ loadingAccount: {
+ type: Boolean,
+ value: false
+ },
+ account: {
+ type: Object,
+ },
+ _flashMessage: {
+ type: String,
+ value: '',
+ },
+
+ route: Object,
+
+ _routeData: Object,
+ _routeTail: Object,
+ _loginEmail: String,
+ };
+ }
+
+ static get observers() {
+ return [
+ '_checkRoute(route, account)'
+ ];
+ }
+
+ ready() {
+ super.ready();
+ this.addEventListener('cloud-done', (ev) => {
+ this._flashMessage = ev.detail.flashMessage;
+ this.navigate('/config/cloud/login');
+ });
+ }
+
+ _checkRoute(route) {
+ if (!route || route.path.substr(0, 6) !== '/cloud') return;
+
+ this._debouncer = Debouncer.debounce(
+ this._debouncer,
+ timeOut.after(0),
+ () => {
+ if (!this.account && !NOT_LOGGED_IN_URLS.includes(route.path)) {
+ this.navigate('/config/cloud/login', true);
+ } else if (this.account && !LOGGED_IN_URLS.includes(route.path)) {
+ this.navigate('/config/cloud/account', true);
+ }
+ }
+ );
+ }
+
+ _equals(a, b) {
+ return a === b;
+ }
}
+
+customElements.define('ha-config-cloud', HaConfigCloud);
diff --git a/src/panels/config/config-entries/ha-config-entries.js b/src/panels/config/config-entries/ha-config-entries.js
index c961825458..4a76bf0465 100644
--- a/src/panels/config/config-entries/ha-config-entries.js
+++ b/src/panels/config/config-entries/ha-config-entries.js
@@ -13,192 +13,190 @@ import './ha-config-flow.js';
import EventsMixin from '../../../mixins/events-mixin.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
-{
- /*
- * @appliesMixin LocalizeMixin
- * @appliesMixin EventsMixin
- */
- class HaConfigManager extends
- LocalizeMixin(EventsMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
-
-
-
- In Progress
-
-
-
-
- [[_computeIntegrationTitle(localize, item.handler)]]
-
-
Configure
-
-
-
-
-
+/*
+ * @appliesMixin LocalizeMixin
+ * @appliesMixin EventsMixin
+ */
+class HaConfigManager extends
+ LocalizeMixin(EventsMixin(PolymerElement)) {
+ static get template() {
+ return html`
+
+
+
- Configured
+ In Progress
-
+
- Nothing configured yet
+ [[_computeIntegrationTitle(localize, item.handler)]]
-
-
-
-
-
- [[item.title]]
- Integration: [[_computeIntegrationTitle(localize, item.domain)]]
- Added by: [[item.source]]
- State: [[item.state]]
-
-
Remove
+
Configure
+
-
- Available
-
-
-
-
- [[_computeIntegrationTitle(localize, item)]]
-
-
Configure
-
-
-
-
-
+
+ Configured
+
+
+
+
+ Nothing configured yet
+
+
+
+
+
+
+ [[item.title]]
+ Integration: [[_computeIntegrationTitle(localize, item.domain)]]
+ Added by: [[item.source]]
+ State: [[item.state]]
+
+
Remove
+
+
+
+
-
+
+ Available
+
+
+
+
+ [[_computeIntegrationTitle(localize, item)]]
+
+
Configure
+
+
+
+
+
+
+
`;
- }
-
- static get properties() {
- return {
- hass: Object,
- isWide: Boolean,
-
- _flowId: {
- type: String,
- value: null,
- },
- /*
- * The step of the current selected flow, if available.
- */
- _flowStep: Object,
-
- /**
- * Existing entries.
- */
- _entries: Array,
-
- /**
- * Current flows that are in progress and have not been started by a user.
- * For example, can be discovered devices that require more config.
- */
- _progress: Array,
-
- _handlers: Array,
- };
- }
-
- ready() {
- super.ready();
- this._loadData();
- }
-
- _createFlow(ev) {
- this.hass.callApi('post', 'config/config_entries/flow', { handler: ev.model.item })
- .then((flow) => {
- this._userCreatedFlow = true;
- this.setProperties({
- _flowStep: flow,
- _flowId: flow.flow_id,
- });
- });
- }
-
- _continueFlow(ev) {
- this._userCreatedFlow = false;
- this.setProperties({
- _flowId: ev.model.item.flow_id,
- _flowStep: null,
- });
- }
-
- _removeEntry(ev) {
- if (!confirm('Are you sure you want to delete this integration?')) return;
-
- const entryId = ev.model.item.entry_id;
-
- this.hass.callApi('delete', `config/config_entries/entry/${entryId}`)
- .then((result) => {
- this._entries = this._entries.filter(entry => entry.entry_id !== entryId);
- if (result.require_restart) {
- alert('Restart Home Assistant to finish removing this integration');
- }
- });
- }
-
- _flowClose(ev) {
- // Was the flow completed?
- if (ev.detail.flowFinished) {
- this._loadData();
-
- // Remove a flow if it was not finished and was started by the user
- } else if (this._userCreatedFlow) {
- this.hass.callApi('delete', `config/config_entries/flow/${this._flowId}`);
- }
-
- this._flowId = null;
- }
-
- _loadData() {
- this._loadEntries();
- this._loadDiscovery();
- this.hass.callApi('get', 'config/config_entries/flow_handlers')
- .then((handlers) => { this._handlers = handlers; });
- }
-
- _loadEntries() {
- this.hass.callApi('get', 'config/config_entries/entry')
- .then((entries) => { this._entries = entries; });
- }
-
- _loadDiscovery() {
- this.hass.callApi('get', 'config/config_entries/flow')
- .then((progress) => { this._progress = progress; });
- }
-
- _computeIntegrationTitle(localize, integration) {
- return localize(`component.${integration}.config.title`);
- }
}
- customElements.define('ha-config-entries', HaConfigManager);
+ static get properties() {
+ return {
+ hass: Object,
+ isWide: Boolean,
+
+ _flowId: {
+ type: String,
+ value: null,
+ },
+ /*
+ * The step of the current selected flow, if available.
+ */
+ _flowStep: Object,
+
+ /**
+ * Existing entries.
+ */
+ _entries: Array,
+
+ /**
+ * Current flows that are in progress and have not been started by a user.
+ * For example, can be discovered devices that require more config.
+ */
+ _progress: Array,
+
+ _handlers: Array,
+ };
+ }
+
+ ready() {
+ super.ready();
+ this._loadData();
+ }
+
+ _createFlow(ev) {
+ this.hass.callApi('post', 'config/config_entries/flow', { handler: ev.model.item })
+ .then((flow) => {
+ this._userCreatedFlow = true;
+ this.setProperties({
+ _flowStep: flow,
+ _flowId: flow.flow_id,
+ });
+ });
+ }
+
+ _continueFlow(ev) {
+ this._userCreatedFlow = false;
+ this.setProperties({
+ _flowId: ev.model.item.flow_id,
+ _flowStep: null,
+ });
+ }
+
+ _removeEntry(ev) {
+ if (!confirm('Are you sure you want to delete this integration?')) return;
+
+ const entryId = ev.model.item.entry_id;
+
+ this.hass.callApi('delete', `config/config_entries/entry/${entryId}`)
+ .then((result) => {
+ this._entries = this._entries.filter(entry => entry.entry_id !== entryId);
+ if (result.require_restart) {
+ alert('Restart Home Assistant to finish removing this integration');
+ }
+ });
+ }
+
+ _flowClose(ev) {
+ // Was the flow completed?
+ if (ev.detail.flowFinished) {
+ this._loadData();
+
+ // Remove a flow if it was not finished and was started by the user
+ } else if (this._userCreatedFlow) {
+ this.hass.callApi('delete', `config/config_entries/flow/${this._flowId}`);
+ }
+
+ this._flowId = null;
+ }
+
+ _loadData() {
+ this._loadEntries();
+ this._loadDiscovery();
+ this.hass.callApi('get', 'config/config_entries/flow_handlers')
+ .then((handlers) => { this._handlers = handlers; });
+ }
+
+ _loadEntries() {
+ this.hass.callApi('get', 'config/config_entries/entry')
+ .then((entries) => { this._entries = entries; });
+ }
+
+ _loadDiscovery() {
+ this.hass.callApi('get', 'config/config_entries/flow')
+ .then((progress) => { this._progress = progress; });
+ }
+
+ _computeIntegrationTitle(localize, integration) {
+ return localize(`component.${integration}.config.title`);
+ }
}
+
+customElements.define('ha-config-entries', HaConfigManager);
diff --git a/src/panels/config/dashboard/ha-config-navigation.js b/src/panels/config/dashboard/ha-config-navigation.js
index c53d054a29..1e01ec40a0 100644
--- a/src/panels/config/dashboard/ha-config-navigation.js
+++ b/src/panels/config/dashboard/ha-config-navigation.js
@@ -11,78 +11,76 @@ import LocalizeMixin from '../../../mixins/localize-mixin.js';
import isComponentLoaded from '../../../common/config/is_component_loaded.js';
-{
- const CORE_PAGES = [
- 'core',
- 'customize',
- ];
- /*
- * @appliesMixin LocalizeMixin
- * @appliesMixin NavigateMixin
- */
- class HaConfigNavigation extends
- LocalizeMixin(NavigateMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
-
-
-
-
- [[_computeCaption(item, localize)]]
- [[_computeDescription(item, localize)]]
-
-
-
-
+const CORE_PAGES = [
+ 'core',
+ 'customize',
+];
+/*
+ * @appliesMixin LocalizeMixin
+ * @appliesMixin NavigateMixin
+ */
+class HaConfigNavigation extends
+ LocalizeMixin(NavigateMixin(PolymerElement)) {
+ static get template() {
+ return html`
+
+
+
+
+
+
+ [[_computeCaption(item, localize)]]
+ [[_computeDescription(item, localize)]]
+
+
+
-
+
+
`;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- },
-
- pages: {
- type: Array,
- value: [
- 'core',
- 'customize',
- 'automation',
- 'script',
- 'zwave',
- ],
- }
- };
- }
-
- _computeLoaded(hass, page) {
- return CORE_PAGES.includes(page) || isComponentLoaded(hass, page);
- }
-
- _computeCaption(page, localize) {
- return localize(`ui.panel.config.${page}.caption`);
- }
-
- _computeDescription(page, localize) {
- return localize(`ui.panel.config.${page}.description`);
- }
-
- _navigate(ev) {
- this.navigate('/config/' + ev.model.item);
- }
}
- customElements.define('ha-config-navigation', HaConfigNavigation);
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ },
+
+ pages: {
+ type: Array,
+ value: [
+ 'core',
+ 'customize',
+ 'automation',
+ 'script',
+ 'zwave',
+ ],
+ }
+ };
+ }
+
+ _computeLoaded(hass, page) {
+ return CORE_PAGES.includes(page) || isComponentLoaded(hass, page);
+ }
+
+ _computeCaption(page, localize) {
+ return localize(`ui.panel.config.${page}.caption`);
+ }
+
+ _computeDescription(page, localize) {
+ return localize(`ui.panel.config.${page}.description`);
+ }
+
+ _navigate(ev) {
+ this.navigate('/config/' + ev.model.item);
+ }
}
+
+customElements.define('ha-config-navigation', HaConfigNavigation);
diff --git a/src/panels/dev-service/ha-panel-dev-service.js b/src/panels/dev-service/ha-panel-dev-service.js
index a73a9a19d0..c87a26726e 100644
--- a/src/panels/dev-service/ha-panel-dev-service.js
+++ b/src/panels/dev-service/ha-panel-dev-service.js
@@ -12,293 +12,291 @@ import '../../components/ha-service-picker.js';
import '../../resources/ha-style.js';
import '../../util/app-localstorage-document.js';
-{
- const ERROR_SENTINEL = {};
- class HaPanelDevService extends PolymerElement {
- static get template() {
- return html`
-
+ .error {
+ color: var(--google-red-500);
+ }
+
-
-
-
-
- Services
-
-
+
+
+
+
+ Services
+
+
-
-
-
-
+
+
+
+
-
-
- The service dev tool allows you to call any available service in Home Assistant.
-
+
+
+ The service dev tool allows you to call any available service in Home Assistant.
+
-
-
-
- Select a service to see the description
+ domain-filter='[[_computeEntityDomainFilter(_domain)]]'
+ allow-custom-entity
+ >
-
-
-
- No description is available
-
-
- [[_description]]
-
-
-
- Parameter |
- Description |
- Example |
-
-
- This service takes no parameters. |
-
-
-
- [[attribute.key]] |
- [[attribute.description]] |
- [[attribute.example]] |
-
-
-
-
+
+ Call Service
+
+ Invalid JSON
-
- `;
- }
+
+ Select a service to see the description
+
- static get properties() {
- return {
- hass: {
- type: Object,
- },
+
+
+ No description is available
+
+
+ [[_description]]
- narrow: {
- type: Boolean,
- value: false,
- },
+
+
+ Parameter |
+ Description |
+ Example |
+
+
+ This service takes no parameters. |
+
+
+
+ [[attribute.key]] |
+ [[attribute.description]] |
+ [[attribute.example]] |
+
+
+
+
+
+
- showMenu: {
- type: Boolean,
- value: false,
- },
+
+ `;
+ }
- domainService: {
- type: String,
- observer: '_domainServiceChanged',
- },
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ },
- _domain: {
- type: String,
- computed: '_computeDomain(domainService)',
- },
+ narrow: {
+ type: Boolean,
+ value: false,
+ },
- _service: {
- type: String,
- computed: '_computeService(domainService)',
- },
+ showMenu: {
+ type: Boolean,
+ value: false,
+ },
- serviceData: {
- type: String,
- value: '',
- },
+ domainService: {
+ type: String,
+ observer: '_domainServiceChanged',
+ },
- parsedJSON: {
- type: Object,
- computed: '_computeParsedServiceData(serviceData)'
- },
+ _domain: {
+ type: String,
+ computed: '_computeDomain(domainService)',
+ },
- validJSON: {
- type: Boolean,
- computed: '_computeValidJSON(parsedJSON)',
- },
+ _service: {
+ type: String,
+ computed: '_computeService(domainService)',
+ },
- _attributes: {
- type: Array,
- computed: '_computeAttributesArray(hass, _domain, _service)',
- },
+ serviceData: {
+ type: String,
+ value: '',
+ },
- _description: {
- type: String,
- computed: '_computeDescription(hass, _domain, _service)',
- },
- };
- }
+ parsedJSON: {
+ type: Object,
+ computed: '_computeParsedServiceData(serviceData)'
+ },
- _domainServiceChanged() {
- this.serviceData = '';
- }
+ validJSON: {
+ type: Boolean,
+ computed: '_computeValidJSON(parsedJSON)',
+ },
- _computeAttributesArray(hass, domain, service) {
- const serviceDomains = hass.config.services;
- if (!(domain in serviceDomains)) return [];
- if (!(service in serviceDomains[domain])) return [];
+ _attributes: {
+ type: Array,
+ computed: '_computeAttributesArray(hass, _domain, _service)',
+ },
- const fields = serviceDomains[domain][service].fields;
- return Object.keys(fields).map(function (field) {
- return Object.assign({ key: field }, fields[field]);
- });
- }
+ _description: {
+ type: String,
+ computed: '_computeDescription(hass, _domain, _service)',
+ },
+ };
+ }
- _computeDescription(hass, domain, service) {
- const serviceDomains = hass.config.services;
- if (!(domain in serviceDomains)) return undefined;
- if (!(service in serviceDomains[domain])) return undefined;
- return serviceDomains[domain][service].description;
- }
+ _domainServiceChanged() {
+ this.serviceData = '';
+ }
- _computeServicedataKey(domainService) {
- return `panel-dev-service-state-servicedata.${domainService}`;
- }
+ _computeAttributesArray(hass, domain, service) {
+ const serviceDomains = hass.config.services;
+ if (!(domain in serviceDomains)) return [];
+ if (!(service in serviceDomains[domain])) return [];
- _computeDomain(domainService) {
- return domainService.split('.', 1)[0];
- }
+ const fields = serviceDomains[domain][service].fields;
+ return Object.keys(fields).map(function (field) {
+ return Object.assign({ key: field }, fields[field]);
+ });
+ }
- _computeService(domainService) {
- return domainService.split('.', 2)[1] || null;
- }
+ _computeDescription(hass, domain, service) {
+ const serviceDomains = hass.config.services;
+ if (!(domain in serviceDomains)) return undefined;
+ if (!(service in serviceDomains[domain])) return undefined;
+ return serviceDomains[domain][service].description;
+ }
- _computeParsedServiceData(serviceData) {
- try {
- return serviceData ? JSON.parse(serviceData) : {};
- } catch (err) {
- return ERROR_SENTINEL;
- }
- }
+ _computeServicedataKey(domainService) {
+ return `panel-dev-service-state-servicedata.${domainService}`;
+ }
- _computeValidJSON(parsedJSON) {
- return parsedJSON !== ERROR_SENTINEL;
- }
+ _computeDomain(domainService) {
+ return domainService.split('.', 1)[0];
+ }
- _computeHasEntity(attributes) {
- return attributes.some(attr => attr.key === 'entity_id');
- }
+ _computeService(domainService) {
+ return domainService.split('.', 2)[1] || null;
+ }
- _computeEntityValue(parsedJSON) {
- return parsedJSON === ERROR_SENTINEL ? '' : parsedJSON.entity_id;
- }
-
- _computeEntityDomainFilter(domain) {
- return domain === 'homeassistant' ? null : domain;
- }
-
- _callService() {
- if (this.parsedJSON === ERROR_SENTINEL) {
- // eslint-disable-next-line
- alert(`Error parsing JSON: ${this.serviceData}`);
- }
-
- this.hass.callService(this._domain, this._service, this.parsedJSON);
- }
-
- _entityPicked(ev) {
- this.serviceData = JSON.stringify(Object.assign({}, this.parsedJSON, {
- entity_id: ev.target.value
- }), null, 2);
+ _computeParsedServiceData(serviceData) {
+ try {
+ return serviceData ? JSON.parse(serviceData) : {};
+ } catch (err) {
+ return ERROR_SENTINEL;
}
}
- customElements.define('ha-panel-dev-service', HaPanelDevService);
+ _computeValidJSON(parsedJSON) {
+ return parsedJSON !== ERROR_SENTINEL;
+ }
+
+ _computeHasEntity(attributes) {
+ return attributes.some(attr => attr.key === 'entity_id');
+ }
+
+ _computeEntityValue(parsedJSON) {
+ return parsedJSON === ERROR_SENTINEL ? '' : parsedJSON.entity_id;
+ }
+
+ _computeEntityDomainFilter(domain) {
+ return domain === 'homeassistant' ? null : domain;
+ }
+
+ _callService() {
+ if (this.parsedJSON === ERROR_SENTINEL) {
+ // eslint-disable-next-line
+ alert(`Error parsing JSON: ${this.serviceData}`);
+ }
+
+ this.hass.callService(this._domain, this._service, this.parsedJSON);
+ }
+
+ _entityPicked(ev) {
+ this.serviceData = JSON.stringify(Object.assign({}, this.parsedJSON, {
+ entity_id: ev.target.value
+ }), null, 2);
+ }
}
+
+customElements.define('ha-panel-dev-service', HaPanelDevService);
diff --git a/src/panels/logbook/ha-logbook-data.js b/src/panels/logbook/ha-logbook-data.js
index e02b8af722..c4188951c1 100644
--- a/src/panels/logbook/ha-logbook-data.js
+++ b/src/panels/logbook/ha-logbook-data.js
@@ -1,76 +1,74 @@
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
-{
- var DATE_CACHE = {};
+var DATE_CACHE = {};
- class HaLogbookData extends PolymerElement {
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: 'hassChanged',
- },
+class HaLogbookData extends PolymerElement {
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: 'hassChanged',
+ },
- filterDate: {
- type: String,
- observer: 'filterDateChanged',
- },
+ filterDate: {
+ type: String,
+ observer: 'filterDateChanged',
+ },
- isLoading: {
- type: Boolean,
- value: true,
- readOnly: true,
- notify: true,
- },
+ isLoading: {
+ type: Boolean,
+ value: true,
+ readOnly: true,
+ notify: true,
+ },
- entries: {
- type: Object,
- value: null,
- readOnly: true,
- notify: true,
- },
- };
- }
+ entries: {
+ type: Object,
+ value: null,
+ readOnly: true,
+ notify: true,
+ },
+ };
+ }
- hassChanged(newHass, oldHass) {
- if (!oldHass && this.filterDate) {
- this.filterDateChanged(this.filterDate);
- }
- }
-
- filterDateChanged(filterDate) {
- if (!this.hass) return;
-
- this._setIsLoading(true);
-
- this.getDate(filterDate).then(function (logbookEntries) {
- this._setEntries(logbookEntries);
- this._setIsLoading(false);
- }.bind(this));
- }
-
- getDate(date) {
- if (!DATE_CACHE[date]) {
- DATE_CACHE[date] = this.hass.callApi('GET', 'logbook/' + date).then(
- function (logbookEntries) {
- logbookEntries.reverse();
- return logbookEntries;
- },
- function () {
- DATE_CACHE[date] = false;
- return null;
- }
- );
- }
-
- return DATE_CACHE[date];
- }
-
- refreshLogbook() {
- DATE_CACHE[this.filterDate] = null;
+ hassChanged(newHass, oldHass) {
+ if (!oldHass && this.filterDate) {
this.filterDateChanged(this.filterDate);
}
}
- customElements.define('ha-logbook-data', HaLogbookData);
+ filterDateChanged(filterDate) {
+ if (!this.hass) return;
+
+ this._setIsLoading(true);
+
+ this.getDate(filterDate).then(function (logbookEntries) {
+ this._setEntries(logbookEntries);
+ this._setIsLoading(false);
+ }.bind(this));
+ }
+
+ getDate(date) {
+ if (!DATE_CACHE[date]) {
+ DATE_CACHE[date] = this.hass.callApi('GET', 'logbook/' + date).then(
+ function (logbookEntries) {
+ logbookEntries.reverse();
+ return logbookEntries;
+ },
+ function () {
+ DATE_CACHE[date] = false;
+ return null;
+ }
+ );
+ }
+
+ return DATE_CACHE[date];
+ }
+
+ refreshLogbook() {
+ DATE_CACHE[this.filterDate] = null;
+ this.filterDateChanged(this.filterDate);
+ }
}
+
+customElements.define('ha-logbook-data', HaLogbookData);
diff --git a/src/util/ha-pref-storage.js b/src/util/ha-pref-storage.js
index 73a6129177..8e103eeb57 100644
--- a/src/util/ha-pref-storage.js
+++ b/src/util/ha-pref-storage.js
@@ -1,49 +1,47 @@
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
-{
- const STORED_STATE = [
- 'dockedSidebar',
- 'selectedTheme',
- 'selectedLanguage',
- ];
+const STORED_STATE = [
+ 'dockedSidebar',
+ 'selectedTheme',
+ 'selectedLanguage',
+];
- class HaPrefStorage extends PolymerElement {
- static get properties() {
- return {
- hass: Object,
- storage: {
- type: Object,
- value: window.localStorage || {},
- },
- };
- }
+class HaPrefStorage extends PolymerElement {
+ static get properties() {
+ return {
+ hass: Object,
+ storage: {
+ type: Object,
+ value: window.localStorage || {},
+ },
+ };
+ }
- storeState() {
- if (!this.hass) return;
-
- try {
- for (var i = 0; i < STORED_STATE.length; i++) {
- var key = STORED_STATE[i];
- var value = this.hass[key];
- this.storage[key] = JSON.stringify(value === undefined ? null : value);
- }
- } catch (err) {
- // Safari throws exception in private mode
- }
- }
-
- getStoredState() {
- var state = {};
+ storeState() {
+ if (!this.hass) return;
+ try {
for (var i = 0; i < STORED_STATE.length; i++) {
var key = STORED_STATE[i];
- if (key in this.storage) {
- state[key] = JSON.parse(this.storage[key]);
- }
+ var value = this.hass[key];
+ this.storage[key] = JSON.stringify(value === undefined ? null : value);
}
-
- return state;
+ } catch (err) {
+ // Safari throws exception in private mode
}
}
- customElements.define('ha-pref-storage', HaPrefStorage);
+
+ getStoredState() {
+ var state = {};
+
+ for (var i = 0; i < STORED_STATE.length; i++) {
+ var key = STORED_STATE[i];
+ if (key in this.storage) {
+ state[key] = JSON.parse(this.storage[key]);
+ }
+ }
+
+ return state;
+ }
}
+customElements.define('ha-pref-storage', HaPrefStorage);
diff --git a/src/util/ha-url-sync.js b/src/util/ha-url-sync.js
index e38529fbd5..09455ac18b 100644
--- a/src/util/ha-url-sync.js
+++ b/src/util/ha-url-sync.js
@@ -2,74 +2,72 @@ import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import EventsMixin from '../mixins/events-mixin.js';
-{
- /* eslint-disable no-console */
- const DEBUG = false;
+/* eslint-disable no-console */
+const DEBUG = false;
- /*
- * @appliesMixin EventsMixin
- */
- class HaUrlSync extends EventsMixin(PolymerElement) {
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: 'hassChanged',
- },
- };
- }
+/*
+ * @appliesMixin EventsMixin
+ */
+class HaUrlSync extends EventsMixin(PolymerElement) {
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: 'hassChanged',
+ },
+ };
+ }
- hassChanged(newHass, oldHass) {
- if (this.ignoreNextHassChange) {
- if (DEBUG) console.log('ignore hasschange');
- this.ignoreNextHassChange = false;
- return;
- } else if (!oldHass || oldHass.moreInfoEntityId === newHass.moreInfoEntityId) {
- return;
- }
-
- if (newHass.moreInfoEntityId) {
- if (DEBUG) console.log('pushing state');
- // We keep track of where we opened moreInfo from so that we don't
- // pop the state when we close the modal if the modal has navigated
- // us away.
- this.moreInfoOpenedFromPath = window.location.pathname;
- history.pushState(null, null, window.location.pathname);
- } else if (window.location.pathname === this.moreInfoOpenedFromPath) {
- if (DEBUG) console.log('history back');
- this.ignoreNextPopstate = true;
- history.back();
- }
- }
-
- popstateChangeListener(ev) {
- if (this.ignoreNextPopstate) {
- if (DEBUG) console.log('ignore popstate');
- this.ignoreNextPopstate = false;
- return;
- }
-
- if (DEBUG) console.log('popstate', ev);
-
- if (this.hass.moreInfoEntityId) {
- if (DEBUG) console.log('deselect entity');
- this.ignoreNextHassChange = true;
- this.fire('hass-more-info', { entityId: null });
- }
- }
-
- connectedCallback() {
- super.connectedCallback();
- this.ignoreNextPopstate = false;
+ hassChanged(newHass, oldHass) {
+ if (this.ignoreNextHassChange) {
+ if (DEBUG) console.log('ignore hasschange');
this.ignoreNextHassChange = false;
- this.popstateChangeListener = this.popstateChangeListener.bind(this);
- window.addEventListener('popstate', this.popstateChangeListener);
+ return;
+ } else if (!oldHass || oldHass.moreInfoEntityId === newHass.moreInfoEntityId) {
+ return;
}
- disconnectedCallback() {
- super.disconnectedCallback();
- window.removeEventListener('popstate', this.popstateChangeListener);
+ if (newHass.moreInfoEntityId) {
+ if (DEBUG) console.log('pushing state');
+ // We keep track of where we opened moreInfo from so that we don't
+ // pop the state when we close the modal if the modal has navigated
+ // us away.
+ this.moreInfoOpenedFromPath = window.location.pathname;
+ history.pushState(null, null, window.location.pathname);
+ } else if (window.location.pathname === this.moreInfoOpenedFromPath) {
+ if (DEBUG) console.log('history back');
+ this.ignoreNextPopstate = true;
+ history.back();
}
}
- customElements.define('ha-url-sync', HaUrlSync);
+
+ popstateChangeListener(ev) {
+ if (this.ignoreNextPopstate) {
+ if (DEBUG) console.log('ignore popstate');
+ this.ignoreNextPopstate = false;
+ return;
+ }
+
+ if (DEBUG) console.log('popstate', ev);
+
+ if (this.hass.moreInfoEntityId) {
+ if (DEBUG) console.log('deselect entity');
+ this.ignoreNextHassChange = true;
+ this.fire('hass-more-info', { entityId: null });
+ }
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.ignoreNextPopstate = false;
+ this.ignoreNextHassChange = false;
+ this.popstateChangeListener = this.popstateChangeListener.bind(this);
+ window.addEventListener('popstate', this.popstateChangeListener);
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ window.removeEventListener('popstate', this.popstateChangeListener);
+ }
}
+customElements.define('ha-url-sync', HaUrlSync);