mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-20 02:38:53 +00:00
@@ -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
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
134
src/components/ha-push-notifications-toggle.html
Normal file
134
src/components/ha-push-notifications-toggle.html
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
39
src/components/ha-start-voice-button.html
Normal file
39
src/components/ha-start-voice-button.html
Normal 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>
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user