1
0
mirror of https://github.com/home-assistant/frontend.git synced 2025-12-20 02:38:53 +00:00

Initial getting rid of HA-JS (#180)

Getting rid of HA-JS
This commit is contained in:
Paulus Schoutsen
2017-01-29 18:34:45 -08:00
committed by GitHub
parent 2f71369dae
commit a1057681f1
70 changed files with 1965 additions and 1085 deletions

View File

@@ -110,24 +110,26 @@ Polymer({
// result in the entity to be turned on. Since the state is not changing,
// the resync is not called automatic.
callService: function (turnOn) {
var domain;
var stateDomain = window.hassUtil.computeDomain(this.stateObj);
var serviceDomain;
var service;
var currentState;
if (this.stateObj.domain === 'lock') {
domain = 'lock';
if (stateDomain === 'lock') {
serviceDomain = 'lock';
service = turnOn ? 'lock' : 'unlock';
} else if (this.stateObj.domain === 'garage_door') {
domain = 'garage_door';
} else if (stateDomain === 'cover') {
serviceDomain = 'cover';
service = turnOn ? 'open' : 'close';
} else {
domain = 'homeassistant';
serviceDomain = 'homeassistant';
service = turnOn ? 'turn_on' : 'turn_off';
}
currentState = this.stateObj;
this.hass.serviceActions.callService(domain, service,
{ entity_id: this.stateObj.entityId })
this.hass.callService(
serviceDomain, service,
{ entity_id: this.stateObj.entity_id })
.then(function () {
setTimeout(function () {
// If after 2 seconds we have not received a state update

View File

@@ -57,13 +57,11 @@ Polymer({
badgeTap: function (ev) {
ev.stopPropagation();
this.async(function () {
this.hass.moreInfoActions.selectEntity(this.state.entityId);
}, 1);
this.fire('hass-more-info', { entityId: this.state.entity_id });
},
computeClasses: function (state) {
switch (state.domain) {
switch (window.hassUtil.computeDomain(state)) {
case 'binary_sensor':
case 'updater':
return 'blue';
@@ -73,7 +71,7 @@ Polymer({
},
computeValue: function (state) {
switch (state.domain) {
switch (window.hassUtil.computeDomain(state)) {
case 'binary_sensor':
case 'device_tracker':
case 'updater':
@@ -90,7 +88,7 @@ Polymer({
if (state.state === 'unavailable') {
return null;
}
switch (state.domain) {
switch (window.hassUtil.computeDomain(state)) {
case 'alarm_control_panel':
if (state.state === 'pending') {
return 'mdi:clock-fast';
@@ -123,7 +121,7 @@ Polymer({
if (state.state === 'unavailable') {
return 'unavai';
}
switch (state.domain) {
switch (window.hassUtil.computeDomain(state)) {
case 'device_tracker':
return state.state === 'not_home' ? 'Away' : state.state;
case 'alarm_control_panel':
@@ -142,7 +140,7 @@ Polymer({
},
computeDescription: function (state) {
return state.entityDisplay;
return window.hassUtil.computeStateName(state);
},
stateChanged: function () {

View File

@@ -38,7 +38,7 @@
<ha-state-icon
id='icon'
state-obj='[[stateObj]]'
data-domain$='[[stateObj.domain]]'
data-domain$='[[computeDomain(stateObj)]]'
data-state$='[[stateObj.state]]'
></ha-state-icon>
</template>
@@ -55,6 +55,10 @@ Polymer({
},
},
computeDomain: function (stateObj) {
return window.hassUtil.computeDomain(stateObj);
},
/**
* Called when an attribute changes that influences the color of the icon.
*/
@@ -71,9 +75,10 @@ Polymer({
// for domain light, set color of icon to light color if available and it is
// not very white (sum rgb colors < 730)
if (newVal.domain === 'light' && newVal.state === 'on' &&
newVal.attributes.rgb_color &&
newVal.attributes.rgb_color.reduce(function (cur, tot) { return cur + tot; }, 0) < 730) {
if (window.hassUtil.computeDomain(newVal) === 'light' &&
newVal.state === 'on' &&
newVal.attributes.rgb_color &&
newVal.attributes.rgb_color.reduce(function (cur, tot) { return cur + tot; }, 0) < 730) {
this.$.icon.style.color = 'rgb(' + newVal.attributes.rgb_color.join(',') + ')';
} else {
this.$.icon.style.color = null;

View File

@@ -40,7 +40,7 @@
<state-badge state-obj='[[stateObj]]'></state-badge>
<div class='info'>
<div class='name' in-dialog$='[[inDialog]]'>[[stateObj.entityDisplay]]</div>
<div class='name' in-dialog$='[[inDialog]]'>[[computeStateName(stateObj)]]</div>
<template is='dom-if' if='[[inDialog]]'>
<div class='time-ago'>
@@ -71,5 +71,9 @@ Polymer({
type: Boolean,
},
},
computeStateName: function (stateObj) {
return window.hassUtil.computeStateName(stateObj);
}
});
</script>

View File

@@ -88,11 +88,18 @@
weather: 4,
};
// 4 types:
// badges: 0 .. 10
// before groups < 0
// groups: X
// rest: 100
var PRIORITY = {
// before groups < 0
configurator: -20,
persistent_notification: -15,
group: -10,
a: -1,
// badges have priority >= 0
updater: 0,
sun: 1,
device_tracker: 2,
@@ -102,14 +109,40 @@
};
function getPriority(domain) {
return (domain in PRIORITY) ? PRIORITY[domain] : 30;
return (domain in PRIORITY) ? PRIORITY[domain] : 100;
}
function entitySortBy(entity) {
return entity.domain === 'group' ? entity.attributes.order :
entity.entityDisplay.toLowerCase();
function sortPriority(domainA, domainB) {
return domainA.priority - domainB.priority;
}
function entitySortBy(entityA, entityB) {
var nameA = (entityA.attributes.friendly_name ||
entityA.entity_id).toLowerCase();
var nameB = (entityB.attributes.friendly_name ||
entityB.entity_id).toLowerCase();
if (nameA < nameB) {
return -1;
}
if (nameB > nameA) {
return 1;
}
return 0;
}
function iterateDomainSorted(collection, func) {
Object.keys(collection)
.map(function (key) { return collection[key]; })
.sort(sortPriority)
.forEach(function (domain) {
domain.states.sort(entitySortBy);
func(domain);
});
}
var computeDomain = window.hassUtil.computeDomain;
Polymer({
is: 'ha-cards',
@@ -138,6 +171,7 @@
viewVisible: {
type: Boolean,
value: false,
},
cards: {
@@ -160,13 +194,11 @@
if (this.panelVisible && this.viewVisible) {
this.cards = this.computeCards(columns, states, showIntroduction);
}
}.bind(this));
}.bind(this), 10);
},
computeCards: function (columns, states, showIntroduction) {
var hass = this.hass;
var byDomain = states.groupBy(function (entity) { return entity.domain; });
var hasGroup = {};
var cards = {
demo: false,
@@ -174,17 +206,12 @@
columns: [],
};
var entityCount = [];
var expandGroup;
var i;
for (i = 0; i < columns; i++) {
cards.columns.push([]);
entityCount.push(0);
}
function filterGrouped(entities) {
return entities.filter(function (entity) { return !(entity.entityId in hasGroup); });
}
// Find column with < 5 entities, else column with lowest count
function getIndex(size) {
var minIndex = 0;
@@ -206,7 +233,7 @@
cards.columns[getIndex(5)].push({
hass: hass,
cardType: 'introduction',
showHideInstruction: states.size > 0 && !hass.demo,
showHideInstruction: states.size > 0 && !window.HASS_DEMO,
});
}
@@ -223,9 +250,11 @@
size = 0;
entities.forEach(function (entity) {
if (entity.domain in DOMAINS_WITH_CARD) {
var domain = computeDomain(entity);
if (domain in DOMAINS_WITH_CARD) {
owncard.push(entity);
size += DOMAINS_WITH_CARD[entity.domain];
size += DOMAINS_WITH_CARD[domain];
} else {
other.push(entity);
size++;
@@ -249,43 +278,71 @@
owncard.forEach(function (entity) {
cards.columns[curIndex].push({
hass: hass,
cardType: entity.domain,
cardType: computeDomain(entity),
stateObj: entity,
});
});
}
expandGroup = this.hass.util.expandGroup;
var sorted = window.HAWS.splitByGroups(states);
byDomain.keySeq().sortBy(function (domain) { return getPriority(domain); })
.forEach(function (domain) {
var priority;
var badgesColl = {};
var beforeGroupColl = {};
var afterGroupedColl = {};
if (domain === 'a') {
cards.demo = true;
return;
}
Object.keys(sorted.ungrouped).forEach(function (key) {
var state = sorted.ungrouped[key];
var domain = computeDomain(state);
priority = getPriority(domain);
if (priority >= 0 && priority < 10) {
cards.badges.push.apply(
cards.badges, filterGrouped(byDomain.get(domain)).sortBy(
entitySortBy).toArray());
} else if (domain === 'group') {
byDomain.get(domain).sortBy(entitySortBy)
.forEach(function (groupState) {
var entities = expandGroup(groupState, states);
entities.forEach(function (entity) { hasGroup[entity.entityId] = true; });
addEntitiesCard(groupState.entityId, entities.toArray(), groupState);
}
);
} else {
addEntitiesCard(
domain, filterGrouped(byDomain.get(domain)).sortBy(entitySortBy).toArray());
}
if (domain === 'a') {
cards.demo = true;
return;
}
);
var priority = getPriority(domain);
var coll;
if (priority < 0) {
coll = beforeGroupColl;
} else if (priority < 10) {
coll = badgesColl;
} else {
coll = afterGroupedColl;
}
if (!(domain in coll)) {
coll[domain] = {
domain: domain,
priority: priority,
states: [],
};
}
coll[domain].states.push(state);
});
iterateDomainSorted(badgesColl, function (domain) {
cards.badges.push.apply(cards.badges, domain.states);
});
iterateDomainSorted(beforeGroupColl, function (domain) {
addEntitiesCard(domain.domain, domain.states);
});
sorted.groups.forEach(function (groupState) {
var entities = window.HAWS.getGroupEntities(states, groupState);
addEntitiesCard(
groupState.entity_id,
Object.keys(entities).map(function (key) {
return entities[key];
}),
groupState
);
});
iterateDomainSorted(afterGroupedColl, function (domain) {
addEntitiesCard(domain.domain, domain.states);
});
// Remove empty columns
cards.columns = cards.columns.filter(function (val) {

View File

@@ -36,8 +36,9 @@ Polymer({
return !narrow && showMenu ? 'invisible' : '';
},
toggleMenu: function () {
this.fire('open-menu');
toggleMenu: function (ev) {
ev.stopPropagation();
this.fire('hass-open-menu');
},
});
</script>

View File

@@ -0,0 +1,134 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-toggle-button/paper-toggle-button.html">
<dom-module id='ha-push-notifications-toggle'>
<template>
<paper-toggle-button
hidden$='[[!pushSupported]]'
disabled='[[loading]]'
on-change='handlePushChange'
checked='[[pushActive]]'
></paper-toggle-button>
</template>
</dom-module>
<script>
Polymer({
is: 'ha-push-notifications-toggle',
properties: {
hass: { type: Object, value: null },
pushSupported: {
type: Boolean,
readOnly: true,
notify: true,
value: (
'PushManager' in window &&
(document.location.protocol === 'https:' ||
document.location.hostname === 'localhost' ||
document.location.hostname === '127.0.0.1')
)
},
pushActive: {
type: Boolean,
value: 'Notification' in window && Notification.permission === 'granted'
},
loading: {
type: Boolean,
value: true,
}
},
attached: function () {
if (!this.pushSupported) return;
var el = this;
navigator.serviceWorker.ready.then(
function (reg) {
reg.pushManager.getSubscription().then(function (subscription) {
el.loading = false;
el.pushActive = !!subscription;
});
},
function () {
// no service worker.
el._setPushSupported(false);
});
},
handlePushChange: function (ev) {
if (ev.target.checked) {
this.subscribePushNotifications();
} else {
this.unsubscribePushNotifications();
}
},
subscribePushNotifications: function () {
var el = this;
navigator.serviceWorker.ready
.then(function (reg) {
return reg.pushManager.subscribe({ userVisibleOnly: true });
})
.then(
function (sub) {
var browserName;
if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
browserName = 'firefox';
} else {
browserName = 'chrome';
}
return el.hass.callApi('POST', 'notify.html5', {
subscription: sub,
browser: browserName
}).then(function () {
el.pushActive = true;
});
},
function (err) {
var message;
if (err.message && err.message.indexOf('gcm_sender_id') !== -1) {
message = 'Please setup the notify.html5 platform.';
} else {
message = 'Notification registration failed.';
}
/* eslint-disable no-console */
console.error(err);
/* eslint-enable no-console */
el.fire('hass-notification', { message: message });
el.pushActive = false;
}
);
},
unsubscribePushNotifications: function () {
var el = this;
navigator.serviceWorker.ready
.then(function (reg) {
return reg.pushManager.getSubscription();
})
.then(function (sub) {
if (!sub) return Promise.resolve();
return el.hass
.callApi('DELETE', 'notify.html5', { subscription: sub })
.then(function () {
sub.unsubscribe();
});
})
.then(function () {
el.pushActive = false;
})
.catch(function (err) {
/* eslint-disable no-console */
console.error('Error in unsub push', err);
/* eslint-enable no-console */
el.fire('hass-notification', {
message: 'Failed unsubscribing for push notifications.'
});
});
}
});
</script>

View File

@@ -3,13 +3,12 @@
<link rel='import' href='../../bower_components/iron-icon/iron-icon.html'>
<link rel='import' href='../../bower_components/paper-menu/paper-menu.html'>
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
<link rel='import' href='../../bower_components/paper-toggle-button/paper-toggle-button.html'>
<link rel='import' href='../../bower_components/paper-item/paper-icon-item.html'>
<link rel='import' href='../../bower_components/paper-icon-button/paper-icon-button.html'>
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel='import' href='../util/hass-behavior.html'>
<link rel='import' href='./ha-push-notifications-toggle.html'>
<dom-module id='ha-sidebar'>
<template>
@@ -98,13 +97,13 @@
<paper-icon-button icon='mdi:chevron-left' hidden$='[[narrow]]' on-tap='toggleMenu'></paper-icon-button>
</app-toolbar>
<paper-menu attr-for-selected='data-panel' selected='[[selected]]' on-iron-select='menuSelect'>
<paper-menu attr-for-selected='data-panel' selected='[[hass.currentPanel]]' on-iron-select='menuSelect'>
<paper-icon-item on-tap='menuClicked' data-panel='states'>
<iron-icon item-icon icon='mdi:apps'></iron-icon>
<span class='item-text'>States</span>
</paper-icon-item>
<template is='dom-repeat' items='[[computePanels(panels)]]'>
<template is='dom-repeat' items='[[panels]]'>
<paper-icon-item on-tap='menuClicked' data-panel$='[[item.url_path]]'>
<iron-icon item-icon icon='[[item.icon]]'></iron-icon>
<span class='item-text'>[[item.title]]</span>
@@ -118,15 +117,15 @@
</paper-menu>
<div>
<template is='dom-if' if='[[supportPush]]'>
<template is='dom-if' if='[[pushSupported]]'>
<div class='divider'></div>
<paper-item class='horizontal layout justified'>
<div class='setting'>Push Notifications</div>
<paper-toggle-button
on-change='handlePushChange'
checked='{{pushToggleChecked}}'
></paper-toggle-button>
<ha-push-notifications-toggle
hass='[[hass]]'
push-supported='{{pushSupported}}'
></ha-push-notifications-toggle>
</paper-item>
</template>
@@ -164,8 +163,6 @@
Polymer({
is: 'ha-sidebar',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
@@ -183,36 +180,14 @@ Polymer({
type: Boolean,
},
selected: {
type: String,
bindNuclear: function (hass) {
return hass.navigationGetters.activePanelName;
},
},
panels: {
type: Array,
bindNuclear: function (hass) {
return [
hass.navigationGetters.panels,
function (res) { return res.toJS(); },
];
},
computed: 'computePanels(hass)',
},
supportPush: {
pushSupported: {
type: Boolean,
value: false,
bindNuclear: function (hass) {
return hass.pushNotificationGetters.isSupported;
},
},
pushToggleChecked: {
type: Boolean,
bindNuclear: function (hass) {
return hass.pushNotificationGetters.isActive;
},
value: true,
},
},
@@ -220,7 +195,8 @@ Polymer({
this._boundUpdateStyles = this.updateStyles.bind(this);
},
computePanels: function (panels) {
computePanels: function (hass) {
var panels = hass.config.panels;
var sortValue = {
map: 1,
logbook: 2,
@@ -280,36 +256,22 @@ Polymer({
},
toggleMenu: function () {
this.fire('close-menu');
this.fire('hass-close-menu');
},
selectPanel: function (newChoice) {
if (newChoice === this.selected) {
if (newChoice === this.hass.currentPanel) {
return;
} else if (newChoice === 'logout') {
this.handleLogOut();
return;
}
this.hass.navigationActions.navigate.apply(null, newChoice.split('/'));
this.fire('hass-navigate', { panel: newChoice });
this.debounce('updateStyles', this._boundUpdateStyles, 1);
},
handlePushChange: function (ev) {
if (ev.target.checked) {
this.hass.pushNotificationActions.subscribePushNotifications()
.then(function (success) {
this.pushToggleChecked = success;
}.bind(this));
} else {
this.hass.pushNotificationActions.unsubscribePushNotifications()
.then(function (success) {
this.pushToggleChecked = !success;
}.bind(this));
}
},
handleLogOut: function () {
this.hass.authActions.logOut();
this.fire('hass-logout');
},
});
</script>

View File

@@ -0,0 +1,39 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
<dom-module id='ha-start-voice-button'>
<template>
<paper-icon-button
icon="mdi:microphone" hidden$='[[!canListen]]'
on-tap="handleListenClick"
></paper-icon-button>
</template>
</dom-module>
<script>
Polymer({
is: 'ha-start-voice-button',
properties: {
hass: {
type: Object,
value: null,
},
canListen: {
type: Boolean,
computed: 'computeCanListen(hass)',
},
},
computeCanListen: function (hass) {
return ('webkitSpeechRecognition' in window &&
window.hassUtil.isComponentLoaded(hass, 'conversation'));
},
handleListenClick: function () {
this.fire('hass-start-voice');
},
});
</script>

View File

@@ -112,7 +112,7 @@
}
startTime = new Date(Math.min.apply(null, deviceStates.map(function (states) {
return states[0].lastChangedAsDate;
return new Date(states[0].last_changed);
})));
endTime = new Date(startTime);
@@ -123,8 +123,8 @@
dataTables = deviceStates.map(function (states) {
var last = states[states.length - 1];
var domain = last.domain;
var name = last.entityDisplay;
var domain = window.hassUtil.computeDomain(last);
var name = window.hassUtil.computeStateName(last);
var data = [];
var dataTable = new window.google.visualization.DataTable();
// array containing [time, value1, value2, etc]
@@ -168,7 +168,9 @@
var curTemp = saveParseFloat(state.attributes.current_temperature);
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
var targetLow = saveParseFloat(state.attributes.target_temp_low);
pushData([state.lastUpdatedAsDate, curTemp, targetHigh, targetLow], noInterpolations);
pushData(
[new Date(state.last_updated), curTemp, targetHigh, targetLow],
noInterpolations);
};
} else {
dataTable.addColumn('number', name + ' target temperature');
@@ -178,7 +180,7 @@
processState = function (state) {
var curTemp = saveParseFloat(state.attributes.current_temperature);
var target = saveParseFloat(state.attributes.temperature);
pushData([state.lastUpdatedAsDate, curTemp, target], noInterpolations);
pushData([new Date(state.last_updated), curTemp, target], noInterpolations);
};
}
@@ -203,7 +205,9 @@
var curTemp = saveParseFloat(state.attributes.current_temperature);
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
var targetLow = saveParseFloat(state.attributes.target_temp_low);
pushData([state.lastUpdatedAsDate, curTemp, targetHigh, targetLow], noInterpolations);
pushData(
[new Date(state.last_updated), curTemp, targetHigh, targetLow],
noInterpolations);
};
} else {
dataTable.addColumn('number', name + ' target temperature');
@@ -213,7 +217,7 @@
processState = function (state) {
var curTemp = saveParseFloat(state.attributes.current_temperature);
var target = saveParseFloat(state.attributes.temperature);
pushData([state.lastUpdatedAsDate, curTemp, target], noInterpolations);
pushData([new Date(state.last_updated), curTemp, target], noInterpolations);
};
}
@@ -226,7 +230,7 @@
states.forEach(function (state) {
var value = saveParseFloat(state.state);
pushData([state.lastChangedAsDate, value], noInterpolations);
pushData([new Date(state.last_changed), value], noInterpolations);
});
}

View File

@@ -73,7 +73,7 @@ Polymer({
startTime = new Date(
stateHistory.reduce(
function (minTime, stateInfo) {
return Math.min(minTime, stateInfo[0].lastChangedAsDate);
return Math.min(minTime, new Date(stateInfo[0].last_changed));
}, new Date()));
// end time is Math.min(curTime, start time + 1 day)
@@ -93,11 +93,11 @@ Polymer({
if (stateInfo.length === 0) return;
entityDisplay = stateInfo[0].entityDisplay;
entityDisplay = window.hassUtil.computeStateName(stateInfo[0]);
stateInfo.forEach(function (state) {
if (prevState !== null && state.state !== prevState) {
newLastChanged = state.lastChangedAsDate;
newLastChanged = new Date(state.last_changed);
addRow(entityDisplay, prevState, prevLastChanged, newLastChanged);
@@ -105,7 +105,7 @@ Polymer({
prevLastChanged = newLastChanged;
} else if (prevState === null) {
prevState = state.state;
prevLastChanged = state.lastChangedAsDate;
prevLastChanged = new Date(state.last_changed);
}
});

View File

@@ -24,30 +24,31 @@
}
</style>
<google-legacy-loader on-api-load="googleApiLoaded"></google-legacy-loader>
<google-legacy-loader on-api-load="_googleApiLoaded"></google-legacy-loader>
<div hidden$="[[!isLoading]]" class='loading-container'>
<paper-spinner active alt='Updating history data'></paper-spinner>
</div>
<template is='dom-if' if='[[_isLoading]]'>
<div class='loading-container'>
<paper-spinner active alt='Updating history data'></paper-spinner>
</div>
</template>
<div class$='[[computeContentClasses(isLoading)]]'>
<template is='dom-if' if='[[computeIsEmpty(stateHistory)]]'>
<template is='dom-if' if='[[!_isLoading]]'>
<template is='dom-if' if='[[_computeIsEmpty(historyData)]]'>
No state history found.
</template>
<state-history-chart-timeline
data='[[groupedStateHistory.timeline]]'
is-single-device='[[isSingleDevice]]'>
data='[[historyData.timeline]]'>
</state-history-chart-timeline>
<template is='dom-repeat' items='[[groupedStateHistory.line]]'>
<template is='dom-repeat' items='[[historyData.line]]'>
<state-history-chart-line
unit='[[item.unit]]'
data='[[item.data]]'
is-single-device='[[isSingleDevice]]'>
is-single-device='[[_computeIsSingleLineChart(historyData)]]'>
</state-history-chart-line>
</template>
</div>
</template>
</template>
</dom-module>
@@ -56,101 +57,48 @@ Polymer({
is: 'state-history-charts',
properties: {
stateHistory: {
historyData: {
type: Object,
value: null,
},
isLoadingData: {
type: Boolean,
value: false,
value: true,
},
apiLoaded: {
_apiLoaded: {
type: Boolean,
value: false,
},
isLoading: {
_isLoading: {
type: Boolean,
computed: 'computeIsLoading(isLoadingData, apiLoaded)',
},
groupedStateHistory: {
type: Object,
computed: 'computeGroupedStateHistory(isLoading, stateHistory)',
},
isSingleDevice: {
type: Boolean,
computed: 'computeIsSingleDevice(stateHistory)',
computed: '_computeIsLoading(isLoadingData, _apiLoaded)',
},
},
computeIsSingleDevice: function (stateHistory) {
return stateHistory && stateHistory.size === 1;
_computeIsSingleLineChart: function (historyData) {
return historyData && historyData.line.length === 1;
},
computeGroupedStateHistory: function (isLoading, stateHistory) {
var lineChartDevices = {};
var timelineDevices = [];
var unitStates;
if (isLoading || !stateHistory) {
return { line: [], timeline: [] };
}
stateHistory.forEach(function (stateInfo) {
var stateWithUnit;
var unit;
if (!stateInfo || stateInfo.size === 0) {
return;
}
stateWithUnit = stateInfo.find(
function (state) { return 'unit_of_measurement' in state.attributes; });
unit = stateWithUnit ?
stateWithUnit.attributes.unit_of_measurement : false;
if (!unit) {
timelineDevices.push(stateInfo.toArray());
} else if (unit in lineChartDevices) {
lineChartDevices[unit].push(stateInfo.toArray());
} else {
lineChartDevices[unit] = [stateInfo.toArray()];
}
});
timelineDevices = timelineDevices.length > 0 && timelineDevices;
unitStates = Object.keys(lineChartDevices).map(
function (unit) {
return { unit: unit, data: lineChartDevices[unit] };
});
return { line: unitStates, timeline: timelineDevices };
},
googleApiLoaded: function () {
_googleApiLoaded: function () {
window.google.load('visualization', '1', {
packages: ['timeline', 'corechart'],
callback: function () {
this.apiLoaded = true;
this._apiLoaded = true;
}.bind(this),
});
},
computeContentClasses: function (isLoading) {
return isLoading ? 'loading' : '';
_computeIsLoading: function (_isLoadingData, _apiLoaded) {
return _isLoadingData || !_apiLoaded;
},
computeIsLoading: function (isLoadingData, apiLoaded) {
return isLoadingData || !apiLoaded;
},
computeIsEmpty: function (stateHistory) {
return stateHistory && stateHistory.size === 0;
_computeIsEmpty: function (historyData) {
return (historyData &&
historyData.timeline.length === 0 &&
historyData.line.length === 0);
},
});
</script>