mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
commit
5ccb7a5856
@ -21,7 +21,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body fullbleed>
|
<body fullbleed>
|
||||||
<h3 id='init' align='center'>Initializing Home Assistant</h3>
|
<h3 id='init' align='center'>Initializing Home Assistant</h3>
|
||||||
<script src='/static/webcomponents.min.js'></script>
|
<script src='/static/webcomponents-lite.min.js'></script>
|
||||||
<link rel='import' href='/static/{{ app_url }}' />
|
<link rel='import' href='/static/{{ app_url }}' />
|
||||||
<home-assistant auth='{{ auth }}'></home-assistant>
|
<home-assistant auth='{{ auth }}'></home-assistant>
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||||
VERSION = "28c0680cf6ebd969dc5710c22d9c4075"
|
VERSION = "c164af7349b4750365f03b190e11cc8d"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -10,38 +10,36 @@
|
|||||||
"ignore": [
|
"ignore": [
|
||||||
"bower_components"
|
"bower_components"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"webcomponentsjs": "Polymer/webcomponentsjs#~0.6",
|
"polymer": "Polymer/polymer#^1.0.0",
|
||||||
"font-roboto": "Polymer/font-roboto#~0.5.5",
|
"webcomponentsjs": "Polymer/webcomponentsjs#^0.7",
|
||||||
"core-header-panel": "polymer/core-header-panel#~0.5.5",
|
"paper-header-panel": "PolymerElements/paper-header-panel#^1.0.0",
|
||||||
"core-toolbar": "polymer/core-toolbar#~0.5.5",
|
"paper-toolbar": "PolymerElements/paper-toolbar#^1.0.0",
|
||||||
"core-tooltip": "Polymer/core-tooltip#~0.5.5",
|
"paper-menu": "PolymerElements/paper-menu#^1.0.0",
|
||||||
"core-menu": "polymer/core-menu#~0.5.5",
|
"iron-input": "PolymerElements/iron-input#^1.0.0",
|
||||||
"core-item": "Polymer/core-item#~0.5.5",
|
"iron-icons": "PolymerElements/iron-icons#^1.0.0",
|
||||||
"core-input": "Polymer/core-input#~0.5.5",
|
"iron-image": "PolymerElements/iron-image#^1.0.0",
|
||||||
"core-icons": "polymer/core-icons#~0.5.5",
|
"paper-toast": "PolymerElements/paper-toast#^1.0.0",
|
||||||
"core-image": "polymer/core-image#~0.5.5",
|
"paper-dialog": "PolymerElements/paper-dialog#^1.0.0",
|
||||||
"core-style": "polymer/core-style#~0.5.5",
|
"paper-dialog-scrollable": "polymerelements/paper-dialog-scrollable#^1.0.0",
|
||||||
"core-label": "polymer/core-label#~0.5.5",
|
"paper-spinner": "PolymerElements/paper-spinner#^1.0.0",
|
||||||
"paper-toast": "Polymer/paper-toast#~0.5.5",
|
"paper-button": "PolymerElements/paper-button#^1.0.0",
|
||||||
"paper-dialog": "Polymer/paper-dialog#~0.5.5",
|
"paper-input": "PolymerElements/paper-input#^1.0.0",
|
||||||
"paper-spinner": "Polymer/paper-spinner#~0.5.5",
|
"paper-toggle-button": "PolymerElements/paper-toggle-button#^1.0.0",
|
||||||
"paper-button": "Polymer/paper-button#~0.5.5",
|
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
|
||||||
"paper-input": "Polymer/paper-input#~0.5.5",
|
"paper-item": "PolymerElements/paper-item#^1.0.0",
|
||||||
"paper-toggle-button": "polymer/paper-toggle-button#~0.5.5",
|
"paper-slider": "PolymerElements/paper-slider#^1.0.0",
|
||||||
"paper-icon-button": "polymer/paper-icon-button#~0.5.5",
|
"paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
|
||||||
"paper-menu-button": "polymer/paper-menu-button#~0.5.5",
|
"paper-drawer-panel": "PolymerElements/paper-drawer-panel#^1.0.0",
|
||||||
"paper-dropdown": "polymer/paper-dropdown#~0.5.5",
|
"paper-scroll-header-panel": "polymerelements/paper-scroll-header-panel#~1.0",
|
||||||
"paper-item": "polymer/paper-item#~0.5.5",
|
"google-apis": "GoogleWebComponents/google-apis#0.8-preview",
|
||||||
"paper-slider": "polymer/paper-slider#~0.5.5",
|
"moment": "^2.10.3",
|
||||||
"paper-checkbox": "polymer/paper-checkbox#~0.5.5",
|
"layout": "Polymer/layout",
|
||||||
"color-picker-element": "~0.0.2",
|
"color-picker-element": "~0.0.3",
|
||||||
"google-apis": "GoogleWebComponents/google-apis#~0.4.4",
|
"paper-styles": "polymerelements/paper-styles#~1.0"
|
||||||
"core-drawer-panel": "polymer/core-drawer-panel#~0.5.5",
|
|
||||||
"core-scroll-header-panel": "polymer/core-scroll-header-panel#~0.5.5",
|
|
||||||
"moment": "~2.10.2"
|
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"webcomponentsjs": "~0.6"
|
"polymer": "^1.0.0",
|
||||||
|
"webcomponentsjs": "^0.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,29 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
|
|
||||||
<link rel="import" href="./state-card-display.html">
|
<link rel='import' href='./state-card-display.html'>
|
||||||
<link rel="import" href="../components/state-info.html">
|
<link rel='import' href='../components/state-info.html'>
|
||||||
|
|
||||||
<polymer-element name="state-card-configurator" attributes="stateObj" noscript>
|
<dom-module id='state-card-configurator'>
|
||||||
<template>
|
<template>
|
||||||
<state-card-display stateObj="{{stateObj}}"></state-card-display>
|
<state-card-display state-obj='[[stateObj]]'></state-card-display>
|
||||||
|
|
||||||
<!-- pre load the image so the dialog is rendered the proper size -->
|
<!-- pre load the image so the dialog is rendered the proper size -->
|
||||||
<template if="{{stateObj.attributes.description_image}}">
|
<template is='dom-if' if='[[stateObj.attributes.description_image]]'>
|
||||||
<img hidden src="{{stateObj.attributes.description_image}}" />
|
<img hidden src='[[stateObj.attributes.description_image]]' />
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</polymer-element>
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-card-configurator',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@ -6,43 +6,52 @@
|
|||||||
<link rel="import" href="state-card-configurator.html">
|
<link rel="import" href="state-card-configurator.html">
|
||||||
<link rel="import" href="state-card-scene.html">
|
<link rel="import" href="state-card-scene.html">
|
||||||
|
|
||||||
<polymer-element name="state-card-content" attributes="stateObj">
|
<dom-module id="state-card-content">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<div id='cardContainer'></div>
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
Polymer({
|
(function() {
|
||||||
stateObjChanged: function(oldVal, newVal) {
|
var uiUtil = window.hass.uiUtil;
|
||||||
var cardContainer = this.$.cardContainer;
|
|
||||||
|
Polymer({
|
||||||
|
is: 'state-card-content',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'stateObjChanged',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stateObjChanged: function(newVal, oldVal) {
|
||||||
|
var root = Polymer.dom(this);
|
||||||
|
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
if (cardContainer.lastChild) {
|
if (root.lastChild) {
|
||||||
cardContainer.removeChild(cardContainer.lastChild);
|
root.removeChild(root.lastChild);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oldVal || oldVal.cardType != newVal.cardType) {
|
var newCardType = uiUtil.stateCardType(newVal);
|
||||||
if (cardContainer.lastChild) {
|
|
||||||
cardContainer.removeChild(cardContainer.lastChild);
|
if (!oldVal || uiUtil.stateCardType(oldVal) != newCardType) {
|
||||||
|
if (root.lastChild) {
|
||||||
|
root.removeChild(root.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
var stateCard = document.createElement("state-card-" + newVal.cardType);
|
var stateCard = document.createElement("state-card-" + newCardType);
|
||||||
stateCard.stateObj = newVal;
|
stateCard.stateObj = newVal;
|
||||||
cardContainer.appendChild(stateCard);
|
root.appendChild(stateCard);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
root.lastChild.stateObj = newVal;
|
||||||
cardContainer.lastChild.stateObj = newVal;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
<link rel="import" href="../components/state-info.html">
|
<link rel="import" href="../components/state-info.html">
|
||||||
|
|
||||||
<polymer-element name="state-card-display" attributes="stateObj" noscript>
|
<dom-module id="state-card-display">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
.state {
|
.state {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
@ -14,9 +13,24 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div horizontal justified layout>
|
<template>
|
||||||
<state-info stateObj="{{stateObj}}"></state-info>
|
<div class='horizontal justified layout'>
|
||||||
<div class='state'>{{stateObj.stateDisplay}}</div>
|
<state-info state-obj="[[stateObj]]"></state-info>
|
||||||
|
<div class='state'>[[stateObj.stateDisplay]]</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</polymer-element>
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-card-display',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@ -3,25 +3,37 @@
|
|||||||
<link rel="import" href="./state-card-display.html">
|
<link rel="import" href="./state-card-display.html">
|
||||||
<link rel="import" href="./state-card-toggle.html">
|
<link rel="import" href="./state-card-toggle.html">
|
||||||
|
|
||||||
<polymer-element name="state-card-scene" attributes="stateObj">
|
<dom-module id="state-card-scene">
|
||||||
<template>
|
<template>
|
||||||
<template if={{allowToggle}}>
|
<template is='dom-if' if=[[allowToggle]]>
|
||||||
<state-card-toggle stateObj="{{stateObj}}"></state-card-toggle>
|
<state-card-toggle state-obj="[[stateObj]]"></state-card-toggle>
|
||||||
</template>
|
</template>
|
||||||
<template if={{!allowToggle}}>
|
<template is='dom-if' if=[[!allowToggle]]>
|
||||||
<state-card-display stateObj="{{stateObj}}"></state-card-display>
|
<state-card-display state-obj="[[stateObj]]"></state-card-display>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
Polymer({
|
Polymer({
|
||||||
allowToggle: false,
|
is: 'state-card-scene',
|
||||||
|
|
||||||
stateObjChanged: function(oldVal, newVal) {
|
properties: {
|
||||||
this.allowToggle = newVal.state === 'off' ||
|
stateObj: {
|
||||||
newVal.attributes.active_requested;
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
allowToggle: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
computed: 'computeAllowToggle(stateObj)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computeAllowToggle: function(stateObj) {
|
||||||
|
return stateObj.state === 'off' || stateObj.attributes.active_requested;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
<link rel="import" href="../components/state-info.html">
|
<link rel="import" href="../components/state-info.html">
|
||||||
|
|
||||||
<polymer-element name="state-card-thermostat" attributes="stateObj api">
|
<dom-module id="state-card-thermostat">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
|
:host {
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
.state {
|
.state {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@ -19,23 +22,36 @@
|
|||||||
.current {
|
.current {
|
||||||
color: darkgrey;
|
color: darkgrey;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
<div horizontal justified layout>
|
<div class='horizontal justified layout'>
|
||||||
<state-info stateObj="{{stateObj}}"></state-info>
|
<state-info state-obj="[[stateObj]]"></state-info>
|
||||||
<div class='state'>
|
<div class='state'>
|
||||||
<div class='target'>
|
<div class='target'>[[stateObj.stateDisplay]]</div>
|
||||||
{{stateObj.stateDisplay}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='current'>
|
<div class='current'>
|
||||||
Currently: {{stateObj.attributes.current_temperature}} {{stateObj.attributes.unit_of_measurement}}
|
<span>Currently: </span>
|
||||||
|
<span>[[stateObj.attributes.current_temperature]]</span>
|
||||||
|
<span> </span>
|
||||||
|
<span>[[stateObj.attributes.unit_of_measurement]]</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
Polymer({});
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-card-thermostat',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,40 +1,50 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/paper-toggle-button/paper-toggle-button.html">
|
<link rel="import" href="../bower_components/paper-toggle-button/paper-toggle-button.html">
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/state-info.html">
|
<link rel="import" href="../components/state-info.html">
|
||||||
|
|
||||||
<polymer-element name="state-card-toggle" attributes="stateObj">
|
<dom-module id="state-card-toggle">
|
||||||
|
<style>
|
||||||
|
paper-toggle-button {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<template>
|
<template>
|
||||||
<core-style ref='ha-paper-toggle'></core-style>
|
<div class='horizontal justified layout'>
|
||||||
|
<state-info state-obj="[[stateObj]]"></state-info>
|
||||||
|
|
||||||
<div horizontal justified layout>
|
<paper-toggle-button class='self-center'
|
||||||
<state-info flex stateObj="{{stateObj}}"></state-info>
|
checked="[[toggleChecked]]"
|
||||||
|
on-change="toggleChanged"
|
||||||
<paper-toggle-button self-center
|
on-click="toggleClicked">
|
||||||
checked="{{toggleChecked}}"
|
|
||||||
on-change="{{toggleChanged}}"
|
|
||||||
on-click="{{toggleClicked}}">
|
|
||||||
</paper-toggle-button>
|
</paper-toggle-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
var serviceActions = window.hass.serviceActions;
|
var serviceActions = window.hass.serviceActions;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
toggleChecked: false,
|
is: 'state-card-toggle',
|
||||||
|
|
||||||
observe: {
|
properties: {
|
||||||
'stateObj.state': 'stateChanged'
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'stateObjChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleChecked: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ready: function() {
|
ready: function() {
|
||||||
this.forceStateChange = this.forceStateChange.bind(this);
|
this.forceStateChange = this.forceStateChange.bind(this);
|
||||||
},
|
this.forceStateChange();
|
||||||
|
|
||||||
toggleClicked: function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleChanged: function(ev) {
|
toggleChanged: function(ev) {
|
||||||
@ -47,22 +57,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stateObjChanged: function(oldVal, newVal) {
|
stateObjChanged: function(newVal) {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
this.stateChanged(null, newVal.state);
|
this.updateToggle(newVal);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stateChanged: function(oldVal, newVal) {
|
updateToggle: function(stateObj) {
|
||||||
this.toggleChecked = newVal === "on";
|
this.toggleChecked = stateObj && stateObj.state === "on";
|
||||||
},
|
},
|
||||||
|
|
||||||
forceStateChange: function() {
|
forceStateChange: function() {
|
||||||
this.stateChanged(null, this.stateObj.state);
|
this.updateToggle(this.stateObj);
|
||||||
},
|
},
|
||||||
|
|
||||||
turn_on: function() {
|
turn_on: function() {
|
||||||
// We call stateChanged after a successful call to re-sync the toggle
|
// We call updateToggle after a successful call to re-sync the toggle
|
||||||
// with the state. It will be out of sync if our service call did not
|
// with the state. It will be out of sync if our service call did not
|
||||||
// result in the entity to be turned on. Since the state is not changing,
|
// result in the entity to be turned on. Since the state is not changing,
|
||||||
// the resync is not called automatic.
|
// the resync is not called automatic.
|
||||||
@ -70,12 +80,12 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
turn_off: function() {
|
turn_off: function() {
|
||||||
// We call stateChanged after a successful call to re-sync the toggle
|
// We call updateToggle after a successful call to re-sync the toggle
|
||||||
// with the state. It will be out of sync if our service call did not
|
// with the state. It will be out of sync if our service call did not
|
||||||
// result in the entity to be turned on. Since the state is not changing,
|
// result in the entity to be turned on. Since the state is not changing,
|
||||||
// the resync is not called automatic.
|
// the resync is not called automatic.
|
||||||
serviceActions.callTurnOff(this.stateObj.entityId).then(this.forceStateChange);
|
serviceActions.callTurnOff(this.stateObj.entityId).then(this.forceStateChange);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
})();
|
||||||
</polymer-element>
|
</script>
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
<link rel="import" href="state-card-content.html">
|
<link rel="import" href="state-card-content.html">
|
||||||
|
|
||||||
<polymer-element name="state-card" attributes="stateObj" on-click="cardClicked">
|
<dom-module id="state-card">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@ -19,15 +18,31 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<state-card-content stateObj={{stateObj}}></state-card-content>
|
<template>
|
||||||
</template>
|
<state-card-content state-obj="[[stateObj]]"></state-card-content>
|
||||||
<script>
|
</template>
|
||||||
var uiActions = window.hass.uiActions;
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var uiActions = window.hass.uiActions;
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'state-card',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
'click': 'cardClicked',
|
||||||
|
},
|
||||||
|
|
||||||
Polymer({
|
|
||||||
cardClicked: function() {
|
cardClicked: function() {
|
||||||
uiActions.showMoreInfoDialog(this.stateObj.entityId);
|
uiActions.showMoreInfoDialog(this.stateObj.entityId);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,26 +1,25 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<link rel="import" href="../resources/moment-js.html">
|
<dom-module id="display-time">
|
||||||
|
<template>[[computeTime(dateObj)]]</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<polymer-element name="display-time" attributes="dateObj">
|
<script>
|
||||||
<template>
|
(function() {
|
||||||
{{ time }}
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
var uiUtil = window.hass.uiUtil;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
time: "",
|
is: 'display-time',
|
||||||
|
|
||||||
dateObjChanged: function(oldVal, newVal) {
|
properties: {
|
||||||
if (newVal) {
|
dateObj: {
|
||||||
this.time = uiUtil.formatTime(newVal);
|
type: Object,
|
||||||
} else {
|
},
|
||||||
this.time = "";
|
},
|
||||||
}
|
|
||||||
|
computeTime: function(dateObj) {
|
||||||
|
return dateObj ? uiUtil.formatTime(dateObj) : '';
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,24 +1,37 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../bower_components/iron-icon/iron-icon.html">
|
||||||
|
|
||||||
<link rel="import" href="../resources/home-assistant-icons.html">
|
<link rel="import" href="../resources/home-assistant-icons.html">
|
||||||
|
|
||||||
<polymer-element name="domain-icon"
|
<dom-module id="domain-icon">
|
||||||
attributes="domain state" constructor="DomainIcon">
|
|
||||||
<template>
|
<template>
|
||||||
<core-icon icon="{{icon}}"></core-icon>
|
<iron-icon icon="[[computeIcon(domain, state)]]"></iron-icon>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
Polymer({
|
|
||||||
icon: '',
|
|
||||||
|
|
||||||
observe: {
|
<script>
|
||||||
'domain': 'updateIcon',
|
(function() {
|
||||||
'state' : 'updateIcon',
|
var uiUtil = window.hass.uiUtil;
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'domain-icon',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
domain: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
updateIcon: function() {
|
state: {
|
||||||
this.icon = window.hass.uiUtil.domainIcon(this.domain, this.state);
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computeIcon: function(domain, state) {
|
||||||
|
return uiUtil.domainIcon(domain, state);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
})();
|
||||||
</polymer-element>
|
</script>
|
||||||
|
@ -1,60 +1,53 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<polymer-element name="entity-list" attributes="cbEntityClicked">
|
<dom-module id="entity-list">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
ul {
|
||||||
display: block;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entityContainer {
|
li {
|
||||||
font-size: 1rem;
|
list-style: none;
|
||||||
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<template if={{cbEntityClicked}}>
|
|
||||||
<style>
|
|
||||||
a {
|
a {
|
||||||
text-decoration: underline;
|
color: var(--accent-color);
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul>
|
||||||
|
<template is='dom-repeat' items='[[entities]]' as='entity'>
|
||||||
|
<li><a href='#' on-click='entitySelected'>[[entity]]</a></li>
|
||||||
</template>
|
</template>
|
||||||
|
</ul>
|
||||||
<div>
|
|
||||||
<template repeat="{{entityID in entityIDs}}">
|
|
||||||
<div class='eventContainer'>
|
|
||||||
<a on-click={{handleClick}}>{{entityID}}</a>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</div>
|
<script>
|
||||||
</template>
|
(function() {
|
||||||
<script>
|
Polymer({
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
is: 'entity-list',
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
behaviors: [StoreListenerBehavior],
|
||||||
cbEventClicked: null,
|
|
||||||
entityIDs: [],
|
|
||||||
|
|
||||||
attached: function() {
|
properties: {
|
||||||
this.listenToStores(true);
|
entities: {
|
||||||
|
type: Array,
|
||||||
|
value: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
this.stopListeningToStores();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stateStoreChanged: function(stateStore) {
|
stateStoreChanged: function(stateStore) {
|
||||||
this.entityIDs = stateStore.entityIDs.toArray();
|
this.entities = stateStore.entityIDs.toArray();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleClick: function(ev) {
|
entitySelected: function(ev) {
|
||||||
if(this.cbEntityClicked) {
|
ev.preventDefault();
|
||||||
this.cbEntityClicked(ev.path[0].innerHTML);
|
this.fire('entity-selected', {entityId: ev.model.entity});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
});
|
||||||
}, storeListenerMixIn));
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,62 +1,56 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<polymer-element name="events-list" attributes="cbEventClicked">
|
<dom-module id="events-list">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
ul {
|
||||||
display: block;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eventContainer {
|
li {
|
||||||
font-size: 1rem;
|
list-style: none;
|
||||||
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template if={{cbEventClicked}}>
|
|
||||||
<style>
|
|
||||||
a {
|
a {
|
||||||
text-decoration: underline;
|
color: var(--accent-color);
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul>
|
||||||
|
<template is='dom-repeat' items='[[events]]' as='event'>
|
||||||
|
<li>
|
||||||
|
<a href='#' on-click='eventSelected'>{{event.event}}</a>
|
||||||
|
<span> (</span><span>{{event.listener_count}}</span><span> listeners)</span>
|
||||||
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
</ul>
|
||||||
<div>
|
|
||||||
<template repeat="{{event in events}}">
|
|
||||||
<div class='eventContainer'>
|
|
||||||
<a on-click={{handleClick}}>{{event.event}}</a>
|
|
||||||
({{event.listener_count}} listeners)
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</div>
|
<script>
|
||||||
</template>
|
(function() {
|
||||||
<script>
|
Polymer({
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
is: 'events-list',
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
behaviors: [StoreListenerBehavior],
|
||||||
cbEventClicked: null,
|
|
||||||
events: [],
|
|
||||||
|
|
||||||
attached: function() {
|
properties: {
|
||||||
this.listenToStores(true);
|
events: {
|
||||||
|
type: Array,
|
||||||
|
value: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
this.stopListeningToStores();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
eventStoreChanged: function(eventStore) {
|
eventStoreChanged: function(eventStore) {
|
||||||
this.events = eventStore.all.toArray();
|
this.events = eventStore.all.toArray();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleClick: function(ev) {
|
eventSelected: function(ev) {
|
||||||
if(this.cbEventClicked) {
|
ev.preventDefault();
|
||||||
this.cbEventClicked(ev.path[0].innerHTML);
|
this.fire('event-selected', {eventType: ev.model.event.event});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
});
|
||||||
}, storeListenerMixIn));
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -2,16 +2,31 @@
|
|||||||
|
|
||||||
<link rel="import" href="../components/logbook-entry.html">
|
<link rel="import" href="../components/logbook-entry.html">
|
||||||
|
|
||||||
<polymer-element name="ha-logbook" attributes="entries" noscript>
|
<dom-module id="ha-logbook">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
.logbook {
|
:host {
|
||||||
|
display: block;
|
||||||
|
padding: 16px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class='logbook'>
|
<template>
|
||||||
<template repeat="{{entries as entry}}">
|
<template is='dom-repeat' items="[[entries]]">
|
||||||
<logbook-entry entryObj="{{entry}}"></logbook-entry>
|
<logbook-entry entry-obj="[[item]]"></logbook-entry>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</dom-module>
|
||||||
</polymer>
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'ha-logbook',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
entries: {
|
||||||
|
type: Object,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-toast/paper-toast.html">
|
|
||||||
|
|
||||||
<polymer-element name="ha-notifications">
|
|
||||||
<template>
|
|
||||||
<paper-toast id="toast" role="alert" text=""></paper-toast>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
|
||||||
lastId: null,
|
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
this.listenToStores(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
this.stopListeningToStores();
|
|
||||||
},
|
|
||||||
|
|
||||||
notificationStoreChanged: function(notificationStore) {
|
|
||||||
if (notificationStore.hasNewNotifications(this.lastId)) {
|
|
||||||
var toast = this.$.toast;
|
|
||||||
var notification = notificationStore.lastNotification;
|
|
||||||
|
|
||||||
if (notification) {
|
|
||||||
this.lastId = notification.id;
|
|
||||||
toast.text = notification.message;
|
|
||||||
toast.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
}, storeListenerMixIn));
|
|
||||||
</script>
|
|
||||||
</polymer-element>
|
|
@ -1,8 +1,7 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
||||||
|
|
||||||
<polymer-element name="loading-box" attributes="text">
|
<dom-module id="loading-box">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
.text {
|
.text {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -11,15 +10,18 @@
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
<div layout='horizontal'>
|
<div layout='horizontal'>
|
||||||
<paper-spinner active="true"></paper-spinner>
|
<paper-spinner active="true"></paper-spinner>
|
||||||
<div class='text'>{{text}}…</div>
|
<div class='text'><content></content>…</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
(function() {
|
||||||
Polymer({
|
Polymer({
|
||||||
text: "Loading"
|
is: 'loading-box',
|
||||||
});
|
});
|
||||||
</script>
|
})();
|
||||||
</polymer-element>
|
</script>
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<link rel="import" href="domain-icon.html">
|
<link rel="import" href="domain-icon.html">
|
||||||
<link rel="import" href="display-time.html">
|
<link rel="import" href="display-time.html">
|
||||||
<link rel="import" href="relative-ha-datetime.html">
|
<link rel="import" href="relative-ha-datetime.html">
|
||||||
|
|
||||||
<polymer-element name="logbook-entry" attributes="entryObj">
|
<dom-module id="logbook-entry">
|
||||||
<template>
|
|
||||||
<core-style ref='ha-main'></core-style>
|
|
||||||
<style>
|
<style>
|
||||||
.logbook-entry {
|
:host {
|
||||||
|
display: block;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
display-time {
|
||||||
width: 55px;
|
width: 55px;
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
domain-icon {
|
||||||
margin: 0 8px 0 16px;
|
margin: 0 8px 0 16px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
@ -27,29 +27,38 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
|
<div class='horizontal layout'>
|
||||||
|
<display-time date-obj="[[entryObj.when]]"></display-time>
|
||||||
|
<domain-icon domain="[[entryObj.domain]]" class='icon'></domain-icon>
|
||||||
|
<div class='message' flex>
|
||||||
|
<template is='dom-if' if="[[!entryObj.entityId]]">
|
||||||
|
<span class='name'>[[entryObj.name]]</span>
|
||||||
|
</template>
|
||||||
|
<template is='dom-if' if="[[entryObj.entityId]]">
|
||||||
|
<a href='#' on-click="entityClicked" class='name'>[[entryObj.name]]</a>
|
||||||
|
<span> </span>
|
||||||
|
</template>
|
||||||
|
<span>[[entryObj.message]]</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<div horizontal layout class='logbook-entry'>
|
|
||||||
<display-time dateObj="{{entryObj.when}}" class='time secondary-text-color'></display-time>
|
|
||||||
<domain-icon domain="{{entryObj.domain}}" class='icon primary-text-color'></domain-icon>
|
|
||||||
<div class='message primary-text-color' flex>
|
|
||||||
<template if="{{!entryObj.entityId}}">
|
|
||||||
<span class='name'>{{entryObj.name}}</span>
|
|
||||||
</template>
|
|
||||||
<template if="{{entryObj.entityId}}">
|
|
||||||
<a href='#' on-click="{{entityClicked}}" class='name'>{{entryObj.name}}</a>
|
|
||||||
</template>
|
|
||||||
{{entryObj.message}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var uiActions = window.hass.uiActions;
|
var uiActions = window.hass.uiActions;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
|
is: 'logbook-entry',
|
||||||
|
|
||||||
entityClicked: function(ev) {
|
entityClicked: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
uiActions.showMoreInfoDialog(this.entryObj.entityId);
|
uiActions.showMoreInfoDialog(this.entryObj.entityId);
|
||||||
@ -58,4 +67,3 @@
|
|||||||
|
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./loading-box.html">
|
|
||||||
<link rel="import" href="relative-ha-datetime.html">
|
|
||||||
|
|
||||||
<polymer-element name="recent-states" attributes="stateObj">
|
|
||||||
<template>
|
|
||||||
<core-style ref='ha-data-table'></core-style>
|
|
||||||
|
|
||||||
<template if="{{recentStates === null}}">
|
|
||||||
<loading-box text="Loading recent states"></loading-box>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template if="{{recentStates !== null}}">
|
|
||||||
<div layout vertical>
|
|
||||||
<template repeat="{{recentStates as state}}">
|
|
||||||
<div layout justified horizontal class='data-entry'>
|
|
||||||
<div>
|
|
||||||
{{state.state}}
|
|
||||||
</div>
|
|
||||||
<div class='data'>
|
|
||||||
<relative-ha-datetime datetime="{{stateObj.last_changed}}">
|
|
||||||
</relative-ha-datetime>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template if="{{recentStates.length == 0}}">
|
|
||||||
There are no recent states.
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
Polymer({
|
|
||||||
recentStates: null,
|
|
||||||
|
|
||||||
stateObjChanged: function() {
|
|
||||||
this.recentStates = null;
|
|
||||||
|
|
||||||
window.hass.callApi(
|
|
||||||
'GET', 'history/entity/' + this.stateObj.entityId + '/recent_states').then(
|
|
||||||
function(states) {
|
|
||||||
this.recentStates = states.slice(1);
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</polymer-element>
|
|
@ -2,17 +2,42 @@
|
|||||||
|
|
||||||
<link rel="import" href="../resources/moment-js.html">
|
<link rel="import" href="../resources/moment-js.html">
|
||||||
|
|
||||||
<polymer-element name="relative-ha-datetime" attributes="datetime datetimeObj">
|
<dom-module id="relative-ha-datetime">
|
||||||
<template>
|
<template>
|
||||||
{{ relativeTime }}
|
<span>[[relativeTime]]</span>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
(function() {
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
var UPDATE_INTERVAL = 60000; // 60 seconds
|
var UPDATE_INTERVAL = 60000; // 60 seconds
|
||||||
|
|
||||||
var parseDateTime = window.hass.util.parseDateTime;
|
var parseDateTime = window.hass.util.parseDateTime;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
|
is: 'relative-ha-datetime',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
datetime: {
|
||||||
|
type: String,
|
||||||
|
observer: 'datetimeChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
datetimeObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'datetimeObjChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
parsedDateTime: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
relativeTime: {
|
||||||
|
type: String,
|
||||||
|
value: 'not set',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
relativeTime: "",
|
relativeTime: "",
|
||||||
parsedDateTime: null,
|
parsedDateTime: null,
|
||||||
|
|
||||||
@ -28,13 +53,13 @@
|
|||||||
clearInterval(this._interval);
|
clearInterval(this._interval);
|
||||||
},
|
},
|
||||||
|
|
||||||
datetimeChanged: function(oldVal, newVal) {
|
datetimeChanged: function(newVal) {
|
||||||
this.parsedDateTime = newVal ? parseDateTime(newVal) : null;
|
this.parsedDateTime = newVal ? parseDateTime(newVal) : null;
|
||||||
|
|
||||||
this.updateRelative();
|
this.updateRelative();
|
||||||
},
|
},
|
||||||
|
|
||||||
datetimeObjChanged: function(oldVal, newVal) {
|
datetimeObjChanged: function(newVal) {
|
||||||
this.parsedDateTime = newVal;
|
this.parsedDateTime = newVal;
|
||||||
|
|
||||||
this.updateRelative();
|
this.updateRelative();
|
||||||
@ -45,6 +70,5 @@
|
|||||||
moment(this.parsedDateTime).fromNow() : "";
|
moment(this.parsedDateTime).fromNow() : "";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,72 +1,58 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/core-menu/core-menu.html">
|
|
||||||
<link rel="import" href="../bower_components/core-menu/core-submenu.html">
|
<link rel="import" href="../bower_components/paper-menu/paper-menu.html">
|
||||||
<link rel="import" href="../bower_components/core-item/core-item.html">
|
|
||||||
|
|
||||||
<link rel="import" href="domain-icon.html">
|
<link rel="import" href="domain-icon.html">
|
||||||
|
|
||||||
<polymer-element name="services-list" attributes="cbServiceClicked">
|
<dom-module id="services-list">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
ul {
|
||||||
display: block;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
core-menu {
|
li {
|
||||||
margin-top: 0;
|
list-style: none;
|
||||||
font-size: 1rem;
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
color: var(--accent-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<template if={{cbServiceClicked}}>
|
<template>
|
||||||
<style>
|
<ul>
|
||||||
a, core-submenu {
|
<template is='dom-repeat' items="[[domains]]" as="domain">
|
||||||
text-decoration: underline;
|
<template is='dom-repeat' items="[[computeServices(domain)]]" as="service">
|
||||||
cursor: pointer;
|
<li><a href='#' on-click='serviceClicked'>
|
||||||
}
|
<span>[[domain]]</span>/<span>[[service]]</span>
|
||||||
</style>
|
</a></li>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div>
|
|
||||||
<core-menu selected="0">
|
|
||||||
|
|
||||||
<template repeat="{{domain in domains}}">
|
|
||||||
<core-submenu icon="{{domain | getIcon}}" label="{{domain}}">
|
|
||||||
<template repeat="{{service in domain | getServices}}">
|
|
||||||
<a on-click={{serviceClicked}} data-domain={{domain}}>{{service}}</a>
|
|
||||||
</template>
|
</template>
|
||||||
</core-submenu>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</core-menu>
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'services-list',
|
||||||
|
|
||||||
</div>
|
behaviors: [StoreListenerBehavior],
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
properties: {
|
||||||
domains: [],
|
domains: {
|
||||||
services: null,
|
type: Array,
|
||||||
cbServiceClicked: null,
|
value: [],
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
this.listenToStores(true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
services: {
|
||||||
this.stopListeningToStores();
|
type: Object,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
getIcon: function(domain) {
|
computeServices: function(domain) {
|
||||||
return hass.uiUtil.domainIcon(domain);
|
|
||||||
},
|
|
||||||
|
|
||||||
getServices: function(domain) {
|
|
||||||
return this.services.get(domain).toArray();
|
return this.services.get(domain).toArray();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -76,15 +62,10 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
serviceClicked: function(ev) {
|
serviceClicked: function(ev) {
|
||||||
if(this.cbServiceClicked) {
|
ev.preventDefault();
|
||||||
var target = ev.path[0];
|
this.fire(
|
||||||
var domain = target.getAttributeNode("data-domain").value;
|
'service-selected', {domain: ev.model.domain, service: ev.model.service});
|
||||||
var service = target.innerHTML;
|
},
|
||||||
|
});
|
||||||
this.cbServiceClicked(domain, service);
|
})();
|
||||||
}
|
</script>
|
||||||
}
|
|
||||||
|
|
||||||
}, storeListenerMixIn));
|
|
||||||
</script>
|
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
<link rel="import" href="../bower_components/core-image/core-image.html">
|
<link rel='import' href='../bower_components/iron-image/iron-image.html'>
|
||||||
|
|
||||||
<link rel="import" href="domain-icon.html">
|
<link rel='import' href='domain-icon.html'>
|
||||||
|
|
||||||
<polymer-element name="state-badge" attributes="stateObj">
|
<dom-module id='state-badge'>
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -13,7 +12,6 @@
|
|||||||
background-color: #4fc3f7;
|
background-color: #4fc3f7;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transition: all .3s ease-in-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
@ -21,12 +19,13 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
core-image {
|
iron-image {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
domain-icon {
|
domain-icon {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
transition: color .3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Color the icon if light or sun is on */
|
/* Color the icon if light or sun is on */
|
||||||
@ -37,41 +36,43 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div horizontal layout center>
|
<template>
|
||||||
<domain-icon id="icon"
|
<div class='layout horizontal center'>
|
||||||
domain="{{stateObj.domain}}" data-domain="{{stateObj.domain}}"
|
<domain-icon id='icon'
|
||||||
state="{{stateObj.state}}" data-state="{{stateObj.state}}">
|
domain='[[stateObj.domain]]' data-domain$='[[stateObj.domain]]'
|
||||||
|
state='[[stateObj.state]]' data-state$='[[stateObj.state]]'>
|
||||||
</domain-icon>
|
</domain-icon>
|
||||||
<template if="{{stateObj.attributes.entity_picture}}">
|
<template is='dom-if' if='[[stateObj.attributes.entity_picture]]'>
|
||||||
<core-image
|
<iron-image
|
||||||
sizing="cover" fit
|
sizing='cover' class='fit'
|
||||||
src="{{stateObj.attributes.entity_picture}}"></core-image>
|
src$="[[stateObj.attributes.entity_picture]]"></iron-image>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
Polymer({
|
|
||||||
observe: {
|
<script>
|
||||||
'stateObj.state': 'updateIconColor',
|
Polymer({
|
||||||
'stateObj.attributes.brightness': 'updateIconColor',
|
is: 'state-badge',
|
||||||
'stateObj.attributes.xy_color[0]': 'updateIconColor',
|
|
||||||
'stateObj.attributes.xy_color[1]': 'updateIconColor'
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'updateIconColor',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an attribute changes that influences the color of the icon.
|
* Called when an attribute changes that influences the color of the icon.
|
||||||
*/
|
*/
|
||||||
updateIconColor: function(oldVal, newVal) {
|
updateIconColor: function(newVal) {
|
||||||
var state = this.stateObj;
|
|
||||||
|
|
||||||
// for domain light, set color of icon to light color if available
|
// for domain light, set color of icon to light color if available
|
||||||
if(state.domain == "light" && state.state == "on" &&
|
if(newVal.domain == "light" && newVal.state == "on" &&
|
||||||
state.attributes.brightness && state.attributes.xy_color) {
|
newVal.attributes.brightness && newVal.attributes.xy_color) {
|
||||||
|
|
||||||
var rgb = this.xyBriToRgb(state.attributes.xy_color[0],
|
var rgb = this.xyBriToRgb(newVal.attributes.xy_color[0],
|
||||||
state.attributes.xy_color[1],
|
newVal.attributes.xy_color[1],
|
||||||
state.attributes.brightness);
|
newVal.attributes.brightness);
|
||||||
this.$.icon.style.color = "rgb(" + rgb.map(Math.floor).join(",") + ")";
|
this.$.icon.style.color = "rgb(" + rgb.map(Math.floor).join(",") + ")";
|
||||||
} else {
|
} else {
|
||||||
this.$.icon.style.color = null;
|
this.$.icon.style.color = null;
|
||||||
@ -94,12 +95,11 @@
|
|||||||
r /= maxValue;
|
r /= maxValue;
|
||||||
g /= maxValue;
|
g /= maxValue;
|
||||||
b /= maxValue;
|
b /= maxValue;
|
||||||
r = r * 255; if (r < 0) { r = 255 };
|
r = r * 255; if (r < 0) { r = 255; }
|
||||||
g = g * 255; if (g < 0) { g = 255 };
|
g = g * 255; if (g < 0) { g = 255; }
|
||||||
b = b * 255; if (b < 0) { b = 255 };
|
b = b * 255; if (b < 0) { b = 255; }
|
||||||
return [r, g, b]
|
return [r, g, b];
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
<link rel="import" href="../cards/state-card.html">
|
<link rel="import" href="../cards/state-card.html">
|
||||||
|
|
||||||
<polymer-element name="state-cards" attributes="states" noscript>
|
<dom-module id="state-cards">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (min-width: 1020px) {
|
@media all and (min-width: 1020px) {
|
||||||
@ -39,13 +37,14 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div horizontal layout wrap>
|
<template>
|
||||||
|
<div class='horizontal layout wrap'>
|
||||||
|
|
||||||
<template repeat="{{states as state}}">
|
<template is='dom-repeat' items="{{states}}">
|
||||||
<state-card class="state-card" stateObj={{state}}></state-card>
|
<state-card class="state-card" state-obj="[[item]]"></state-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template if="{{states.length == 0}}">
|
<template if="[[computeEmptyStates(states)]]">
|
||||||
<div class='no-states-content'>
|
<div class='no-states-content'>
|
||||||
<content></content>
|
<content></content>
|
||||||
</div>
|
</div>
|
||||||
@ -53,4 +52,23 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</polymer-element>
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-cards',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
states: {
|
||||||
|
type: Array,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computeEmptyStates: function(states) {
|
||||||
|
return states.length === 0;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@ -0,0 +1,196 @@
|
|||||||
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-history-chart-line',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'dataChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
unit: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
isSingleDevice: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isAttached: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
observer: 'dataChanged',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created: function() {
|
||||||
|
this.style.display = 'block';
|
||||||
|
},
|
||||||
|
|
||||||
|
attached: function() {
|
||||||
|
this.isAttached = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
dataChanged: function() {
|
||||||
|
this.drawChart();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**************************************************
|
||||||
|
The following code gererates line line graphs for devices with continuous
|
||||||
|
values(which are devices that have a unit_of_measurement values defined).
|
||||||
|
On each graph the devices are grouped by their unit of measurement, eg. all
|
||||||
|
sensors measuring MB will be a separate line on single graph. The google
|
||||||
|
chart API takes data as a 2 dimensional array in the format:
|
||||||
|
|
||||||
|
DateTime, device1, device2, device3
|
||||||
|
2015-04-01, 1, 2, 0
|
||||||
|
2015-04-01, 0, 1, 0
|
||||||
|
2015-04-01, 2, 1, 1
|
||||||
|
|
||||||
|
NOTE: the first column is a javascript date objects.
|
||||||
|
|
||||||
|
The first thing we do is build up the data with rows for each time of a state
|
||||||
|
change and initialise the values to 0. THen we loop through each device and
|
||||||
|
fill in its data.
|
||||||
|
|
||||||
|
**************************************************/
|
||||||
|
drawChart: function() {
|
||||||
|
if (!this.isAttached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = Polymer.dom(this);
|
||||||
|
var unit = this.unit;
|
||||||
|
var deviceStates = this.data;
|
||||||
|
|
||||||
|
while (root.lastChild) {
|
||||||
|
root.removeChild(root.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deviceStates.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var chart = new google.visualization.LineChart(this);
|
||||||
|
var dataTable = new google.visualization.DataTable();
|
||||||
|
|
||||||
|
dataTable.addColumn({ type: 'datetime', id: 'Time' });
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
legend: { position: 'top' },
|
||||||
|
titlePosition: 'none',
|
||||||
|
vAxes: {
|
||||||
|
// Adds units to the left hand side of the graph
|
||||||
|
0: {title: unit}
|
||||||
|
},
|
||||||
|
hAxis: {
|
||||||
|
format: 'H:mm'
|
||||||
|
},
|
||||||
|
lineWidth: 1,
|
||||||
|
chartArea:{left:'60',width:"95%"},
|
||||||
|
explorer: {
|
||||||
|
actions: ['dragToZoom', 'rightClickToReset', 'dragToPan'],
|
||||||
|
keepInBounds: true,
|
||||||
|
axis: 'horizontal',
|
||||||
|
maxZoomIn: 0.1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(this.isSingleDevice) {
|
||||||
|
options.legend.position = 'none';
|
||||||
|
options.vAxes[0].title = null;
|
||||||
|
options.chartArea.left = 40;
|
||||||
|
options.chartArea.height = '80%';
|
||||||
|
options.chartArea.top = 5;
|
||||||
|
options.enableInteractivity = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a unique list of times of state changes for all the device
|
||||||
|
// for a particular unit of measureent.
|
||||||
|
var times = _.pluck(_.flatten(deviceStates), "lastChangedAsDate");
|
||||||
|
times = _.uniq(times, function(e) {
|
||||||
|
return e.getTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
times = _.sortBy(times, function(o) { return o; });
|
||||||
|
|
||||||
|
var data = [];
|
||||||
|
var empty = new Array(deviceStates.length);
|
||||||
|
for(var i = 0; i < empty.length; i++) {
|
||||||
|
empty[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeIndex = 1;
|
||||||
|
var endDate = new Date();
|
||||||
|
var prevDate = times[0];
|
||||||
|
|
||||||
|
for(i = 0; i < times.length; i++) {
|
||||||
|
var currentDate = new Date(prevDate);
|
||||||
|
|
||||||
|
// because we only have state changes we add an extra point at the same time
|
||||||
|
// that holds the previous state which makes the line display correctly
|
||||||
|
var beforePoint = new Date(times[i]);
|
||||||
|
data.push([beforePoint].concat(empty));
|
||||||
|
|
||||||
|
data.push([times[i]].concat(empty));
|
||||||
|
prevDate = times[i];
|
||||||
|
timeIndex++;
|
||||||
|
}
|
||||||
|
data.push([endDate].concat(empty));
|
||||||
|
|
||||||
|
|
||||||
|
var deviceCount = 0;
|
||||||
|
deviceStates.forEach(function(device) {
|
||||||
|
var attributes = device[device.length - 1].attributes;
|
||||||
|
dataTable.addColumn('number', attributes.friendly_name);
|
||||||
|
|
||||||
|
var currentState = 0;
|
||||||
|
var previousState = 0;
|
||||||
|
var lastIndex = 0;
|
||||||
|
var count = 0;
|
||||||
|
var prevTime = data[0][0];
|
||||||
|
device.forEach(function(state) {
|
||||||
|
|
||||||
|
currentState = state.state;
|
||||||
|
var start = state.lastChangedAsDate;
|
||||||
|
if(state.state == 'None') {
|
||||||
|
currentState = previousState;
|
||||||
|
}
|
||||||
|
for(var i = lastIndex; i < data.length; i++) {
|
||||||
|
data[i][1 + deviceCount] = parseFloat(previousState);
|
||||||
|
// this is where data gets filled in for each time for the particular device
|
||||||
|
// because for each time two entries were create we fill the first one with the
|
||||||
|
// previous value and the second one with the new value
|
||||||
|
if(prevTime.getTime() == data[i][0].getTime() && data[i][0].getTime() == start.getTime()) {
|
||||||
|
data[i][1 + deviceCount] = parseFloat(currentState);
|
||||||
|
lastIndex = i;
|
||||||
|
prevTime = data[i][0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prevTime = data[i][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
previousState = currentState;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
//fill in the rest of the Array
|
||||||
|
for(var i = lastIndex; i < data.length; i++) {
|
||||||
|
data[i][1 + deviceCount] = parseFloat(previousState);
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceCount++;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
dataTable.addRows(data);
|
||||||
|
chart.draw(dataTable, options);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
</script>
|
@ -0,0 +1,115 @@
|
|||||||
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-history-chart-timeline',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'dataChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
isAttached: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
observer: 'dataChanged',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created: function() {
|
||||||
|
this.style.display = 'block';
|
||||||
|
},
|
||||||
|
|
||||||
|
attached: function() {
|
||||||
|
this.isAttached = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
dataChanged: function() {
|
||||||
|
this.drawChart();
|
||||||
|
},
|
||||||
|
|
||||||
|
drawChart: function() {
|
||||||
|
if (!this.isAttached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = Polymer.dom(this);
|
||||||
|
var stateHistory = this.data;
|
||||||
|
|
||||||
|
while (root.lastChild) {
|
||||||
|
root.removeChild(root.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stateHistory || stateHistory.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// debugger;
|
||||||
|
var chart = new google.visualization.Timeline(this);
|
||||||
|
var dataTable = new google.visualization.DataTable();
|
||||||
|
|
||||||
|
dataTable.addColumn({ type: 'string', id: 'Entity' });
|
||||||
|
dataTable.addColumn({ type: 'string', id: 'State' });
|
||||||
|
dataTable.addColumn({ type: 'date', id: 'Start' });
|
||||||
|
dataTable.addColumn({ type: 'date', id: 'End' });
|
||||||
|
|
||||||
|
var addRow = function(entityDisplay, stateStr, start, end) {
|
||||||
|
stateStr = stateStr.replace(/_/g, ' ');
|
||||||
|
dataTable.addRow([entityDisplay, stateStr, start, end]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// people can pass in history of 1 entityId or a collection.
|
||||||
|
// var stateHistory;
|
||||||
|
// if (_.isArray(data[0])) {
|
||||||
|
// stateHistory = data;
|
||||||
|
// } else {
|
||||||
|
// stateHistory = [data];
|
||||||
|
// isSingleDevice = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
var numTimelines = 0;
|
||||||
|
// stateHistory is a list of lists of sorted state objects
|
||||||
|
stateHistory.forEach(function(stateInfo) {
|
||||||
|
if(stateInfo.length === 0) return;
|
||||||
|
|
||||||
|
var entityDisplay = stateInfo[0].entityDisplay;
|
||||||
|
var newLastChanged, prevState = null, prevLastChanged = null;
|
||||||
|
|
||||||
|
stateInfo.forEach(function(state) {
|
||||||
|
if (prevState !== null && state.state !== prevState) {
|
||||||
|
newLastChanged = state.lastChangedAsDate;
|
||||||
|
|
||||||
|
addRow(entityDisplay, prevState, prevLastChanged, newLastChanged);
|
||||||
|
|
||||||
|
prevState = state.state;
|
||||||
|
prevLastChanged = newLastChanged;
|
||||||
|
} else if (prevState === null) {
|
||||||
|
prevState = state.state;
|
||||||
|
prevLastChanged = state.lastChangedAsDate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addRow(entityDisplay, prevState, prevLastChanged, new Date());
|
||||||
|
numTimelines++;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
chart.draw(dataTable, {
|
||||||
|
height: 55 + numTimelines * 42,
|
||||||
|
|
||||||
|
// interactive properties require CSS, the JS api puts it on the document
|
||||||
|
// instead of inside our Shadow DOM.
|
||||||
|
enableInteractivity: false,
|
||||||
|
|
||||||
|
timeline: {
|
||||||
|
showRowLabels: stateHistory.length > 1
|
||||||
|
},
|
||||||
|
|
||||||
|
hAxis: {
|
||||||
|
format: 'H:mm'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
@ -0,0 +1,145 @@
|
|||||||
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../bower_components/google-apis/google-legacy-loader.html">
|
||||||
|
|
||||||
|
<link rel="import" href="./loading-box.html">
|
||||||
|
<link rel="import" href="./state-history-chart-timeline.html">
|
||||||
|
<link rel="import" href="./state-history-chart-line.html">
|
||||||
|
|
||||||
|
<dom-module id="state-history-charts">
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<google-legacy-loader on-api-load="googleApiLoaded"></google-legacy-loader>
|
||||||
|
|
||||||
|
<div hidden$="{{!isLoading}}" class='loading-container'>
|
||||||
|
<loading-box>Loading history data</loading-box>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template is='dom-if' if='[[!isLoading]]'>
|
||||||
|
<template is='dom-if' if='[[groupedStateHistory.timeline]]'>
|
||||||
|
<state-history-chart-timeline data='[[groupedStateHistory.timeline]]'
|
||||||
|
is-single-device='[[isSingleDevice]]'>
|
||||||
|
</state-history-chart-timeline>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template is='dom-if' if='[[groupedStateHistory.line]]'>
|
||||||
|
<template is='dom-repeat' items='[[groupedStateHistory.line]]'>
|
||||||
|
<state-history-chart-line unit='[[extractUnit(item)]]'
|
||||||
|
data='[[extractData(item)]]' is-single-device='[[isSingleDevice]]'>
|
||||||
|
</state-history-chart-line>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-history-charts',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateHistory: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
isLoadingData: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
apiLoaded: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsLoading(isLoadingData, apiLoaded)',
|
||||||
|
},
|
||||||
|
|
||||||
|
groupedStateHistory: {
|
||||||
|
type: Object,
|
||||||
|
computed: 'computeGroupedStateHistory(stateHistory)',
|
||||||
|
},
|
||||||
|
|
||||||
|
isSingleDevice: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsSingleDevice(stateHistory)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computeIsSingleDevice: function(stateHistory) {
|
||||||
|
return stateHistory && stateHistory.length == 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
computeGroupedStateHistory: function(stateHistory) {
|
||||||
|
var lineChartDevices = {};
|
||||||
|
var timelineDevices = [];
|
||||||
|
|
||||||
|
if (!stateHistory) {
|
||||||
|
return {line: unitStates, timeline: timelineDevices};
|
||||||
|
}
|
||||||
|
|
||||||
|
stateHistory.forEach(function(stateInfo) {
|
||||||
|
if (!stateInfo || stateInfo.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var unit;
|
||||||
|
|
||||||
|
for (var i = 0; i < stateInfo.length && !unit; i++) {
|
||||||
|
unit = stateInfo[i].attributes.unit_of_measurement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unit) {
|
||||||
|
if (!(unit in lineChartDevices)) {
|
||||||
|
lineChartDevices[unit] = [stateInfo];
|
||||||
|
} else {
|
||||||
|
lineChartDevices[unit].push(stateInfo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timelineDevices.push(stateInfo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
timelineDevices = timelineDevices.length > 0 && timelineDevices;
|
||||||
|
|
||||||
|
var unitStates = Object.keys(lineChartDevices).map(function(unit) {
|
||||||
|
return [unit, lineChartDevices[unit]]; });
|
||||||
|
|
||||||
|
return {line: unitStates, timeline: timelineDevices};
|
||||||
|
},
|
||||||
|
|
||||||
|
googleApiLoaded: function() {
|
||||||
|
google.load("visualization", "1", {
|
||||||
|
packages: ["timeline", "corechart"],
|
||||||
|
callback: function() {
|
||||||
|
this.apiLoaded = true;
|
||||||
|
}.bind(this)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
computeIsLoading: function(isLoadingData, apiLoaded) {
|
||||||
|
return isLoadingData || !apiLoaded;
|
||||||
|
},
|
||||||
|
|
||||||
|
extractUnit: function(arr) {
|
||||||
|
return arr[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
extractData: function(arr) {
|
||||||
|
return arr[1];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
@ -1,13 +1,15 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/core-tooltip/core-tooltip.html">
|
<!-- <link rel="import" href="../bower_components/core-tooltip/core-tooltip.html"> -->
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<link rel="import" href="state-badge.html">
|
<link rel="import" href="state-badge.html">
|
||||||
<link rel="import" href="relative-ha-datetime.html">
|
<link rel="import" href="relative-ha-datetime.html">
|
||||||
|
|
||||||
<polymer-element name="state-info" attributes="stateObj" noscript>
|
<dom-module id="state-info">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
|
:host {
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
state-badge {
|
state-badge {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
@ -25,24 +27,43 @@
|
|||||||
.time-ago {
|
.time-ago {
|
||||||
color: darkgrey;
|
color: darkgrey;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<state-badge stateObj="{{stateObj}}"></state-badge>
|
<state-badge state-obj='[[stateObj]]'></state-badge>
|
||||||
|
|
||||||
<div class='info'>
|
<div class='info'>
|
||||||
<div class='name'>
|
<div class='name'>[[stateObj.entityDisplay]]</div>
|
||||||
{{stateObj.entityDisplay}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="time-ago">
|
<div class='time-ago'>
|
||||||
<core-tooltip label="{{stateObj.lastChangedAsDate | formatDateTime}}" position="bottom">
|
<!-- <core-tooltip label="[[computeTooltipLabel(stateObj)]]" position="bottom"> -->
|
||||||
<relative-ha-datetime datetimeObj="{{stateObj.lastChangedAsDate}}"></relative-ha-datetime>
|
<relative-ha-datetime datetime-obj='[[stateObj.lastChangedAsDate]]'></relative-ha-datetime>
|
||||||
</core-tooltip>
|
<!-- </core-tooltip> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</polymer-element>
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'state-info',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computeTooltipLabel: function(stateObj) {
|
||||||
|
// stateObj.lastChangedAsDate | formatDateTime
|
||||||
|
return 'Label TODO';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
@ -1,321 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/google-apis/google-jsapi.html">
|
|
||||||
|
|
||||||
<polymer-element name="state-timeline" attributes="stateHistory isLoadingData">
|
|
||||||
<template>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadingbox {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loadingmessage {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.singlelinechart {
|
|
||||||
min-height:140px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div style='width: 100%; height: auto;' hidden?="{{!isLoading}}" >
|
|
||||||
<div layout horizontal center id="splash">
|
|
||||||
<div layout vertical center flex>
|
|
||||||
<div id="loadingbox">
|
|
||||||
<paper-spinner active="true"></paper-spinner><br />
|
|
||||||
<div class="loadingmessage">{{spinnerMessage}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<google-jsapi on-api-load="{{googleApiLoaded}}"></google-jsapi>
|
|
||||||
<div id="timeline" style='width: 100%; height: auto;' class="{{ {singlelinechart: isSingleDevice && hasLineChart } | tokenList}}" hidden?="{{isLoadingData}}"></div>
|
|
||||||
<div id="line_graphs" style='width: 100%; height: auto;' hidden?="{{isLoadingData}}"></div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
Polymer({
|
|
||||||
apiLoaded: false,
|
|
||||||
stateHistory: null,
|
|
||||||
isLoading: true,
|
|
||||||
isLoadingData: false,
|
|
||||||
spinnerMessage: "Loading history data...",
|
|
||||||
isSingleDevice: false,
|
|
||||||
hasLineChart: false,
|
|
||||||
|
|
||||||
googleApiLoaded: function() {
|
|
||||||
google.load("visualization", "1", {
|
|
||||||
packages: ["timeline", "corechart"],
|
|
||||||
callback: function() {
|
|
||||||
this.apiLoaded = true;
|
|
||||||
this.drawChart();
|
|
||||||
}.bind(this)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
stateHistoryChanged: function() {
|
|
||||||
this.drawChart();
|
|
||||||
},
|
|
||||||
|
|
||||||
isLoadingDataChanged: function() {
|
|
||||||
if(this.isLoadingData) {
|
|
||||||
isLoading = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
drawChart: function() {
|
|
||||||
if (!this.apiLoaded || !this.stateHistory) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isLoading = true;
|
|
||||||
|
|
||||||
var container = this.$.timeline;
|
|
||||||
var chart = new google.visualization.Timeline(container);
|
|
||||||
var dataTable = new google.visualization.DataTable();
|
|
||||||
|
|
||||||
dataTable.addColumn({ type: 'string', id: 'Entity' });
|
|
||||||
dataTable.addColumn({ type: 'string', id: 'State' });
|
|
||||||
dataTable.addColumn({ type: 'date', id: 'Start' });
|
|
||||||
dataTable.addColumn({ type: 'date', id: 'End' });
|
|
||||||
|
|
||||||
var addRow = function(entityDisplay, stateStr, start, end) {
|
|
||||||
stateStr = stateStr.replace(/_/g, ' ');
|
|
||||||
dataTable.addRow([entityDisplay, stateStr, start, end]);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.stateHistory.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.hasLineChart = false;
|
|
||||||
this.isSingleDevice = false;
|
|
||||||
|
|
||||||
// people can pass in history of 1 entityId or a collection.
|
|
||||||
var stateHistory;
|
|
||||||
if (_.isArray(this.stateHistory[0])) {
|
|
||||||
stateHistory = this.stateHistory;
|
|
||||||
} else {
|
|
||||||
stateHistory = [this.stateHistory];
|
|
||||||
this.isSingleDevice = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lineChartDevices = {};
|
|
||||||
var numTimelines = 0;
|
|
||||||
// stateHistory is a list of lists of sorted state objects
|
|
||||||
stateHistory.forEach(function(stateInfo) {
|
|
||||||
if(stateInfo.length === 0) return;
|
|
||||||
|
|
||||||
var entityDisplay = stateInfo[0].entityDisplay;
|
|
||||||
var newLastChanged, prevState = null, prevLastChanged = null;
|
|
||||||
//get the latest update to get the graph type from the component attributes
|
|
||||||
var attributes = stateInfo[stateInfo.length - 1].attributes;
|
|
||||||
|
|
||||||
//if the device has a unit of meaurment it will be added as a line graph further down
|
|
||||||
if(attributes.unit_of_measurement) {
|
|
||||||
if(!lineChartDevices[attributes.unit_of_measurement]){
|
|
||||||
lineChartDevices[attributes.unit_of_measurement] = [];
|
|
||||||
}
|
|
||||||
lineChartDevices[attributes.unit_of_measurement].push(stateInfo);
|
|
||||||
this.hasLineChart = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stateInfo.forEach(function(state) {
|
|
||||||
if (prevState !== null && state.state !== prevState) {
|
|
||||||
newLastChanged = state.lastChangedAsDate;
|
|
||||||
|
|
||||||
addRow(entityDisplay, prevState, prevLastChanged, newLastChanged);
|
|
||||||
|
|
||||||
prevState = state.state;
|
|
||||||
prevLastChanged = newLastChanged;
|
|
||||||
} else if (prevState === null) {
|
|
||||||
prevState = state.state;
|
|
||||||
prevLastChanged = state.lastChangedAsDate;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
addRow(entityDisplay, prevState, prevLastChanged, new Date());
|
|
||||||
numTimelines++;
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
chart.draw(dataTable, {
|
|
||||||
height: 55 + numTimelines * 42,
|
|
||||||
|
|
||||||
// interactive properties require CSS, the JS api puts it on the document
|
|
||||||
// instead of inside our Shadow DOM.
|
|
||||||
enableInteractivity: false,
|
|
||||||
|
|
||||||
timeline: {
|
|
||||||
showRowLabels: stateHistory.length > 1
|
|
||||||
},
|
|
||||||
|
|
||||||
hAxis: {
|
|
||||||
format: 'H:mm'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**************************************************
|
|
||||||
The following code gererates line line graphs for devices with continuous
|
|
||||||
values(which are devices that have a unit_of_measurment values defined).
|
|
||||||
On each graph the devices are grouped by their unit of measurement, eg. all
|
|
||||||
sensors measuring MB will be a separate line on single graph. The google
|
|
||||||
chart API takes data as a 2 dimensional array in the format:
|
|
||||||
|
|
||||||
DateTime, device1, device2, device3
|
|
||||||
2015-04-01, 1, 2, 0
|
|
||||||
2015-04-01, 0, 1, 0
|
|
||||||
2015-04-01, 2, 1, 1
|
|
||||||
|
|
||||||
NOTE: the first column is a javascript date objects.
|
|
||||||
|
|
||||||
The first thing we do is build up the data with rows for each time of a state
|
|
||||||
change and initialise the values to 0. THen we loop through each device and
|
|
||||||
fill in its data.
|
|
||||||
|
|
||||||
**************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
while (this.$.line_graphs.firstChild) {
|
|
||||||
this.$.line_graphs.removeChild(this.$.line_graphs.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var key in lineChartDevices) {
|
|
||||||
var deviceStates = lineChartDevices[key];
|
|
||||||
|
|
||||||
if(this.isSingleDevice) {
|
|
||||||
container = this.$.timeline;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
container = document.createElement("DIV");
|
|
||||||
this.$.line_graphs.appendChild(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var chart = new google.visualization.LineChart(container);
|
|
||||||
|
|
||||||
|
|
||||||
var dataTable = new google.visualization.DataTable();
|
|
||||||
dataTable.addColumn({ type: 'datetime', id: 'Time' });
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
legend: { position: 'top' },
|
|
||||||
titlePosition: 'none',
|
|
||||||
vAxes: {
|
|
||||||
// Adds units to the left hand side of the graph
|
|
||||||
0: {title: key}
|
|
||||||
},
|
|
||||||
hAxis: {
|
|
||||||
format: 'H:mm'
|
|
||||||
},
|
|
||||||
lineWidth: 1,
|
|
||||||
chartArea:{left:'60',width:"95%"},
|
|
||||||
explorer: {
|
|
||||||
actions: ['dragToZoom', 'rightClickToReset', 'dragToPan'],
|
|
||||||
keepInBounds: true,
|
|
||||||
axis: 'horizontal',
|
|
||||||
maxZoomIn: 0.1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if(this.isSingleDevice) {
|
|
||||||
options.legend.position = 'none';
|
|
||||||
options.vAxes[0].title = null;
|
|
||||||
options.chartArea.left = 40;
|
|
||||||
options.chartArea.height = '80%';
|
|
||||||
options.chartArea.top = 5;
|
|
||||||
options.enableInteractivity = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a unique list of times of state changes for all the device
|
|
||||||
// for a particular unit of measureent.
|
|
||||||
var times = _.pluck(_.flatten(deviceStates), "lastChangedAsDate");
|
|
||||||
times = _.uniq(times, function(e) {
|
|
||||||
return e.getTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
times = _.sortBy(times, function(o) { return o; });
|
|
||||||
|
|
||||||
var data = [];
|
|
||||||
var empty = new Array(deviceStates.length);
|
|
||||||
for(var i = 0; i < empty.length; i++) {
|
|
||||||
empty[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeIndex = 1;
|
|
||||||
var endDate = new Date();
|
|
||||||
var prevDate = times[0];
|
|
||||||
|
|
||||||
for(var i = 0; i < times.length; i++) {
|
|
||||||
var currentDate = new Date(prevDate);
|
|
||||||
|
|
||||||
// because we only have state changes we add an extra point at the same time
|
|
||||||
// that holds the previous state which makes the line display correctly
|
|
||||||
var beforePoint = new Date(times[i]);
|
|
||||||
data.push([beforePoint].concat(empty));
|
|
||||||
|
|
||||||
data.push([times[i]].concat(empty));
|
|
||||||
prevDate = times[i];
|
|
||||||
timeIndex++;
|
|
||||||
}
|
|
||||||
data.push([endDate].concat(empty));
|
|
||||||
|
|
||||||
|
|
||||||
var deviceCount = 0;
|
|
||||||
deviceStates.forEach(function(device) {
|
|
||||||
var attributes = device[device.length - 1].attributes;
|
|
||||||
dataTable.addColumn('number', attributes.friendly_name);
|
|
||||||
|
|
||||||
var currentState = 0;
|
|
||||||
var previousState = 0;
|
|
||||||
var lastIndex = 0;
|
|
||||||
var count = 0;
|
|
||||||
var prevTime = data[0][0];
|
|
||||||
device.forEach(function(state) {
|
|
||||||
|
|
||||||
currentState = state.state;
|
|
||||||
var start = state.lastChangedAsDate;
|
|
||||||
if(state.state == 'None') {
|
|
||||||
currentState = previousState;
|
|
||||||
}
|
|
||||||
for(var i = lastIndex; i < data.length; i++) {
|
|
||||||
data[i][1 + deviceCount] = parseFloat(previousState);
|
|
||||||
// this is where data gets filled in for each time for the particular device
|
|
||||||
// because for each time two entires were create we fill the first one with the
|
|
||||||
// previous value and the second one with the new value
|
|
||||||
if(prevTime.getTime() == data[i][0].getTime() && data[i][0].getTime() == start.getTime()) {
|
|
||||||
data[i][1 + deviceCount] = parseFloat(currentState);
|
|
||||||
lastIndex = i;
|
|
||||||
prevTime = data[i][0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prevTime = data[i][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
previousState = currentState;
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
//fill in the rest of the Array
|
|
||||||
for(var i = lastIndex; i < data.length; i++) {
|
|
||||||
data[i][1 + deviceCount] = parseFloat(previousState);
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceCount++;
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
dataTable.addRows(data);
|
|
||||||
chart.draw(dataTable, options);
|
|
||||||
}
|
|
||||||
this.isLoading = (!this.isLoadingData) ? false : true;
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</polymer-element>
|
|
@ -1,11 +1,11 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/core-icons/notification-icons.html">
|
<link rel="import" href="../bower_components/iron-icon/iron-icon.html">
|
||||||
|
<link rel="import" href="../bower_components/paper-toggle-button/paper-toggle-button.html">
|
||||||
|
|
||||||
<polymer-element name="stream-status">
|
<link rel="import" href="../bower_components/iron-icons/notification-icons.html">
|
||||||
<template>
|
|
||||||
|
<dom-module id="stream-status">
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -16,29 +16,31 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<core-style ref='ha-paper-toggle'></core-style>
|
<template>
|
||||||
|
<iron-icon icon="warning" hidden$="{{!hasError}}"></iron-icon>
|
||||||
|
<paper-toggle-button id="toggle" on-change='toggleChanged' hidden$="{{hasError}}"></paper-toggle-button>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<core-icon icon="warning" hidden?="{{!hasError}}"></core-icon>
|
|
||||||
<paper-toggle-button id="toggle" on-change={{toggleChanged}} hidden?="{{hasError}}"></paper-toggle-button>
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
var streamActions = window.hass.streamActions;
|
var streamActions = window.hass.streamActions;
|
||||||
var authStore = window.hass.authStore;
|
var authStore = window.hass.authStore;
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
isStreaming: false,
|
is: 'stream-status',
|
||||||
hasError: false,
|
|
||||||
|
|
||||||
icon: "swap-vert-circle",
|
behaviors: [StoreListenerBehavior],
|
||||||
color: 'red',
|
|
||||||
|
|
||||||
attached: function() {
|
properties: {
|
||||||
this.listenToStores(true);
|
isStreaming: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
hasError: {
|
||||||
this.stopListeningToStores();
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
streamStoreChanged: function(streamStore) {
|
streamStoreChanged: function(streamStore) {
|
||||||
@ -53,6 +55,5 @@
|
|||||||
streamActions.start(authStore.authToken);
|
streamActions.start(authStore.authToken);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
});
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-dialog/paper-dialog.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-dialog/paper-dialog-transition.html">
|
|
||||||
|
|
||||||
<polymer-element name="ha-dialog" extends="paper-dialog">
|
|
||||||
<template>
|
|
||||||
<core-style ref='ha-dialog'></core-style>
|
|
||||||
<shadow></shadow>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
Polymer({
|
|
||||||
layered: true,
|
|
||||||
backdrop: true,
|
|
||||||
transition: 'core-transition-bottom',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</polymer-element>
|
|
@ -1,53 +1,95 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<link rel="import" href="ha-dialog.html">
|
<link rel="import" href="../bower_components/paper-dialog/paper-dialog.html">
|
||||||
|
<link rel="import" href="../bower_components/paper-dialog-scrollable/paper-dialog-scrollable.html">
|
||||||
|
<!-- <link rel="import" href="../bower_components/neon-animation/animations/slide-up-animation.html">
|
||||||
|
<link rel="import" href="../bower_components/neon-animation/animations/slide-down-animation.html">
|
||||||
|
-->
|
||||||
<link rel="import" href="../cards/state-card-content.html">
|
<link rel="import" href="../cards/state-card-content.html">
|
||||||
<link rel="import" href="../components/state-timeline.html">
|
<link rel="import" href="../components/state-history-charts.html">
|
||||||
<link rel="import" href="../more-infos/more-info-content.html">
|
<link rel="import" href="../more-infos/more-info-content.html">
|
||||||
|
|
||||||
<polymer-element name="more-info-dialog">
|
<dom-module id="more-info-dialog">
|
||||||
<template>
|
<style>
|
||||||
<ha-dialog id="dialog" on-core-overlay-open="{{dialogOpenChanged}}">
|
state-card-content {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 450px) {
|
||||||
|
paper-dialog {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-height: calc(100% - 64px);
|
||||||
|
|
||||||
|
position: fixed !important;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<!-- entry-animation='slide-up-animation' exit-animation='slide-down-animation' -->
|
||||||
|
<paper-dialog id="dialog" with-backdrop>
|
||||||
|
<h2><state-card-content state-obj="[[stateObj]]"></state-card-content></h2>
|
||||||
<div>
|
<div>
|
||||||
<state-card-content stateObj="{{stateObj}}" style='margin-bottom: 24px;'>
|
<template is='dom-if' if="[[hasHistoryComponent]]">
|
||||||
</state-card-content>
|
<state-history-charts state-history="[[stateHistory]]"
|
||||||
<template if="{{hasHistoryComponent}}">
|
is-loading-data="[[isLoadingHistoryData]]"></state-history-charts>
|
||||||
<state-timeline stateHistory="{{stateHistory}}" isLoadingData="{{isLoadingHistoryData}}"></state-timeline>
|
|
||||||
</template>
|
</template>
|
||||||
<more-info-content
|
<paper-dialog-scrollable>
|
||||||
stateObj="{{stateObj}}"
|
<more-info-content state-obj="[[stateObj]]"
|
||||||
dialogOpen="{{dialogOpen}}"></more-info-content>
|
dialog-open="[[dialogOpen]]"></more-info-content>
|
||||||
|
</paper-dialog-scrollable>
|
||||||
</div>
|
</div>
|
||||||
</ha-dialog>
|
</paper-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
(function() {
|
||||||
var stateStore = window.hass.stateStore;
|
var stateStore = window.hass.stateStore;
|
||||||
var stateHistoryStore = window.hass.stateHistoryStore;
|
var stateHistoryStore = window.hass.stateHistoryStore;
|
||||||
var stateHistoryActions = window.hass.stateHistoryActions;
|
var stateHistoryActions = window.hass.stateHistoryActions;
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
entityId: false,
|
is: 'more-info-dialog',
|
||||||
stateObj: null,
|
|
||||||
stateHistory: null,
|
|
||||||
hasHistoryComponent: false,
|
|
||||||
dialogOpen: false,
|
|
||||||
isLoadingHistoryData: false,
|
|
||||||
|
|
||||||
observe: {
|
behaviors: [StoreListenerBehavior],
|
||||||
'stateObj.attributes': 'reposition'
|
|
||||||
|
properties: {
|
||||||
|
entityId: {
|
||||||
|
type: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
created: function() {
|
stateObj: {
|
||||||
this.dialogOpenChanged = this.dialogOpenChanged.bind(this);
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
attached: function() {
|
stateHistory: {
|
||||||
this.listenToStores(true);
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
isLoadingHistoryData: {
|
||||||
this.stopListeningToStores();
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
hasHistoryComponent: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
dialogOpen: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
'iron-overlay-opened': 'onIronOverlayOpened',
|
||||||
|
'iron-overlay-closed': 'onIronOverlayClosed'
|
||||||
},
|
},
|
||||||
|
|
||||||
componentStoreChanged: function(componentStore) {
|
componentStoreChanged: function(componentStore) {
|
||||||
@ -66,21 +108,24 @@ Polymer(Polymer.mixin({
|
|||||||
var newHistory;
|
var newHistory;
|
||||||
|
|
||||||
if (this.hasHistoryComponent && this.entityId) {
|
if (this.hasHistoryComponent && this.entityId) {
|
||||||
newHistory = stateHistoryStore.get(this.entityId);
|
newHistory = [stateHistoryStore.get(this.entityId)];
|
||||||
} else {
|
} else {
|
||||||
newHistory = null;
|
newHistory = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLoadingHistoryData = false;
|
this.isLoadingHistoryData = false;
|
||||||
|
|
||||||
if (newHistory !== this.stateHistory) {
|
if (newHistory !== this.stateHistory) {
|
||||||
this.stateHistory = newHistory;
|
this.stateHistory = newHistory;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
dialogOpenChanged: function(ev) {
|
onIronOverlayOpened: function() {
|
||||||
// we get CustomEvent, undefined and true/false from polymer…
|
this.dialogOpen = true;
|
||||||
if (typeof ev === 'object') {
|
},
|
||||||
this.dialogOpen = ev.detail;
|
|
||||||
}
|
onIronOverlayClosed: function() {
|
||||||
|
this.dialogOpen = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
changeEntityId: function(entityId) {
|
changeEntityId: function(entityId) {
|
||||||
@ -95,26 +140,13 @@ Polymer(Polymer.mixin({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Whenever the attributes change, the more info component can
|
|
||||||
* hide or show elements. We will reposition the dialog.
|
|
||||||
*/
|
|
||||||
reposition: function(oldVal, newVal) {
|
|
||||||
// Only resize if already open
|
|
||||||
if(this.$.dialog.opened) {
|
|
||||||
this.job('resizeAfterLayoutChange', function() {
|
|
||||||
this.$.dialog.resizeHandler();
|
|
||||||
}.bind(this), 1000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
show: function(entityId) {
|
show: function(entityId) {
|
||||||
this.changeEntityId(entityId);
|
this.changeEntityId(entityId);
|
||||||
|
|
||||||
this.job('showDialogAfterRender', function() {
|
this.debounce('showDialogAfterRender', function() {
|
||||||
this.$.dialog.toggle();
|
this.$.dialog.toggle();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 69ee1c49af12caf00655c66d56474b5c1bcac1c1
|
Subproject commit 015edf9c28a63122aa8f6bc153f0c0ddfaad1caa
|
@ -1,41 +1,62 @@
|
|||||||
<link rel="import" href="bower_components/polymer/polymer.html">
|
<link rel='import' href='bower_components/polymer/polymer.html'>
|
||||||
<link rel="import" href="bower_components/font-roboto/roboto.html">
|
|
||||||
|
|
||||||
<link rel="import" href="resources/home-assistant-style.html">
|
<link rel='import' href='bower_components/paper-styles/typography.html'>
|
||||||
<link rel="import" href="resources/home-assistant-js.html">
|
|
||||||
|
|
||||||
<link rel="import" href="layouts/login-form.html">
|
<link rel='import' href='resources/home-assistant-js.html'>
|
||||||
<link rel="import" href="layouts/home-assistant-main.html">
|
<link rel='import' href='resources/home-assistant-icons.html'>
|
||||||
|
<link rel='import' href='resources/store-listener-behavior.html'>
|
||||||
|
|
||||||
<polymer-element name="home-assistant" attributes="auth">
|
<link rel='import' href='layouts/login-form.html'>
|
||||||
<template>
|
<link rel='import' href='layouts/home-assistant-main.html'>
|
||||||
|
|
||||||
|
<link rel='import' href='resources/home-assistant-style.html'>
|
||||||
|
|
||||||
|
<dom-module id='home-assistant'>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
|
font-family: 'Roboto', 'Noto', sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<home-assistant-api auth="{{auth}}"></home-assistant-api>
|
<home-assistant-icons></home-assistant-icons>
|
||||||
|
|
||||||
<template if="{{!loaded}}">
|
<template>
|
||||||
|
<template is='dom-if' if='[[!loaded]]'>
|
||||||
<login-form></login-form>
|
<login-form></login-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template if="{{loaded}}">
|
<template is='dom-if' if='[[loaded]]'>
|
||||||
<home-assistant-main></home-assistant-main>
|
<home-assistant-main></home-assistant-main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn,
|
var storeListenerMixIn = window.hass.storeListenerMixIn,
|
||||||
uiActions = window.hass.uiActions,
|
uiActions = window.hass.uiActions,
|
||||||
preferenceStore = window.hass.preferenceStore;
|
preferenceStore = window.hass.preferenceStore;
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
loaded: false,
|
is: 'home-assistant',
|
||||||
|
|
||||||
|
hostAttributes: {
|
||||||
|
auth: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
behaviors: [StoreListenerBehavior],
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
loaded: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
ready: function() {
|
ready: function() {
|
||||||
// remove the HTML init message
|
// remove the HTML init message
|
||||||
@ -49,17 +70,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
this.listenToStores(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
this.stopListeningToStores();
|
|
||||||
},
|
|
||||||
|
|
||||||
syncStoreChanged: function(syncStore) {
|
syncStoreChanged: function(syncStore) {
|
||||||
this.loaded = syncStore.initialLoadDone;
|
this.loaded = syncStore.initialLoadDone;
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
});
|
||||||
</script>
|
|
||||||
</polymer-element>
|
})();
|
||||||
|
</script>
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"removeComments": true,
|
||||||
|
"removeCommentsFromCDATA": true,
|
||||||
|
"removeCDATASectionsFromCDATA": true,
|
||||||
|
"collapseWhitespace": true,
|
||||||
|
"collapseBooleanAttributes": true,
|
||||||
|
"removeScriptTypeAttributes": true,
|
||||||
|
"removeStyleLinkTypeAttributes": true,
|
||||||
|
"minifyJS": true,
|
||||||
|
"minifyCSS": true
|
||||||
|
}
|
@ -1,64 +1,50 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
|
<link rel='import' href='../bower_components/layout/layout.html'>
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/core-drawer-panel/core-drawer-panel.html">
|
<link rel='import' href='../bower_components/paper-drawer-panel/paper-drawer-panel.html'>
|
||||||
<link rel="import" href="../bower_components/core-header-panel/core-header-panel.html">
|
<link rel='import' href='../bower_components/paper-header-panel/paper-header-panel.html'>
|
||||||
<link rel="import" href="../bower_components/core-toolbar/core-toolbar.html">
|
<link rel='import' href='../bower_components/paper-toolbar/paper-toolbar.html'>
|
||||||
<link rel="import" href="../bower_components/core-menu/core-menu.html">
|
<link rel='import' href='../bower_components/paper-menu/paper-menu.html'>
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
<link rel='import' href='../bower_components/iron-icon/iron-icon.html'>
|
||||||
<link rel="import" href="../bower_components/core-icon/core-icon.html">
|
<link rel='import' href='../bower_components/paper-item/paper-item.html'>
|
||||||
<link rel="import" href="../bower_components/paper-item/paper-item.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="../layouts/partial-states.html">
|
<link rel='import' href='../layouts/partial-states.html'>
|
||||||
<link rel="import" href="../layouts/partial-history.html">
|
<link rel='import' href='../layouts/partial-logbook.html'>
|
||||||
<link rel="import" href="../layouts/partial-logbook.html">
|
<link rel='import' href='../layouts/partial-history.html'>
|
||||||
<link rel="import" href="../layouts/partial-dev-fire-event.html">
|
<link rel='import' href='../layouts/partial-dev-call-service.html'>
|
||||||
<link rel="import" href="../layouts/partial-dev-call-service.html">
|
<link rel='import' href='../layouts/partial-dev-fire-event.html'>
|
||||||
<link rel="import" href="../layouts/partial-dev-set-state.html">
|
<link rel='import' href='../layouts/partial-dev-set-state.html'>
|
||||||
|
|
||||||
<link rel="import" href="../components/ha-notifications.html">
|
<link rel='import' href='../managers/notification-manager.html'>
|
||||||
<link rel="import" href="../components/ha-modals.html">
|
<link rel='import' href='../managers/modal-manager.html'>
|
||||||
<link rel="import" href="../components/stream-status.html">
|
|
||||||
|
|
||||||
<polymer-element name="home-assistant-main">
|
<link rel='import' href='../components/stream-status.html'>
|
||||||
<template>
|
|
||||||
<core-style ref="ha-headers"></core-style>
|
|
||||||
|
|
||||||
|
<dom-module id='home-assistant-main'>
|
||||||
<style>
|
<style>
|
||||||
.sidenav {
|
.sidenav {
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
box-shadow: 1px 0 1px rgba(0, 0, 0, 0.1);
|
box-shadow: 1px 0 1px rgba(0, 0, 0, 0.1);
|
||||||
color: #757575;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
core-toolbar {
|
.sidenav paper-menu {
|
||||||
font-weight: normal;
|
--paper-menu-color: var(--secondary-text-color);
|
||||||
padding-left: 24px;
|
--paper-menu-background-color: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidenav-menu {
|
paper-icon-item {
|
||||||
overflow: auto;
|
cursor: pointer;
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidenav-menu core-icon {
|
.divider {
|
||||||
margin-right: 24px;
|
border-top: 1px solid #e0e0e0;
|
||||||
}
|
|
||||||
|
|
||||||
.sidenav-menu > paper-item {
|
|
||||||
min-height: 53px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,126 +53,218 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ha-notifications></ha-notifications>
|
<template>
|
||||||
<ha-modals></ha-modals>
|
<notification-manager></notification-manager>
|
||||||
|
<modal-manager></modal-manager>
|
||||||
|
|
||||||
<core-drawer-panel id="drawer" on-core-responsive-change="{{responsiveChanged}}">
|
<paper-drawer-panel id='drawer' narrow='{{narrow}}'>
|
||||||
<core-header-panel mode="scroll" drawer class='sidenav'>
|
<paper-header-panel mode='scroll' drawer class='sidenav fit'>
|
||||||
<core-toolbar>
|
<paper-toolbar>
|
||||||
Home Assistant
|
<!-- forces paper toolbar to style title appropriate -->
|
||||||
</core-toolbar>
|
<paper-icon-button hidden></paper-icon-button>
|
||||||
<core-menu id="menu" class="sidenav-menu"
|
<div title>Home Assistant</div>
|
||||||
selected="0" excludedLocalNames="div" on-core-select="{{menuSelect}}"
|
</paper-toolbar>
|
||||||
layout vertical>
|
|
||||||
<paper-item data-panel="states">
|
|
||||||
<core-icon icon="apps"></core-icon>
|
|
||||||
States
|
|
||||||
</paper-item>
|
|
||||||
|
|
||||||
<template repeat="{{activeFilters as filter}}">
|
<paper-menu id='menu' class='layout vertical fit'
|
||||||
<paper-item data-panel="states_{{filter}}">
|
selectable='[data-panel]' attr-for-selected='data-panel'
|
||||||
<core-icon icon="{{filter | filterIcon}}"></core-icon>
|
on-iron-select='menuSelect' selected='[[selected]]'>
|
||||||
{{filter | filterName}}
|
<paper-icon-item data-panel='states'>
|
||||||
</paper-item>
|
<iron-icon item-icon icon='apps'></iron-icon> States
|
||||||
|
</paper-icon-item>
|
||||||
|
|
||||||
|
<template is='dom-repeat' items='{{activeFilters}}'>
|
||||||
|
<paper-icon-item data-panel$='[[filterType(item)]]'>
|
||||||
|
<iron-icon item-icon icon='[[filterIcon(item)]]'></iron-icon>
|
||||||
|
<span>[[filterName(item)]]</span>
|
||||||
|
</paper-icon-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template if="{{hasHistoryComponent}}">
|
<template is='dom-if' if='[[hasHistoryComponent]]'>
|
||||||
<paper-item data-panel="history">
|
<paper-icon-item data-panel='history'>
|
||||||
<core-icon icon="assessment"></core-icon>
|
<iron-icon item-icon icon='assessment'></iron-icon>
|
||||||
History
|
History
|
||||||
</paper-item>
|
</paper-icon-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template if="{{hasLogbookComponent}}">
|
<template is='dom-if' if='[[hasLogbookComponent]]'>
|
||||||
<paper-item data-panel="logbook">
|
<paper-icon-item data-panel='logbook'>
|
||||||
<core-icon icon="list"></core-icon>
|
<iron-icon item-icon icon='list'></iron-icon>
|
||||||
Logbook
|
Logbook
|
||||||
</paper-item>
|
</paper-icon-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div flex></div>
|
<div class='flex'></div>
|
||||||
|
|
||||||
<paper-item on-click="{{handleLogOutClick}}">
|
<paper-icon-item data-panel='logout'>
|
||||||
<core-icon icon="exit-to-app"></core-icon>
|
<iron-icon item-icon icon='exit-to-app'></iron-icon>
|
||||||
Log Out
|
Log Out
|
||||||
|
</paper-icon-item>
|
||||||
|
|
||||||
|
<paper-item class='divider horizontal layout justified'>
|
||||||
|
<div>Streaming updates</div>
|
||||||
|
<stream-status></stream-status>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
|
|
||||||
<div class='text' horizontal layout center>
|
<div class='text label divider'>Developer Tools</div>
|
||||||
<div flex>Streaming updates</div>
|
<div class='dev-tools layout horizontal justified'>
|
||||||
<stream-status></stream-status>
|
<paper-icon-button
|
||||||
|
icon='settings-remote' data-panel$='[[selectedDevService]]'
|
||||||
|
on-click='handleDevClick'></paper-icon-button>
|
||||||
|
<paper-icon-button
|
||||||
|
icon='settings-ethernet' data-panel$='[[selectedDevState]]'
|
||||||
|
on-click='handleDevClick'></paper-icon-button>
|
||||||
|
<paper-icon-button
|
||||||
|
icon='settings-input-antenna' data-panel$='[[selectedDevEvent]]'
|
||||||
|
on-click='handleDevClick'></paper-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
</paper-menu>
|
||||||
|
</paper-header-panel>
|
||||||
|
|
||||||
<div class='text label'>Developer Tools</div>
|
<template is='dom-if' if='[[!hideStates]]'>
|
||||||
<div class='dev-tools' layout horizontal justified>
|
<partial-states
|
||||||
<paper-icon-button
|
main narrow='[[narrow]]'
|
||||||
icon="settings-remote" data-panel='call-service'
|
filter='[[stateFilter]]'>
|
||||||
on-click="{{handleDevClick}}"></paper-icon-button>
|
|
||||||
<paper-icon-button
|
|
||||||
icon="settings-ethernet" data-panel='set-state'
|
|
||||||
on-click="{{handleDevClick}}"></paper-icon-button>
|
|
||||||
<paper-icon-button
|
|
||||||
icon="settings-input-antenna" data-panel='fire-event'
|
|
||||||
on-click="{{handleDevClick}}"></paper-icon-button>
|
|
||||||
</div>
|
|
||||||
</core-menu>
|
|
||||||
</core-header-panel>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This is the main partial, never remove it from the DOM but hide it
|
|
||||||
to speed up when people click on states.
|
|
||||||
-->
|
|
||||||
<partial-states hidden?="{{hideStates}}"
|
|
||||||
main narrow="{{narrow}}"
|
|
||||||
togglePanel="{{togglePanel}}"
|
|
||||||
filter="{{stateFilter}}">
|
|
||||||
</partial-states>
|
</partial-states>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template if="{{selected == 'history'}}">
|
<template is='dom-if' if='[[isSelectedLogbook]]'>
|
||||||
<partial-history main narrow="{{narrow}}" togglePanel="{{togglePanel}}"></partial-history>
|
<partial-logbook main narrow='[[narrow]]'></partial-logbook>
|
||||||
</template>
|
</template>
|
||||||
<template if="{{selected == 'logbook'}}">
|
<template is='dom-if' if='[[isSelectedHistory]]'>
|
||||||
<partial-logbook main narrow="{{narrow}}" togglePanel="{{togglePanel}}"></partial-logbook>
|
<partial-history main narrow='[[narrow]]'></partial-history>
|
||||||
</template>
|
</template>
|
||||||
<template if="{{selected == 'fire-event'}}">
|
<template is='dom-if' if='[[isSelectedDevService]]'>
|
||||||
<partial-dev-fire-event main narrow="{{narrow}}" togglePanel="{{togglePanel}}"></partial-dev-fire-event>
|
<partial-dev-call-service main narrow='[[narrow]]'></partial-dev-call-service>
|
||||||
</template>
|
</template>
|
||||||
<template if="{{selected == 'set-state'}}">
|
<template is='dom-if' if='[[isSelectedDevEvent]]'>
|
||||||
<partial-dev-set-state main narrow="{{narrow}}" togglePanel="{{togglePanel}}"></partial-dev-set-state>
|
<partial-dev-fire-event main narrow='[[narrow]]'></partial-dev-fire-event>
|
||||||
</template>
|
</template>
|
||||||
<template if="{{selected == 'call-service'}}">
|
<template is='dom-if' if='[[isSelectedDevState]]'>
|
||||||
<partial-dev-call-service main narrow="{{narrow}}" togglePanel="{{togglePanel}}"></partial-dev-call-service>
|
<partial-dev-set-state main narrow='[[narrow]]'></partial-dev-set-state>
|
||||||
</template>
|
</template>
|
||||||
</core-drawer-panel>
|
</paper-drawer-panel>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
var authActions = window.hass.authActions;
|
||||||
var authActions = window.hass.authActions;
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
var uiConstants = window.hass.uiConstants;
|
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
var uiUtil = window.hass.uiUtil;
|
||||||
selected: "states",
|
var uiConstants = window.hass.uiConstants;
|
||||||
stateFilter: null,
|
|
||||||
narrow: false,
|
|
||||||
activeFilters: [],
|
|
||||||
hasHistoryComponent: false,
|
|
||||||
hasLogbookComponent: false,
|
|
||||||
|
|
||||||
isStreaming: false,
|
Polymer({
|
||||||
hasStreamError: false,
|
is: 'home-assistant-main',
|
||||||
|
|
||||||
hideStates: false,
|
behaviors: [StoreListenerBehavior],
|
||||||
|
|
||||||
attached: function() {
|
properties: {
|
||||||
this.togglePanel = this.togglePanel.bind(this);
|
selected: {
|
||||||
|
type: String,
|
||||||
this.listenToStores(true);
|
value: 'states',
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
stateFilter: {
|
||||||
this.stopListeningToStores();
|
type: String,
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
narrow: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
|
||||||
|
activeFilters: {
|
||||||
|
type: Array,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
hasHistoryComponent: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
hasLogbookComponent: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isStreaming: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
hasStreamError: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
hideStates: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedHistory: {
|
||||||
|
type: String,
|
||||||
|
value: 'history',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isSelectedHistory: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsSelected(selected, selectedHistory)',
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedLogbook: {
|
||||||
|
type: String,
|
||||||
|
value: 'logbook',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isSelectedLogbook: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsSelected(selected, selectedLogbook)',
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedDevEvent: {
|
||||||
|
type: String,
|
||||||
|
value: 'devEvent',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isSelectedDevEvent: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsSelected(selected, selectedDevEvent)',
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedDevState: {
|
||||||
|
type: String,
|
||||||
|
value: 'devState',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isSelectedDevState: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsSelected(selected, selectedDevState)',
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedDevService: {
|
||||||
|
type: String,
|
||||||
|
value: 'devService',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isSelectedDevService: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsSelected(selected, selectedDevService)',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
'menu.core-select': 'menuSelect',
|
||||||
|
'open-menu': 'openDrawer',
|
||||||
},
|
},
|
||||||
|
|
||||||
stateStoreChanged: function(stateStore) {
|
stateStoreChanged: function(stateStore) {
|
||||||
@ -200,51 +278,52 @@ Polymer(Polymer.mixin({
|
|||||||
this.hasLogbookComponent = componentStore.isLoaded('logbook');
|
this.hasLogbookComponent = componentStore.isLoaded('logbook');
|
||||||
},
|
},
|
||||||
|
|
||||||
streamStoreChanged: function(streamStore) {
|
|
||||||
this.isStreaming = streamStore.isStreaming;
|
|
||||||
this.hasStreamError = streamStore.hasError;
|
|
||||||
},
|
|
||||||
|
|
||||||
menuSelect: function(ev, detail, sender) {
|
menuSelect: function(ev, detail, sender) {
|
||||||
if (detail.isSelected) {
|
this.selectPanel(this.$.menu.selected);
|
||||||
this.selectPanel(detail.item);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDevClick: function(ev, detail, sender) {
|
handleDevClick: function(ev, detail, sender) {
|
||||||
this.$.menu.selected = -1;
|
// prevent it from highlighting first menu item
|
||||||
this.selectPanel(ev.target);
|
document.activeElement.blur();
|
||||||
|
this.selectPanel(ev.target.parentElement.dataset.panel);
|
||||||
},
|
},
|
||||||
|
|
||||||
selectPanel: function(element) {
|
selectPanel: function(newChoice) {
|
||||||
var newChoice = element.dataset.panel;
|
if (newChoice == 'logout') {
|
||||||
|
this.handleLogOut();
|
||||||
if(newChoice !== this.selected) {
|
return;
|
||||||
this.togglePanel();
|
} else if(newChoice == this.selected) {
|
||||||
this.selected = newChoice;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selected.substr(0, 7) === 'states_') {
|
this.closeDrawer();
|
||||||
|
this.selected = newChoice;
|
||||||
|
|
||||||
|
if (newChoice.substr(0, 7) === 'states_') {
|
||||||
this.hideStates = false;
|
this.hideStates = false;
|
||||||
this.stateFilter = this.selected.substr(7);
|
this.stateFilter = newChoice.substr(7);
|
||||||
} else {
|
} else {
|
||||||
this.hideStates = this.selected !== 'states';
|
this.hideStates = newChoice !== 'states';
|
||||||
this.stateFilter = null;
|
this.stateFilter = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
responsiveChanged: function(ev, detail, sender) {
|
openDrawer: function() {
|
||||||
this.narrow = detail.narrow;
|
this.$.drawer.openDrawer();
|
||||||
},
|
},
|
||||||
|
|
||||||
togglePanel: function() {
|
closeDrawer: function() {
|
||||||
this.$.drawer.togglePanel();
|
this.$.drawer.closeDrawer();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleLogOutClick: function() {
|
handleLogOut: function() {
|
||||||
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);
|
||||||
},
|
},
|
||||||
@ -252,7 +331,10 @@ Polymer(Polymer.mixin({
|
|||||||
filterName: function(filter) {
|
filterName: function(filter) {
|
||||||
return uiConstants.STATE_FILTERS[filter];
|
return uiConstants.STATE_FILTERS[filter];
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
|
||||||
|
filterType: function(filter) {
|
||||||
|
return 'states_' + filter;
|
||||||
|
}
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../bower_components/layout/layout.html">
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/core-label/core-label.html">
|
<!-- WIP <link rel="import" href="../bower_components/core-label/core-label.html"> -->
|
||||||
<link rel="import" href="../bower_components/paper-checkbox/paper-checkbox.html">
|
<link rel="import" href="../bower_components/paper-checkbox/paper-checkbox.html">
|
||||||
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input-decorator.html">
|
<link rel="import" href="../bower_components/paper-input/paper-input-container.html">
|
||||||
<link rel="import" href="../bower_components/core-input/core-input.html">
|
<link rel="import" href="../bower_components/paper-input/paper-input-error.html">
|
||||||
|
<link rel="import" href="../bower_components/iron-input/iron-input.html">
|
||||||
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
||||||
|
|
||||||
<polymer-element name="login-form">
|
<link rel="import" href="../resources/store-listener-behavior.html">
|
||||||
<template>
|
|
||||||
|
<dom-module id="login-form">
|
||||||
<style>
|
<style>
|
||||||
#passwordDecorator {
|
#passwordDecorator {
|
||||||
display: block;
|
display: block;
|
||||||
@ -45,35 +48,32 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div layout horizontal center fit class='login' id="splash">
|
<template>
|
||||||
<div layout vertical center flex>
|
<div class="layout horizontal center fit login" id="splash">
|
||||||
|
<div class="layout vertical center flex">
|
||||||
|
|
||||||
<img src="/static/favicon-192x192.png" />
|
<img src="/static/favicon-192x192.png" />
|
||||||
<h1>Home Assistant</h1>
|
<h1>Home Assistant</h1>
|
||||||
|
|
||||||
<a href="#" id="hideKeyboardOnFocus"></a>
|
<a href="#" id="hideKeyboardOnFocus"></a>
|
||||||
|
|
||||||
<div class='interact' layout vertical>
|
<div class='interact'>
|
||||||
|
<div id='loginform' hidden$="[[isValidating]]">
|
||||||
|
<paper-input-container id="passwordDecorator" invalid="[[isInvalid]]">
|
||||||
|
<label>Password</label>
|
||||||
|
<input is="iron-input" type="password" id="passwordInput" />
|
||||||
|
<paper-input-error invalid="[[isInvalid]]">[[errorMessage]]</paper-input-error>
|
||||||
|
</paper-input-container>
|
||||||
|
|
||||||
<div id='loginform' hidden?="{{isValidating || isLoggedIn}}">
|
<div class="layout horizontal center">
|
||||||
<paper-input-decorator label="Password" id="passwordDecorator">
|
<paper-checkbox for id='rememberLogin'>Remember</paper-checkbox>
|
||||||
<input is="core-input" type="password" id="passwordInput"
|
<paper-button id='loginButton'>Log In</paper-button>
|
||||||
value="{{authToken}}" on-keyup="{{passwordKeyup}}">
|
|
||||||
</paper-input-decorator>
|
|
||||||
|
|
||||||
<div horizontal center layout>
|
|
||||||
<core-label horizontal layout>
|
|
||||||
<paper-checkbox for checked={{rememberLogin}}></paper-checkbox>
|
|
||||||
Remember
|
|
||||||
</core-label>
|
|
||||||
|
|
||||||
<paper-button on-click={{validatePassword}}>Log In</paper-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="validatebox" hidden?="{{!(isValidating || isLoggedIn)}}">
|
<div id="validatebox" hidden$="[[!isValidating]]">
|
||||||
<paper-spinner active="true"></paper-spinner><br />
|
<paper-spinner active="true"></paper-spinner><br />
|
||||||
<div class="validatemessage">{{spinnerMessage}}</div>
|
<div class="validatemessage">Loading data…</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -81,43 +81,53 @@
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
</dom-module>
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
var uiActions = window.hass.uiActions;
|
var uiActions = window.hass.uiActions;
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
MSG_VALIDATING: "Validating password…",
|
is: 'login-form',
|
||||||
MSG_LOADING_DATA: "Loading data…",
|
|
||||||
|
|
||||||
authToken: "",
|
behaviors: [StoreListenerBehavior],
|
||||||
rememberLogin: false,
|
|
||||||
|
|
||||||
isValidating: false,
|
properties: {
|
||||||
isLoggedIn: false,
|
isValidating: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
spinnerMessage: "",
|
isInvalid: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
errorMessage: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
'passwordInput.keydown': 'passwordKeyDown',
|
||||||
|
'loginButton.click': 'validatePassword',
|
||||||
|
},
|
||||||
|
|
||||||
attached: function() {
|
attached: function() {
|
||||||
this.focusPassword();
|
this.focusPassword();
|
||||||
this.listenToStores(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
this.stopListeningToStores();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
authStoreChanged: function(authStore) {
|
authStoreChanged: function(authStore) {
|
||||||
this.isValidating = authStore.isValidating;
|
this.isValidating = authStore.isValidating;
|
||||||
this.isLoggedIn = authStore.isLoggedIn;
|
|
||||||
this.spinnerMessage = this.isValidating ? this.MSG_VALIDATING : this.MSG_LOADING_DATA;
|
|
||||||
|
|
||||||
if (authStore.lastAttemptInvalid) {
|
if (authStore.lastAttemptInvalid) {
|
||||||
this.$.passwordDecorator.error = authStore.lastAttemptMessage;
|
this.errorMessage = authStore.lastAttemptMessage;
|
||||||
this.$.passwordDecorator.isInvalid = true;
|
this.isInvalid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(this.isValidating && this.isLoggedIn)) {
|
if (!this.isValidating) {
|
||||||
this.job('focusPasswordBox', this.focusPassword.bind(this));
|
setTimeout(this.focusPassword.bind(this), 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -125,23 +135,25 @@
|
|||||||
this.$.passwordInput.focus();
|
this.$.passwordInput.focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
passwordKeyup: function(ev) {
|
passwordKeyDown: function(ev) {
|
||||||
// validate on enter
|
// validate on enter
|
||||||
if(ev.keyCode === 13) {
|
if(ev.keyCode === 13) {
|
||||||
this.validatePassword();
|
this.validatePassword();
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
// clear error after we start typing again
|
// clear error after we start typing again
|
||||||
} else if(this.$.passwordDecorator.isInvalid) {
|
} else if(this.isInvalid) {
|
||||||
this.$.passwordDecorator.isInvalid = false;
|
this.isInvalid = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
validatePassword: function() {
|
validatePassword: function() {
|
||||||
this.$.hideKeyboardOnFocus.focus();
|
this.$.hideKeyboardOnFocus.focus();
|
||||||
|
|
||||||
uiActions.validateAuth(this.authToken, this.rememberLogin);
|
uiActions.validateAuth(this.$.passwordInput.value, this.$.rememberLogin.checked);
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
});
|
||||||
</script>
|
})();
|
||||||
</polymer-element>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,28 +1,41 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/core-scroll-header-panel/core-scroll-header-panel.html">
|
<link rel='import' href='../bower_components/paper-scroll-header-panel/paper-scroll-header-panel.html'>
|
||||||
<link rel="import" href="../bower_components/core-toolbar/core-toolbar.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
<link rel='import' href='../bower_components/paper-toolbar/paper-toolbar.html'>
|
||||||
|
<link rel='import' href='../bower_components/paper-icon-button/paper-icon-button.html'>
|
||||||
|
|
||||||
<polymer-element name="partial-base" attributes="narrow togglePanel" noscript>
|
<dom-module id='partial-base'>
|
||||||
<template>
|
<template>
|
||||||
<core-style ref="ha-headers"></core-style>
|
<paper-scroll-header-panel class='fit'>
|
||||||
|
<paper-toolbar>
|
||||||
<core-scroll-header-panel fit fixed="{{!narrow}}">
|
<paper-icon-button icon='menu' hidden$='[[!narrow]]' on-click='toggleMenu'></paper-icon-button>
|
||||||
<core-toolbar>
|
<div title>
|
||||||
<paper-icon-button
|
<content select='[header-title]'></content>
|
||||||
id="navicon" icon="menu" hidden?="{{!narrow}}"
|
|
||||||
on-click="{{togglePanel}}"></paper-icon-button>
|
|
||||||
<div flex>
|
|
||||||
<content select="[header-title]"></content>
|
|
||||||
</div>
|
</div>
|
||||||
<content select="[header-buttons]"></content>
|
<content select='[header-buttons]'></content>
|
||||||
</core-toolbar>
|
</paper-toolbar>
|
||||||
|
|
||||||
<content></content>
|
<content></content>
|
||||||
|
</paper-scroll-header-panel>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</core-scroll-header-panel>
|
<script>
|
||||||
</template>
|
(function() {
|
||||||
</polymer>
|
Polymer({
|
||||||
|
is: 'partial-base',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
narrow: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleMenu: function() {
|
||||||
|
this.fire('open-menu');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@ -2,87 +2,100 @@
|
|||||||
|
|
||||||
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input.html">
|
<link rel="import" href="../bower_components/paper-input/paper-input.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input-decorator.html">
|
<link rel="import" href="../bower_components/paper-input/paper-textarea.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-autogrow-textarea.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
<link rel="import" href="./partial-base.html">
|
||||||
|
|
||||||
<link rel="import" href="../components/services-list.html">
|
<link rel="import" href="../components/services-list.html">
|
||||||
|
|
||||||
<polymer-element name="partial-dev-call-service" attributes="narrow togglePanel">
|
<dom-module id="partial-dev-call-service">
|
||||||
<template>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.form {
|
.form {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<partial-base narrow="{{narrow}}" togglePanel="{{togglePanel}}">
|
.ha-form {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<partial-base narrow="[[narrow]]">
|
||||||
<span header-title>Call Service</span>
|
<span header-title>Call Service</span>
|
||||||
|
|
||||||
<div class='form' fit>
|
<div class='form fit'>
|
||||||
<p>
|
<p>
|
||||||
Call a service from a component.
|
Call a service from a component.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div layout horizontal?="{{!narrow}}" vertical?="{{narrow}}">
|
<div class$='[[computeFormClasses(narrow)]]'>
|
||||||
<div class='ha-form' flex?="{{!narrow}}">
|
<div class='ha-form flex'>
|
||||||
<paper-input id="inputDomain" label="Domain" floatingLabel="true" autofocus required></paper-input>
|
<paper-input label="Domain" autofocus value='{{domain}}'></paper-input>
|
||||||
<paper-input id="inputService" label="Service" floatingLabel="true" required></paper-input>
|
<paper-input label="Service" value='{{service}}'></paper-input>
|
||||||
<paper-input-decorator
|
<paper-textarea label="Service Data (JSON, optional)" value='{{serviceData}}'></paper-textarea>
|
||||||
label="Service Data (JSON, optional)"
|
<paper-button on-click='callService' raised>Call Service</paper-button>
|
||||||
floatingLabel="true">
|
|
||||||
|
|
||||||
<paper-autogrow-textarea id="inputDataWrapper">
|
|
||||||
<textarea id="inputData"></textarea>
|
|
||||||
</paper-autogrow-textarea>
|
|
||||||
|
|
||||||
</paper-input-decorator>
|
|
||||||
<paper-button on-click={{clickCallService}}>Call Service</paper-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='sidebar'>
|
<div>
|
||||||
<b>Available services:</b>
|
<h4>Available services:</h4>
|
||||||
<services-list cbServiceClicked={{serviceSelected}}></services-list>
|
<services-list on-service-selected='serviceSelected'></services-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</partial-base>
|
</partial-base>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
var serviceActions = window.hass.serviceActions;
|
(function() {
|
||||||
|
var serviceActions = window.hass.serviceActions;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
ready: function() {
|
is: 'partial-dev-call-service',
|
||||||
// to ensure callback methods work..
|
|
||||||
this.serviceSelected = this.serviceSelected.bind(this);
|
properties: {
|
||||||
|
narrow: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
setService: function(domain, service) {
|
domain: {
|
||||||
this.$.inputDomain.value = domain;
|
type: String,
|
||||||
this.$.inputService.value = service;
|
value: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
serviceSelected: function(domain, service) {
|
service: {
|
||||||
this.setService(domain, service);
|
type: String,
|
||||||
|
value: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
clickCallService: function() {
|
serviceData: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceSelected: function(ev) {
|
||||||
|
this.domain = ev.detail.domain;
|
||||||
|
this.service = ev.detail.service;
|
||||||
|
},
|
||||||
|
|
||||||
|
callService: function() {
|
||||||
|
var serviceData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serviceActions.callService(
|
serviceData = this.serviceData ? JSON.parse(this.serviceData): {};
|
||||||
this.$.inputDomain.value,
|
|
||||||
this.$.inputService.value,
|
|
||||||
this.$.inputData.value ? JSON.parse(this.$.inputData.value) : {});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Error parsing JSON: " + err);
|
alert("Error parsing JSON: " + err);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
serviceActions.callService(this.domain, this.service, serviceData);
|
||||||
|
},
|
||||||
|
|
||||||
|
computeFormClasses: function(narrow) {
|
||||||
|
return 'layout ' + (narrow ? 'vertical' : 'horizontal');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -2,79 +2,90 @@
|
|||||||
|
|
||||||
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input.html">
|
<link rel="import" href="../bower_components/paper-input/paper-input.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input-decorator.html">
|
<link rel="import" href="../bower_components/paper-input/paper-textarea.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-autogrow-textarea.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
<link rel="import" href="./partial-base.html">
|
||||||
|
|
||||||
<link rel="import" href="../components/events-list.html">
|
<link rel="import" href="../components/events-list.html">
|
||||||
|
|
||||||
<polymer-element name="partial-dev-fire-event" attributes="narrow togglePanel">
|
<dom-module id="partial-dev-fire-event">
|
||||||
<template>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.form {
|
.form {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ha-form {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<partial-base narrow="{{narrow}}" togglePanel="{{togglePanel}}">
|
<template>
|
||||||
|
<partial-base narrow="{{narrow}}">
|
||||||
<span header-title>Fire Event</span>
|
<span header-title>Fire Event</span>
|
||||||
|
|
||||||
<div class='form' fit>
|
<div class='form fit'>
|
||||||
<p>
|
<p>
|
||||||
Fire an event on the event bus.
|
Fire an event on the event bus.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div layout horizontal?="{{!narrow}}" vertical?="{{narrow}}">
|
<div class$='[[computeFormClasses(narrow)]]'>
|
||||||
<div class='ha-form' flex?="{{!narrow}}">
|
<div class='ha-form flex'>
|
||||||
<paper-input
|
<paper-input label="Event Type" autofocus required value='{{eventType}}'></paper-input>
|
||||||
id="inputType" label="Event Type" floatingLabel="true"
|
<paper-textarea label="Event Data (JSON, optional)" value='{{eventData}}'></paper-textarea>
|
||||||
autofocus required></paper-input>
|
<paper-button on-click='fireEvent' raised>Fire Event</paper-button>
|
||||||
<paper-input-decorator
|
|
||||||
label="Event Data (JSON, optional)"
|
|
||||||
floatingLabel="true">
|
|
||||||
|
|
||||||
<paper-autogrow-textarea id="inputDataWrapper">
|
|
||||||
<textarea id="inputData"></textarea>
|
|
||||||
</paper-autogrow-textarea>
|
|
||||||
</paper-input-decorator>
|
|
||||||
|
|
||||||
<paper-button on-click={{clickFireEvent}}>Fire Event</paper-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='sidebar'>
|
<div>
|
||||||
<b>Available events:</b>
|
<h4>Available events:</h4>
|
||||||
<events-list cbEventClicked={{eventSelected}}></event-list>
|
<events-list on-event-selected='eventSelected'></event-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</partial-base>
|
</partial-base>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var eventActions = window.hass.eventActions;
|
(function() {
|
||||||
|
var eventActions = window.hass.eventActions;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
ready: function() {
|
is: 'partial-dev-fire-event',
|
||||||
// to ensure callback methods work..
|
|
||||||
this.eventSelected = this.eventSelected.bind(this);
|
properties: {
|
||||||
|
eventType: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
eventSelected: function(eventType) {
|
eventData: {
|
||||||
this.$.inputType.value = eventType;
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
clickFireEvent: function() {
|
eventSelected: function(ev) {
|
||||||
|
this.eventType = ev.detail.eventType;
|
||||||
|
},
|
||||||
|
|
||||||
|
fireEvent: function() {
|
||||||
|
var eventData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
eventActions.fire(
|
eventData = this.eventData ? JSON.parse(this.eventData) : {};
|
||||||
this.$.inputType.value,
|
|
||||||
this.$.inputData.value ? JSON.parse(this.$.inputData.value) : {});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Error parsing JSON: " + err);
|
alert("Error parsing JSON: " + err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
eventActions.fire(this.eventType, eventData);
|
||||||
|
},
|
||||||
|
|
||||||
|
computeFormClasses: function(narrow) {
|
||||||
|
return 'layout ' + (narrow ? 'vertical' : 'horizontal');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -2,75 +2,75 @@
|
|||||||
|
|
||||||
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input.html">
|
<link rel="import" href="../bower_components/paper-input/paper-input.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input-decorator.html">
|
<link rel="import" href="../bower_components/paper-input/paper-textarea.html">
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-autogrow-textarea.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
<link rel="import" href="./partial-base.html">
|
||||||
|
|
||||||
<link rel="import" href="../components/entity-list.html">
|
<link rel="import" href="../components/entity-list.html">
|
||||||
|
|
||||||
<polymer-element name="partial-dev-set-state" attributes="narrow togglePanel">
|
<dom-module id="partial-dev-set-state">
|
||||||
<template>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.form {
|
.form {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ha-form {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<partial-base narrow="{{narrow}}" togglePanel="{{togglePanel}}">
|
<template>
|
||||||
|
<partial-base narrow="[[narrow]]">
|
||||||
<span header-title>Set State</span>
|
<span header-title>Set State</span>
|
||||||
|
|
||||||
<div class='form' fit>
|
<div class='form fit'>
|
||||||
<div>
|
<div>
|
||||||
Set the representation of a device within Home Assistant.<br />
|
Set the representation of a device within Home Assistant.<br />
|
||||||
This will not communicate with the actual device.
|
This will not communicate with the actual device.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div layout horizontal?="{{!narrow}}" vertical?="{{narrow}}">
|
<div class$='[[computeFormClasses(narrow)]]'>
|
||||||
<div class='ha-form' flex?="{{!narrow}}">
|
<div class='ha-form flex'>
|
||||||
<paper-input id="inputEntityID" label="Entity ID" floatingLabel="true" autofocus required></paper-input>
|
<paper-input label="Entity ID" autofocus required value='{{entityId}}'></paper-input>
|
||||||
<paper-input id="inputState" label="State" floatingLabel="true" required></paper-input>
|
<paper-input label="State" required value='{{state}}'></paper-input>
|
||||||
<paper-input-decorator
|
<paper-textarea label="State attributes (JSON, optional)" value='{{stateAttributes}}'></paper-textarea>
|
||||||
label="State attributes (JSON, optional)"
|
<paper-button on-click='handleSetState' raised>Set State</paper-button>
|
||||||
floatingLabel="true">
|
|
||||||
|
|
||||||
<paper-autogrow-textarea id="inputDataWrapper">
|
|
||||||
<textarea id="inputData"></textarea>
|
|
||||||
</paper-autogrow-textarea>
|
|
||||||
|
|
||||||
</paper-input-decorator>
|
|
||||||
|
|
||||||
<paper-button on-click={{clickSetState}}>Set State</paper-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='sidebar'>
|
<div class='sidebar'>
|
||||||
<b>Current entities:</b>
|
<h4>Current entities:</h4>
|
||||||
<entity-list cbEntityClicked={{entitySelected}}></entity-list>
|
<entity-list on-entity-selected='entitySelected'></entity-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</partial-base>
|
</partial-base>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
var stateStore = window.hass.stateStore;
|
(function() {
|
||||||
var stateActions = window.hass.stateActions;
|
var stateStore = window.hass.stateStore;
|
||||||
|
var stateActions = window.hass.stateActions;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
ready: function() {
|
is: 'partial-dev-set-state',
|
||||||
// to ensure callback methods work..
|
|
||||||
this.entitySelected = this.entitySelected.bind(this);
|
properties: {
|
||||||
|
entityId: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
setEntityId: function(entityId) {
|
state: {
|
||||||
this.$.inputEntityID.value = entityId;
|
type: String,
|
||||||
|
value: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
setState: function(state) {
|
stateAttributes: {
|
||||||
this.$.inputState.value = state;
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setStateData: function(stateData) {
|
setStateData: function(stateData) {
|
||||||
@ -82,25 +82,29 @@ Polymer({
|
|||||||
this.$.inputDataWrapper.update(this.$.inputData);
|
this.$.inputDataWrapper.update(this.$.inputData);
|
||||||
},
|
},
|
||||||
|
|
||||||
entitySelected: function(entityId) {
|
entitySelected: function(ev) {
|
||||||
this.setEntityId(entityId);
|
var state = stateStore.get(ev.detail.entityId);
|
||||||
|
|
||||||
var state = stateStore.get(entityId);
|
this.entityId = state.entityId;
|
||||||
this.setState(state.state);
|
this.state = state.state;
|
||||||
this.setStateData(state.attributes);
|
this.stateAttributes = JSON.stringify(state.attributes, null, ' ');
|
||||||
},
|
},
|
||||||
|
|
||||||
clickSetState: function(ev) {
|
handleSetState: function() {
|
||||||
|
var attr;
|
||||||
try {
|
try {
|
||||||
stateActions.set(
|
attr = this.stateAttributes ? JSON.parse(this.stateAttributes) : {};
|
||||||
this.$.inputEntityID.value,
|
|
||||||
this.$.inputState.value,
|
|
||||||
this.$.inputData.value ? JSON.parse(this.$.inputData.value) : {}
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Error parsing JSON: " + err);
|
alert("Error parsing JSON: " + err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
stateActions.set(this.entityId, this.state, attr);
|
||||||
|
},
|
||||||
|
|
||||||
|
computeFormClasses: function(narrow) {
|
||||||
|
return 'layout ' + (narrow ? 'vertical' : 'horizontal');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
<link rel="import" href="./partial-base.html">
|
||||||
|
|
||||||
<link rel="import" href="../components/state-timeline.html">
|
<link rel="import" href="../components/state-history-charts.html">
|
||||||
|
|
||||||
<polymer-element name="partial-history" attributes="narrow togglePanel">
|
<dom-module id="partial-history">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
.content {
|
.content {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@ -17,33 +15,42 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<partial-base narrow="{{narrow}}" togglePanel="{{togglePanel}}">
|
<template>
|
||||||
|
<partial-base narrow="[[narrow]]">
|
||||||
<span header-title>History</span>
|
<span header-title>History</span>
|
||||||
|
|
||||||
<span header-buttons>
|
<paper-icon-button icon="refresh" header-buttons
|
||||||
<paper-icon-button icon="refresh"
|
on-click="handleRefreshClick"></paper-icon-button>
|
||||||
on-click="{{handleRefreshClick}}"></paper-icon-button>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div flex class="{{ {content: true, narrow: narrow, wide: !narrow} | tokenList }}">
|
<div class$="[[computeContentClasses(narrow)]]">
|
||||||
<state-timeline stateHistory="{{stateHistory}}" isLoadingData="{{isLoadingData}}"></state-timeline>
|
<state-history-charts state-history="[[stateHistory]]"
|
||||||
|
is-loading-data="[[isLoadingData]]"></state-history-charts>
|
||||||
</div>
|
</div>
|
||||||
</partial-base>
|
</partial-base>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
<script>
|
<script>
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
(function() {
|
||||||
var stateHistoryActions = window.hass.stateHistoryActions;
|
var stateHistoryActions = window.hass.stateHistoryActions;
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
stateHistory: null,
|
is: 'partial-history',
|
||||||
isLoadingData: false,
|
|
||||||
|
|
||||||
attached: function() {
|
behaviors: [StoreListenerBehavior],
|
||||||
this.listenToStores(true);
|
|
||||||
|
properties: {
|
||||||
|
narrow: {
|
||||||
|
type: Boolean,
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
stateHistory: {
|
||||||
this.stopListeningToStores();
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
isLoadingData: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
stateHistoryStoreChanged: function(stateHistoryStore) {
|
stateHistoryStoreChanged: function(stateHistoryStore) {
|
||||||
@ -62,6 +69,10 @@
|
|||||||
this.isLoadingData = true;
|
this.isLoadingData = true;
|
||||||
stateHistoryActions.fetchAll();
|
stateHistoryActions.fetchAll();
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
|
||||||
|
computeContentClasses: function(narrow) {
|
||||||
|
return 'flex content ' + (narrow ? 'narrow' : 'wide');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer>
|
|
||||||
|
@ -1,43 +1,50 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
<link rel="import" href="./partial-base.html">
|
||||||
|
|
||||||
<link rel="import" href="../components/ha-logbook.html">
|
<link rel="import" href="../components/ha-logbook.html">
|
||||||
|
|
||||||
<polymer-element name="partial-logbook" attributes="narrow togglePanel">
|
<dom-module id="partial-logbook">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
.content {
|
.content {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<partial-base narrow="{{narrow}}" togglePanel="{{togglePanel}}">
|
<template>
|
||||||
|
<partial-base narrow="[[narrow]]">
|
||||||
<span header-title>Logbook</span>
|
<span header-title>Logbook</span>
|
||||||
|
|
||||||
<span header-buttons>
|
<paper-icon-button icon="refresh" header-buttons
|
||||||
<paper-icon-button icon="refresh"
|
on-click="handleRefresh"></paper-icon-button>
|
||||||
on-click="{{handleRefreshClick}}"></paper-icon-button>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div flex class="{{ {content: true, narrow: narrow, wide: !narrow} | tokenList }}">
|
<ha-logbook entries="[[entries]]"></ha-logbook>
|
||||||
<ha-logbook entries="{{entries}}"></ha-logbook>
|
|
||||||
</div>
|
|
||||||
</partial-base>
|
</partial-base>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
(function() {
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
||||||
var logbookActions = window.hass.logbookActions;
|
var logbookActions = window.hass.logbookActions;
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
entries: null,
|
is: 'partial-logbook',
|
||||||
|
|
||||||
attached: function() {
|
behaviors: [StoreListenerBehavior],
|
||||||
this.listenToStores(true);
|
|
||||||
|
properties: {
|
||||||
|
narrow: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
entries: {
|
||||||
this.stopListeningToStores();
|
type: Array,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
logbookStoreChanged: function(logbookStore) {
|
logbookStoreChanged: function(logbookStore) {
|
||||||
@ -48,9 +55,9 @@
|
|||||||
this.entries = logbookStore.all.toArray();
|
this.entries = logbookStore.all.toArray();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleRefreshClick: function() {
|
handleRefresh: function() {
|
||||||
logbookActions.fetch();
|
logbookActions.fetch();
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer>
|
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/core-icon/core-icon.html">
|
<link rel="import" href="../bower_components/iron-icon/iron-icon.html">
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
<link rel="import" href="./partial-base.html">
|
||||||
|
|
||||||
<link rel="import" href="../components/state-cards.html">
|
<link rel="import" href="../components/state-cards.html">
|
||||||
|
|
||||||
<polymer-element name="partial-states" attributes="narrow togglePanel filter">
|
<dom-module id="partial-states">
|
||||||
<template>
|
|
||||||
<core-style ref="ha-animations"></core-style>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.listening {
|
.content-wrapper {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #E5E5E5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper ::content .listening {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -36,23 +39,26 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<partial-base narrow="{{narrow}}" togglePanel="{{togglePanel}}">
|
<template>
|
||||||
|
<partial-base narrow="[[narrow]]">
|
||||||
<span header-title>{{headerTitle}}</span>
|
<span header-title>{{headerTitle}}</span>
|
||||||
|
|
||||||
<span header-buttons>
|
<span header-buttons>
|
||||||
<paper-icon-button icon="refresh" class="{{isFetching && 'ha-spin'}}"
|
<paper-icon-button icon="refresh" class$="[[computeRefreshButtonClass(isFetching)]]"
|
||||||
on-click="{{handleRefreshClick}}" hidden?="{{isStreaming}}"></paper-icon-button>
|
on-click="handleRefresh" hidden$="[[isStreaming]]"></paper-icon-button>
|
||||||
<paper-icon-button icon="{{isListening ? 'av:mic-off' : 'av:mic' }}" hidden?={{!canListen}}
|
<paper-icon-button icon="[[listenButtonIcon]]" hidden$={{!canListen}}
|
||||||
on-click="{{handleListenClick}}"></paper-icon-button>
|
on-click="handleListenClick"></paper-icon-button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class='listening' hidden?="{{!isListening && !isTransmitting}}" on-click={{handleListenClick}}>
|
<div class='content-wrapper'>
|
||||||
<core-icon icon="av:hearing"></core-icon> {{finalTranscript}}
|
<div class='listening' hidden$="[[!showListenInterface]]"
|
||||||
<span class='interimTranscript'>{{interimTranscript}}</span>
|
on-click="handleListenClick">
|
||||||
<paper-spinner active?="{{isTransmitting}}"></paper-spinner>
|
<iron-icon icon="av:hearing"></iron-icon> <span>{{finalTranscript}}</span>
|
||||||
|
<span class='interimTranscript'>[[interimTranscript]]</span>
|
||||||
|
<paper-spinner active$="[[isTransmitting]]" alt="Sending voice command to Home Assistant"></paper-spinner>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<state-cards states="{{states}}">
|
<state-cards states="[[states]]">
|
||||||
<h3>Hi there!</h3>
|
<h3>Hi there!</h3>
|
||||||
<p>
|
<p>
|
||||||
It looks like we have nothing to show you right now. It could be that we have not yet discovered all your devices but it is more likely that you have not configured Home Assistant yet.
|
It looks like we have nothing to show you right now. It could be that we have not yet discovered all your devices but it is more likely that you have not configured Home Assistant yet.
|
||||||
@ -61,40 +67,96 @@
|
|||||||
Please see the <a href='https://home-assistant.io/getting-started/' target='_blank'>Getting Started</a> section on how to setup your devices.
|
Please see the <a href='https://home-assistant.io/getting-started/' target='_blank'>Getting Started</a> section on how to setup your devices.
|
||||||
</p>
|
</p>
|
||||||
</state-cards>
|
</state-cards>
|
||||||
|
</div>
|
||||||
</partial-base>
|
</partial-base>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function(){
|
(function(){
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
|
||||||
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 stateStore = window.hass.stateStore;
|
||||||
var uiConstants = window.hass.uiConstants;
|
var uiConstants = window.hass.uiConstants;
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
headerTitle: "States",
|
is: 'partial-states',
|
||||||
states: [],
|
|
||||||
isFetching: false,
|
|
||||||
isStreaming: false,
|
|
||||||
|
|
||||||
canListen: false,
|
behaviors: [StoreListenerBehavior],
|
||||||
voiceSupported: false,
|
|
||||||
hasConversationComponent: false,
|
|
||||||
isListening: false,
|
|
||||||
isTransmittingVoice: false,
|
|
||||||
interimTranscript: '',
|
|
||||||
finalTranscript: '',
|
|
||||||
|
|
||||||
ready: function() {
|
properties: {
|
||||||
this.voiceSupported = voiceActions.isSupported();
|
/**
|
||||||
|
* Title to show in the header
|
||||||
|
*/
|
||||||
|
headerTitle: {
|
||||||
|
type: String,
|
||||||
|
value: 'States',
|
||||||
},
|
},
|
||||||
|
|
||||||
attached: function() {
|
/**
|
||||||
this.listenToStores(true);
|
* If header is to be shown in narrow mode.
|
||||||
|
*/
|
||||||
|
narrow: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
filter: {
|
||||||
this.stopListeningToStores();
|
type: String,
|
||||||
|
value: null,
|
||||||
|
observer: 'filterChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
voiceSupported: {
|
||||||
|
type: Boolean,
|
||||||
|
value: voiceActions.isSupported(),
|
||||||
|
},
|
||||||
|
|
||||||
|
isFetching: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isStreaming: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
canListen: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isListening: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isTransmitting: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
interimTranscript: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
finalTranscript: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
listenButtonIcon: {
|
||||||
|
type: String,
|
||||||
|
computed: 'computeListenButtonIcon(isListening)'
|
||||||
|
},
|
||||||
|
|
||||||
|
showListenInterface: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeShowListenInterface(isListening,isTransmitting)'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
componentStoreChanged: function(componentStore) {
|
componentStoreChanged: function(componentStore) {
|
||||||
@ -145,10 +207,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.states = states.toArray().filter(
|
this.states = states.toArray().filter(
|
||||||
function (el) {return !el.attributes.hidden});
|
function (el) {return !el.attributes.hidden;});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleRefreshClick: function() {
|
handleRefresh: function() {
|
||||||
syncActions.fetchAll();
|
syncActions.fetchAll();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -159,7 +221,20 @@
|
|||||||
voiceActions.listen();
|
voiceActions.listen();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
|
||||||
|
computeListenButtonIcon: function(isListening) {
|
||||||
|
return isListening ? 'av:mic-off' : 'av:mic';
|
||||||
|
},
|
||||||
|
|
||||||
|
computeShowListenInterface: function(isListening,isTransmitting) {
|
||||||
|
return isListening || isTransmitting;
|
||||||
|
},
|
||||||
|
|
||||||
|
computeRefreshButtonClass: function(isFetching) {
|
||||||
|
if (isFetching) {
|
||||||
|
return 'ha-spin';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer>
|
|
||||||
|
@ -2,24 +2,29 @@
|
|||||||
|
|
||||||
<link rel="import" href="../dialogs/more-info-dialog.html">
|
<link rel="import" href="../dialogs/more-info-dialog.html">
|
||||||
|
|
||||||
<polymer-element name="ha-modals">
|
<dom-module id="modal-manager">
|
||||||
<template>
|
<template>
|
||||||
<more-info-dialog id="moreInfoDialog"></more-info-dialog>
|
<more-info-dialog id="moreInfoDialog"></more-info-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
var uiActions = window.hass.uiActions,
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var uiConstants = window.hass.uiConstants,
|
||||||
dispatcher = window.hass.dispatcher;
|
dispatcher = window.hass.dispatcher;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
|
is: 'modal-manager',
|
||||||
|
|
||||||
ready: function() {
|
ready: function() {
|
||||||
dispatcher.register(function(payload) {
|
dispatcher.register(function(payload) {
|
||||||
switch (payload.actionType) {
|
switch (payload.actionType) {
|
||||||
case uiActions.ACTION_SHOW_DIALOG_MORE_INFO:
|
case uiConstants.ACTION_SHOW_DIALOG_MORE_INFO:
|
||||||
this.$.moreInfoDialog.show(payload.entityId);
|
this.$.moreInfoDialog.show(payload.entityId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
})();
|
||||||
</polymer-element>
|
</script>
|
@ -0,0 +1,47 @@
|
|||||||
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../bower_components/paper-toast/paper-toast.html">
|
||||||
|
|
||||||
|
<dom-module id="notification-manager">
|
||||||
|
<style>
|
||||||
|
paper-toast {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<paper-toast id="toast" text='{{text}}'></paper-toast>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'notification-manager',
|
||||||
|
|
||||||
|
behaviors: [StoreListenerBehavior],
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
lastId: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notificationStoreChanged: function(notificationStore) {
|
||||||
|
if (notificationStore.hasNewNotifications(this.lastId)) {
|
||||||
|
var notification = notificationStore.lastNotification;
|
||||||
|
|
||||||
|
this.lastId = notification.id;
|
||||||
|
this.text = notification.message;
|
||||||
|
|
||||||
|
this.$.toast.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
@ -1,9 +1,9 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
<link rel='import' href='../bower_components/paper-button/paper-button.html'>
|
||||||
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
|
||||||
|
|
||||||
<polymer-element name="more-info-configurator" attributes="stateObj">
|
<link rel='import' href='../components/loading-box.html'>
|
||||||
<template>
|
|
||||||
|
<dom-module id='more-info-configurator'>
|
||||||
<style>
|
<style>
|
||||||
p {
|
p {
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
@ -25,62 +25,78 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
height: 41px;
|
height: 41px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.submit paper-spinner {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.submit span {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-top: 6px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
|
<div class='layout vertical'>
|
||||||
|
<template is='dom-if' if='[[isConfigurable]]'>
|
||||||
|
|
||||||
<div layout vertical>
|
<p hidden$='[[!stateObj.attributes.description]]'>[[stateObj.attributes.description]]</p>
|
||||||
<template if="{{stateObj.state == 'configure'}}">
|
|
||||||
|
|
||||||
<p hidden?="{{!stateObj.attributes.description}}">
|
<p class='error' hidden$='[[!stateObj.attributes.errors]]'>[[stateObj.attributes.errors]]</p>
|
||||||
{{stateObj.attributes.description}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='error' hidden?="{{!stateObj.attributes.errors}}">
|
<p class='center' hidden$='[[!stateObj.attributes.description_image]]'>
|
||||||
{{stateObj.attributes.errors}}
|
<img src='[[stateObj.attributes.description_image]]' />
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='center' hidden?="{{!stateObj.attributes.description_image}}">
|
|
||||||
<img src='{{stateObj.attributes.description_image}}' />
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class='submit'>
|
<p class='submit'>
|
||||||
<paper-button raised on-click="{{submitClicked}}"
|
<paper-button raised on-click='submitClicked'
|
||||||
hidden?="{{action !== 'display'}}">
|
hidden$='[[isConfiguring]]'>[[submitCaption]]</paper-button>
|
||||||
{{stateObj.attributes.submit_caption || "Set configuration"}}
|
|
||||||
</paper-button>
|
|
||||||
|
|
||||||
<span hidden?="{{action !== 'configuring'}}">
|
<loading-box hidden$='[[!isConfiguring]]'>Configuring</loading-box>
|
||||||
<paper-spinner active="true"></paper-spinner><span>Configuring…</span>
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
(function() {
|
||||||
var syncActions = window.hass.syncActions;
|
var syncActions = window.hass.syncActions;
|
||||||
var serviceActions = window.hass.serviceActions;
|
var serviceActions = window.hass.serviceActions;
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
Polymer({
|
||||||
action: "display",
|
is: 'more-info-configurator',
|
||||||
isStreaming: false,
|
|
||||||
|
|
||||||
attached: function() {
|
behaviors: [StoreListenerBehavior],
|
||||||
this.listenToStores(true);
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
action: {
|
||||||
this.stopListeningToStores();
|
type: String,
|
||||||
|
value: 'display',
|
||||||
|
},
|
||||||
|
|
||||||
|
isStreaming: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isConfigurable: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeIsConfigurable(stateObj)',
|
||||||
|
},
|
||||||
|
|
||||||
|
isConfiguring: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
submitCaption: {
|
||||||
|
type: String,
|
||||||
|
computed: 'computeSubmitCaption(stateObj)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computeIsConfigurable: function(stateObj) {
|
||||||
|
return stateObj.state == 'configure';
|
||||||
|
},
|
||||||
|
|
||||||
|
computeSubmitCaption: function(stateObj) {
|
||||||
|
return stateObj.attributes.submit_caption || 'Set configuration';
|
||||||
},
|
},
|
||||||
|
|
||||||
streamStoreChanged: function(streamStore) {
|
streamStoreChanged: function(streamStore) {
|
||||||
@ -88,15 +104,15 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
submitClicked: function() {
|
submitClicked: function() {
|
||||||
this.action = "configuring";
|
this.isConfiguring = true;
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
configure_id: this.stateObj.attributes.configure_id
|
configure_id: this.stateObj.attributes.configure_id
|
||||||
};
|
};
|
||||||
|
|
||||||
serviceActions.callService('configurator', 'configure', data).then(
|
serviceActions.callService('configurator', 'configure', data).then(
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
this.action = 'display';
|
this.isConfiguring = false;
|
||||||
|
|
||||||
if (!this.isStreaming) {
|
if (!this.isStreaming) {
|
||||||
syncActions.fetchAll();
|
syncActions.fetchAll();
|
||||||
@ -104,9 +120,9 @@
|
|||||||
}.bind(this),
|
}.bind(this),
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
this.action = 'display';
|
this.isConfiguring = false;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,73 +1,77 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
|
|
||||||
<link rel="import" href="more-info-default.html">
|
<link rel='import' href='more-info-default.html'>
|
||||||
<link rel="import" href="more-info-light.html">
|
<link rel='import' href='more-info-group.html'>
|
||||||
<link rel="import" href="more-info-group.html">
|
<link rel='import' href='more-info-sun.html'>
|
||||||
<link rel="import" href="more-info-sun.html">
|
<link rel='import' href='more-info-configurator.html'>
|
||||||
<link rel="import" href="more-info-configurator.html">
|
<link rel='import' href='more-info-thermostat.html'>
|
||||||
<link rel="import" href="more-info-thermostat.html">
|
<link rel='import' href='more-info-script.html'>
|
||||||
<link rel="import" href="more-info-script.html">
|
<link rel='import' href='more-info-light.html'>
|
||||||
|
|
||||||
<polymer-element name="more-info-content" attributes="stateObj dialogOpen">
|
<dom-module id='more-info-content'>
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id='moreInfoContainer' class='{{classNames}}'></div>
|
</dom-module>
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
Polymer({
|
|
||||||
classNames: '',
|
|
||||||
dialogOpen: false,
|
|
||||||
|
|
||||||
observe: {
|
<script>
|
||||||
'stateObj.attributes': 'stateAttributesChanged',
|
(function() {
|
||||||
|
var uiUtil = window.hass.uiUtil;
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'more-info-content',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'stateObjChanged',
|
||||||
},
|
},
|
||||||
|
|
||||||
dialogOpenChanged: function(oldVal, newVal) {
|
dialogOpen: {
|
||||||
var moreInfoContainer = this.$.moreInfoContainer;
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
if (moreInfoContainer.lastChild) {
|
dialogOpenChanged: function(newVal, oldVal) {
|
||||||
moreInfoContainer.lastChild.dialogOpen = newVal;
|
var root = Polymer.dom(this);
|
||||||
|
|
||||||
|
if (root.lastChild) {
|
||||||
|
root.lastChild.dialogOpen = newVal;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stateObjChanged: function(oldVal, newVal) {
|
stateObjChanged: function(newVal, oldVal) {
|
||||||
var moreInfoContainer = this.$.moreInfoContainer;
|
var root = Polymer.dom(this);
|
||||||
|
var newMoreInfoType;
|
||||||
|
|
||||||
if (!newVal) {
|
if (!newVal || !(newMoreInfoType = uiUtil.stateMoreInfoType(newVal))) {
|
||||||
if (moreInfoContainer.lastChild) {
|
if (root.lastChild) {
|
||||||
moreInfoContainer.removeChild(moreInfoContainer.lastChild);
|
root.removeChild(root.lastChild);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oldVal || oldVal.moreInfoType != newVal.moreInfoType) {
|
if (!oldVal || uiUtil.stateMoreInfoType(oldVal) != newMoreInfoType) {
|
||||||
if (moreInfoContainer.lastChild) {
|
|
||||||
moreInfoContainer.removeChild(moreInfoContainer.lastChild);
|
if (root.lastChild) {
|
||||||
|
root.removeChild(root.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
var moreInfo = document.createElement("more-info-" + newVal.moreInfoType);
|
var moreInfo = document.createElement('more-info-' + newMoreInfoType);
|
||||||
moreInfo.stateObj = newVal;
|
moreInfo.stateObj = newVal;
|
||||||
moreInfo.dialogOpen = this.dialogOpen;
|
moreInfo.dialogOpen = this.dialogOpen;
|
||||||
moreInfoContainer.appendChild(moreInfo);
|
root.appendChild(moreInfo);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
moreInfoContainer.lastChild.dialogOpen = this.dialogOpen;
|
root.lastChild.dialogOpen = this.dialogOpen;
|
||||||
moreInfoContainer.lastChild.stateObj = newVal;
|
root.lastChild.stateObj = newVal;
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
});
|
||||||
stateAttributesChanged: function(oldVal, newVal) {
|
})();
|
||||||
if (!newVal) return;
|
|
||||||
|
|
||||||
this.classNames = Object.keys(newVal).map(
|
|
||||||
function(key) { return "has-" + key; }).join(' ');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,34 +1,41 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<polymer-element name="more-info-default" attributes="stateObj">
|
<dom-module id="more-info-default">
|
||||||
<template>
|
|
||||||
<core-style ref='ha-key-value-table'></core-style>
|
|
||||||
<style>
|
<style>
|
||||||
.data-entry .value {
|
.data-entry .value {
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
<div layout vertical>
|
<div class='layout vertical'>
|
||||||
|
<template is='dom-repeat' items="[[getAttributes(stateObj)]]" as="attribute">
|
||||||
<template repeat="{{key in stateObj.attributes | getKeys}}">
|
<div class='data-entry layout justified horizontal'>
|
||||||
<div layout justified horizontal class='data-entry'>
|
<div class='key'>[[attribute]]</div>
|
||||||
<div class='key'>
|
<div class='value'>[[getAttributeValue(stateObj, attribute)]]</div>
|
||||||
{{key}}
|
|
||||||
</div>
|
|
||||||
<div class='value'>
|
|
||||||
{{stateObj.attributes[key]}}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
(function() {
|
||||||
Polymer({
|
Polymer({
|
||||||
getKeys: function(obj) {
|
is: 'more-info-default',
|
||||||
return Object.keys(obj || {});
|
|
||||||
}
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
getAttributes: function(stateObj) {
|
||||||
|
return stateObj ? Object.keys(stateObj.attributes) : [];
|
||||||
|
},
|
||||||
|
|
||||||
|
getAttributeValue: function(stateObj, attribute) {
|
||||||
|
return stateObj.attributes[attribute];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
<link rel="import" href="../cards/state-card-content.html">
|
<link rel="import" href="../cards/state-card-content.html">
|
||||||
|
|
||||||
<polymer-element name="more-info-group" attributes="stateObj">
|
<dom-module id="more-info-group">
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
.child-card {
|
.child-card {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
@ -13,37 +12,44 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
<template repeat="{{states as state}}">
|
<template is='dom-repeat' items="[[states]]" as='state'>
|
||||||
<state-card-content stateObj="{{state}}" class='child-card'>
|
<div class='child-card'>
|
||||||
</state-card-content>
|
<state-card-content state-obj="[[state]]"></state-card-content>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
</dom-module>
|
||||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
|
||||||
var stateStore = window.hass.stateStore;
|
|
||||||
|
|
||||||
Polymer(Polymer.mixin({
|
<script>
|
||||||
attached: function() {
|
(function() {
|
||||||
this.listenToStores(true);
|
var stateStore = window.hass.stateStore;
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'more-info-group',
|
||||||
|
|
||||||
|
behaviors: [StoreListenerBehavior],
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'updateStates',
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
states: {
|
||||||
this.stopListeningToStores();
|
type: Array,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
stateStoreChanged: function() {
|
stateStoreChanged: function() {
|
||||||
this.updateStates();
|
this.updateStates();
|
||||||
},
|
},
|
||||||
|
|
||||||
stateObjChanged: 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 ?
|
||||||
stateStore.gets(this.stateObj.attributes.entity_id).toArray() : [];
|
stateStore.gets(this.stateObj.attributes.entity_id).toArray() : [];
|
||||||
},
|
},
|
||||||
}, storeListenerMixIn));
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
<link rel="import" href="../bower_components/paper-slider/paper-slider.html">
|
<link rel='import' href='../bower_components/paper-slider/paper-slider.html'>
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/color-picker-element/dist/color-picker.html">
|
<link rel='import' href='../bower_components/color-picker-element/dist/color-picker.html'>
|
||||||
|
|
||||||
<polymer-element name="more-info-light" attributes="stateObj">
|
<dom-module id='more-info-light'>
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
.brightness {
|
.brightness {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
@ -26,66 +25,77 @@
|
|||||||
|
|
||||||
max-height: 0px;
|
max-height: 0px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: max-height .5s ease-in .3s;
|
transition: max-height .5s ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(.has-brightness) .brightness {
|
.has-brightness .brightness {
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(.has-xy_color) color-picker {
|
.has-xy_color color-picker {
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<div>
|
<template>
|
||||||
<div class='brightness'>
|
<div class$='[[computeClassNames(stateObj)]]'>
|
||||||
<div center horizontal layout>
|
<div class='brightness center horizontal layout'>
|
||||||
<div>Brightness</div>
|
<div>Brightness</div>
|
||||||
<paper-slider
|
<paper-slider
|
||||||
max="255" flex id='brightness' value='{{brightnessSliderValue}}'
|
max='255' id='brightness' value='{{brightnessSliderValue}}'
|
||||||
on-change="{{brightnessSliderChanged}}">
|
on-change='brightnessSliderChanged' class='flex'>
|
||||||
</paper-slider>
|
</paper-slider>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<color-picker id="colorpicker" width="350" height="200">
|
<color-picker on-colorselected='colorPicked' width='350' height='200'>
|
||||||
</color-picker>
|
</color-picker>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var serviceActions = window.hass.serviceActions;
|
(function() {
|
||||||
|
var serviceActions = window.hass.serviceActions;
|
||||||
|
var uiUtil = window.hass.uiUtil;
|
||||||
|
var ATTRIBUTE_CLASSES = ['brightness', 'xy_color'];
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
brightnessSliderValue: 0,
|
is: 'more-info-light',
|
||||||
|
|
||||||
observe: {
|
properties: {
|
||||||
'stateObj.attributes.brightness': 'stateObjBrightnessChanged',
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'stateObjChanged',
|
||||||
},
|
},
|
||||||
|
|
||||||
stateObjChanged: function(oldVal, newVal) {
|
brightnessSliderValue: {
|
||||||
|
type: Number,
|
||||||
|
value: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stateObjChanged: function(newVal, oldVal) {
|
||||||
if (newVal && newVal.state === 'on') {
|
if (newVal && newVal.state === 'on') {
|
||||||
this.brightnessSliderValue = newVal.attributes.brightness;
|
this.brightnessSliderValue = newVal.attributes.brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.debounce('more-info-light-animation-finish', function() {
|
||||||
|
this.fire('iron-resize');
|
||||||
|
}.bind(this), 500);
|
||||||
},
|
},
|
||||||
|
|
||||||
stateObjBrightnessChanged: function(oldVal, newVal) {
|
computeClassNames: function(stateObj) {
|
||||||
this.brightnessSliderValue = newVal;
|
return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||||
},
|
},
|
||||||
|
|
||||||
domReady: function() {
|
brightnessSliderChanged: function(ev) {
|
||||||
this.$.colorpicker.addEventListener('colorselected', this.colorPicked.bind(this));
|
var bri = parseInt(ev.target.value);
|
||||||
},
|
|
||||||
|
|
||||||
brightnessSliderChanged: function(ev, details, target) {
|
|
||||||
var bri = parseInt(target.value);
|
|
||||||
|
|
||||||
if(isNaN(bri)) return;
|
if(isNaN(bri)) return;
|
||||||
|
|
||||||
if(bri === 0) {
|
if(bri === 0) {
|
||||||
serviceActions.callTurnOff(this.stateObj.entityId);
|
serviceActions.callTurnOff(this.stateObj.entityId);
|
||||||
} else {
|
} else {
|
||||||
serviceActions.callService("light", "turn_on", {
|
serviceActions.callService('light', 'turn_on', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entityId,
|
||||||
brightness: bri
|
brightness: bri
|
||||||
});
|
});
|
||||||
@ -95,12 +105,12 @@ Polymer({
|
|||||||
colorPicked: function(ev) {
|
colorPicked: function(ev) {
|
||||||
var color = ev.detail.rgb;
|
var color = ev.detail.rgb;
|
||||||
|
|
||||||
serviceActions.callService("light", "turn_on", {
|
serviceActions.callService('light', 'turn_on', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entityId,
|
||||||
rgb_color: [color.r, color.g, color.b]
|
rgb_color: [color.r, color.g, color.b]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<polymer-element name="more-info-script" attributes="stateObj" noscript>
|
<dom-module id='more-info-script'>
|
||||||
<template>
|
<template>
|
||||||
<core-style ref='ha-key-value-table'></core-style>
|
<div class='layout vertical'>
|
||||||
<style>
|
<div class='data-entry layout justified horizontal'>
|
||||||
.data-entry .value {
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div layout vertical>
|
|
||||||
<div layout justified horizontal class='data-entry'>
|
|
||||||
<div class='key'>Last Action</div>
|
<div class='key'>Last Action</div>
|
||||||
<div class='value'>
|
<div class='value'>[[stateObj.attributes.last_action]]</div>
|
||||||
{{stateObj.attributes.last_action}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</dom-module>
|
||||||
</polymer-element>
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Polymer({
|
||||||
|
is: 'more-info-script',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@ -1,53 +1,71 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/relative-ha-datetime.html">
|
<link rel="import" href="../components/relative-ha-datetime.html">
|
||||||
|
|
||||||
<polymer-element name="more-info-sun" attributes="stateObj">
|
<dom-module id="more-info-sun">
|
||||||
<template>
|
<template>
|
||||||
<core-style ref='ha-key-value-table'></core-style>
|
<div class='data-entry layout justified horizontal' id='rising'>
|
||||||
|
|
||||||
<div layout vertical id='sunData'>
|
|
||||||
|
|
||||||
<div layout justified horizontal class='data-entry' id='rising'>
|
|
||||||
<div class='key'>
|
<div class='key'>
|
||||||
Rising <relative-ha-datetime datetimeObj="{{rising}}"></relative-ha-datetime>
|
Rising <relative-ha-datetime datetime-obj="[[risingDate]]"></relative-ha-datetime>
|
||||||
</div>
|
|
||||||
<div class='value'>
|
|
||||||
{{rising | formatTime}}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class='value'>[[risingTime]]</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div layout justified horizontal class='data-entry' id='setting'>
|
<div class='data-entry layout justified horizontal' id='setting'>
|
||||||
<div class='key'>
|
<div class='key'>
|
||||||
Setting <relative-ha-datetime datetimeObj="{{setting}}"></relative-ha-datetime>
|
Setting <relative-ha-datetime datetime-obj="[[settingDate]]"></relative-ha-datetime>
|
||||||
</div>
|
|
||||||
<div class='value'>
|
|
||||||
{{setting | formatTime}}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class='value'>[[settingTime]]</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var parseDateTime = window.hass.util.parseDateTime;
|
var parseDateTime = window.hass.util.parseDateTime;
|
||||||
|
var formatTime = window.hass.uiUtil.formatTime;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
rising: null,
|
is: 'more-info-sun',
|
||||||
setting: null,
|
|
||||||
|
properties: {
|
||||||
|
stateObj: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'stateObjChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
risingDate: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
settingDate: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
risingTime: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
settingTime: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
stateObjChanged: function() {
|
stateObjChanged: function() {
|
||||||
this.rising = parseDateTime(this.stateObj.attributes.next_rising);
|
this.risingDate = parseDateTime(this.stateObj.attributes.next_rising);
|
||||||
this.setting = parseDateTime(this.stateObj.attributes.next_setting);
|
this.risingTime = formatTime(this.risingDate);
|
||||||
|
|
||||||
if(self.rising > self.setting) {
|
this.settingDate = parseDateTime(this.stateObj.attributes.next_setting);
|
||||||
this.$.sunData.appendChild(this.$.rising);
|
this.settingTime = formatTime(this.settingDate);
|
||||||
|
|
||||||
|
var root = Polymer.dom(this);
|
||||||
|
|
||||||
|
if(self.risingDate > self.settingDate) {
|
||||||
|
root.appendChild(this.$.rising);
|
||||||
} else {
|
} else {
|
||||||
this.$.sunData.appendChild(this.$.setting);
|
root.appendChild(this.$.setting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,66 +1,80 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
||||||
<link rel="import" href="../bower_components/paper-slider/paper-slider.html">
|
<link rel='import' href='../bower_components/paper-slider/paper-slider.html'>
|
||||||
<link rel="import" href="../bower_components/paper-toggle-button/paper-toggle-button.html">
|
<link rel='import' href='../bower_components/paper-toggle-button/paper-toggle-button.html'>
|
||||||
|
|
||||||
<polymer-element name="more-info-thermostat" attributes="stateObj">
|
<dom-module id='more-info-thermostat'>
|
||||||
<template>
|
|
||||||
<style>
|
<style>
|
||||||
paper-slider {
|
paper-slider {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-slider::shadow #sliderKnobInner,
|
|
||||||
paper-slider::shadow #sliderBar::shadow #activeProgress {
|
|
||||||
background-color: #039be5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.away-mode-toggle {
|
.away-mode-toggle {
|
||||||
display: none;
|
display: none;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(.has-away_mode) .away-mode-toggle {
|
.has-away_mode .away-mode-toggle {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
<div>
|
<div class$='[[computeClassNames(stateObj)]]'>
|
||||||
<div>
|
<div>
|
||||||
<div>Target Temperature</div>
|
<div>Target Temperature</div>
|
||||||
<paper-slider
|
<paper-slider
|
||||||
min="{{tempMin}}" max="{{tempMax}}"
|
min='[[tempMin]]' max='[[tempMax]]'
|
||||||
value='{{targetTemperatureSliderValue}}' pin
|
value='[[targetTemperatureSliderValue]]' pin
|
||||||
on-change="{{targetTemperatureSliderChanged}}">
|
on-change='targetTemperatureSliderChanged'>
|
||||||
</paper-slider>
|
</paper-slider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='away-mode-toggle'>
|
<div class='away-mode-toggle'>
|
||||||
<div center horizontal layout>
|
<div class='center horizontal layout'>
|
||||||
<div flex>Away Mode</div>
|
<div class='flex'>Away Mode</div>
|
||||||
<paper-toggle-button
|
<paper-toggle-button checked='[[awayToggleChecked]]' on-change='toggleChanged'>
|
||||||
checked="{{awayToggleChecked}}"
|
|
||||||
on-change="{{toggleChanged}}">
|
|
||||||
</paper-toggle-button>
|
</paper-toggle-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var constants = window.hass.constants;
|
(function() {
|
||||||
|
var constants = window.hass.constants;
|
||||||
|
var serviceActions = window.hass.serviceActions;
|
||||||
|
var uiUtil = window.hass.uiUtil;
|
||||||
|
var ATTRIBUTE_CLASSES = ['away_mode'];
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
tempMin: 10,
|
is: 'more-info-thermostat',
|
||||||
tempMax: 40,
|
|
||||||
targetTemperatureSliderValue: 0,
|
|
||||||
|
|
||||||
awayToggleChecked: false,
|
properties: {
|
||||||
|
stateObj: {
|
||||||
observe: {
|
type: Object,
|
||||||
'stateObj.attributes.away_mode': 'awayChanged'
|
observer: 'stateObjChanged',
|
||||||
},
|
},
|
||||||
|
|
||||||
stateObjChanged: function(oldVal, newVal) {
|
tempMin: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
tempMax: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
targetTemperatureSliderValue: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
awayToggleChecked: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
stateObjChanged: function(newVal, oldVal) {
|
||||||
this.targetTemperatureSliderValue = this.stateObj.state;
|
this.targetTemperatureSliderValue = this.stateObj.state;
|
||||||
|
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 === constants.UNIT_TEMP_F) {
|
||||||
this.tempMin = 45;
|
this.tempMin = 45;
|
||||||
@ -71,12 +85,16 @@ Polymer({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
targetTemperatureSliderChanged: function(ev, details, target) {
|
computeClassNames: function(stateObj) {
|
||||||
var temp = parseInt(target.value);
|
return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||||
|
},
|
||||||
|
|
||||||
|
targetTemperatureSliderChanged: function(ev) {
|
||||||
|
var temp = parseInt(ev.target.value);
|
||||||
|
|
||||||
if(isNaN(temp)) return;
|
if(isNaN(temp)) return;
|
||||||
|
|
||||||
serviceActions.callService("thermostat", "set_temperature", {
|
serviceActions.callService('thermostat', 'set_temperature', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entityId,
|
||||||
temperature: temp
|
temperature: temp
|
||||||
});
|
});
|
||||||
@ -92,10 +110,6 @@ Polymer({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
awayChanged: function(oldVal, newVal) {
|
|
||||||
this.awayToggleChecked = newVal == 'on';
|
|
||||||
},
|
|
||||||
|
|
||||||
service_set_away: function(away_mode) {
|
service_set_away: function(away_mode) {
|
||||||
// We call stateChanged after a successful call to re-sync the toggle
|
// We call stateChanged after a successful call to re-sync the toggle
|
||||||
// with the state. It will be out of sync if our service call did not
|
// with the state. It will be out of sync if our service call did not
|
||||||
@ -106,9 +120,9 @@ Polymer({
|
|||||||
{entity_id: this.stateObj.entityId, away_mode: away_mode})
|
{entity_id: this.stateObj.entityId, away_mode: away_mode})
|
||||||
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
this.awayChanged(null, this.stateObj.attributes.away_mode);
|
this.stateObjChanged(this.stateObj);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</polymer-element>
|
|
||||||
|
@ -1,24 +1,22 @@
|
|||||||
<link rel="import" href="../bower_components/core-icon/core-icon.html">
|
<link rel="import" href="../bower_components/iron-iconset-svg/iron-iconset-svg.html">
|
||||||
<link rel="import" href="../bower_components/core-iconset-svg/core-iconset-svg.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/core-icon/core-icon.html">
|
<link rel="import" href="../bower_components/iron-icons/iron-icons.html">
|
||||||
<link rel="import" href="../bower_components/core-icons/social-icons.html">
|
<link rel="import" href="../bower_components/iron-icons/social-icons.html">
|
||||||
<link rel="import" href="../bower_components/core-icons/image-icons.html">
|
<link rel="import" href="../bower_components/iron-icons/image-icons.html">
|
||||||
<link rel="import" href="../bower_components/core-icons/hardware-icons.html">
|
<link rel="import" href="../bower_components/iron-icons/hardware-icons.html">
|
||||||
<link rel="import" href="../bower_components/core-icons/av-icons.html">
|
<link rel="import" href="../bower_components/iron-icons/av-icons.html">
|
||||||
|
|
||||||
<core-iconset-svg id="homeassistant-100" iconSize="100">
|
<iron-iconset-svg name="homeassistant-100" size="100">
|
||||||
<svg><defs>
|
<svg><defs>
|
||||||
<g id="thermostat">
|
|
||||||
<!--
|
<!--
|
||||||
Thermostat icon created by Scott Lewis from the Noun Project
|
Thermostat icon created by Scott Lewis from the Noun Project
|
||||||
Licensed under CC BY 3.0 - http://creativecommons.org/licenses/by/3.0/us/
|
Licensed under CC BY 3.0 - http://creativecommons.org/licenses/by/3.0/us/
|
||||||
-->
|
-->
|
||||||
<path d="M66.861,60.105V17.453c0-9.06-7.347-16.405-16.408-16.405c-9.06,0-16.404,7.345-16.404,16.405v42.711 c-4.04,4.14-6.533,9.795-6.533,16.035c0,12.684,10.283,22.967,22.967,22.967c12.682,0,22.964-10.283,22.964-22.967 C73.447,69.933,70.933,64.254,66.861,60.105z M60.331,20.38h-13.21v6.536h6.63v6.539h-6.63v6.713h6.63v6.538h-6.63v6.5h6.63v6.536 h-6.63v7.218c-3.775,1.373-6.471,4.993-6.471,9.24h-6.626c0-5.396,2.598-10.182,6.61-13.185V17.446c0-0.038,0.004-0.075,0.004-0.111 l-0.004-0.007c0-5.437,4.411-9.846,9.849-9.846c5.438,0,9.848,4.409,9.848,9.846V20.38z"/></g>
|
<g id="thermostat"><path d="M66.861,60.105V17.453c0-9.06-7.347-16.405-16.408-16.405c-9.06,0-16.404,7.345-16.404,16.405v42.711 c-4.04,4.14-6.533,9.795-6.533,16.035c0,12.684,10.283,22.967,22.967,22.967c12.682,0,22.964-10.283,22.964-22.967 C73.447,69.933,70.933,64.254,66.861,60.105z M60.331,20.38h-13.21v6.536h6.63v6.539h-6.63v6.713h6.63v6.538h-6.63v6.5h6.63v6.536 h-6.63v7.218c-3.775,1.373-6.471,4.993-6.471,9.24h-6.626c0-5.396,2.598-10.182,6.61-13.185V17.446c0-0.038,0.004-0.075,0.004-0.111 l-0.004-0.007c0-5.437,4.411-9.846,9.849-9.846c5.438,0,9.848,4.409,9.848,9.846V20.38z"/></g>
|
||||||
</defs></svg>
|
</defs></svg>
|
||||||
</core-iconset-svg>
|
</iron-iconset-svg>
|
||||||
|
|
||||||
<core-iconset-svg id="homeassistant-24" iconSize="24">
|
<iron-iconset-svg name="homeassistant-24" size="24">
|
||||||
<svg><defs>
|
<svg><defs>
|
||||||
<!--
|
<!--
|
||||||
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
||||||
@ -29,9 +27,8 @@
|
|||||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||||
-->
|
-->
|
||||||
<g id="group"><path d="M9 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm5-3c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-7c-5.52 0-10 4.48-10 10s4.48 10 10 10 10-4.48 10-10-4.48-10-10-10zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
|
<g id="group"><path d="M9 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm5-3c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-7c-5.52 0-10 4.48-10 10s4.48 10 10 10 10-4.48 10-10-4.48-10-10-10zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
|
||||||
|
|
||||||
</defs></svg>
|
</defs></svg>
|
||||||
</core-iconset-svg>
|
</iron-iconset-svg>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.hass.uiUtil.domainIcon = function(domain, state) {
|
window.hass.uiUtil.domainIcon = function(domain, state) {
|
||||||
@ -88,7 +85,7 @@ window.hass.uiUtil.domainIcon = function(domain, state) {
|
|||||||
return 'social:pages';
|
return 'social:pages';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "bookmark-outline";
|
return "bookmark";
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,30 +6,25 @@
|
|||||||
var DOMAINS_WITH_MORE_INFO = [
|
var DOMAINS_WITH_MORE_INFO = [
|
||||||
'light', 'group', 'sun', 'configurator', 'thermostat', 'script'
|
'light', 'group', 'sun', 'configurator', 'thermostat', 'script'
|
||||||
];
|
];
|
||||||
|
var DOMAINS_HIDE_MORE_INFO = [
|
||||||
|
'sensor',
|
||||||
|
];
|
||||||
|
|
||||||
// Add some frontend specific helpers to the models
|
// Add some frontend specific helpers to the models
|
||||||
Object.defineProperties(window.hass.stateModel.prototype, {
|
Object.defineProperties(window.hass.stateModel.prototype, {
|
||||||
// how to render the card for this state
|
// how to render the card for this state
|
||||||
cardType: {
|
cardType: {
|
||||||
get: function() {
|
get: function() {
|
||||||
if(DOMAINS_WITH_CARD.indexOf(this.domain) !== -1) {
|
console.warn('Deprecated method. Please use hass.uiUtil.stateCardType');
|
||||||
return this.domain;
|
return window.hass.uiUtil.stateCardType(this);
|
||||||
} else if(this.canToggle) {
|
|
||||||
return "toggle";
|
|
||||||
} else {
|
|
||||||
return "display";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// how to render the more info of this state
|
// how to render the more info of this state
|
||||||
moreInfoType: {
|
moreInfoType: {
|
||||||
get: function() {
|
get: function() {
|
||||||
if(DOMAINS_WITH_MORE_INFO.indexOf(this.domain) !== -1) {
|
console.warn('Deprecated method. Please use hass.uiUtil.stateMoreInfoType');
|
||||||
return this.domain;
|
return window.hass.uiUtil.stateMoreInfoType(this);
|
||||||
} else {
|
|
||||||
return 'default';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -52,7 +47,7 @@
|
|||||||
window.hass.uiActions = {
|
window.hass.uiActions = {
|
||||||
showMoreInfoDialog: function(entityId) {
|
showMoreInfoDialog: function(entityId) {
|
||||||
dispatcher.dispatch({
|
dispatcher.dispatch({
|
||||||
actionType: this.ACTION_SHOW_DIALOG_MORE_INFO,
|
actionType: window.hass.uiConstants.ACTION_SHOW_DIALOG_MORE_INFO,
|
||||||
entityId: entityId,
|
entityId: entityId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -66,7 +61,34 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// UI specific util methods
|
// UI specific util methods
|
||||||
window.hass.uiUtil = {};
|
window.hass.uiUtil = {
|
||||||
|
stateCardType: function(state) {
|
||||||
|
if(DOMAINS_WITH_CARD.indexOf(state.domain) !== -1) {
|
||||||
|
return state.domain;
|
||||||
|
} else if(state.canToggle) {
|
||||||
|
return "toggle";
|
||||||
|
} else {
|
||||||
|
return "display";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stateMoreInfoType: function(state) {
|
||||||
|
if(DOMAINS_HIDE_MORE_INFO.indexOf(state.domain) !== -1) {
|
||||||
|
return false;
|
||||||
|
} else if(DOMAINS_WITH_MORE_INFO.indexOf(state.domain) !== -1) {
|
||||||
|
return state.domain;
|
||||||
|
} else {
|
||||||
|
return 'default';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
attributeClassNames: function(stateObj, attributes) {
|
||||||
|
if (!stateObj) return '';
|
||||||
|
return attributes.map(function(attribute) {
|
||||||
|
return attribute in stateObj.attributes ? 'has-' + attribute : '';
|
||||||
|
}).join(' ');
|
||||||
|
},
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,31 +1,23 @@
|
|||||||
<link rel="import" href="../bower_components/core-style/core-style.html">
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<core-style id='ha-main'>
|
<style is="custom-style">
|
||||||
/* Palette generated by Material Palette - materialpalette.com/light-blue/orange */
|
:root {
|
||||||
|
--dark-primary-color: #0288D1;
|
||||||
|
--default-primary-color: #03A9F4;
|
||||||
|
--light-primary-color: #B3E5FC;
|
||||||
|
--text-primary-color: #ffffff;
|
||||||
|
--accent-color: #FF9800;
|
||||||
|
--primary-background-color: #ffffff;
|
||||||
|
--primary-text-color: #212121;
|
||||||
|
--secondary-text-color: #727272;
|
||||||
|
--disabled-text-color: #bdbdbd;
|
||||||
|
--divider-color: #B6B6B6;
|
||||||
|
|
||||||
.dark-primary-color { background: #0288D1; }
|
--paper-toggle-button-checked-ink-color: #039be5;
|
||||||
.default-primary-color { background: #03A9F4; }
|
--paper-toggle-button-checked-button-color: #039be5;
|
||||||
.light-primary-color { background: #B3E5FC; }
|
--paper-toggle-button-checked-bar-color: #039be5;
|
||||||
.text-primary-color { color: #FFFFFF; }
|
}
|
||||||
.accent-color { background: #FF9800; }
|
|
||||||
.primary-text-color { color: #212121; }
|
|
||||||
.secondary-text-color { color: #727272; }
|
|
||||||
.divider-color { border-color: #B6B6B6; }
|
|
||||||
|
|
||||||
/* extra */
|
|
||||||
.accent-text-color { color: #FF9800; }
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: #212121;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #FF9800;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</core-style>
|
|
||||||
|
|
||||||
<core-style id='ha-animations'>
|
|
||||||
@-webkit-keyframes ha-spin {
|
@-webkit-keyframes ha-spin {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: rotate(0deg);
|
-webkit-transform: rotate(0deg);
|
||||||
@ -47,106 +39,8 @@ a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ha-spin {
|
body /deep/ .ha-spin {
|
||||||
-webkit-animation: ha-spin 2s infinite linear;
|
-webkit-animation: ha-spin 2s infinite linear;
|
||||||
animation: ha-spin 2s infinite linear;
|
animation: ha-spin 2s infinite linear;
|
||||||
}
|
}
|
||||||
</core-style>
|
</style>
|
||||||
<core-style id="ha-headers">
|
|
||||||
core-scroll-header-panel, core-header-panel {
|
|
||||||
background-color: #E5E5E5;
|
|
||||||
}
|
|
||||||
|
|
||||||
core-toolbar {
|
|
||||||
background: #03a9f4;
|
|
||||||
color: white;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
</core-style>
|
|
||||||
|
|
||||||
<core-style id="ha-dialog">
|
|
||||||
:host {
|
|
||||||
font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
|
|
||||||
|
|
||||||
min-width: 350px;
|
|
||||||
max-width: 700px;
|
|
||||||
|
|
||||||
/* First two are from core-transition-bottom */
|
|
||||||
transition:
|
|
||||||
transform 0.2s ease-in-out,
|
|
||||||
opacity 0.2s ease-in,
|
|
||||||
top .3s,
|
|
||||||
left .3s !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host .sidebar {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (max-width: 620px) {
|
|
||||||
:host.two-column {
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
max-height: calc(100% - 64px);
|
|
||||||
bottom: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host .sidebar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (max-width: 464px) {
|
|
||||||
:host {
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
max-height: calc(100% - 64px);
|
|
||||||
bottom: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html /deep/ .ha-form paper-input {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
html /deep/ .ha-form paper-input:first-child {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
</core-style>
|
|
||||||
|
|
||||||
<core-style id='ha-key-value-table'>
|
|
||||||
.data-entry {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-entry:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-entry .key {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-entry .value {
|
|
||||||
text-align: right;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
</core-style>
|
|
||||||
|
|
||||||
<core-style id='ha-paper-toggle'>
|
|
||||||
paper-toggle-button::shadow .toggle-ink {
|
|
||||||
color: #039be5;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-toggle-button::shadow [checked] .toggle-bar {
|
|
||||||
background-color: #039be5;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-toggle-button::shadow [checked] .toggle-button {
|
|
||||||
background-color: #039be5;
|
|
||||||
}
|
|
||||||
</core-style>
|
|
||||||
|
@ -17,8 +17,4 @@ window.hass.uiUtil.formatDate = function(dateObj) {
|
|||||||
return moment(dateObj).format('ll');
|
return moment(dateObj).format('ll');
|
||||||
};
|
};
|
||||||
|
|
||||||
PolymerExpressions.prototype.formatTime = window.hass.uiUtil.formatTime;
|
|
||||||
PolymerExpressions.prototype.formatDateTime = window.hass.uiUtil.formatDateTime;
|
|
||||||
PolymerExpressions.prototype.formatDate = window.hass.uiUtil.formatDate;
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var StoreListenerMixIn = window.hass.storeListenerMixIn;
|
||||||
|
|
||||||
|
window.StoreListenerBehavior = {
|
||||||
|
|
||||||
|
attached: function() {
|
||||||
|
StoreListenerMixIn.listenToStores(true, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
detached: function() {
|
||||||
|
StoreListenerMixIn.stopListeningToStores(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
</script>
|
12
homeassistant/components/frontend/www_static/webcomponents-lite.min.js
vendored
Normal file
12
homeassistant/components/frontend/www_static/webcomponents-lite.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -8,23 +8,19 @@ fi
|
|||||||
|
|
||||||
scripts/build_js $1
|
scripts/build_js $1
|
||||||
|
|
||||||
# To build the frontend, you need node, bower and vulcanize
|
# To build the frontend, you need node, bower, vulcanize and html-minifier
|
||||||
# npm install -g bower vulcanize
|
# npm install -g bower vulcanize html-minifier
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
cd homeassistant/components/frontend/www_static/polymer
|
cd homeassistant/components/frontend/www_static/polymer
|
||||||
bower install
|
bower install
|
||||||
cd ..
|
cd ..
|
||||||
cp polymer/bower_components/webcomponentsjs/webcomponents.min.js .
|
cp polymer/bower_components/webcomponentsjs/webcomponents-lite.min.js .
|
||||||
|
|
||||||
# Let Polymer refer to the minified JS version before we compile
|
vulcanize --inline-css --inline-scripts --strip-comments polymer/home-assistant.html > frontend.html
|
||||||
sed -i.bak 's/polymer\.js/polymer\.min\.js/' polymer/bower_components/polymer/polymer.html
|
|
||||||
|
|
||||||
vulcanize -o frontend.html --inline --strip polymer/home-assistant.html
|
# html-minifier crashes on frontend, minimize kills the CSS
|
||||||
|
# html-minifier --config-file polymer/html-minifier.conf -o frontend.html frontend.html
|
||||||
# Revert back the change to the Polymer component
|
|
||||||
rm polymer/bower_components/polymer/polymer.html
|
|
||||||
mv polymer/bower_components/polymer/polymer.html.bak polymer/bower_components/polymer/polymer.html
|
|
||||||
|
|
||||||
# Generate the MD5 hash of the new frontend
|
# Generate the MD5 hash of the new frontend
|
||||||
cd ..
|
cd ..
|
||||||
|
Loading…
x
Reference in New Issue
Block a user