Major cleanup frontend

This commit is contained in:
Paulus Schoutsen 2014-11-01 15:04:44 -07:00
parent 990ac057db
commit b686f04121
12 changed files with 8169 additions and 1813 deletions

View File

@ -356,14 +356,15 @@ class RequestHandler(SimpleHTTPRequestHandler):
"<link rel='shortcut icon' href='/static/favicon.ico' />"
"<link rel='icon' type='image/png' "
" href='/static/favicon-192x192.png' sizes='192x192'>"
"<script"
" src='/static/polymer_platform.js'></script>"
"<link rel='import' href='/static/{}' />"
"<meta name='viewport' content='width=device-width, "
" user-scalable=no, initial-scale=1.0, "
" minimum-scale=1.0, maximum-scale=1.0' />"
"</head>"
"<body fullbleed>"
"<h3 id='init' align='center'>Initializing Home Assistant</h3>"
"<script"
" src='/static/polymer_platform.js'></script>"
"<link rel='import' href='/static/{}' />"
"<splash-login auth='{}'></splash-login>"
"</body></html>").format(app_url, data.get('api_password', '')))

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_polymer script """
VERSION = "5ae474d6905e074fa2a6aa0cfa2941be"
VERSION = "835922d1958738763525d6670c11bfb5"

File diff suppressed because it is too large Load Diff

View File

@ -19,8 +19,6 @@
"font-roboto": "Polymer/font-roboto#~0.4.2",
"core-header-panel": "Polymer/core-header-panel#~0.4.2",
"core-toolbar": "Polymer/core-toolbar#~0.4.2",
"core-icon-button": "Polymer/core-icon-button#~0.4.2",
"paper-fab": "Polymer/paper-fab#~0.4.2",
"paper-toast": "Polymer/paper-toast#~0.4.2",
"paper-dialog": "Polymer/paper-dialog#~0.4.2",
"paper-button": "Polymer/paper-button#~0.4.2",
@ -31,6 +29,9 @@
"core-item": "Polymer/core-item#~0.4.2",
"core-icons": "polymer/core-icons#~0.4.2",
"paper-toggle-button": "polymer/paper-toggle-button#~0.4.2",
"paper-tabs": "polymer/paper-tabs#~0.4.2"
"paper-tabs": "polymer/paper-tabs#~0.4.2",
"paper-icon-button": "polymer/paper-icon-button#~0.4.2",
"paper-menu-button": "polymer/paper-menu-button#~0.4.2",
"paper-item": "polymer/paper-item#~0.4.2"
}
}

View File

@ -6,15 +6,19 @@
<link rel="import" href="bower_components/core-icons/image-icons.html">
<link rel="import" href="bower_components/core-icons/hardware-icons.html">
<polymer-element name="domain-icon" attributes="domain">
<polymer-element name="domain-icon"
attributes="domain state" constructor="DomainIcon">
<template>
<core-icon icon="{{icon(domain)}}"></core-icon>
<core-icon icon="{{icon(domain, state)}}"></core-icon>
</template>
<script>
Polymer({
icon: function() {
switch(this.domain) {
icon: function(domain, state) {
switch(domain) {
case "homeassistant":
return "home";
case "group":
return "social:communities";
@ -25,8 +29,11 @@
return "settings-input-svideo";
case "chromecast":
// hardware:cast-connected
return "hardware:cast";
if(state && state != "idle") {
return "hardware:cast-connected";
} else {
return "hardware:cast";
}
case "process":
return "hardware:memory"

View File

@ -58,7 +58,7 @@
},
_sortStates: function(states) {
return states.sort(function(one, two) {
states.sort(function(one, two) {
if (one.entity_id > two.entity_id) {
return 1;
} else if (one.entity_id < two.entity_id) {
@ -86,6 +86,7 @@
}
if(!stateFound) {
this._enhanceState(state);
this.states.push(new_state);
this._sortStates(this.states);
}
@ -93,6 +94,14 @@
this.fire('states-updated')
},
_enhanceState: function(state) {
var parts = state.entity_id.split(".");
state.domain = parts[0];
state.entity = parts[1];
state.entityDisplay = state.entity.replace(/_/g, " ");
state.stateDisplay = state.state.replace(/_/g, " ");
},
// call api methods
fetchState: function(entityId) {
var successStateUpdate = function(new_state) {
@ -104,7 +113,9 @@
fetchStates: function(onSuccess, onError) {
var successStatesUpdate = function(newStates) {
this.states = this._sortStates(newStates);
this._sortStates(newStates);
newStates.map(this._enhanceState);
this.states = newStates;
this.fire('states-updated')
@ -155,7 +166,7 @@
}
var successToast = function(new_state) {
this.showToast("State of "+entity_id+" successful set to "+state+".");
this.showToast("State of "+entity_id+" set to "+state+".");
this._pushNewState(new_state);
}
@ -165,7 +176,13 @@
call_service: function(domain, service, parameters) {
var successToast = function() {
this.showToast("Service "+domain+"/"+service+" successful called.");
if(service == "turn_on" && parameters.entity_id) {
this.showToast("Turned on " + parameters.entity_id + '.');
} else if(service == "turn_off") {
this.showToast("Turned off " + parameters.entity_id + '.');
} else {
this.showToast("Service "+domain+"/"+service+" called.");
}
// if we call a service on an entity_id, update the state
if(parameters && parameters.entity_id) {
@ -197,7 +214,7 @@
eventData = eventData ? JSON.parse(eventData) : "";
var successToast = function() {
this.showToast("Event "+eventType+" successful fired.");
this.showToast("Event "+eventType+" fired.");
}
this.call_api("POST", "events/" + eventType,
@ -267,6 +284,10 @@
showToast: function(message) {
this.$.toast.text = message;
this.$.toast.show();
},
logOut: function() {
this.auth = "";
}
});

View File

@ -1,7 +1,8 @@
<link rel="import" href="bower_components/core-header-panel/core-header-panel.html">
<link rel="import" href="bower_components/core-toolbar/core-toolbar.html">
<link rel="import" href="bower_components/core-icon-button/core-icon-button.html">
<link rel="import" href="bower_components/paper-fab/paper-fab.html">
<link rel="import" href="bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="bower_components/paper-item/paper-item.html">
<link rel="import" href="bower_components/paper-menu-button/paper-menu-button.html">
<link rel="import" href="bower_components/paper-tabs/paper-tabs.html">
<link rel="import" href="bower_components/paper-tabs/paper-tab.html">
@ -24,33 +25,41 @@
core-toolbar {
background: #03a9f4;
font-size: 1.4rem;
font-size: 1.3rem;
color: white;
}
.content {
padding-bottom: 75px;
padding-right: 10px;
paper-tab {
text-transform: uppercase;
}
paper-fab {
position: fixed;
bottom: 10px;
right: 10px;
.content {
padding-bottom: 10px;
padding-right: 10px;
}
</style>
<core-header-panel unresolved fullbleed>
<core-header-panel fullbleed>
<core-toolbar class='medium-tall'>
<div flex>
Home Assistant
</div>
<core-icon-button icon="refresh" on-click="{{handleRefreshClick}}"></core-icon-button>
<core-icon-button icon="developer-mode-tv" on-click="{{handleEventClick}}"></core-icon-button>
<core-icon-button icon="settings-remote" on-click="{{handleServiceClick}}"></core-icon-button>
<paper-icon-button icon="refresh" on-click="{{handleRefreshClick}}"></paper-icon-button>
<paper-icon-button icon="settings-remote"
on-click="{{handleServiceClick}}"></paper-icon-button>
<paper-menu-button icon="more-vert" halign="right">
<paper-item label="Set State">
<a on-click={{handleAddStateClick}}></a>
</paper-item>
<paper-item label="Trigger Event">
<a on-click={{handleEventClick}}></a>
</paper-item>
<paper-item label="Log Out">
<a on-click={{handleLogOutClick}}></a>
</paper-item>
</paper-menu-button>
<div class="bottom fit" horizontal layout>
<paper-tabs id="tabsHolder" noink flex
@ -59,8 +68,10 @@
<paper-tab>ALL</paper-tab>
<template repeat="{{state in api.states}}">
<template if="{{isCustomGroup(state)}}">
<paper-tab data-entity="{{state.entity_id}}">{{state.entity_id | groupName}}</paper-tab>
<template if="{{state.domain == 'group' && !state.attributes.auto}}">
<paper-tab data-entity="{{state.entity_id}}">
{{state.entityDisplay}}
</paper-tab>
</template>
</template>
@ -70,7 +81,6 @@
<div class="content" flex>
<states-cards api="{{api}}" filter="{{selectedTab}}"></states-cards>
<paper-fab icon="add" on-click={{handleAddStateClick}}></paper-fab>
</div>
</core-header-panel>
@ -80,15 +90,6 @@
Polymer({
selectedTab: null,
isCustomGroup: function(state) {
return (state.entity_id.lastIndexOf('group.') == 0 &&
!state.attributes.auto);
},
groupName: function(entity_id) {
return entity_id.substring(6).toUpperCase().replace(/_/g, " ");
},
tabClicked: function(ev) {
if(ev.detail.isSelected) {
// will be null for ALL tab
@ -110,6 +111,10 @@
handleAddStateClick: function() {
this.api.showSetStateDialog();
},
handleLogOutClick: function() {
this.api.logOut();
}
});

View File

@ -3,6 +3,8 @@
<link rel="import" href="bower_components/core-menu/core-submenu.html">
<link rel="import" href="bower_components/core-item/core-item.html">
<link rel="import" href="domain-icon.html">
<polymer-element name="services-list" attributes="api cbServiceClicked">
<template>
<style>
@ -22,7 +24,7 @@
<template if={{cbServiceClicked}}>
<style>
a {
a, core-submenu {
text-decoration: underline;
cursor: pointer;
}
@ -33,7 +35,7 @@
<core-menu selected="0">
<template repeat="{{serv in services}}">
<core-submenu icon="settings" label="{{serv.domain}}">
<core-submenu icon="{{serv.domain | getIcon}}" label="{{serv.domain}}">
<template repeat="{{service in serv.services}}">
<a on-click={{serviceClicked}} data-domain={{serv.domain}}>{{service}}</a>
</template>
@ -55,6 +57,10 @@
this.api.addEventListener('services-updated', this.servicesUpdated.bind(this))
},
getIcon: function(domain) {
return (new DomainIcon).icon(domain);
},
servicesUpdated: function() {
this.services = this.api.services;
},

View File

@ -64,11 +64,20 @@
},
domReady: function() {
document.getElementById('init').remove();
if(this.auth) {
this.validatePassword();
}
},
authChanged: function(oldVal, newVal) {
// log out functionality
if(newVal == "" && this.state == "valid_auth") {
this.state = "no_auth";
}
},
passwordKeyup: function(ev) {
if(ev.keyCode == 13) {
this.validatePassword();

View File

@ -2,7 +2,7 @@
<link rel="import" href="domain-icon.html">
<polymer-element name="state-badge" attributes="domain state">
<polymer-element name="state-badge" attributes="stateObj" noscript>
<template>
<style>
:host {
@ -12,6 +12,7 @@
color: white;
border-radius: 23px;
}
div {
height: 45px;
text-align: center;
@ -23,12 +24,9 @@
</style>
<div horizontal layout center>
<domain-icon domain="{{domain}}"></domain-icon>
<domain-icon domain="{{stateObj.domain}}" state="{{stateObj.state}}">
</domain-icon>
</div>
</template>
<script>
Polymer({
});
</script>
</polymer-element>

View File

@ -7,7 +7,7 @@
<link rel="import" href="state-badge.html">
<polymer-element name="state-card"
attributes="entity state last_changed state_attr cb_turn_on, cb_turn_off cb_edit">
attributes="stateObj cb_turn_on, cb_turn_off cb_edit">
<template>
<style>
:host {
@ -79,44 +79,45 @@
<div class="entity">
<state-badge
id="badge"
domain="{{domain}}"
state="{{state}}"
data-domain="{{domain}}"
data-state="{{state}}"
id="badge"
stateObj="{{stateObj}}"
data-domain="{{stateObj.domain}}"
data-state="{{stateObj.state}}"
on-click="{{editClicked}}">
</state-badge>
<div class='info'>
<div class='name'>
<template if="{{state_attr['friendly_name']}}">{{state_attr['friendly_name']}}</template>
<template if="{{!state_attr['friendly_name']}}">{{entity_id | makeReadable}}</template>
<template if="{{state_attr['friendly_name']}}">
{{state_attr['friendly_name']}}
</template>
<template if="{{!state_attr['friendly_name']}}">
{{stateObj.entityDisplay}}
</template>
</div>
<div class="time-ago">
<core-tooltip label="{{last_changed}}" position="bottom">
{{last_changed_from_now}}
{{lastChangedFromNow}}
</core-tooltip>
</div>
</div>
</div>
<template if="{{!state_unknown}}">
<template if="{{state == 'on' || state == 'off'}}">
<template if="{{!stateUnknown}}">
<template if="{{stateObj.state == 'on' || stateObj.state == 'off'}}">
<div class='state toggle' self-center flex>
<paper-toggle-button checked="{{toggleChecked}}">
</paper-toggle-button>
</div>
</template>
<template if="{{state != 'on' && state != 'off'}}">
<div class='state text'>
{{state | makeReadable}}
</div>
<template if="{{stateObj.state != 'on' && stateObj.state != 'off'}}">
<div class='state text'>{{stateObj.stateDisplay}}</div>
</template>
</template>
<template if="{{state_unknown}}">
<template if="{{stateUnknown}}">
<div class="state" self-center flex>Updating..</div>
</template>
@ -126,28 +127,20 @@
<script>
Polymer({
// attributes
entity: "",
state: "",
last_changed: "never",
state_attr: {},
stateObj: {},
cb_turn_on: null,
cb_turn_off: null,
cb_edit: null,
state_unknown: false,
stateUnknown: false,
toggleChecked: -1,
computed: {
domain: "entity | parseDomain",
entity_id: "entity | parseEntityId",
last_changed_from_now: "last_changed | parseLastChangedFromNow"
lastChangedFromNow: "stateObj.last_changed | parseLastChangedFromNow",
},
parseDomain: function(entity) {
return entity.split('.')[0];
},
parseEntityId: function(entity) {
return entity.split('.')[1];
observe: {
'stateObj.state': 'stateChanged'
},
parseLastChangedFromNow: function(lastChanged) {
@ -160,24 +153,26 @@
return;
}
if(newVal && this.state == "off") {
if(newVal && this.stateObj.state == "off") {
this.turn_on();
} else if(!newVal && this.state == "on") {
} else if(!newVal && this.stateObj.state == "on") {
this.turn_off();
}
},
stateChanged: function(oldVal, newVal) {
this.state_unknown = newVal == null;
this.stateUnknown = newVal == null;
this.toggleChecked = newVal == "on"
// for domain light, set color of icon to light color if available
if(this.domain == "light" && newVal == "on" &&
this.state_attr.brightness && this.state_attr.xy_color) {
var state = this.stateObj;
var rgb = this.xyBriToRgb(this.state_attr.xy_color[0],
this.state_attr.xy_color[1],
this.state_attr.brightness);
// for domain light, set color of icon to light color if available
if(state.domain == "light" && newVal == "on" &&
state.attributes.brightness && state.attributes.xy_color) {
var rgb = this.xyBriToRgb(state.attributes.xy_color[0],
state.attributes.xy_color[1],
state.attributes.brightness);
this.$.badge.style.color = "rgb(" + rgb.map(Math.floor).join(",") + ")";
} else {
this.$.badge.style.color = null;
@ -186,11 +181,11 @@
turn_on: function() {
if(this.cb_turn_on) {
this.cb_turn_on(this.entity);
this.cb_turn_on(this.stateObj.entity_id);
// unset state while we wait for an update
var delayUnsetSate = function() {
this.state = null;
this.stateObj.state = null;
}
setTimeout(delayUnsetSate.bind(this), 500);
}
@ -198,11 +193,11 @@
turn_off: function() {
if(this.cb_turn_off) {
this.cb_turn_off(this.entity);
this.cb_turn_off(this.stateObj.entity_id);
// unset state while we wait for an update
var delayUnsetSate = function() {
this.state = null;
this.stateObj.state = null;
}
setTimeout(delayUnsetSate.bind(this), 500);
}
@ -210,20 +205,7 @@
editClicked: function() {
if(this.cb_edit) {
this.cb_edit(this.entity);
}
},
// used as filter
makeReadable: function(value) {
if(typeof value == "string") {
return value.replace(/_/g, " ");
} else if(Array.isArray(value)) {
return value.join(", ");
} else {
return value;
this.cb_edit(this.stateObj.entity_id);
}
},

View File

@ -24,10 +24,7 @@
<div horizontal layout wrap>
<template repeat="{{state in states}}">
<state-card
entity="{{state.entity_id}}"
state="{{state.state}}"
last_changed="{{state.last_changed}}"
state_attr="{{state.attributes}}"
stateObj="{{state}}"
cb_turn_on="{{api.turn_on}}"
cb_turn_off="{{api.turn_off}}"
cb_edit={{editCallback}}>