Update frontend to use NuclearJS

This commit is contained in:
Paulus Schoutsen 2015-06-22 00:25:56 -07:00
parent 4221eef428
commit 7ef0dec185
34 changed files with 1691 additions and 1542 deletions

View file

@ -102,8 +102,8 @@ def setup(hass, config):
{ {
"auto": True, "auto": True,
ATTR_ENTITY_ID: [ ATTR_ENTITY_ID: [
"device_tracker.Paulus", "device_tracker.paulus",
"device_tracker.Anne_Therese" "device_tracker.anne_therese"
] ]
}) })

View file

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """ """ DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "010d9683fa9d210abd199b3cde4edbc0" VERSION = "18d02dc9820b907ac4159da09cd20c4b"

File diff suppressed because one or more lines are too long

View file

@ -37,7 +37,8 @@
"layout": "Polymer/layout", "layout": "Polymer/layout",
"color-picker-element": "~0.0.3", "color-picker-element": "~0.0.3",
"paper-styles": "polymerelements/paper-styles#^1.0.0", "paper-styles": "polymerelements/paper-styles#^1.0.0",
"paper-date-picker": "vsimonian/paper-date-picker#master" "paper-date-picker": "vsimonian/paper-date-picker#master",
"lodash": "~3.9.3"
}, },
"resolutions": { "resolutions": {
"polymer": "^1.0.0", "polymer": "^1.0.0",

View file

@ -26,7 +26,7 @@
<script> <script>
(function(){ (function(){
var uiActions = window.hass.uiActions; var moreInfoActions = window.hass.moreInfoActions;
Polymer({ Polymer({
is: 'state-card', is: 'state-card',
@ -44,7 +44,7 @@
cardTapped: function(ev) { cardTapped: function(ev) {
ev.stopPropagation(); ev.stopPropagation();
this.debounce('show-more-info-dialog', function() { this.debounce('show-more-info-dialog', function() {
uiActions.showMoreInfoDialog(this.stateObj.entityId); moreInfoActions.selectEntity(this.stateObj.entityId);
}.bind(this), 100); }.bind(this), 100);
}, },
}); });

View file

@ -20,7 +20,7 @@
<template> <template>
<ul> <ul>
<template is='dom-repeat' items='[[entities]]' as='entity'> <template is='dom-repeat' items='[[entities]]' as='entity'>
<li><a href='#' on-click='entitySelected'>[[entity]]</a></li> <li><a href='#' on-click='entitySelected'>[[entity.entityId]]</a></li>
</template> </template>
</ul> </ul>
</template> </template>
@ -28,25 +28,30 @@
<script> <script>
(function() { (function() {
var entityGetters = window.hass.entityGetters;
Polymer({ Polymer({
is: 'entity-list', is: 'entity-list',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
entities: { entities: {
type: Array, type: Array,
value: [], bindNuclear: [
entityGetters.entityMap,
function(map) {
return map.valueSeq().
sortBy(function(entity) { return entity.entityId; })
.toArray();
},
],
}, },
}, },
stateStoreChanged: function(stateStore) {
this.entities = stateStore.entityIDs.toArray();
},
entitySelected: function(ev) { entitySelected: function(ev) {
ev.preventDefault(); ev.preventDefault();
this.fire('entity-selected', {entityId: ev.model.entity}); this.fire('entity-selected', {entityId: ev.model.entity.entityId});
}, },
}); });
})(); })();

View file

@ -31,22 +31,27 @@
<script> <script>
(function() { (function() {
var eventGetters = window.hass.eventGetters;
Polymer({ Polymer({
is: 'events-list', is: 'events-list',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
events: { events: {
type: Array, type: Array,
value: [], bindNuclear: [
eventGetters.entityMap,
function(map) {
return map.valueSeq()
.sortBy(function(event) { return event.event; })
.toArray();
}
],
}, },
}, },
eventStoreChanged: function(eventStore) {
this.events = eventStore.all.toArray();
},
eventSelected: function(ev) { eventSelected: function(ev) {
ev.preventDefault(); ev.preventDefault();
this.fire('event-selected', {eventType: ev.model.event.event}); this.fire('event-selected', {eventType: ev.model.event.event});

View file

@ -54,14 +54,14 @@
<script> <script>
(function() { (function() {
var uiActions = window.hass.uiActions; var moreInfoActions = window.hass.moreInfoActions;
Polymer({ Polymer({
is: 'logbook-entry', is: 'logbook-entry',
entityClicked: function(ev) { entityClicked: function(ev) {
ev.preventDefault(); ev.preventDefault();
uiActions.showMoreInfoDialog(this.entryObj.entityId); moreInfoActions.selectEntity(this.entryObj.entityId);
} }
}); });

View file

@ -23,10 +23,10 @@
<template> <template>
<ul> <ul>
<template is='dom-repeat' items="[[domains]]" as="domain"> <template is='dom-repeat' items="[[serviceDomains]]" as="domain">
<template is='dom-repeat' items="[[computeServices(domain)]]" as="service"> <template is='dom-repeat' items="[[domain.services]]" as="service">
<li><a href='#' on-click='serviceClicked'> <li><a href='#' on-click='serviceClicked'>
<span>[[domain]]</span>/<span>[[service]]</span> <span>[[domain.domain]]</span>/<span>[[service]]</span>
</a></li> </a></li>
</template> </template>
</template> </template>
@ -36,19 +36,24 @@
<script> <script>
(function() { (function() {
var serviceGetters = window.hass.serviceGetters;
Polymer({ Polymer({
is: 'services-list', is: 'services-list',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
domains: { serviceDomains: {
type: Array, type: Array,
value: [], bindNuclear: [
}, serviceGetters.entityMap,
function(map) {
services: { return map.valueSeq()
type: Object, .sortBy(function(domain) { return domain.domain; })
.toJS();
},
],
}, },
}, },
@ -56,15 +61,10 @@
return this.services.get(domain).toArray(); return this.services.get(domain).toArray();
}, },
serviceStoreChanged: function(serviceStore) {
this.services = serviceStore.all;
this.domains = this.services.keySeq().sort().toArray();
},
serviceClicked: function(ev) { serviceClicked: function(ev) {
ev.preventDefault(); ev.preventDefault();
this.fire( this.fire(
'service-selected', {domain: ev.model.domain, service: ev.model.service}); 'service-selected', {domain: ev.model.domain.domain, service: ev.model.service});
}, },
}); });
})(); })();

View file

@ -1,5 +1,7 @@
<link rel="import" href="../bower_components/polymer/polymer.html"> <link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../resources/lodash.html">
<script> <script>
(function() { (function() {
Polymer({ Polymer({

View file

@ -39,12 +39,11 @@
if (!this.isAttached) { if (!this.isAttached) {
return; return;
} }
var root = Polymer.dom(this); var root = Polymer.dom(this);
var stateHistory = this.data; var stateHistory = this.data;
while (root.lastChild) { while (root.node.lastChild) {
root.removeChild(root.lastChild); root.node.removeChild(root.node.lastChild);
} }
if (!stateHistory || stateHistory.length === 0) { if (!stateHistory || stateHistory.length === 0) {
@ -64,11 +63,12 @@
dataTable.addRow([entityDisplay, stateStr, start, end]); dataTable.addRow([entityDisplay, stateStr, start, end]);
}; };
var startTime = new Date(stateHistory.map(function(stateInfo) { var startTime = new Date(
return stateInfo[0].lastChangedAsDate; stateHistory.reduce(function(minTime, stateInfo) {
}).reduce(function(prev, cur) { return Math.min(
return Math.min(prev, cur); minTime, stateInfo[0].lastChangedAsDate);
}, new Date())); }, new Date())
);
// end time is Math.min(curTime, start time + 1 day) // end time is Math.min(curTime, start time + 1 day)
var endTime = new Date(startTime); var endTime = new Date(startTime);

View file

@ -34,8 +34,9 @@
No state history found. No state history found.
</template> </template>
<state-history-chart-timeline data='[[groupedStateHistory.timeline]]' <state-history-chart-timeline
is-single-device='[[isSingleDevice]]'> data='[[groupedStateHistory.timeline]]'
is-single-device='[[isSingleDevice]]'>
</state-history-chart-timeline> </state-history-chart-timeline>
<template is='dom-repeat' items='[[groupedStateHistory.line]]'> <template is='dom-repeat' items='[[groupedStateHistory.line]]'>
@ -84,36 +85,35 @@
}, },
computeIsSingleDevice: function(stateHistory) { computeIsSingleDevice: function(stateHistory) {
return stateHistory && stateHistory.length == 1; return stateHistory && stateHistory.size == 1;
}, },
computeGroupedStateHistory: function(isLoading, stateHistory) { computeGroupedStateHistory: function(isLoading, stateHistory) {
if (isLoading || !stateHistory) {
return {line: [], timeline: []};
}
var lineChartDevices = {}; var lineChartDevices = {};
var timelineDevices = []; var timelineDevices = [];
if (isLoading || !stateHistory) {
return {line: unitStates, timeline: timelineDevices};
}
stateHistory.forEach(function(stateInfo) { stateHistory.forEach(function(stateInfo) {
if (!stateInfo || stateInfo.length === 0) { if (!stateInfo || stateInfo.size === 0) {
return; return;
} }
var unit; var stateWithUnit = stateInfo.find(function(state) {
return 'unit_of_measurement' in state.attributes;
});
for (var i = 0; i < stateInfo.length && !unit; i++) { var unit = stateWithUnit ?
unit = stateInfo[i].attributes.unit_of_measurement; stateWithUnit.attributes.unit_of_measurement : false;
}
if (unit) { if (!unit) {
if (!(unit in lineChartDevices)) { timelineDevices.push(stateInfo.toArray());
lineChartDevices[unit] = [stateInfo]; } else if(unit in lineChartDevices) {
} else { lineChartDevices[unit].push(stateInfo.toArray());
lineChartDevices[unit].push(stateInfo);
}
} else { } else {
timelineDevices.push(stateInfo); lineChartDevices[unit] = [stateInfo.toArray()];
} }
}); });
@ -143,7 +143,7 @@
}, },
computeIsEmpty: function(stateHistory) { computeIsEmpty: function(stateHistory) {
return stateHistory && stateHistory.length === 0; return stateHistory && stateHistory.size === 0;
}, },
extractUnit: function(arr) { extractUnit: function(arr) {

View file

@ -17,42 +17,37 @@
} }
</style> </style>
<template> <template>
<iron-icon icon="warning" hidden$="{{!hasError}}"></iron-icon> <iron-icon icon="warning" hidden$="[[!hasError]]"></iron-icon>
<paper-toggle-button id="toggle" on-change='toggleChanged' hidden$="{{hasError}}"></paper-toggle-button> <paper-toggle-button id="toggle" on-change='toggleChanged' checked$='[[isStreaming]]' hidden$="[[hasError]]"></paper-toggle-button>
</template> </template>
</dom-module> </dom-module>
<script> <script>
var streamGetters = window.hass.streamGetters;
var streamActions = window.hass.streamActions; var streamActions = window.hass.streamActions;
var authStore = window.hass.authStore;
Polymer({ Polymer({
is: 'stream-status', is: 'stream-status',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
isStreaming: { isStreaming: {
type: Boolean, type: Boolean,
value: false, bindNuclear: streamGetters.isStreamingEvents,
}, },
hasError: { hasError: {
type: Boolean, type: Boolean,
value: false, bindNuclear: streamGetters.hasStreamingEventsError,
}, },
}, },
streamStoreChanged: function(streamStore) { toggleChanged: function() {
this.hasError = streamStore.hasError;
this.$.toggle.checked = this.isStreaming = streamStore.isStreaming;
},
toggleChanged: function(ev) {
if (this.isStreaming) { if (this.isStreaming) {
streamActions.stop(); streamActions.stop();
} else { } else {
streamActions.start(authStore.authToken); streamActions.start();
} }
}, },
}); });

View file

@ -49,36 +49,51 @@
<script> <script>
(function() { (function() {
var stateStore = window.hass.stateStore;
var stateHistoryStore = window.hass.stateHistoryStore; var configGetters = window.hass.configGetters;
var stateHistoryActions = window.hass.stateHistoryActions; var entityHistoryGetters = window.hass.entityHistoryGetters;
var entityHistoryActions = window.hass.entityHistoryActions;
var moreInfoGetters = window.hass.moreInfoGetters;
var moreInfoActions = window.hass.moreInfoActions;
Polymer({ Polymer({
is: 'more-info-dialog', is: 'more-info-dialog',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
entityId: {
type: String,
},
stateObj: { stateObj: {
type: Object, type: Object,
bindNuclear: moreInfoGetters.currentEntity,
observer: 'fetchHistoryData',
}, },
stateHistory: { stateHistory: {
type: Object, type: Object,
bindNuclear: [
moreInfoGetters.currentEntityHistory,
function(history) {
return history ? [history] : false;
},
],
}, },
isLoadingHistoryData: { isLoadingHistoryData: {
type: Boolean, type: Boolean,
value: false, bindNuclear: entityHistoryGetters.isLoadingEntityHistory,
}, },
hasHistoryComponent: { hasHistoryComponent: {
type: Boolean, type: Boolean,
value: false, bindNuclear: configGetters.isComponentLoaded('history'),
observer: 'fetchHistoryData',
},
shouldFetchHistory: {
type: Boolean,
bindNuclear: moreInfoGetters.isCurrentEntityHistoryStale,
observer: 'fetchHistoryData',
}, },
dialogOpen: { dialogOpen: {
@ -92,31 +107,10 @@
'iron-overlay-closed': 'onIronOverlayClosed' 'iron-overlay-closed': 'onIronOverlayClosed'
}, },
componentStoreChanged: function(componentStore) { fetchHistoryData: function(newVal) {
this.hasHistoryComponent = componentStore.isLoaded('history'); if (this.stateObj && this.hasHistoryComponent &&
}, this.shouldFetchHistory) {
entityHistoryActions.fetchRecent(this.stateObj.entityId);
stateStoreChanged: function() {
var newState = this.entityId ? stateStore.get(this.entityId) : null;
if (newState !== this.stateObj) {
this.stateObj = newState;
}
},
stateHistoryStoreChanged: function() {
var newHistory;
if (this.hasHistoryComponent && this.entityId) {
newHistory = [stateHistoryStore.get(this.entityId)];
} else {
newHistory = null;
}
this.isLoadingHistoryData = false;
if (newHistory !== this.stateHistory) {
this.stateHistory = newHistory;
} }
}, },
@ -126,30 +120,13 @@
onIronOverlayClosed: function() { onIronOverlayClosed: function() {
this.dialogOpen = false; this.dialogOpen = false;
moreInfoActions.deselectEntity();
}, },
changeEntityId: function(entityId) { show: function() {
if (entityId == this.entityId) {
return;
}
this.entityId = entityId;
this.stateStoreChanged();
this.stateHistoryStoreChanged();
if (this.hasHistoryComponent && stateHistoryStore.shouldFetchEntity(entityId)) {
this.isLoadingHistoryData = true;
stateHistoryActions.fetch(entityId);
}
},
show: function(entityId) {
this.changeEntityId(entityId);
this.debounce('showDialogAfterRender', function() { this.debounce('showDialogAfterRender', function() {
this.$.dialog.toggle(); this.$.dialog.open();
}.bind(this)); }.bind(this), 1);
}, },
}); });
})(); })();

@ -1 +1 @@
Subproject commit 5a7165b272fe2ed3e1b1432e2e621c3b971cc4bf Subproject commit 53941ad076fa5d453370cb6922cf6770202dc76e

View file

@ -21,9 +21,8 @@
} }
</style> </style>
<home-assistant-icons></home-assistant-icons>
<template> <template>
<home-assistant-icons></home-assistant-icons>
<template is='dom-if' if='[[!loaded]]'> <template is='dom-if' if='[[!loaded]]'>
<login-form></login-form> <login-form></login-form>
</template> </template>
@ -37,10 +36,10 @@
<script> <script>
(function() { (function() {
var uiActions = window.hass.uiActions;
var storeListenerMixIn = window.hass.storeListenerMixIn, var authGetters = window.hass.authGetters;
uiActions = window.hass.uiActions, var syncGetters = window.hass.syncGetters;
preferenceStore = window.hass.preferenceStore; var uiPreferences = window.hass.uiPreferences;
Polymer({ Polymer({
is: 'home-assistant', is: 'home-assistant',
@ -49,15 +48,24 @@
auth: null, auth: null,
}, },
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
auth: {
type: String,
},
loaded: { loaded: {
type: Boolean, type: Boolean,
value: false, value: false,
observer: 'loadedChanged',
bindNuclear: syncGetters.isDataLoaded,
}, },
}, },
loadedChanged: function(newVal, oldVal) {
console.log("Loaded changed", newVal);
},
ready: function() { ready: function() {
// remove the HTML init message // remove the HTML init message
document.getElementById('init').remove(); document.getElementById('init').remove();
@ -65,13 +73,11 @@
// if auth was given, tell the backend // if auth was given, tell the backend
if(this.auth) { if(this.auth) {
uiActions.validateAuth(this.auth, false); uiActions.validateAuth(this.auth, false);
} else if (preferenceStore.hasAuthToken) { } else if (uiPreferences.authToken) {
uiActions.validateAuth(preferenceStore.authToken, false); uiActions.validateAuth(uiPreferences.authToken, false);
} }
},
syncStoreChanged: function(syncStore) { uiPreferences.startSync();
this.loaded = syncStore.initialLoadDone;
}, },
}); });

View file

@ -76,7 +76,7 @@
<iron-icon item-icon icon='apps'></iron-icon> States <iron-icon item-icon icon='apps'></iron-icon> States
</paper-icon-item> </paper-icon-item>
<template is='dom-repeat' items='{{activeFilters}}'> <template is='dom-repeat' items='{{possibleFilters}}'>
<paper-icon-item data-panel$='[[filterType(item)]]'> <paper-icon-item data-panel$='[[filterType(item)]]'>
<iron-icon item-icon icon='[[filterIcon(item)]]'></iron-icon> <iron-icon item-icon icon='[[filterIcon(item)]]'></iron-icon>
<span>[[filterName(item)]]</span> <span>[[filterName(item)]]</span>
@ -110,25 +110,22 @@
<div class='text label divider'>Developer Tools</div> <div class='text label divider'>Developer Tools</div>
<div class='dev-tools layout horizontal justified'> <div class='dev-tools layout horizontal justified'>
<paper-icon-button <paper-icon-button
icon='settings-remote' data-panel$='[[selectedDevService]]' icon='settings-remote' data-panel='devService'
on-click='handleDevClick'></paper-icon-button> on-click='handleDevClick'></paper-icon-button>
<paper-icon-button <paper-icon-button
icon='settings-ethernet' data-panel$='[[selectedDevState]]' icon='settings-ethernet' data-panel='devState'
on-click='handleDevClick'></paper-icon-button> on-click='handleDevClick'></paper-icon-button>
<paper-icon-button <paper-icon-button
icon='settings-input-antenna' data-panel$='[[selectedDevEvent]]' icon='settings-input-antenna' data-panel='devEvent'
on-click='handleDevClick'></paper-icon-button> on-click='handleDevClick'></paper-icon-button>
</div> </div>
</paper-menu> </paper-menu>
</paper-header-panel> </paper-header-panel>
<template is='dom-if' if='[[!hideStates]]'> <template is='dom-if' if='[[isSelectedStates]]'>
<partial-states <partial-states main narrow='[[narrow]]'>
main narrow='[[narrow]]'
filter='[[stateFilter]]'>
</partial-states> </partial-states>
</template> </template>
<template is='dom-if' if='[[isSelectedLogbook]]'> <template is='dom-if' if='[[isSelectedLogbook]]'>
<partial-logbook main narrow='[[narrow]]'></partial-logbook> <partial-logbook main narrow='[[narrow]]'></partial-logbook>
</template> </template>
@ -151,117 +148,86 @@
<script> <script>
(function() { (function() {
var configGetters = window.hass.configGetters;
var entityGetters = window.hass.entityGetters;
var navigationGetters = window.hass.navigationGetters;
var authActions = window.hass.authActions; var authActions = window.hass.authActions;
var navigationActions = window.hass.navigationActions;
var uiUtil = window.hass.uiUtil; var uiUtil = window.hass.uiUtil;
var uiConstants = window.hass.uiConstants; var entityDomainFilters = window.hass.util.entityDomainFilters;
Polymer({ Polymer({
is: 'home-assistant-main', is: 'home-assistant-main',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
selected: {
type: String,
value: 'states',
},
stateFilter: {
type: String,
value: null,
},
narrow: { narrow: {
type: Boolean, type: Boolean,
}, },
activeFilters: { selected: {
type: String,
bindNuclear: [
navigationGetters.activePane,
navigationGetters.activeFilter,
function(pane, filter) {
return filter ? pane + '/' + filter : pane;
},
],
observer: 'selectedChanged',
},
possibleFilters: {
type: Array, type: Array,
value: [], value: [],
bindNuclear: [
navigationGetters.possibleEntityDomainFilters,
function(domains) { return domains.toArray(); }
],
}, },
hasHistoryComponent: { hasHistoryComponent: {
type: Boolean, type: Boolean,
value: false, bindNuclear: configGetters.isComponentLoaded('history'),
}, },
hasLogbookComponent: { hasLogbookComponent: {
type: Boolean, type: Boolean,
value: false, bindNuclear: configGetters.isComponentLoaded('logbook'),
}, },
isStreaming: { isSelectedStates: {
type: Boolean, type: Boolean,
value: false, bindNuclear: navigationGetters.isActivePane('states'),
},
hasStreamError: {
type: Boolean,
value: false,
},
hideStates: {
type: Boolean,
value: false,
},
selectedHistory: {
type: String,
value: 'history',
readOnly: true,
}, },
isSelectedHistory: { isSelectedHistory: {
type: Boolean, type: Boolean,
computed: 'computeIsSelected(selected, selectedHistory)', bindNuclear: navigationGetters.isActivePane('history'),
},
selectedLogbook: {
type: String,
value: 'logbook',
readOnly: true,
}, },
isSelectedLogbook: { isSelectedLogbook: {
type: Boolean, type: Boolean,
computed: 'computeIsSelected(selected, selectedLogbook)', bindNuclear: navigationGetters.isActivePane('logbook'),
},
selectedDevEvent: {
type: String,
value: 'devEvent',
readOnly: true,
}, },
isSelectedDevEvent: { isSelectedDevEvent: {
type: Boolean, type: Boolean,
computed: 'computeIsSelected(selected, selectedDevEvent)', bindNuclear: navigationGetters.isActivePane('devEvent'),
},
selectedDevState: {
type: String,
value: 'devState',
readOnly: true,
}, },
isSelectedDevState: { isSelectedDevState: {
type: Boolean, type: Boolean,
computed: 'computeIsSelected(selected, selectedDevState)', bindNuclear: navigationGetters.isActivePane('devState'),
},
selectedDevService: {
type: String,
value: 'devService',
readOnly: true,
}, },
isSelectedDevService: { isSelectedDevService: {
type: Boolean, type: Boolean,
computed: 'computeIsSelected(selected, selectedDevService)', bindNuclear: navigationGetters.isActivePane('devService'),
}, },
}, },
listeners: { listeners: {
@ -269,17 +235,6 @@
'open-menu': 'openDrawer', 'open-menu': 'openDrawer',
}, },
stateStoreChanged: function(stateStore) {
this.activeFilters = stateStore.domains.filter(function(domain) {
return domain in uiConstants.STATE_FILTERS;
}).toArray();
},
componentStoreChanged: function(componentStore) {
this.hasHistoryComponent = componentStore.isLoaded('history');
this.hasLogbookComponent = componentStore.isLoaded('logbook');
},
menuSelect: function(ev, detail, sender) { menuSelect: function(ev, detail, sender) {
this.selectPanel(this.$.menu.selected); this.selectPanel(this.$.menu.selected);
}, },
@ -299,15 +254,9 @@
} }
this.closeDrawer(); this.closeDrawer();
this.selected = newChoice;
if (newChoice.substr(0, 7) === 'states_') { navigationActions.navigate.apply(
this.hideStates = false; null, newChoice.split('/'));
this.stateFilter = newChoice.substr(7);
} else {
this.hideStates = newChoice !== 'states';
this.stateFilter = null;
}
}, },
openDrawer: function() { openDrawer: function() {
@ -322,21 +271,41 @@
authActions.logOut(); authActions.logOut();
}, },
computeIsSelected: function(selected, selectedType) {
return selected === selectedType;
},
filterIcon: function(filter) { filterIcon: function(filter) {
return uiUtil.domainIcon(filter); return uiUtil.domainIcon(filter);
}, },
filterName: function(filter) { filterName: function(filter) {
return uiConstants.STATE_FILTERS[filter]; return entityDomainFilters[filter];
}, },
filterType: function(filter) { filterType: function(filter) {
return 'states_' + filter; return 'states/' + filter;
} },
hashChanged: function(ev) {
var parts = ev.newURL.split('#');
if (parts[1]) {
this.selectPanel(parts[1]);
}
},
selectedChanged: function(newVal) {
window.location.hash = newVal;
},
ready: function() {
this.hashChanged({newURL: window.location.toString()});
},
attached: function() {
this.hashChanged = this.hashChanged.bind(this);
window.addEventListener('hashchange', this.hashChanged);
},
detached: function() {
window.removeEventListener('hashchange', this.hashChanged);
},
}); });
})(); })();
</script> </script>

View file

@ -86,27 +86,29 @@
<script> <script>
(function() { (function() {
var uiActions = window.hass.uiActions; var uiActions = window.hass.uiActions;
var authGetters = window.hass.authGetters;
Polymer({ Polymer({
is: 'login-form', is: 'login-form',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
isValidating: { isValidating: {
type: Boolean, type: Boolean,
value: false, observer: 'isValidatingChanged',
bindNuclear: authGetters.isValidating,
}, },
isInvalid: { isInvalid: {
type: Boolean, type: Boolean,
value: false, bindNuclear: authGetters.isInvalidAttempt,
}, },
errorMessage: { errorMessage: {
type: String, type: String,
value: '', bindNuclear: authGetters.attemptErrorMessage,
} },
}, },
listeners: { listeners: {
@ -114,19 +116,12 @@
'loginButton.click': 'validatePassword', 'loginButton.click': 'validatePassword',
}, },
attached: function() { // attached: function() {
this.focusPassword(); // this.focusPassword();
}, // },
authStoreChanged: function(authStore) { isValidatingChanged: function(newVal) {
this.isValidating = authStore.isValidating; if (!newVal) {
if (authStore.lastAttemptInvalid) {
this.errorMessage = authStore.lastAttemptMessage;
this.isInvalid = true;
}
if (!this.isValidating) {
setTimeout(this.focusPassword.bind(this), 0); setTimeout(this.focusPassword.bind(this), 0);
} }
}, },

View file

@ -80,7 +80,7 @@
return; return;
} }
eventActions.fire(this.eventType, eventData); eventActions.fireEvent(this.eventType, eventData);
}, },
computeFormClasses: function(narrow) { computeFormClasses: function(narrow) {

View file

@ -50,8 +50,9 @@
<script> <script>
(function() { (function() {
var stateStore = window.hass.stateStore; var reactor = window.hass.reactor;
var stateActions = window.hass.stateActions; var entityGetters = window.hass.entityGetters;
var entityActions = window.hass.entityActions;
Polymer({ Polymer({
is: 'partial-dev-set-state', is: 'partial-dev-set-state',
@ -83,7 +84,7 @@
}, },
entitySelected: function(ev) { entitySelected: function(ev) {
var state = stateStore.get(ev.detail.entityId); var state = reactor.evaluate(entityGetters.byId(ev.detail.entityId));
this.entityId = state.entityId; this.entityId = state.entityId;
this.state = state.state; this.state = state.state;
@ -99,7 +100,11 @@
return; return;
} }
stateActions.set(this.entityId, this.state, attr); entityActions.save({
entityId: this.entityId,
state: this.state,
attributes: attr,
});
}, },
computeFormClasses: function(narrow) { computeFormClasses: function(narrow) {

View file

@ -33,7 +33,7 @@
<div class$="[[computeContentClasses(narrow)]]"> <div class$="[[computeContentClasses(narrow)]]">
<paper-input label='Showing entries for' on-click='handleShowDatePicker' <paper-input label='Showing entries for' on-click='handleShowDatePicker'
value='[[computeDateCaption(selectedDate)]]'></paper-input> value='[[selectedDate]]'></paper-input>
<state-history-charts state-history="[[stateHistory]]" <state-history-charts state-history="[[stateHistory]]"
is-loading-data="[[isLoadingData]]"></state-history-charts> is-loading-data="[[isLoadingData]]"></state-history-charts>
@ -43,68 +43,57 @@
</dom-module> </dom-module>
<script> <script>
(function() { (function() {
var stateHistoryActions = window.hass.stateHistoryActions; var entityHistoryGetters = window.hass.entityHistoryGetters;
var stateHistoryStore = window.hass.stateHistoryStore; var entityHistoryActions = window.hass.entityHistoryActions;
var uiActions = window.hass.uiActions; var uiActions = window.hass.uiActions;
function date_to_str(date) {
return date.getFullYear() + '-' + (date.getMonth()+1) + '-' + date.getDate();
}
Polymer({ Polymer({
is: 'partial-history', is: 'partial-history',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
narrow: { narrow: {
type: Boolean, type: Boolean,
}, },
isDataLoaded: {
type: Boolean,
bindNuclear: entityHistoryGetters.hasDataForCurrentDate,
observer: 'isDataLoadedChanged',
},
stateHistory: { stateHistory: {
type: Object, type: Object,
bindNuclear: entityHistoryGetters.entityHistoryForCurrentDate,
}, },
isLoadingData: { isLoadingData: {
type: Boolean, type: Boolean,
value: false, bindNuclear: entityHistoryGetters.isLoadingEntityHistory,
}, },
selectedDate: { selectedDate: {
type: String, type: String,
value: null, value: null,
observer: 'fetchIfNeeded', bindNuclear: entityHistoryGetters.currentDate,
}, },
}, },
stateHistoryStoreChanged: function() { isDataLoadedChanged: function(newVal) {
this.isLoadingData = this.fetchIfNeeded(); if (!newVal) {
this.stateHistory = this.isLoadingData ? entityHistoryActions.fetchSelectedDate();
[] : stateHistoryStore.all(this.selectedDate);
},
computeDateCaption: function(selectedDate) {
return selectedDate || 'today';
},
fetchIfNeeded: function() {
if (stateHistoryStore.shouldFetch(this.selectedDate)) {
this.isLoadingData = true;
stateHistoryActions.fetchAll(this.selectedDate);
return true;
} }
return false;
}, },
handleRefreshClick: function() { handleRefreshClick: function() {
this.isLoadingData = true; entityHistoryActions.fetchSelectedDate();
stateHistoryActions.fetchAll(this.selectedDate);
}, },
handleShowDatePicker: function() { handleShowDatePicker: function() {
uiActions.showDatePicker(function(selectedDate) { uiActions.showDatePicker(
this.selectedDate = date_to_str(selectedDate); entityHistoryActions.changeCurrentDate,
}.bind(this), this.selectedDate); this.selectedDate);
}, },
computeContentClasses: function(narrow) { computeContentClasses: function(narrow) {

View file

@ -28,7 +28,7 @@
<div> <div>
<div class='selected-date-container'> <div class='selected-date-container'>
<paper-input label='Showing entries for' on-click='handleShowDatePicker' <paper-input label='Showing entries for' on-click='handleShowDatePicker'
value='[[computeDateCaption(selectedDate)]]'></paper-input> value='[[selectedDate]]'></paper-input>
<loading-box hidden$='[[!isLoading]]'>Loading logbook entries</loading-box> <loading-box hidden$='[[!isLoading]]'>Loading logbook entries</loading-box>
</div> </div>
@ -40,19 +40,15 @@
<script> <script>
(function() { (function() {
var storeListenerMixIn = window.hass.storeListenerMixIn; var logbookGetters = window.hass.logbookGetters;
var logbookActions = window.hass.logbookActions; var logbookActions = window.hass.logbookActions;
var logbookStore = window.hass.logbookStore;
var uiActions = window.hass.uiActions; var uiActions = window.hass.uiActions;
var dateToStr = window.hass.util.dateToStr;
function date_to_str(date) {
return date.getFullYear() + '-' + (date.getMonth()+1) + '-' + date.getDate();
}
Polymer({ Polymer({
is: 'partial-logbook', is: 'partial-logbook',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
narrow: { narrow: {
@ -62,48 +58,46 @@
selectedDate: { selectedDate: {
type: String, type: String,
value: null, bindNuclear: logbookGetters.currentDate,
observer: 'fetchIfNeeded',
}, },
isLoading: { isLoading: {
type: Boolean, type: Boolean,
value: true, bindNuclear: logbookGetters.isLoadingEntries,
},
isStale: {
type: Boolean,
bindNuclear: logbookGetters.isCurrentStale,
observer: 'isStaleChanged',
}, },
entries: { entries: {
type: Array, type: Array,
value: null, bindNuclear: [
logbookGetters.currentEntries,
function(entries) { return entries.toArray(); },
],
}, },
}, },
logbookStoreChanged: function() { isStaleChanged: function(newVal) {
this.isLoading = this.fetchIfNeeded(); if (newVal) {
var entries = logbookStore.all.toArray(); // isLoading wouldn't update without debounce <_<
this.entries = entries.length > 0 ? entries : false; this.debounce('fetch-logbook-entries', function() {
}, logbookActions.fetchDate(this.selectedDate);
}, 0);
computeDateCaption: function(selectedDate) {
return selectedDate || 'today';
},
fetchIfNeeded: function() {
if (logbookStore.shouldFetch(this.selectedDate)) {
this.isLoading = true;
logbookActions.fetch(this.selectedDate);
return true;
} }
return false;
}, },
handleShowDatePicker: function() { handleShowDatePicker: function() {
uiActions.showDatePicker(function(selectedDate) { uiActions.showDatePicker(
this.selectedDate = date_to_str(selectedDate); logbookActions.changeCurrentDate,
}.bind(this), this.selectedDate); this.selectedDate);
}, },
handleRefresh: function() { handleRefresh: function() {
logbookActions.fetch(this.selectedDate); logbookActions.fetchDate(this.selectedDate);
}, },
}); });
})(); })();

View file

@ -41,12 +41,17 @@
<template> <template>
<partial-base narrow="[[narrow]]"> <partial-base narrow="[[narrow]]">
<span header-title>{{headerTitle}}</span> <span header-title>[[computeHeaderTitle(filter)]]</span>
<span header-buttons> <span header-buttons>
<paper-icon-button icon="refresh" class$="[[computeRefreshButtonClass(isFetching)]]" <paper-icon-button
on-click="handleRefresh" hidden$="[[isStreaming]]"></paper-icon-button> icon="refresh"
<paper-icon-button icon="[[listenButtonIcon]]" hidden$={{!canListen}} class$="[[computeRefreshButtonClass(isFetching)]]"
on-click="handleRefresh" hidden$="[[isStreaming]]"
></paper-icon-button>
<paper-icon-button
icon="[[computeListenButtonIcon(isListening)]]"
hidden$='[[!canListen]]'
on-click="handleListenClick"></paper-icon-button> on-click="handleListenClick"></paper-icon-button>
</span> </span>
@ -75,139 +80,93 @@
<script> <script>
(function(){ (function(){
var configGetters = window.hass.configGetters;
var entityGetters = window.hass.entityGetters;
var navigationGetters = window.hass.navigationGetters;
var voiceGetters = window.hass.voiceGetters;
var streamGetters = window.hass.streamGetters;
var serviceGetters = window.hass.serviceGetters;
var syncGetters = window.hass.syncGetters;
var syncActions = window.hass.syncActions; var syncActions = window.hass.syncActions;
var voiceActions = window.hass.voiceActions; var voiceActions = window.hass.voiceActions;
var stateStore = window.hass.stateStore;
var uiConstants = window.hass.uiConstants; var uiConstants = window.hass.uiConstants;
var entityDomainFilters = window.hass.util.entityDomainFilters;
Polymer({ Polymer({
is: 'partial-states', is: 'partial-states',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
/**
* Title to show in the header
*/
headerTitle: {
type: String,
value: 'States',
},
/**
* If header is to be shown in narrow mode.
*/
narrow: { narrow: {
type: Boolean, type: Boolean,
value: false, value: false,
}, },
filter: {
type: String,
value: null,
observer: 'filterChanged',
},
voiceSupported: {
type: Boolean,
value: voiceActions.isSupported(),
},
isFetching: { isFetching: {
type: Boolean, type: Boolean,
value: false, bindNuclear: syncGetters.isFetching,
}, },
isStreaming: { isStreaming: {
type: Boolean, type: Boolean,
value: false, bindNuclear: streamGetters.isStreamingEvents,
}, },
canListen: { canListen: {
type: Boolean, type: Boolean,
value: false, bindNuclear: [
voiceGetters.isVoiceSupported,
configGetters.isComponentLoaded('conversation'),
function(isVoiceSupported, componentLoaded) {
return isVoiceSupported && componentLoaded;
}
]
}, },
isListening: { isListening: {
type: Boolean, type: Boolean,
value: false, bindNuclear: voiceGetters.isListening,
}, },
isTransmitting: { isTransmitting: {
type: Boolean, type: Boolean,
value: false, bindNuclear: voiceGetters.isTransmitting,
}, },
interimTranscript: { interimTranscript: {
type: String, type: String,
value: '', bindNuclear: voiceGetters.extraInterimTranscript,
}, },
finalTranscript: { finalTranscript: {
type: String, type: String,
value: '', bindNuclear: voiceGetters.finalTranscript,
},
listenButtonIcon: {
type: String,
computed: 'computeListenButtonIcon(isListening)'
}, },
showListenInterface: { showListenInterface: {
type: Boolean, type: Boolean,
computed: 'computeShowListenInterface(isListening,isTransmitting)' bindNuclear: [
} voiceGetters.isListening,
}, voiceGetters.isTransmitting,
function(isListening, isTransmitting) {
return isListening || isTransmitting;
},
],
},
componentStoreChanged: function(componentStore) { states: {
this.canListen = this.voiceSupported && type: Array,
componentStore.isLoaded('conversation'); bindNuclear: [
}, navigationGetters.filteredStates,
// are here so a change to services causes a re-render.
stateStoreChanged: function() { // we need this to decide if we show toggles for states.
this.refreshStates(); serviceGetters.entityMap,
}, function(states) { return states.toArray(); },
],
syncStoreChanged: function(syncStore) { },
this.isFetching = syncStore.isFetching;
},
streamStoreChanged: function(streamStore) {
this.isStreaming = streamStore.isStreaming;
},
voiceStoreChanged: function(voiceStore) {
this.isListening = voiceStore.isListening;
this.isTransmitting = voiceStore.isTransmitting;
this.finalTranscript = voiceStore.finalTranscript;
this.interimTranscript = voiceStore.interimTranscript.slice(
this.finalTranscript.length);
},
filterChanged: function() {
this.refreshStates();
this.headerTitle = uiConstants.STATE_FILTERS[this.filter] || 'States';
},
refreshStates: function() {
var states;
if (this.filter) {
var filter = this.filter;
states = stateStore.all.filter(function(state) {
return state.domain === filter;
});
} else {
// all but the STATE_FILTER keys
states = stateStore.all.filter(function(state) {
return !(state.domain in uiConstants.STATE_FILTERS);
});
}
this.states = states.toArray().filter(
function (el) {return !el.attributes.hidden;});
}, },
handleRefresh: function() { handleRefresh: function() {
@ -222,12 +181,12 @@
} }
}, },
computeListenButtonIcon: function(isListening) { computeHeaderTitle: function(filter) {
return isListening ? 'av:mic-off' : 'av:mic'; return filter ? entityDomainFilters[filter] : 'States';
}, },
computeShowListenInterface: function(isListening,isTransmitting) { computeListenButtonIcon: function(isListening) {
return isListening || isTransmitting; return isListening ? 'av:mic-off' : 'av:mic';
}, },
computeRefreshButtonClass: function(isFetching) { computeRefreshButtonClass: function(isFetching) {

View file

@ -14,33 +14,38 @@
<script> <script>
(function() { (function() {
var uiConstants = window.hass.uiConstants, var moreInfoGetters = window.hass.moreInfoGetters;
dispatcher = window.hass.dispatcher; var uiActions = window.hass.uiActions;
Polymer({ Polymer({
is: 'modal-manager', is: 'modal-manager',
behaviors: [nuclearObserver],
properties: { properties: {
moreInfoEntityId: {
type: String,
observer: 'moreInfoEntityIdChanged',
bindNuclear: moreInfoGetters.currentEntityId,
},
datePickerCallback: { datePickerCallback: {
type: Function, type: Function,
value: null, value: null,
}, },
}, },
ready: function() { moreInfoEntityIdChanged: function(newVal) {
dispatcher.register(function(payload) { if (newVal) {
switch (payload.actionType) { this.$.moreInfoDialog.show(newVal);
case uiConstants.ACTION_SHOW_DIALOG_MORE_INFO: }
this.$.moreInfoDialog.show(payload.entityId); },
break;
case uiConstants.ACTION_SHOW_DATE_PICKER: ready: function() {
this.datePickerCallback = payload.dateSelectedCallback; uiActions.showDatePicker = function(dateSelectedCallback, startDate) {
this.$.date = payload.date; this.datePickerCallback = dateSelectedCallback;
this.$.datePicker.toggle(); this.$.date = startDate;
break; this.$.datePicker.toggle();
} }.bind(this);
}.bind(this));
}, },
datePickerValueChanged: function(ev) { datePickerValueChanged: function(ev) {

View file

@ -15,33 +15,26 @@
<script> <script>
(function() { (function() {
var notificationGetters = window.hass.notificationGetters;
Polymer({ Polymer({
is: 'notification-manager', is: 'notification-manager',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
text: { text: {
type: String, type: String,
value: '', bindNuclear: notificationGetters.lastNotificationMessage,
}, observer: 'showNotification',
lastId: {
type: Number,
}, },
}, },
notificationStoreChanged: function(notificationStore) { showNotification: function(newText) {
if (notificationStore.hasNewNotifications(this.lastId)) { if (newText) {
var notification = notificationStore.lastNotification;
this.lastId = notification.id;
this.text = notification.message;
this.$.toast.show(); this.$.toast.show();
} }
}, }
}); });
})(); })();
</script> </script>

View file

@ -0,0 +1,40 @@
<link rel="import" href="../bower_components/polymer/polymer.html">
<script>
(function() {
var authGetters = window.hass.authGetters;
var streamGetters = window.hass.streamGetters;
Polymer({
is: 'preferences-manager',
behaviors: [nuclearObserver],
properties: {
authToken: {
type: String,
bindNuclear: authGetters.currentAuthToken,
observer: 'updateStorage',
},
useStreaming: {
type: String,
bindNuclear: ,
observer: 'updateStorage',
},
},
updateStorage: function() {
if (!('localStorage' in window)) {
return;
}
var storage = localStorage;
Object.keys(this.properties).forEach(function(prop) {
storage[prop] = this.prop;
});
},
});
})();
</script>

View file

@ -52,13 +52,14 @@
<script> <script>
(function() { (function() {
var streamGetters = window.hass.streamGetters;
var syncActions = window.hass.syncActions; var syncActions = window.hass.syncActions;
var serviceActions = window.hass.serviceActions; var serviceActions = window.hass.serviceActions;
Polymer({ Polymer({
is: 'more-info-configurator', is: 'more-info-configurator',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
stateObj: { stateObj: {
@ -72,7 +73,7 @@
isStreaming: { isStreaming: {
type: Boolean, type: Boolean,
value: false, bindNuclear: streamGetters.isStreamingEvents,
}, },
isConfigurable: { isConfigurable: {
@ -99,10 +100,6 @@
return stateObj.attributes.submit_caption || 'Set configuration'; return stateObj.attributes.submit_caption || 'Set configuration';
}, },
streamStoreChanged: function(streamStore) {
this.isStreaming = streamStore.isStreaming;
},
submitClicked: function() { submitClicked: function() {
this.isConfiguring = true; this.isConfiguring = true;

View file

@ -23,28 +23,36 @@
<script> <script>
(function() { (function() {
var stateStore = window.hass.stateStore; var entityGetters = window.hass.entityGetters;
var moreInfoGetters = window.hass.moreInfoGetters;
Polymer({ Polymer({
is: 'more-info-group', is: 'more-info-group',
behaviors: [StoreListenerBehavior], behaviors: [nuclearObserver],
properties: { properties: {
stateObj: { stateObj: {
type: Object, type: Object,
observer: 'updateStates',
}, },
states: { states: {
type: Array, type: Array,
value: [], bindNuclear: [
moreInfoGetters.currentEntity,
entityGetters.entityMap,
function(currentEntity, entities) {
// weird bug??
if (!currentEntity) {
return;
}
return currentEntity.attributes.entity_id.map(
entities.get.bind(entities));
},
],
}, },
}, },
stateStoreChanged: function() {
this.updateStates();
},
updateStates: function() { updateStates: function() {
this.states = this.stateObj && this.stateObj.attributes.entity_id ? this.states = this.stateObj && this.stateObj.attributes.entity_id ?

View file

@ -41,7 +41,7 @@
<script> <script>
(function() { (function() {
var constants = window.hass.constants; var temperatureUnits = window.hass.util.temperatureUnits;
var serviceActions = window.hass.serviceActions; var serviceActions = window.hass.serviceActions;
var uiUtil = window.hass.uiUtil; var uiUtil = window.hass.uiUtil;
var ATTRIBUTE_CLASSES = ['away_mode']; var ATTRIBUTE_CLASSES = ['away_mode'];
@ -76,7 +76,8 @@
this.targetTemperatureSliderValue = this.stateObj.state; this.targetTemperatureSliderValue = this.stateObj.state;
this.awayToggleChecked = this.stateObj.attributes.away_mode == 'on'; this.awayToggleChecked = this.stateObj.attributes.away_mode == 'on';
if (this.stateObj.attributes.unit_of_measurement === constants.UNIT_TEMP_F) { if (this.stateObj.attributes.unit_of_measurement ===
temperatureUnits.UNIT_TEMP_F) {
this.tempMin = 45; this.tempMin = 45;
this.tempMax = 95; this.tempMax = 95;
} else { } else {

View file

@ -0,0 +1,51 @@
<script>
(function() {
var reactor = window.hass.reactor;
var authGetters = window.hass.authGetters;
var streamGetters = window.hass.streamGetters;
var storage = 'localStorage' in window ? localStorage : {};
var observe = {
authToken: {
bindNuclear: [
authGetters.currentAuthToken,
authGetters.rememberAuth,
function(authToken, rememberAuth) {
return rememberAuth ? authToken : null;
},
],
defaultValue: null,
},
useStreaming: {
bindNuclear: streamGetters.useStreaming,
defaultValue: true,
},
};
var uiPreferences = {};
Object.keys(observe).forEach(function(prop) {
if (!(prop in storage)) {
storage[prop] = observe[prop].defaultValue;
}
Object.defineProperty(uiPreferences, prop, {
get: function() { return JSON.parse(storage[prop]); }
});
});
uiPreferences.startSync = function startSync() {
Object.keys(observe).forEach(function(prop) {
var getter = observe[prop].bindNuclear;
var valueChanged = function valueChanged(value) {
storage[prop] = JSON.stringify(value);
};
reactor.observe(getter, valueChanged);
valueChanged(reactor.evaluate(getter));
});
};
window.hass.uiPreferences = uiPreferences;
})();
</script>

View file

@ -1,4 +1,5 @@
<script src="../home-assistant-js/dist/homeassistant.min.js"></script> <script src="../home-assistant-js/dist/homeassistant.min.js"></script>
<link rel="import" href="./ha-preferences.html">
<script> <script>
(function() { (function() {
@ -10,63 +11,16 @@
'sensor', 'sensor',
]; ];
// Add some frontend specific helpers to the models var reactor = window.hass.reactor;
Object.defineProperties(window.hass.stateModel.prototype, { var serviceGetters = window.hass.serviceGetters;
// how to render the card for this state var authActions = window.hass.authActions;
cardType: { var uiPreferences = window.hass.uiPreferences;
get: function() {
console.warn('Deprecated method. Please use hass.uiUtil.stateCardType');
return window.hass.uiUtil.stateCardType(this);
}
},
// how to render the more info of this state
moreInfoType: {
get: function() {
console.warn('Deprecated method. Please use hass.uiUtil.stateMoreInfoType');
return window.hass.uiUtil.stateMoreInfoType(this);
}
},
});
var dispatcher = window.hass.dispatcher,
constants = window.hass.constants,
preferenceStore = window.hass.preferenceStore,
authActions = window.hass.authActions;
window.hass.uiConstants = {
ACTION_SHOW_DIALOG_MORE_INFO: 'ACTION_SHOW_DIALOG_MORE_INFO',
ACTION_SHOW_DATE_PICKER: 'ACTION_SHOW_DATE_PICKER',
STATE_FILTERS: {
'group': 'Groups',
'script': 'Scripts',
'scene': 'Scenes',
},
};
window.hass.uiActions = { window.hass.uiActions = {
showMoreInfoDialog: function(entityId) { validateAuth: function(authToken, rememberAuth) {
dispatcher.dispatch({
actionType: window.hass.uiConstants.ACTION_SHOW_DIALOG_MORE_INFO,
entityId: entityId,
});
},
showDatePicker: function(dateSelectedCallback, startDate) {
startDate = startDate || null;
dispatcher.dispatch({
actionType: window.hass.uiConstants.ACTION_SHOW_DATE_PICKER,
dateSelectedCallback: dateSelectedCallback,
startDate: startDate,
});
},
validateAuth: function(authToken, rememberLogin) {
authActions.validate(authToken, { authActions.validate(authToken, {
useStreaming: preferenceStore.useStreaming, useStreaming: uiPreferences.useStreaming,
rememberLogin: rememberLogin, rememberAuth: rememberAuth,
}); });
}, },
}; };
@ -76,7 +30,7 @@
stateCardType: function(state) { stateCardType: function(state) {
if(DOMAINS_WITH_CARD.indexOf(state.domain) !== -1) { if(DOMAINS_WITH_CARD.indexOf(state.domain) !== -1) {
return state.domain; return state.domain;
} else if(state.canToggle) { } else if(reactor.evaluate(serviceGetters.canToggle(state.entityId))) {
return "toggle"; return "toggle";
} else { } else {
return "display"; return "display";

View file

@ -0,0 +1,5 @@
<!--
Wrapping JS in an HTML file will prevent it from being loaded twice.
-->
<script src="../bower_components/lodash/lodash.min.js"></script>

View file

@ -1,21 +1,42 @@
<script> <script>
(function() { (function() {
var NuclearObserver = function NuclearObserver(reactor) {
return {
var StoreListenerMixIn = window.hass.storeListenerMixIn; attached: function() {
var component = this;
this.__unwatchFns = Object.keys(component.properties).reduce(
function(unwatchFns, key) {
if (!('bindNuclear' in component.properties[key])) {
return unwatchFns;
}
var getter = component.properties[key].bindNuclear;
window.StoreListenerBehavior = { if (!getter) {
throw 'Undefined getter specified for key ' + key;
}
attached: function() { // console.log(key, getter);
StoreListenerMixIn.listenToStores(true, this);
},
detached: function() { component[key] = reactor.evaluate(getter);
StoreListenerMixIn.stopListeningToStores(this);
},
return unwatchFns.concat(reactor.observe(getter, function(val) {
// console.log('New value for', key, val);
component[key] = val;
}));
}, []);
},
detached: function() {
while (this.__unwatchFns.length) {
this.__unwatchFns.shift()();
}
},
};
}; };
window.nuclearObserver = NuclearObserver(window.hass.reactor);
})(); })();
</script> </script>

File diff suppressed because one or more lines are too long