mirror of
https://github.com/home-assistant/core.git
synced 2025-04-30 20:27:57 +00:00
Merge pull request #216 from balloob/polymer-es6
Extract frontend code into own repository
This commit is contained in:
commit
34b6627f9a
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -10,9 +10,6 @@
|
|||||||
[submodule "homeassistant/external/noop"]
|
[submodule "homeassistant/external/noop"]
|
||||||
path = homeassistant/external/noop
|
path = homeassistant/external/noop
|
||||||
url = https://github.com/balloob/noop.git
|
url = https://github.com/balloob/noop.git
|
||||||
[submodule "homeassistant/components/frontend/www_static/polymer/home-assistant-js"]
|
|
||||||
path = homeassistant/components/frontend/www_static/polymer/home-assistant-js
|
|
||||||
url = https://github.com/balloob/home-assistant-js.git
|
|
||||||
[submodule "homeassistant/external/vera"]
|
[submodule "homeassistant/external/vera"]
|
||||||
path = homeassistant/external/vera
|
path = homeassistant/external/vera
|
||||||
url = https://github.com/jamespcole/home-assistant-vera-api.git
|
url = https://github.com/jamespcole/home-assistant-vera-api.git
|
||||||
@ -22,3 +19,6 @@
|
|||||||
[submodule "homeassistant/external/pymysensors"]
|
[submodule "homeassistant/external/pymysensors"]
|
||||||
path = homeassistant/external/pymysensors
|
path = homeassistant/external/pymysensors
|
||||||
url = https://github.com/theolind/pymysensors
|
url = https://github.com/theolind/pymysensors
|
||||||
|
[submodule "homeassistant/components/frontend/www_static/home-assistant-polymer"]
|
||||||
|
path = homeassistant/components/frontend/www_static/home-assistant-polymer
|
||||||
|
url = https://github.com/balloob/home-assistant-polymer.git
|
||||||
|
@ -55,7 +55,7 @@ def _handle_get_root(handler, path_match, data):
|
|||||||
handler.end_headers()
|
handler.end_headers()
|
||||||
|
|
||||||
if handler.server.development:
|
if handler.server.development:
|
||||||
app_url = "polymer/home-assistant.html"
|
app_url = "home-assistant-polymer/src/home-assistant.html"
|
||||||
else:
|
else:
|
||||||
app_url = "frontend-{}.html".format(version.VERSION)
|
app_url = "frontend-{}.html".format(version.VERSION)
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||||
VERSION = "85f0078ea394a12dd95395799e345c83"
|
VERSION = "85ce191d6484327ae20265d457c6c4e7"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
Subproject commit b97996be23de374f0e47b42c0518c05f6250cf41
|
@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Home Assistant",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"authors": [
|
|
||||||
"Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>"
|
|
||||||
],
|
|
||||||
"main": "splash-login.html",
|
|
||||||
"license": "MIT",
|
|
||||||
"private": true,
|
|
||||||
"ignore": [
|
|
||||||
"bower_components"
|
|
||||||
],
|
|
||||||
"devDependencies": {
|
|
||||||
"polymer": "Polymer/polymer#^1.0.0",
|
|
||||||
"webcomponentsjs": "Polymer/webcomponentsjs#^0.7",
|
|
||||||
"paper-header-panel": "PolymerElements/paper-header-panel#^1.0.0",
|
|
||||||
"paper-toolbar": "PolymerElements/paper-toolbar#^1.0.0",
|
|
||||||
"paper-menu": "PolymerElements/paper-menu#^1.0.0",
|
|
||||||
"iron-input": "PolymerElements/iron-input#^1.0.0",
|
|
||||||
"iron-icons": "PolymerElements/iron-icons#^1.0.0",
|
|
||||||
"iron-image": "PolymerElements/iron-image#^1.0.0",
|
|
||||||
"paper-toast": "PolymerElements/paper-toast#^1.0.0",
|
|
||||||
"paper-dialog": "PolymerElements/paper-dialog#^1.0.0",
|
|
||||||
"paper-dialog-scrollable": "polymerelements/paper-dialog-scrollable#^1.0.0",
|
|
||||||
"paper-spinner": "PolymerElements/paper-spinner#^1.0.0",
|
|
||||||
"paper-button": "PolymerElements/paper-button#^1.0.0",
|
|
||||||
"paper-input": "PolymerElements/paper-input#^1.0.0",
|
|
||||||
"paper-toggle-button": "PolymerElements/paper-toggle-button#^1.0.0",
|
|
||||||
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
|
|
||||||
"paper-item": "PolymerElements/paper-item#^1.0.0",
|
|
||||||
"paper-slider": "PolymerElements/paper-slider#^1.0.0",
|
|
||||||
"paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
|
|
||||||
"paper-drawer-panel": "PolymerElements/paper-drawer-panel#^1.0.0",
|
|
||||||
"paper-scroll-header-panel": "polymerelements/paper-scroll-header-panel#^1.0.0",
|
|
||||||
"google-apis": "GoogleWebComponents/google-apis#0.8-preview",
|
|
||||||
"moment": "^2.10.3",
|
|
||||||
"layout": "Polymer/layout",
|
|
||||||
"paper-styles": "polymerelements/paper-styles#^1.0.0",
|
|
||||||
"lodash": "~3.9.3",
|
|
||||||
"pikaday": "~1.3.2"
|
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"polymer": "^1.0.0",
|
|
||||||
"webcomponentsjs": "^0.7.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='./state-card-display.html'>
|
|
||||||
<link rel='import' href='../components/state-info.html'>
|
|
||||||
|
|
||||||
<dom-module id='state-card-configurator'>
|
|
||||||
<template>
|
|
||||||
<state-card-display state-obj='[[stateObj]]'></state-card-display>
|
|
||||||
|
|
||||||
<!-- pre load the image so the dialog is rendered the proper size -->
|
|
||||||
<template is='dom-if' if='[[stateObj.attributes.description_image]]'>
|
|
||||||
<img hidden src='[[stateObj.attributes.description_image]]' />
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card-configurator',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,50 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="state-card-display.html">
|
|
||||||
<link rel="import" href="state-card-toggle.html">
|
|
||||||
<link rel="import" href="state-card-thermostat.html">
|
|
||||||
<link rel="import" href="state-card-configurator.html">
|
|
||||||
<link rel="import" href="state-card-scene.html">
|
|
||||||
<link rel="import" href="state-card-media_player.html">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card-content',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function(newVal, oldVal) {
|
|
||||||
var root = Polymer.dom(this);
|
|
||||||
|
|
||||||
if (!newVal) {
|
|
||||||
if (root.lastChild) {
|
|
||||||
root.removeChild(root.lastChild);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newCardType = uiUtil.stateCardType(newVal);
|
|
||||||
|
|
||||||
if (!oldVal || uiUtil.stateCardType(oldVal) != newCardType) {
|
|
||||||
if (root.lastChild) {
|
|
||||||
root.removeChild(root.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
var stateCard = document.createElement("state-card-" + newCardType);
|
|
||||||
stateCard.stateObj = newVal;
|
|
||||||
root.appendChild(stateCard);
|
|
||||||
} else {
|
|
||||||
root.lastChild.stateObj = newVal;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,36 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/state-info.html">
|
|
||||||
|
|
||||||
<dom-module id="state-card-display">
|
|
||||||
<style>
|
|
||||||
.state {
|
|
||||||
margin-left: 16px;
|
|
||||||
text-transform: capitalize;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 1.3rem;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class='horizontal justified layout'>
|
|
||||||
<state-info state-obj="[[stateObj]]"></state-info>
|
|
||||||
<div class='state'>[[stateObj.stateDisplay]]</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card-display',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,91 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/state-info.html">
|
|
||||||
|
|
||||||
<dom-module id="state-card-media_player">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.state {
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-text {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-transform: capitalize;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 1.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary-text {
|
|
||||||
color: darkgrey;
|
|
||||||
margin-top: -2px;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class='horizontal justified layout'>
|
|
||||||
<state-info state-obj="[[stateObj]]"></state-info>
|
|
||||||
<div class='state'>
|
|
||||||
<div class='main-text'>[[computePrimaryText(stateObj, isPlaying)]]</div>
|
|
||||||
<div class='secondary-text'>[[computeSecondaryText(stateObj, isPlaying)]]</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var PLAYING_STATES = ['playing', 'paused'];
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card-media_player',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
isPlaying: {
|
|
||||||
type: Boolean,
|
|
||||||
computed: 'computeIsPlaying(stateObj)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeIsPlaying: function(stateObj) {
|
|
||||||
return PLAYING_STATES.indexOf(stateObj.state) !== -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
computePrimaryText: function(stateObj, isPlaying) {
|
|
||||||
return isPlaying ? stateObj.attributes.media_title : stateObj.stateDisplay;
|
|
||||||
},
|
|
||||||
|
|
||||||
computeSecondaryText: function(stateObj, isPlaying) {
|
|
||||||
var text;
|
|
||||||
|
|
||||||
if (stateObj.attributes.media_content_type == 'music') {
|
|
||||||
return stateObj.attributes.media_artist;
|
|
||||||
|
|
||||||
} else if (stateObj.attributes.media_content_type == 'tvshow') {
|
|
||||||
text = stateObj.attributes.media_series_title;
|
|
||||||
|
|
||||||
if (stateObj.attributes.media_season && stateObj.attributes.media_episode) {
|
|
||||||
text += ' S' + stateObj.attributes.media_season + 'E' + stateObj.attributes.media_episode;
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
|
|
||||||
} else if (stateObj.attributes.app_name) {
|
|
||||||
return stateObj.attributes.app_name;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,39 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./state-card-display.html">
|
|
||||||
<link rel="import" href="./state-card-toggle.html">
|
|
||||||
|
|
||||||
<dom-module id="state-card-scene">
|
|
||||||
<template>
|
|
||||||
<template is='dom-if' if='[[allowToggle]]'>
|
|
||||||
<state-card-toggle state-obj="[[stateObj]]"></state-card-toggle>
|
|
||||||
</template>
|
|
||||||
<template is='dom-if' if='[[!allowToggle]]'>
|
|
||||||
<state-card-display state-obj="[[stateObj]]"></state-card-display>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card-scene',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
allowToggle: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
computed: 'computeAllowToggle(stateObj)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeAllowToggle: function(stateObj) {
|
|
||||||
return stateObj.state === 'off' || stateObj.attributes.active_requested;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,57 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/state-info.html">
|
|
||||||
|
|
||||||
<dom-module id="state-card-thermostat">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.state {
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.target {
|
|
||||||
text-transform: capitalize;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 1.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.current {
|
|
||||||
color: darkgrey;
|
|
||||||
margin-top: -2px;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class='horizontal justified layout'>
|
|
||||||
<state-info state-obj="[[stateObj]]"></state-info>
|
|
||||||
<div class='state'>
|
|
||||||
<div class='target'>[[stateObj.stateDisplay]]</div>
|
|
||||||
|
|
||||||
<div class='current'>
|
|
||||||
<span>Currently: </span>
|
|
||||||
<span>[[stateObj.attributes.current_temperature]]</span>
|
|
||||||
<span> </span>
|
|
||||||
<span>[[stateObj.attributes.unit_of_measurement]]</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card-thermostat',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,95 +0,0 @@
|
|||||||
<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="../components/state-info.html">
|
|
||||||
|
|
||||||
<dom-module id="state-card-toggle">
|
|
||||||
<style>
|
|
||||||
paper-toggle-button {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class='horizontal justified layout'>
|
|
||||||
<state-info state-obj="[[stateObj]]"></state-info>
|
|
||||||
|
|
||||||
<paper-toggle-button class='self-center'
|
|
||||||
checked="[[toggleChecked]]"
|
|
||||||
on-change="toggleChanged"
|
|
||||||
on-tap="toggleTapped">
|
|
||||||
</paper-toggle-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var serviceActions = window.hass.serviceActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card-toggle',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleChecked: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function() {
|
|
||||||
this.forceStateChange = this.forceStateChange.bind(this);
|
|
||||||
this.forceStateChange();
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleTapped: function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleChanged: function(ev) {
|
|
||||||
var newVal = ev.target.checked;
|
|
||||||
|
|
||||||
if(newVal && this.stateObj.state === "off") {
|
|
||||||
this.turn_on();
|
|
||||||
} else if(!newVal && this.stateObj.state === "on") {
|
|
||||||
this.turn_off();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function(newVal) {
|
|
||||||
if (newVal) {
|
|
||||||
this.updateToggle(newVal);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateToggle: function(stateObj) {
|
|
||||||
this.toggleChecked = stateObj && stateObj.state === "on";
|
|
||||||
},
|
|
||||||
|
|
||||||
forceStateChange: function() {
|
|
||||||
this.updateToggle(this.stateObj);
|
|
||||||
},
|
|
||||||
|
|
||||||
turn_on: function() {
|
|
||||||
// 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
|
|
||||||
// result in the entity to be turned on. Since the state is not changing,
|
|
||||||
// the resync is not called automatic.
|
|
||||||
serviceActions.callTurnOn(this.stateObj.entityId).then(this.forceStateChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
turn_off: function() {
|
|
||||||
// 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
|
|
||||||
// result in the entity to be turned on. Since the state is not changing,
|
|
||||||
// the resync is not called automatic.
|
|
||||||
serviceActions.callTurnOff(this.stateObj.entityId).then(this.forceStateChange);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,55 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="state-card-content.html">
|
|
||||||
|
|
||||||
<dom-module id="state-card">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: rgba(0, 0, 0, 0.098) 0px 2px 4px, rgba(0, 0, 0, 0.098) 0px 0px 3px;
|
|
||||||
transition: all 0.30s ease-out;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
background-color: white;
|
|
||||||
padding: 16px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
-ms-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<state-card-content state-obj="[[stateObj]]"></state-card-content>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function(){
|
|
||||||
var moreInfoActions = window.hass.moreInfoActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'state-card',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
listeners: {
|
|
||||||
'tap': 'cardTapped',
|
|
||||||
},
|
|
||||||
|
|
||||||
cardTapped: function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.async(moreInfoActions.selectEntity.bind(
|
|
||||||
this, this.stateObj.entityId), 100);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,25 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<dom-module id="display-time">
|
|
||||||
<template>[[computeTime(dateObj)]]</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'display-time',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
dateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeTime: function(dateObj) {
|
|
||||||
return dateObj ? uiUtil.formatTime(dateObj) : '';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,37 +0,0 @@
|
|||||||
<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">
|
|
||||||
|
|
||||||
<dom-module id="domain-icon">
|
|
||||||
<template>
|
|
||||||
<iron-icon icon="[[computeIcon(domain, state)]]"></iron-icon>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'domain-icon',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
domain: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
state: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeIcon: function(domain, state) {
|
|
||||||
return uiUtil.domainIcon(domain, state);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,58 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<dom-module id="entity-list">
|
|
||||||
<style>
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
list-style: none;
|
|
||||||
line-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<template is='dom-repeat' items='[[entities]]' as='entity'>
|
|
||||||
<li><a href='#' on-click='entitySelected'>[[entity.entityId]]</a></li>
|
|
||||||
</template>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var entityGetters = window.hass.entityGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'entity-list',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
entities: {
|
|
||||||
type: Array,
|
|
||||||
bindNuclear: [
|
|
||||||
entityGetters.entityMap,
|
|
||||||
function(map) {
|
|
||||||
return map.valueSeq().
|
|
||||||
sortBy(function(entity) { return entity.entityId; })
|
|
||||||
.toArray();
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
entitySelected: function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
this.fire('entity-selected', {entityId: ev.model.entity.entityId});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,61 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<dom-module id="events-list">
|
|
||||||
<style>
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
list-style: none;
|
|
||||||
line-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
</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>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var eventGetters = window.hass.eventGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'events-list',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
events: {
|
|
||||||
type: Array,
|
|
||||||
bindNuclear: [
|
|
||||||
eventGetters.entityMap,
|
|
||||||
function(map) {
|
|
||||||
return map.valueSeq()
|
|
||||||
.sortBy(function(event) { return event.event; })
|
|
||||||
.toArray();
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
eventSelected: function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
this.fire('event-selected', {eventType: ev.model.event.event});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,169 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
|
|
||||||
<dom-module id='ha-color-picker'>
|
|
||||||
<style>
|
|
||||||
canvas {
|
|
||||||
cursor: crosshair;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<canvas width='[[width]]' height='[[height]]'></canvas>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Color-picker custom element
|
|
||||||
* Originally created by bbrewer97202 (Ben Brewer). MIT Licensed.
|
|
||||||
* https://github.com/bbrewer97202/color-picker-element
|
|
||||||
*
|
|
||||||
* Adapted to work with Polymer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
/**
|
|
||||||
* given red, green, blue values, return the equivalent hexidecimal value
|
|
||||||
* base source: http://stackoverflow.com/a/5624139
|
|
||||||
*/
|
|
||||||
var componentToHex = function(c) {
|
|
||||||
var hex = c.toString(16);
|
|
||||||
return hex.length === 1 ? "0" + hex : hex;
|
|
||||||
};
|
|
||||||
|
|
||||||
var rgbToHex = function(color) {
|
|
||||||
return "#" + componentToHex(color.r) + componentToHex(color.g) +
|
|
||||||
componentToHex(color.b);
|
|
||||||
};
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'ha-color-picker',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
width: {
|
|
||||||
type: Number,
|
|
||||||
value: 300,
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
value: 300,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
listeners: {
|
|
||||||
'mousedown': 'onMouseDown',
|
|
||||||
'mouseup': 'onMouseUp',
|
|
||||||
'touchstart': 'onTouchStart',
|
|
||||||
'touchend': 'onTouchEnd',
|
|
||||||
'tap': 'onTap',
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseDown: function(e) {
|
|
||||||
this.onMouseMove(e);
|
|
||||||
this.addEventListener('mousemove', this.onMouseMove);
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseUp: function(e) {
|
|
||||||
this.removeEventListener('mousemove', this.onMouseMove);
|
|
||||||
},
|
|
||||||
|
|
||||||
onTouchStart: function(e) {
|
|
||||||
this.onTouchMove(e);
|
|
||||||
this.addEventListener('touchmove', this.onTouchMove);
|
|
||||||
},
|
|
||||||
|
|
||||||
onTouchEnd: function(e) {
|
|
||||||
this.removeEventListener('touchmove', this.onTouchMove);
|
|
||||||
},
|
|
||||||
|
|
||||||
onTap: function(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
},
|
|
||||||
|
|
||||||
onTouchMove: function(e) {
|
|
||||||
var touch = e.touches[0];
|
|
||||||
this.onColorSelect(e, {x: touch.clientX, y: touch.clientY});
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseMove: function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (this.mouseMoveIsThrottled) {
|
|
||||||
this.mouseMoveIsThrottled = false;
|
|
||||||
this.onColorSelect(e);
|
|
||||||
this.async(
|
|
||||||
function() { this.mouseMoveIsThrottled = true; }.bind(this), 100);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onColorSelect: function(e, coords) {
|
|
||||||
if (this.context) {
|
|
||||||
coords = coords || this.relativeMouseCoordinates(e);
|
|
||||||
var data = this.context.getImageData(coords.x, coords.y, 1, 1).data;
|
|
||||||
|
|
||||||
this.setColor({r: data[0], g: data[1], b: data[2]});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setColor: function(rgb) {
|
|
||||||
//save calculated color
|
|
||||||
this.color = {hex: rgbToHex(rgb), rgb: rgb};
|
|
||||||
|
|
||||||
this.fire('colorselected', {
|
|
||||||
rgb: this.color.rgb,
|
|
||||||
hex: this.color.hex
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given a mouse click event, return x,y coordinates relative to the clicked target
|
|
||||||
* @returns object with x, y values
|
|
||||||
*/
|
|
||||||
relativeMouseCoordinates: function(e) {
|
|
||||||
var x = 0, y = 0;
|
|
||||||
|
|
||||||
if (this.canvas) {
|
|
||||||
var rect = this.canvas.getBoundingClientRect();
|
|
||||||
x = e.clientX - rect.left;
|
|
||||||
y = e.clientY - rect.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: x,
|
|
||||||
y: y
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function() {
|
|
||||||
this.setColor = this.setColor.bind(this);
|
|
||||||
this.mouseMoveIsThrottled = true;
|
|
||||||
this.canvas = this.children[0];
|
|
||||||
this.context = this.canvas.getContext('2d');
|
|
||||||
|
|
||||||
var colorGradient = this.context.createLinearGradient(0, 0, this.width, 0);
|
|
||||||
colorGradient.addColorStop(0, "rgb(255,0,0)");
|
|
||||||
colorGradient.addColorStop(0.16, "rgb(255,0,255)");
|
|
||||||
colorGradient.addColorStop(0.32, "rgb(0,0,255)");
|
|
||||||
colorGradient.addColorStop(0.48, "rgb(0,255,255)");
|
|
||||||
colorGradient.addColorStop(0.64, "rgb(0,255,0)");
|
|
||||||
colorGradient.addColorStop(0.80, "rgb(255,255,0)");
|
|
||||||
colorGradient.addColorStop(1, "rgb(255,0,0)");
|
|
||||||
this.context.fillStyle = colorGradient;
|
|
||||||
this.context.fillRect(0, 0, this.width, this.height);
|
|
||||||
|
|
||||||
var bwGradient = this.context.createLinearGradient(0, 0, 0, this.height);
|
|
||||||
bwGradient.addColorStop(0, "rgba(255,255,255,1)");
|
|
||||||
bwGradient.addColorStop(0.5, "rgba(255,255,255,0)");
|
|
||||||
bwGradient.addColorStop(0.5, "rgba(0,0,0,0)");
|
|
||||||
bwGradient.addColorStop(1, "rgba(0,0,0,1)");
|
|
||||||
|
|
||||||
this.context.fillStyle = bwGradient;
|
|
||||||
this.context.fillRect(0, 0, this.width, this.height);
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,39 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/logbook-entry.html">
|
|
||||||
|
|
||||||
<dom-module id="ha-logbook">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<template is='dom-if' if='[[noEntries(entries)]]'>
|
|
||||||
No logbook entries found.
|
|
||||||
</template>
|
|
||||||
<template is='dom-repeat' items="[[entries]]">
|
|
||||||
<logbook-entry entry-obj="[[item]]"></logbook-entry>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'ha-logbook',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
entries: {
|
|
||||||
type: Object,
|
|
||||||
value: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
noEntries: function(entries) {
|
|
||||||
return !entries.length;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,247 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
<link rel='import' href='../bower_components/layout/layout.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='../bower_components/paper-header-panel/paper-header-panel.html'>
|
|
||||||
<link rel='import' href='../bower_components/paper-toolbar/paper-toolbar.html'>
|
|
||||||
<!--
|
|
||||||
Too broken for now.
|
|
||||||
<link rel='import' href='../bower_components/paper-menu/paper-menu.html'> -->
|
|
||||||
<link rel='import' href='../bower_components/iron-icon/iron-icon.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='../components/stream-status.html'>
|
|
||||||
|
|
||||||
<dom-module id='ha-sidebar'>
|
|
||||||
<style>
|
|
||||||
.sidenav {
|
|
||||||
background: #fafafa;
|
|
||||||
box-shadow: 1px 0 1px rgba(0, 0, 0, 0.1);
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
-ms-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*.sidenav paper-menu {
|
|
||||||
--paper-menu-color: var(--secondary-text-color);
|
|
||||||
--paper-menu-background-color: #fafafa;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
div.menu {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-icon-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-icon-item.selected {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-icon-item.logout {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
padding: 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-tools {
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<paper-header-panel mode='scroll' class='sidenav fit'>
|
|
||||||
<paper-toolbar>
|
|
||||||
<!-- forces paper toolbar to style title appropriate -->
|
|
||||||
<paper-icon-button hidden></paper-icon-button>
|
|
||||||
<div class="title">Home Assistant</div>
|
|
||||||
</paper-toolbar>
|
|
||||||
<!-- <paper-menu id='menu' selected='{{menuSelected}}'
|
|
||||||
selectable='[data-panel]' attr-for-selected='data-panel'> -->
|
|
||||||
<div class='menu'>
|
|
||||||
<paper-icon-item on-click='menuClicked' data-panel='states'>
|
|
||||||
<iron-icon item-icon icon='apps'></iron-icon> States
|
|
||||||
</paper-icon-item>
|
|
||||||
|
|
||||||
<template is='dom-repeat' items='{{possibleFilters}}'>
|
|
||||||
<paper-icon-item on-click='menuClicked' data-panel$='[[filterType(item)]]'>
|
|
||||||
<iron-icon item-icon icon='[[filterIcon(item)]]'></iron-icon>
|
|
||||||
<span>[[filterName(item)]]</span>
|
|
||||||
</paper-icon-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is='dom-if' if='[[hasHistoryComponent]]'>
|
|
||||||
<paper-icon-item on-click='menuClicked' data-panel='history'>
|
|
||||||
<iron-icon item-icon icon='assessment'></iron-icon>
|
|
||||||
History
|
|
||||||
</paper-icon-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is='dom-if' if='[[hasLogbookComponent]]'>
|
|
||||||
<paper-icon-item on-click='menuClicked' data-panel='logbook'>
|
|
||||||
<iron-icon item-icon icon='list'></iron-icon>
|
|
||||||
Logbook
|
|
||||||
</paper-icon-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<paper-icon-item on-click='menuClicked' data-panel='logout' class='logout'>
|
|
||||||
<iron-icon item-icon icon='exit-to-app'></iron-icon>
|
|
||||||
Log Out
|
|
||||||
</paper-icon-item>
|
|
||||||
|
|
||||||
<paper-item class='divider horizontal layout justified'>
|
|
||||||
<div>Streaming updates</div>
|
|
||||||
<stream-status></stream-status>
|
|
||||||
</paper-item>
|
|
||||||
|
|
||||||
<div class='text label divider'>Developer Tools</div>
|
|
||||||
<div class='dev-tools layout horizontal justified'>
|
|
||||||
<paper-icon-button
|
|
||||||
icon='settings-remote' data-panel='devService'
|
|
||||||
on-click='handleDevClick'></paper-icon-button>
|
|
||||||
<paper-icon-button
|
|
||||||
icon='settings-ethernet' data-panel='devState'
|
|
||||||
on-click='handleDevClick'></paper-icon-button>
|
|
||||||
<paper-icon-button
|
|
||||||
icon='settings-input-antenna' data-panel='devEvent'
|
|
||||||
on-click='handleDevClick'></paper-icon-button>
|
|
||||||
</div>
|
|
||||||
<!-- </paper-menu> -->
|
|
||||||
</div>
|
|
||||||
</paper-header-panel>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var configGetters = window.hass.configGetters;
|
|
||||||
var navigationGetters = window.hass.navigationGetters;
|
|
||||||
|
|
||||||
var authActions = window.hass.authActions;
|
|
||||||
var navigationActions = window.hass.navigationActions;
|
|
||||||
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
var entityDomainFilters = window.hass.util.entityDomainFilters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'ha-sidebar',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
menuSelected: {
|
|
||||||
type: String,
|
|
||||||
// observer: 'menuSelectedChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
selected: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: navigationGetters.activePage,
|
|
||||||
observer: 'selectedChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
possibleFilters: {
|
|
||||||
type: Array,
|
|
||||||
value: [],
|
|
||||||
bindNuclear: [
|
|
||||||
navigationGetters.possibleEntityDomainFilters,
|
|
||||||
function(domains) { return domains.toArray(); }
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
hasHistoryComponent: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: configGetters.isComponentLoaded('history'),
|
|
||||||
},
|
|
||||||
|
|
||||||
hasLogbookComponent: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: configGetters.isComponentLoaded('logbook'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// menuSelectedChanged: function(newVal) {
|
|
||||||
// if (this.selected !== newVal) {
|
|
||||||
// this.selectPanel(newVal);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
|
|
||||||
selectedChanged: function(newVal) {
|
|
||||||
// if (this.menuSelected !== newVal) {
|
|
||||||
// this.menuSelected = newVal;
|
|
||||||
// }
|
|
||||||
var menuItems = this.querySelectorAll('.menu [data-panel]');
|
|
||||||
|
|
||||||
for (var i = 0; i < menuItems.length; i++) {
|
|
||||||
if(menuItems[i].dataset.panel === newVal) {
|
|
||||||
menuItems[i].classList.add('selected');
|
|
||||||
} else {
|
|
||||||
menuItems[i].classList.remove('selected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
menuClicked: function(ev) {
|
|
||||||
var target = ev.target;
|
|
||||||
var checks = 5;
|
|
||||||
|
|
||||||
// find panel to select
|
|
||||||
while(checks && !target.dataset.panel) {
|
|
||||||
target = target.parentElement;
|
|
||||||
checks--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checks) {
|
|
||||||
this.selectPanel(target.dataset.panel);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDevClick: function(ev) {
|
|
||||||
// prevent it from highlighting first menu item
|
|
||||||
document.activeElement.blur();
|
|
||||||
this.menuClicked(ev);
|
|
||||||
},
|
|
||||||
|
|
||||||
selectPanel: function(newChoice) {
|
|
||||||
if(newChoice === this.selected) {
|
|
||||||
return;
|
|
||||||
} else if (newChoice == 'logout') {
|
|
||||||
this.handleLogOut();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
navigationActions.navigate.apply(null, newChoice.split('/'));
|
|
||||||
},
|
|
||||||
|
|
||||||
handleLogOut: function() {
|
|
||||||
authActions.logOut();
|
|
||||||
},
|
|
||||||
|
|
||||||
filterIcon: function(filter) {
|
|
||||||
return uiUtil.domainIcon(filter);
|
|
||||||
},
|
|
||||||
|
|
||||||
filterName: function(filter) {
|
|
||||||
return entityDomainFilters[filter];
|
|
||||||
},
|
|
||||||
|
|
||||||
filterType: function(filter) {
|
|
||||||
return 'states/' + filter;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,61 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/iron-icon/iron-icon.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
|
||||||
|
|
||||||
<dom-module id="ha-voice-command-progress">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
iron-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interimTranscript {
|
|
||||||
color: darkgrey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listening paper-spinner {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function(){
|
|
||||||
var voiceGetters = window.hass.voiceGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'ha-voice-command-progress',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
isTransmitting: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: voiceGetters.isTransmitting,
|
|
||||||
},
|
|
||||||
|
|
||||||
interimTranscript: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: voiceGetters.extraInterimTranscript,
|
|
||||||
},
|
|
||||||
|
|
||||||
finalTranscript: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: voiceGetters.finalTranscript,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,27 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
|
||||||
|
|
||||||
<dom-module id="loading-box">
|
|
||||||
<style>
|
|
||||||
.text {
|
|
||||||
display: inline-block;
|
|
||||||
line-height: 28px;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div layout='horizontal'>
|
|
||||||
<paper-spinner active="true"></paper-spinner>
|
|
||||||
<div class='text'><content></content>…</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'loading-box',
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,69 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="domain-icon.html">
|
|
||||||
<link rel="import" href="display-time.html">
|
|
||||||
<link rel="import" href="relative-ha-datetime.html">
|
|
||||||
|
|
||||||
<dom-module id="logbook-entry">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
line-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
display-time {
|
|
||||||
width: 55px;
|
|
||||||
font-size: .8em;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
domain-icon {
|
|
||||||
margin: 0 8px 0 16px;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
</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>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var moreInfoActions = window.hass.moreInfoActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'logbook-entry',
|
|
||||||
|
|
||||||
entityClicked: function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
moreInfoActions.selectEntity(this.entryObj.entityId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,74 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../resources/moment-js.html">
|
|
||||||
|
|
||||||
<dom-module id="relative-ha-datetime">
|
|
||||||
<template>
|
|
||||||
<span>[[relativeTime]]</span>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var UPDATE_INTERVAL = 60000; // 60 seconds
|
|
||||||
|
|
||||||
var parseDateTime = window.hass.util.parseDateTime;
|
|
||||||
|
|
||||||
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: "",
|
|
||||||
parsedDateTime: null,
|
|
||||||
|
|
||||||
created: function() {
|
|
||||||
this.updateRelative = this.updateRelative.bind(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
this._interval = setInterval(this.updateRelative, UPDATE_INTERVAL);
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
clearInterval(this._interval);
|
|
||||||
},
|
|
||||||
|
|
||||||
datetimeChanged: function(newVal) {
|
|
||||||
this.parsedDateTime = newVal ? parseDateTime(newVal) : null;
|
|
||||||
|
|
||||||
this.updateRelative();
|
|
||||||
},
|
|
||||||
|
|
||||||
datetimeObjChanged: function(newVal) {
|
|
||||||
this.parsedDateTime = newVal;
|
|
||||||
|
|
||||||
this.updateRelative();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateRelative: function() {
|
|
||||||
this.relativeTime = this.parsedDateTime ?
|
|
||||||
moment(this.parsedDateTime).fromNow() : "";
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,71 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/paper-menu/paper-menu.html">
|
|
||||||
|
|
||||||
<link rel="import" href="domain-icon.html">
|
|
||||||
|
|
||||||
<dom-module id="services-list">
|
|
||||||
<style>
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
list-style: none;
|
|
||||||
line-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<template is='dom-repeat' items="[[serviceDomains]]" as="domain">
|
|
||||||
<template is='dom-repeat' items="[[domain.services]]" as="service">
|
|
||||||
<li><a href='#' on-click='serviceClicked'>
|
|
||||||
<span>[[domain.domain]]</span>/<span>[[service]]</span>
|
|
||||||
</a></li>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var serviceGetters = window.hass.serviceGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'services-list',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
serviceDomains: {
|
|
||||||
type: Array,
|
|
||||||
bindNuclear: [
|
|
||||||
serviceGetters.entityMap,
|
|
||||||
function(map) {
|
|
||||||
return map.valueSeq()
|
|
||||||
.sortBy(function(domain) { return domain.domain; })
|
|
||||||
.toJS();
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeServices: function(domain) {
|
|
||||||
return this.services.get(domain).toArray();
|
|
||||||
},
|
|
||||||
|
|
||||||
serviceClicked: function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
this.fire(
|
|
||||||
'service-selected', {domain: ev.model.domain.domain, service: ev.model.service});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,105 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
<link rel='import' href='../bower_components/iron-image/iron-image.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='domain-icon.html'>
|
|
||||||
|
|
||||||
<dom-module id='state-badge'>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 45px;
|
|
||||||
background-color: #4fc3f7;
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
height: 45px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
iron-image {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
domain-icon {
|
|
||||||
margin: 0 auto;
|
|
||||||
transition: color .3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Color the icon if light or sun is on */
|
|
||||||
domain-icon[data-domain=light][data-state=on],
|
|
||||||
domain-icon[data-domain=switch][data-state=on],
|
|
||||||
domain-icon[data-domain=sun][data-state=above_horizon] {
|
|
||||||
color: #fff176;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class='layout horizontal center'>
|
|
||||||
<domain-icon id='icon'
|
|
||||||
domain='[[stateObj.domain]]' data-domain$='[[stateObj.domain]]'
|
|
||||||
state='[[stateObj.state]]' data-state$='[[stateObj.state]]'>
|
|
||||||
</domain-icon>
|
|
||||||
<template is='dom-if' if='[[stateObj.attributes.entity_picture]]'>
|
|
||||||
<iron-image
|
|
||||||
sizing='cover' class='fit'
|
|
||||||
src$="[[stateObj.attributes.entity_picture]]"></iron-image>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
Polymer({
|
|
||||||
is: 'state-badge',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'updateIconColor',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when an attribute changes that influences the color of the icon.
|
|
||||||
*/
|
|
||||||
updateIconColor: function(newVal) {
|
|
||||||
// for domain light, set color of icon to light color if available
|
|
||||||
if(newVal.domain == "light" && newVal.state == "on" &&
|
|
||||||
newVal.attributes.brightness && newVal.attributes.xy_color) {
|
|
||||||
|
|
||||||
var rgb = this.xyBriToRgb(newVal.attributes.xy_color[0],
|
|
||||||
newVal.attributes.xy_color[1],
|
|
||||||
newVal.attributes.brightness);
|
|
||||||
this.$.icon.style.color = "rgb(" + rgb.map(Math.floor).join(",") + ")";
|
|
||||||
} else {
|
|
||||||
this.$.icon.style.color = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// from http://stackoverflow.com/questions/22894498/philips-hue-convert-xy-from-api-to-hex-or-rgb
|
|
||||||
xyBriToRgb: function (x, y, bri) {
|
|
||||||
z = 1.0 - x - y;
|
|
||||||
Y = bri / 255.0; // Brightness of lamp
|
|
||||||
X = (Y / y) * x;
|
|
||||||
Z = (Y / y) * z;
|
|
||||||
r = X * 1.612 - Y * 0.203 - Z * 0.302;
|
|
||||||
g = -X * 0.509 + Y * 1.412 + Z * 0.066;
|
|
||||||
b = X * 0.026 - Y * 0.072 + Z * 0.962;
|
|
||||||
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
|
|
||||||
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
|
|
||||||
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
|
|
||||||
maxValue = Math.max(r,g,b);
|
|
||||||
r /= maxValue;
|
|
||||||
g /= maxValue;
|
|
||||||
b /= maxValue;
|
|
||||||
r = r * 255; if (r < 0) { r = 255; }
|
|
||||||
g = g * 255; if (g < 0) { g = 255; }
|
|
||||||
b = b * 255; if (b < 0) { b = 255; }
|
|
||||||
return [r, g, b];
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,80 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../cards/state-card.html">
|
|
||||||
|
|
||||||
<dom-module id="state-cards">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (min-width: 1020px) {
|
|
||||||
.state-card {
|
|
||||||
width: calc(50% - 44px);
|
|
||||||
margin: 8px 0 0 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (min-width: 1356px) {
|
|
||||||
.state-card {
|
|
||||||
width: calc(33% - 38px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (min-width: 1706px) {
|
|
||||||
.state-card {
|
|
||||||
width: calc(25% - 42px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-states-content {
|
|
||||||
max-width: 500px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: rgba(0, 0, 0, 0.098) 0px 2px 4px, rgba(0, 0, 0, 0.098) 0px 0px 3px;
|
|
||||||
padding: 0 16px 8px;
|
|
||||||
margin: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class='horizontal layout wrap'>
|
|
||||||
|
|
||||||
<template is='dom-repeat' items="{{states}}">
|
|
||||||
<state-card class="state-card" state-obj="[[item]]"></state-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is='dom-if' if="[[computeEmptyStates(states)]]">
|
|
||||||
<div class='no-states-content'>
|
|
||||||
<h3>Hi there!</h3>
|
|
||||||
<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.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Please see the <a href='https://home-assistant.io/getting-started/' target='_blank'>Getting Started</a> section on how to setup your devices.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'state-cards',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
states: {
|
|
||||||
type: Array,
|
|
||||||
value: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeEmptyStates: function(states) {
|
|
||||||
return states.length === 0;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,198 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../resources/lodash.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>
|
|
@ -1,120 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<dom-module is='state-history-chart-timeline'>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template></template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'state-history-chart-timeline',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'dataChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
isAttached: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
observer: 'dataChanged',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
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.node.lastChild) {
|
|
||||||
root.node.removeChild(root.node.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stateHistory || stateHistory.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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]);
|
|
||||||
};
|
|
||||||
|
|
||||||
var startTime = new Date(
|
|
||||||
stateHistory.reduce(function(minTime, stateInfo) {
|
|
||||||
return Math.min(
|
|
||||||
minTime, stateInfo[0].lastChangedAsDate);
|
|
||||||
}, new Date())
|
|
||||||
);
|
|
||||||
|
|
||||||
// end time is Math.min(curTime, start time + 1 day)
|
|
||||||
var endTime = new Date(startTime);
|
|
||||||
endTime.setDate(endTime.getDate()+1);
|
|
||||||
if (endTime > new Date()) {
|
|
||||||
endTime = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
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, endTime);
|
|
||||||
numTimelines++;
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
chart.draw(dataTable, {
|
|
||||||
height: 55 + numTimelines * 42,
|
|
||||||
|
|
||||||
timeline: {
|
|
||||||
showRowLabels: stateHistory.length > 1
|
|
||||||
},
|
|
||||||
|
|
||||||
hAxis: {
|
|
||||||
format: 'H:mm'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,158 +0,0 @@
|
|||||||
<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
height: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<google-legacy-loader on-api-load="googleApiLoaded"></google-legacy-loader>
|
|
||||||
|
|
||||||
<div hidden$="{{!isLoading}}" class='loading-container'>
|
|
||||||
<loading-box>Updating history data</loading-box>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class$='[[computeContentClasses(isLoading)]]'>
|
|
||||||
<template is='dom-if' if='[[computeIsEmpty(stateHistory)]]'>
|
|
||||||
No state history found.
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<state-history-chart-timeline
|
|
||||||
data='[[groupedStateHistory.timeline]]'
|
|
||||||
is-single-device='[[isSingleDevice]]'>
|
|
||||||
</state-history-chart-timeline>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</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(isLoading, stateHistory)',
|
|
||||||
},
|
|
||||||
|
|
||||||
isSingleDevice: {
|
|
||||||
type: Boolean,
|
|
||||||
computed: 'computeIsSingleDevice(stateHistory)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeIsSingleDevice: function(stateHistory) {
|
|
||||||
return stateHistory && stateHistory.size == 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
computeGroupedStateHistory: function(isLoading, stateHistory) {
|
|
||||||
if (isLoading || !stateHistory) {
|
|
||||||
return {line: [], timeline: []};
|
|
||||||
}
|
|
||||||
|
|
||||||
var lineChartDevices = {};
|
|
||||||
var timelineDevices = [];
|
|
||||||
|
|
||||||
stateHistory.forEach(function(stateInfo) {
|
|
||||||
if (!stateInfo || stateInfo.size === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var stateWithUnit = stateInfo.find(function(state) {
|
|
||||||
return 'unit_of_measurement' in state.attributes;
|
|
||||||
});
|
|
||||||
|
|
||||||
var unit = stateWithUnit ?
|
|
||||||
stateWithUnit.attributes.unit_of_measurement : false;
|
|
||||||
|
|
||||||
if (!unit) {
|
|
||||||
timelineDevices.push(stateInfo.toArray());
|
|
||||||
} else if(unit in lineChartDevices) {
|
|
||||||
lineChartDevices[unit].push(stateInfo.toArray());
|
|
||||||
} else {
|
|
||||||
lineChartDevices[unit] = [stateInfo.toArray()];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
timelineDevices = timelineDevices.length > 0 && timelineDevices;
|
|
||||||
|
|
||||||
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)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
computeContentClasses: function(isLoading) {
|
|
||||||
return isLoading ? 'loading' : '';
|
|
||||||
},
|
|
||||||
|
|
||||||
computeIsLoading: function(isLoadingData, apiLoaded) {
|
|
||||||
return isLoadingData || !apiLoaded;
|
|
||||||
},
|
|
||||||
|
|
||||||
computeIsEmpty: function(stateHistory) {
|
|
||||||
return stateHistory && stateHistory.size === 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
extractUnit: function(arr) {
|
|
||||||
return arr[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
extractData: function(arr) {
|
|
||||||
return arr[1];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,75 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
<!-- <link rel="import" href="../bower_components/core-tooltip/core-tooltip.html"> -->
|
|
||||||
|
|
||||||
<link rel="import" href="state-badge.html">
|
|
||||||
<link rel="import" href="relative-ha-datetime.html">
|
|
||||||
|
|
||||||
<dom-module id="state-info">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
line-height: normal;
|
|
||||||
min-width: 150px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
state-badge {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
margin-left: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
text-transform: capitalize;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 1.3rem;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-ago {
|
|
||||||
color: darkgrey;
|
|
||||||
margin-top: -2px;
|
|
||||||
font-size: 1rem;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<state-badge state-obj='[[stateObj]]'></state-badge>
|
|
||||||
|
|
||||||
<div class='info'>
|
|
||||||
<div class='name'>[[stateObj.entityDisplay]]</div>
|
|
||||||
|
|
||||||
<div class='time-ago'>
|
|
||||||
<!-- <core-tooltip label="[[computeTooltipLabel(stateObj)]]" position="bottom"> -->
|
|
||||||
<relative-ha-datetime datetime-obj='[[stateObj.lastChangedAsDate]]'></relative-ha-datetime>
|
|
||||||
<!-- </core-tooltip> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'state-info',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeTooltipLabel: function(stateObj) {
|
|
||||||
// stateObj.lastChangedAsDate | formatDateTime
|
|
||||||
return 'Label TODO';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,54 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.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">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/iron-icons/notification-icons.html">
|
|
||||||
|
|
||||||
<dom-module id="stream-status">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: inline-block;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-toggle-button {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<iron-icon icon="warning" hidden$="[[!hasError]]"></iron-icon>
|
|
||||||
<paper-toggle-button id="toggle" on-change='toggleChanged' checked$='[[isStreaming]]' hidden$="[[hasError]]"></paper-toggle-button>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var streamGetters = window.hass.streamGetters;
|
|
||||||
var streamActions = window.hass.streamActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'stream-status',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
isStreaming: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: streamGetters.isStreamingEvents,
|
|
||||||
},
|
|
||||||
|
|
||||||
hasError: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: streamGetters.hasStreamingEventsError,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleChanged: function() {
|
|
||||||
if (this.isStreaming) {
|
|
||||||
streamActions.stop();
|
|
||||||
} else {
|
|
||||||
streamActions.start();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,151 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.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="../components/state-history-charts.html">
|
|
||||||
<link rel="import" href="../more-infos/more-info-content.html">
|
|
||||||
|
|
||||||
<dom-module id="more-info-dialog">
|
|
||||||
<style>
|
|
||||||
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 opened='{{dialogOpen}}'>
|
|
||||||
<h2><state-card-content state-obj="[[stateObj]]"></state-card-content></h2>
|
|
||||||
<div>
|
|
||||||
<template is='dom-if' if="[[showHistoryComponent]]">
|
|
||||||
<state-history-charts state-history="[[stateHistory]]"
|
|
||||||
is-loading-data="[[isLoadingHistoryData]]"></state-history-charts>
|
|
||||||
</template>
|
|
||||||
<paper-dialog-scrollable>
|
|
||||||
<more-info-content state-obj="[[stateObj]]"
|
|
||||||
dialog-open="[[dialogOpen]]"></more-info-content>
|
|
||||||
</paper-dialog-scrollable>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var configGetters = window.hass.configGetters;
|
|
||||||
var entityHistoryGetters = window.hass.entityHistoryGetters;
|
|
||||||
|
|
||||||
var entityHistoryActions = window.hass.entityHistoryActions;
|
|
||||||
var moreInfoGetters = window.hass.moreInfoGetters;
|
|
||||||
var moreInfoActions = window.hass.moreInfoActions;
|
|
||||||
|
|
||||||
// if you don't want the history component to show add the domain to this array
|
|
||||||
var DOMAINS_WITH_NO_HISTORY = ['camera'];
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-dialog',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
bindNuclear: moreInfoGetters.currentEntity,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
stateHistory: {
|
|
||||||
type: Object,
|
|
||||||
bindNuclear: [
|
|
||||||
moreInfoGetters.currentEntityHistory,
|
|
||||||
function(history) {
|
|
||||||
return history ? [history] : false;
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
isLoadingHistoryData: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: entityHistoryGetters.isLoadingEntityHistory,
|
|
||||||
},
|
|
||||||
|
|
||||||
hasHistoryComponent: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: configGetters.isComponentLoaded('history'),
|
|
||||||
observer: 'fetchHistoryData',
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldFetchHistory: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: moreInfoGetters.isCurrentEntityHistoryStale,
|
|
||||||
observer: 'fetchHistoryData',
|
|
||||||
},
|
|
||||||
|
|
||||||
showHistoryComponent: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
dialogOpen: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
observer: 'dialogOpenChanged',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchHistoryData: function() {
|
|
||||||
if (this.stateObj && this.hasHistoryComponent &&
|
|
||||||
this.shouldFetchHistory) {
|
|
||||||
entityHistoryActions.fetchRecent(this.stateObj.entityId);
|
|
||||||
}
|
|
||||||
if(this.stateObj) {
|
|
||||||
if(DOMAINS_WITH_NO_HISTORY.indexOf(this.stateObj.domain) !== -1) {
|
|
||||||
this.showHistoryComponent = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.showHistoryComponent = this.hasHistoryComponent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function(newVal) {
|
|
||||||
if (!newVal) {
|
|
||||||
this.dialogOpen = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fetchHistoryData();
|
|
||||||
|
|
||||||
// allow dialog to render content before showing it so it is
|
|
||||||
// positioned correctly.
|
|
||||||
this.async(function() {
|
|
||||||
this.dialogOpen = true;
|
|
||||||
}.bind(this), 10);
|
|
||||||
},
|
|
||||||
|
|
||||||
dialogOpenChanged: function(newVal) {
|
|
||||||
if (!newVal) {
|
|
||||||
moreInfoActions.deselectEntity();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 94d8682c1e7679ae744e8419896d5d7b0bdd16cc
|
|
@ -1,78 +0,0 @@
|
|||||||
<link rel='import' href='bower_components/polymer/polymer.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='bower_components/paper-styles/typography.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='resources/home-assistant-js.html'>
|
|
||||||
<link rel='import' href='resources/home-assistant-icons.html'>
|
|
||||||
<link rel='import' href='resources/store-listener-behavior.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='layouts/login-form.html'>
|
|
||||||
<link rel='import' href='layouts/home-assistant-main.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='resources/home-assistant-style.html'>
|
|
||||||
|
|
||||||
<dom-module id='home-assistant'>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
font-family: 'Roboto', 'Noto', sans-serif;
|
|
||||||
font-weight: 300;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<home-assistant-icons></home-assistant-icons>
|
|
||||||
<template is='dom-if' if='[[!loaded]]'>
|
|
||||||
<login-form></login-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is='dom-if' if='[[loaded]]'>
|
|
||||||
<home-assistant-main></home-assistant-main>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var uiActions = window.hass.uiActions;
|
|
||||||
var syncGetters = window.hass.syncGetters;
|
|
||||||
var preferences = window.hass.localStoragePreferences;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'home-assistant',
|
|
||||||
|
|
||||||
hostAttributes: {
|
|
||||||
auth: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
auth: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
loaded: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: syncGetters.isDataLoaded,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function() {
|
|
||||||
// remove the HTML init message
|
|
||||||
document.getElementById('init').remove();
|
|
||||||
|
|
||||||
// if auth was given, tell the backend
|
|
||||||
if(this.auth) {
|
|
||||||
uiActions.validateAuth(this.auth, false);
|
|
||||||
} else if (preferences.authToken) {
|
|
||||||
uiActions.validateAuth(preferences.authToken, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences.startSync();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"removeComments": true,
|
|
||||||
"removeCommentsFromCDATA": true,
|
|
||||||
"removeCDATASectionsFromCDATA": true,
|
|
||||||
"collapseWhitespace": true,
|
|
||||||
"collapseBooleanAttributes": true,
|
|
||||||
"removeScriptTypeAttributes": true,
|
|
||||||
"removeStyleLinkTypeAttributes": true,
|
|
||||||
"minifyJS": true,
|
|
||||||
"minifyCSS": true
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
<link rel='import' href='../bower_components/layout/layout.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='../bower_components/paper-drawer-panel/paper-drawer-panel.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='../layouts/partial-states.html'>
|
|
||||||
<link rel='import' href='../layouts/partial-logbook.html'>
|
|
||||||
<link rel='import' href='../layouts/partial-history.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='../managers/notification-manager.html'>
|
|
||||||
<link rel="import" href="../dialogs/more-info-dialog.html">
|
|
||||||
|
|
||||||
<link rel='import' href='../components/ha-sidebar.html'>
|
|
||||||
|
|
||||||
<dom-module id='home-assistant-main'>
|
|
||||||
<template>
|
|
||||||
<notification-manager></notification-manager>
|
|
||||||
<more-info-dialog></more-info-dialog>
|
|
||||||
|
|
||||||
<paper-drawer-panel id='drawer' narrow='{{narrow}}'>
|
|
||||||
<ha-sidebar drawer></ha-sidebar>
|
|
||||||
|
|
||||||
<template is='dom-if' if='[[isSelectedStates]]'>
|
|
||||||
<partial-states main narrow='[[narrow]]'>
|
|
||||||
</partial-states>
|
|
||||||
</template>
|
|
||||||
<template is='dom-if' if='[[isSelectedLogbook]]'>
|
|
||||||
<partial-logbook main narrow='[[narrow]]'></partial-logbook>
|
|
||||||
</template>
|
|
||||||
<template is='dom-if' if='[[isSelectedHistory]]'>
|
|
||||||
<partial-history main narrow='[[narrow]]'></partial-history>
|
|
||||||
</template>
|
|
||||||
<template is='dom-if' if='[[isSelectedDevService]]'>
|
|
||||||
<partial-dev-call-service main narrow='[[narrow]]'></partial-dev-call-service>
|
|
||||||
</template>
|
|
||||||
<template is='dom-if' if='[[isSelectedDevEvent]]'>
|
|
||||||
<partial-dev-fire-event main narrow='[[narrow]]'></partial-dev-fire-event>
|
|
||||||
</template>
|
|
||||||
<template is='dom-if' if='[[isSelectedDevState]]'>
|
|
||||||
<partial-dev-set-state main narrow='[[narrow]]'></partial-dev-set-state>
|
|
||||||
</template>
|
|
||||||
</paper-drawer-panel>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var configGetters = window.hass.configGetters;
|
|
||||||
var entityGetters = window.hass.entityGetters;
|
|
||||||
var navigationGetters = window.hass.navigationGetters;
|
|
||||||
|
|
||||||
var authActions = window.hass.authActions;
|
|
||||||
var navigationActions = window.hass.navigationActions;
|
|
||||||
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
var entityDomainFilters = window.hass.util.entityDomainFilters;
|
|
||||||
var urlSync = window.hass.urlSync;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'home-assistant-main',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
activePage: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: navigationGetters.activePage,
|
|
||||||
observer: 'activePageChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
isSelectedStates: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: navigationGetters.isActivePane('states'),
|
|
||||||
},
|
|
||||||
|
|
||||||
isSelectedHistory: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: navigationGetters.isActivePane('history'),
|
|
||||||
},
|
|
||||||
|
|
||||||
isSelectedLogbook: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: navigationGetters.isActivePane('logbook'),
|
|
||||||
},
|
|
||||||
|
|
||||||
isSelectedDevEvent: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: navigationGetters.isActivePane('devEvent'),
|
|
||||||
},
|
|
||||||
|
|
||||||
isSelectedDevState: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: navigationGetters.isActivePane('devState'),
|
|
||||||
},
|
|
||||||
|
|
||||||
isSelectedDevService: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: navigationGetters.isActivePane('devService'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
listeners: {
|
|
||||||
'open-menu': 'openDrawer',
|
|
||||||
},
|
|
||||||
|
|
||||||
openDrawer: function() {
|
|
||||||
this.$.drawer.openDrawer();
|
|
||||||
},
|
|
||||||
|
|
||||||
activePageChanged: function() {
|
|
||||||
this.$.drawer.closeDrawer();
|
|
||||||
},
|
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
urlSync.startSync();
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
urlSync.stopSync();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,160 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
<link rel="import" href="../bower_components/layout/layout.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-button/paper-button.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-input-container.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="../resources/store-listener-behavior.html">
|
|
||||||
|
|
||||||
<dom-module id="login-form">
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
#passwordDecorator {
|
|
||||||
display: block;
|
|
||||||
height: 57px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-checkbox {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-checkbox::shadow #checkbox.checked {
|
|
||||||
background-color: #03a9f4;
|
|
||||||
border-color: #03a9f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-checkbox::shadow #ink[checked] {
|
|
||||||
color: #03a9f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-button {
|
|
||||||
margin-left: 72px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interact {
|
|
||||||
height: 125px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#validatebox {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validatemessage {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="layout horizontal center fit login" id="splash">
|
|
||||||
<div class="layout vertical center flex">
|
|
||||||
|
|
||||||
<img src="/static/favicon-192x192.png" />
|
|
||||||
<h1>Home Assistant</h1>
|
|
||||||
|
|
||||||
<a href="#" id="hideKeyboardOnFocus"></a>
|
|
||||||
|
|
||||||
<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 class="layout horizontal center">
|
|
||||||
<paper-checkbox for id='rememberLogin'>Remember</paper-checkbox>
|
|
||||||
<paper-button id='loginButton'>Log In</paper-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="validatebox" hidden$="[[!isValidating]]">
|
|
||||||
<paper-spinner active="true"></paper-spinner><br />
|
|
||||||
<div class="validatemessage">Loading data…</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var uiActions = window.hass.uiActions;
|
|
||||||
var authGetters = window.hass.authGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'login-form',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
isValidating: {
|
|
||||||
type: Boolean,
|
|
||||||
observer: 'isValidatingChanged',
|
|
||||||
bindNuclear: authGetters.isValidating,
|
|
||||||
},
|
|
||||||
|
|
||||||
isInvalid: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: authGetters.isInvalidAttempt,
|
|
||||||
},
|
|
||||||
|
|
||||||
errorMessage: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: authGetters.attemptErrorMessage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
listeners: {
|
|
||||||
'keydown': 'passwordKeyDown',
|
|
||||||
'loginButton.click': 'validatePassword',
|
|
||||||
},
|
|
||||||
|
|
||||||
observers: [
|
|
||||||
'validatingChanged(isValidating, isInvalid)',
|
|
||||||
],
|
|
||||||
|
|
||||||
validatingChanged: function(isValidating, isInvalid) {
|
|
||||||
if (!isValidating && !isInvalid) {
|
|
||||||
this.$.passwordInput.value = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
isValidatingChanged: function(newVal) {
|
|
||||||
if (!newVal) {
|
|
||||||
this.async(function() { this.$.passwordInput.focus(); }.bind(this), 10);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
passwordKeyDown: function(ev) {
|
|
||||||
// validate on enter
|
|
||||||
if(ev.keyCode === 13) {
|
|
||||||
this.validatePassword();
|
|
||||||
ev.preventDefault();
|
|
||||||
|
|
||||||
// clear error after we start typing again
|
|
||||||
} else if(this.isInvalid) {
|
|
||||||
this.isInvalid = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validatePassword: function() {
|
|
||||||
this.$.hideKeyboardOnFocus.focus();
|
|
||||||
|
|
||||||
uiActions.validateAuth(this.$.passwordInput.value, this.$.rememberLogin.checked);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='../bower_components/paper-scroll-header-panel/paper-scroll-header-panel.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'>
|
|
||||||
|
|
||||||
<dom-module id='partial-base'>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
-ms-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<paper-scroll-header-panel class='fit'>
|
|
||||||
<paper-toolbar>
|
|
||||||
<paper-icon-button icon='menu' hidden$='[[!narrow]]' on-click='toggleMenu'></paper-icon-button>
|
|
||||||
<div class="title">
|
|
||||||
<content select='[header-title]'></content>
|
|
||||||
</div>
|
|
||||||
<content select='[header-buttons]'></content>
|
|
||||||
</paper-toolbar>
|
|
||||||
|
|
||||||
<content></content>
|
|
||||||
</paper-scroll-header-panel>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'partial-base',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleMenu: function() {
|
|
||||||
this.fire('open-menu');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,101 +0,0 @@
|
|||||||
<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-input/paper-input.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-textarea.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/services-list.html">
|
|
||||||
|
|
||||||
<dom-module id="partial-dev-call-service">
|
|
||||||
<style>
|
|
||||||
.form {
|
|
||||||
padding: 24px;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ha-form {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<partial-base narrow="[[narrow]]">
|
|
||||||
<span header-title>Call Service</span>
|
|
||||||
|
|
||||||
<div class='form fit'>
|
|
||||||
<p>
|
|
||||||
Call a service from a component.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class$='[[computeFormClasses(narrow)]]'>
|
|
||||||
<div class='ha-form flex'>
|
|
||||||
<paper-input label="Domain" autofocus value='{{domain}}'></paper-input>
|
|
||||||
<paper-input label="Service" value='{{service}}'></paper-input>
|
|
||||||
<paper-textarea label="Service Data (JSON, optional)" value='{{serviceData}}'></paper-textarea>
|
|
||||||
<paper-button on-click='callService' raised>Call Service</paper-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h4>Available services:</h4>
|
|
||||||
<services-list on-service-selected='serviceSelected'></services-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</partial-base>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var serviceActions = window.hass.serviceActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'partial-dev-call-service',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
domain: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
service: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
serviceData: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
serviceSelected: function(ev) {
|
|
||||||
this.domain = ev.detail.domain;
|
|
||||||
this.service = ev.detail.service;
|
|
||||||
},
|
|
||||||
|
|
||||||
callService: function() {
|
|
||||||
var serviceData;
|
|
||||||
|
|
||||||
try {
|
|
||||||
serviceData = this.serviceData ? JSON.parse(this.serviceData): {};
|
|
||||||
} catch (err) {
|
|
||||||
alert("Error parsing JSON: " + err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceActions.callService(this.domain, this.service, serviceData);
|
|
||||||
},
|
|
||||||
|
|
||||||
computeFormClasses: function(narrow) {
|
|
||||||
return 'layout ' + (narrow ? 'vertical' : 'horizontal');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,91 +0,0 @@
|
|||||||
<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-input/paper-input.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-textarea.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/events-list.html">
|
|
||||||
|
|
||||||
<dom-module id="partial-dev-fire-event">
|
|
||||||
<style>
|
|
||||||
.form {
|
|
||||||
padding: 24px;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ha-form {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<partial-base narrow="{{narrow}}">
|
|
||||||
<span header-title>Fire Event</span>
|
|
||||||
|
|
||||||
<div class='form fit'>
|
|
||||||
<p>
|
|
||||||
Fire an event on the event bus.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class$='[[computeFormClasses(narrow)]]'>
|
|
||||||
<div class='ha-form flex'>
|
|
||||||
<paper-input label="Event Type" autofocus required value='{{eventType}}'></paper-input>
|
|
||||||
<paper-textarea label="Event Data (JSON, optional)" value='{{eventData}}'></paper-textarea>
|
|
||||||
<paper-button on-click='fireEvent' raised>Fire Event</paper-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h4>Available events:</h4>
|
|
||||||
<events-list on-event-selected='eventSelected'></event-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</partial-base>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var eventActions = window.hass.eventActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'partial-dev-fire-event',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
eventType: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
eventData: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
eventSelected: function(ev) {
|
|
||||||
this.eventType = ev.detail.eventType;
|
|
||||||
},
|
|
||||||
|
|
||||||
fireEvent: function() {
|
|
||||||
var eventData;
|
|
||||||
|
|
||||||
try {
|
|
||||||
eventData = this.eventData ? JSON.parse(this.eventData) : {};
|
|
||||||
} catch (err) {
|
|
||||||
alert("Error parsing JSON: " + err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
eventActions.fireEvent(this.eventType, eventData);
|
|
||||||
},
|
|
||||||
|
|
||||||
computeFormClasses: function(narrow) {
|
|
||||||
return 'layout ' + (narrow ? 'vertical' : 'horizontal');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,115 +0,0 @@
|
|||||||
<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-input/paper-input.html">
|
|
||||||
<link rel="import" href="../bower_components/paper-input/paper-textarea.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/entity-list.html">
|
|
||||||
|
|
||||||
<dom-module id="partial-dev-set-state">
|
|
||||||
<style>
|
|
||||||
.form {
|
|
||||||
padding: 24px;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ha-form {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<partial-base narrow="[[narrow]]">
|
|
||||||
<span header-title>Set State</span>
|
|
||||||
|
|
||||||
<div class='form fit'>
|
|
||||||
<div>
|
|
||||||
Set the representation of a device within Home Assistant.<br />
|
|
||||||
This will not communicate with the actual device.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class$='[[computeFormClasses(narrow)]]'>
|
|
||||||
<div class='ha-form flex'>
|
|
||||||
<paper-input label="Entity ID" autofocus required value='{{entityId}}'></paper-input>
|
|
||||||
<paper-input label="State" required value='{{state}}'></paper-input>
|
|
||||||
<paper-textarea label="State attributes (JSON, optional)" value='{{stateAttributes}}'></paper-textarea>
|
|
||||||
<paper-button on-click='handleSetState' raised>Set State</paper-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='sidebar'>
|
|
||||||
<h4>Current entities:</h4>
|
|
||||||
<entity-list on-entity-selected='entitySelected'></entity-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</partial-base>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var reactor = window.hass.reactor;
|
|
||||||
var entityGetters = window.hass.entityGetters;
|
|
||||||
var entityActions = window.hass.entityActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'partial-dev-set-state',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
entityId: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
state: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
stateAttributes: {
|
|
||||||
type: String,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setStateData: function(stateData) {
|
|
||||||
var value = stateData ? JSON.stringify(stateData, null, ' ') : "";
|
|
||||||
|
|
||||||
this.$.inputData.value = value;
|
|
||||||
|
|
||||||
// not according to the spec but it works...
|
|
||||||
this.$.inputDataWrapper.update(this.$.inputData);
|
|
||||||
},
|
|
||||||
|
|
||||||
entitySelected: function(ev) {
|
|
||||||
var state = reactor.evaluate(entityGetters.byId(ev.detail.entityId));
|
|
||||||
|
|
||||||
this.entityId = state.entityId;
|
|
||||||
this.state = state.state;
|
|
||||||
this.stateAttributes = JSON.stringify(state.attributes, null, ' ');
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSetState: function() {
|
|
||||||
var attr;
|
|
||||||
try {
|
|
||||||
attr = this.stateAttributes ? JSON.parse(this.stateAttributes) : {};
|
|
||||||
} catch (err) {
|
|
||||||
alert("Error parsing JSON: " + err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityActions.save({
|
|
||||||
entityId: this.entityId,
|
|
||||||
state: this.state,
|
|
||||||
attributes: attr,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
computeFormClasses: function(narrow) {
|
|
||||||
return 'layout ' + (narrow ? 'vertical' : 'horizontal');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,116 +0,0 @@
|
|||||||
<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/paper-input/paper-input.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/state-history-charts.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../resources/pikaday-js.html">
|
|
||||||
|
|
||||||
<dom-module id="partial-history">
|
|
||||||
<style>
|
|
||||||
.content {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content.wide {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-input {
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.narrow paper-input {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<partial-base narrow="[[narrow]]">
|
|
||||||
<span header-title>History</span>
|
|
||||||
|
|
||||||
<paper-icon-button icon="refresh" header-buttons
|
|
||||||
on-click="handleRefreshClick"></paper-icon-button>
|
|
||||||
|
|
||||||
<div class$="[[computeContentClasses(narrow)]]">
|
|
||||||
<paper-input label='Showing entries for' id='datePicker'
|
|
||||||
value='[[selectedDate]]'></paper-input>
|
|
||||||
|
|
||||||
<state-history-charts state-history="[[stateHistory]]"
|
|
||||||
is-loading-data="[[isLoadingData]]"></state-history-charts>
|
|
||||||
</div>
|
|
||||||
</partial-base>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var entityHistoryGetters = window.hass.entityHistoryGetters;
|
|
||||||
var entityHistoryActions = window.hass.entityHistoryActions;
|
|
||||||
var uiActions = window.hass.uiActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'partial-history',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
isDataLoaded: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: entityHistoryGetters.hasDataForCurrentDate,
|
|
||||||
observer: 'isDataLoadedChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
stateHistory: {
|
|
||||||
type: Object,
|
|
||||||
bindNuclear: entityHistoryGetters.entityHistoryForCurrentDate,
|
|
||||||
},
|
|
||||||
|
|
||||||
isLoadingData: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: entityHistoryGetters.isLoadingEntityHistory,
|
|
||||||
},
|
|
||||||
|
|
||||||
selectedDate: {
|
|
||||||
type: String,
|
|
||||||
value: null,
|
|
||||||
bindNuclear: entityHistoryGetters.currentDate,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
isDataLoadedChanged: function(newVal) {
|
|
||||||
if (!newVal) {
|
|
||||||
entityHistoryActions.fetchSelectedDate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleRefreshClick: function() {
|
|
||||||
entityHistoryActions.fetchSelectedDate();
|
|
||||||
},
|
|
||||||
|
|
||||||
datepickerFocus: function() {
|
|
||||||
this.datePicker.adjustPosition();
|
|
||||||
this.datePicker.gotoDate(moment('2015-06-30').toDate());
|
|
||||||
},
|
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
this.datePicker = new Pikaday({
|
|
||||||
field: this.$.datePicker.inputElement,
|
|
||||||
onSelect: entityHistoryActions.changeCurrentDate,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
this.datePicker.destroy();
|
|
||||||
},
|
|
||||||
|
|
||||||
computeContentClasses: function(narrow) {
|
|
||||||
return 'flex content ' + (narrow ? 'narrow' : 'wide');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,119 +0,0 @@
|
|||||||
<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/paper-input/paper-input.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./partial-base.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/ha-logbook.html">
|
|
||||||
<link rel="import" href="../components/loading-box.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../resources/pikaday-js.html">
|
|
||||||
|
|
||||||
<dom-module id="partial-logbook">
|
|
||||||
<style>
|
|
||||||
.selected-date-container {
|
|
||||||
padding: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-input {
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<partial-base narrow="[[narrow]]">
|
|
||||||
<span header-title>Logbook</span>
|
|
||||||
|
|
||||||
<paper-icon-button icon="refresh" header-buttons
|
|
||||||
on-click="handleRefresh"></paper-icon-button>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class='selected-date-container'>
|
|
||||||
<paper-input label='Showing entries for' id='datePicker'
|
|
||||||
value='[[selectedDate]]' on-focus='datepickerFocus'></paper-input>
|
|
||||||
|
|
||||||
<loading-box hidden$='[[!isLoading]]'>Loading logbook entries</loading-box>
|
|
||||||
</div>
|
|
||||||
<ha-logbook entries="[[entries]]" hidden$='[[isLoading]]'></ha-logbook>
|
|
||||||
</div>
|
|
||||||
</partial-base>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var logbookGetters = window.hass.logbookGetters;
|
|
||||||
var logbookActions = window.hass.logbookActions;
|
|
||||||
var uiActions = window.hass.uiActions;
|
|
||||||
var dateToStr = window.hass.util.dateToStr;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'partial-logbook',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
selectedDate: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: logbookGetters.currentDate,
|
|
||||||
},
|
|
||||||
|
|
||||||
isLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: logbookGetters.isLoadingEntries,
|
|
||||||
},
|
|
||||||
|
|
||||||
isStale: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: logbookGetters.isCurrentStale,
|
|
||||||
observer: 'isStaleChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
entries: {
|
|
||||||
type: Array,
|
|
||||||
bindNuclear: [
|
|
||||||
logbookGetters.currentEntries,
|
|
||||||
function(entries) { return entries.toArray(); },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
datePicker: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
isStaleChanged: function(newVal) {
|
|
||||||
if (newVal) {
|
|
||||||
// isLoading wouldn't update without async <_<
|
|
||||||
this.async(
|
|
||||||
function() { logbookActions.fetchDate(this.selectedDate); }, 10);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleRefresh: function() {
|
|
||||||
logbookActions.fetchDate(this.selectedDate);
|
|
||||||
},
|
|
||||||
|
|
||||||
datepickerFocus: function() {
|
|
||||||
this.datePicker.adjustPosition();
|
|
||||||
this.datePicker.gotoDate(moment('2015-06-30').toDate());
|
|
||||||
},
|
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
this.datePicker = new Pikaday({
|
|
||||||
field: this.$.datePicker.inputElement,
|
|
||||||
onSelect: logbookActions.changeCurrentDate,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
this.datePicker.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,185 +0,0 @@
|
|||||||
<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="../components/state-cards.html">
|
|
||||||
<link rel="import" href="../components/ha-voice-command-progress.html">
|
|
||||||
|
|
||||||
<dom-module id="partial-states">
|
|
||||||
<style>
|
|
||||||
.content-wrapper {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #E5E5E5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-wrapper ::content .listening {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: rgba(0, 0, 0, 0.098) 0px 2px 4px, rgba(0, 0, 0, 0.098) 0px 0px 3px;
|
|
||||||
padding: 16px;
|
|
||||||
background-color: rgba(255, 255, 255, 0.95);
|
|
||||||
line-height: 2em;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interimTranscript {
|
|
||||||
color: darkgrey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listening paper-spinner {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<partial-base narrow="[[narrow]]">
|
|
||||||
<span header-title>[[computeHeaderTitle(filter)]]</span>
|
|
||||||
|
|
||||||
<span header-buttons>
|
|
||||||
<paper-icon-button
|
|
||||||
icon="refresh"
|
|
||||||
class$="[[computeRefreshButtonClass(isFetching)]]"
|
|
||||||
on-click="handleRefresh" hidden$="[[isStreaming]]"
|
|
||||||
></paper-icon-button>
|
|
||||||
<paper-icon-button
|
|
||||||
icon="[[computeListenButtonIcon(isListening)]]"
|
|
||||||
hidden$='[[!canListen]]'
|
|
||||||
on-click="handleListenClick"></paper-icon-button>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class='content-wrapper'>
|
|
||||||
<div class='listening' hidden$="[[!showListenInterface]]"
|
|
||||||
on-click="handleListenClick">
|
|
||||||
<ha-voice-command-progress></ha-voice-command-progress>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<state-cards states="[[states]]">
|
|
||||||
<h3>Hi there!</h3>
|
|
||||||
<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.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Please see the <a href='https://home-assistant.io/getting-started/' target='_blank'>Getting Started</a> section on how to setup your devices.
|
|
||||||
</p>
|
|
||||||
</state-cards>
|
|
||||||
</div>
|
|
||||||
</partial-base>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function(){
|
|
||||||
var configGetters = window.hass.configGetters;
|
|
||||||
var navigationGetters = window.hass.navigationGetters;
|
|
||||||
var voiceGetters = window.hass.voiceGetters;
|
|
||||||
var streamGetters = window.hass.streamGetters;
|
|
||||||
var serviceGetters = window.hass.serviceGetters;
|
|
||||||
var syncGetters = window.hass.syncGetters;
|
|
||||||
|
|
||||||
var syncActions = window.hass.syncActions;
|
|
||||||
var voiceActions = window.hass.voiceActions;
|
|
||||||
|
|
||||||
var entityDomainFilters = window.hass.util.entityDomainFilters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'partial-states',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
filter: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: navigationGetters.activeFilter,
|
|
||||||
},
|
|
||||||
|
|
||||||
isFetching: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: syncGetters.isFetching,
|
|
||||||
},
|
|
||||||
|
|
||||||
isStreaming: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: streamGetters.isStreamingEvents,
|
|
||||||
},
|
|
||||||
|
|
||||||
canListen: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: [
|
|
||||||
voiceGetters.isVoiceSupported,
|
|
||||||
configGetters.isComponentLoaded('conversation'),
|
|
||||||
function(isVoiceSupported, componentLoaded) {
|
|
||||||
return isVoiceSupported && componentLoaded;
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
isListening: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: voiceGetters.isListening,
|
|
||||||
},
|
|
||||||
|
|
||||||
showListenInterface: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: [
|
|
||||||
voiceGetters.isListening,
|
|
||||||
voiceGetters.isTransmitting,
|
|
||||||
function(isListening, isTransmitting) {
|
|
||||||
return isListening || isTransmitting;
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
states: {
|
|
||||||
type: Array,
|
|
||||||
bindNuclear: [
|
|
||||||
navigationGetters.filteredStates,
|
|
||||||
// are here so a change to services causes a re-render.
|
|
||||||
// we need this to decide if we show toggles for states.
|
|
||||||
serviceGetters.entityMap,
|
|
||||||
function(states) { return states.toArray(); },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
handleRefresh: function() {
|
|
||||||
syncActions.fetchAll();
|
|
||||||
},
|
|
||||||
|
|
||||||
handleListenClick: function() {
|
|
||||||
if (this.isListening) {
|
|
||||||
voiceActions.stop();
|
|
||||||
} else {
|
|
||||||
voiceActions.listen();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computeHeaderTitle: function(filter) {
|
|
||||||
return filter ? entityDomainFilters[filter] : 'States';
|
|
||||||
},
|
|
||||||
|
|
||||||
computeListenButtonIcon: function(isListening) {
|
|
||||||
return isListening ? 'av:mic-off' : 'av:mic';
|
|
||||||
},
|
|
||||||
|
|
||||||
computeRefreshButtonClass: function(isFetching) {
|
|
||||||
if (isFetching) {
|
|
||||||
return 'ha-spin';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,40 +0,0 @@
|
|||||||
<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() {
|
|
||||||
var notificationGetters = window.hass.notificationGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'notification-manager',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
text: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: notificationGetters.lastNotificationMessage,
|
|
||||||
observer: 'showNotification',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
showNotification: function(newText) {
|
|
||||||
if (newText) {
|
|
||||||
this.$.toast.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,40 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var authGetters = window.hass.authGetters;
|
|
||||||
var streamGetters = window.hass.streamGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'preferences-manager',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
authToken: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: authGetters.currentAuthToken,
|
|
||||||
observer: 'updateStorage',
|
|
||||||
},
|
|
||||||
useStreaming: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: ,
|
|
||||||
observer: 'updateStorage',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
updateStorage: function() {
|
|
||||||
if (!('localStorage' in window)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var storage = localStorage;
|
|
||||||
|
|
||||||
Object.keys(this.properties).forEach(function(prop) {
|
|
||||||
storage[prop] = this.prop;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,52 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<dom-module id='more-info-camera'>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
max-width:640px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-image {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<img class='camera-image' src="[[computeCameraImageUrl(dialogOpen)]]"
|
|
||||||
on-load='imageLoaded' />
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var demo = hass.demo;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-camera',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
dialogOpen: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
imageLoaded: function() {
|
|
||||||
this.fire('iron-resize');
|
|
||||||
},
|
|
||||||
|
|
||||||
computeCameraImageUrl: function(dialogOpen) {
|
|
||||||
if (demo) {
|
|
||||||
return 'http://194.218.96.92/jpg/image.jpg';
|
|
||||||
} else if (dialogOpen) {
|
|
||||||
return '/api/camera_proxy_stream/' + this.stateObj.entityId;
|
|
||||||
} else {
|
|
||||||
// Return an empty image if dialog is not open
|
|
||||||
return 'data:image/gif;base64,R0lGODlhAQABAAAAACw=';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,125 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
<link rel='import' href='../bower_components/paper-button/paper-button.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='../components/loading-box.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-configurator'>
|
|
||||||
<style>
|
|
||||||
p {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p > img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.error {
|
|
||||||
color: #C62828;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.submit {
|
|
||||||
text-align: center;
|
|
||||||
height: 41px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class='layout vertical'>
|
|
||||||
<template is='dom-if' if='[[isConfigurable]]'>
|
|
||||||
|
|
||||||
<p hidden$='[[!stateObj.attributes.description]]'>[[stateObj.attributes.description]]</p>
|
|
||||||
|
|
||||||
<p class='error' hidden$='[[!stateObj.attributes.errors]]'>[[stateObj.attributes.errors]]</p>
|
|
||||||
|
|
||||||
<p class='center' hidden$='[[!stateObj.attributes.description_image]]'>
|
|
||||||
<img src='[[stateObj.attributes.description_image]]' />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='submit'>
|
|
||||||
<paper-button raised on-click='submitClicked'
|
|
||||||
hidden$='[[isConfiguring]]'>[[submitCaption]]</paper-button>
|
|
||||||
|
|
||||||
<loading-box hidden$='[[!isConfiguring]]'>Configuring</loading-box>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var streamGetters = window.hass.streamGetters;
|
|
||||||
var syncActions = window.hass.syncActions;
|
|
||||||
var serviceActions = window.hass.serviceActions;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-configurator',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
action: {
|
|
||||||
type: String,
|
|
||||||
value: 'display',
|
|
||||||
},
|
|
||||||
|
|
||||||
isStreaming: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: streamGetters.isStreamingEvents,
|
|
||||||
},
|
|
||||||
|
|
||||||
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';
|
|
||||||
},
|
|
||||||
|
|
||||||
submitClicked: function() {
|
|
||||||
this.isConfiguring = true;
|
|
||||||
|
|
||||||
var data = {
|
|
||||||
configure_id: this.stateObj.attributes.configure_id
|
|
||||||
};
|
|
||||||
|
|
||||||
serviceActions.callService('configurator', 'configure', data).then(
|
|
||||||
function() {
|
|
||||||
this.isConfiguring = false;
|
|
||||||
|
|
||||||
if (!this.isStreaming) {
|
|
||||||
syncActions.fetchAll();
|
|
||||||
}
|
|
||||||
}.bind(this),
|
|
||||||
|
|
||||||
function() {
|
|
||||||
this.isConfiguring = false;
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,81 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='more-info-default.html'>
|
|
||||||
<link rel='import' href='more-info-group.html'>
|
|
||||||
<link rel='import' href='more-info-sun.html'>
|
|
||||||
<link rel='import' href='more-info-configurator.html'>
|
|
||||||
<link rel='import' href='more-info-thermostat.html'>
|
|
||||||
<link rel='import' href='more-info-script.html'>
|
|
||||||
<link rel='import' href='more-info-light.html'>
|
|
||||||
<link rel='import' href='more-info-media_player.html'>
|
|
||||||
<link rel='import' href='more-info-camera.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-content'>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-content',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
dialogOpen: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
observer: 'dialogOpenChanged',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
dialogOpenChanged: function(newVal, oldVal) {
|
|
||||||
var root = Polymer.dom(this);
|
|
||||||
|
|
||||||
if (root.lastChild) {
|
|
||||||
root.lastChild.dialogOpen = newVal;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function(newVal, oldVal) {
|
|
||||||
var root = Polymer.dom(this);
|
|
||||||
|
|
||||||
if (!newVal) {
|
|
||||||
if (root.lastChild) {
|
|
||||||
root.removeChild(root.lastChild);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newMoreInfoType = uiUtil.stateMoreInfoType(newVal);
|
|
||||||
|
|
||||||
if (!oldVal || uiUtil.stateMoreInfoType(oldVal) != newMoreInfoType) {
|
|
||||||
|
|
||||||
if (root.lastChild) {
|
|
||||||
root.removeChild(root.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
var moreInfo = document.createElement('more-info-' + newMoreInfoType);
|
|
||||||
moreInfo.stateObj = newVal;
|
|
||||||
moreInfo.dialogOpen = this.dialogOpen;
|
|
||||||
root.appendChild(moreInfo);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
root.lastChild.dialogOpen = this.dialogOpen;
|
|
||||||
root.lastChild.stateObj = newVal;
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,49 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<dom-module id="more-info-default">
|
|
||||||
<style>
|
|
||||||
.data-entry .value {
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class='layout vertical'>
|
|
||||||
<template is='dom-repeat' items="[[computeDisplayAttributes(stateObj)]]" as="attribute">
|
|
||||||
<div class='data-entry layout justified horizontal'>
|
|
||||||
<div class='key'>[[attribute]]</div>
|
|
||||||
<div class='value'>[[getAttributeValue(stateObj, attribute)]]</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var FILTER_KEYS = ['entity_picture', 'friendly_name', 'unit_of_measurement'];
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-default',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computeDisplayAttributes: function(stateObj) {
|
|
||||||
if (!stateObj) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(stateObj.attributes).filter(function(key) {
|
|
||||||
return FILTER_KEYS.indexOf(key) === -1;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getAttributeValue: function(stateObj, attribute) {
|
|
||||||
return stateObj.attributes[attribute];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,63 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../cards/state-card-content.html">
|
|
||||||
|
|
||||||
<dom-module id="more-info-group">
|
|
||||||
<style>
|
|
||||||
.child-card {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.child-card:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<template is='dom-repeat' items="[[states]]" as='state'>
|
|
||||||
<div class='child-card'>
|
|
||||||
<state-card-content state-obj="[[state]]"></state-card-content>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var entityGetters = window.hass.entityGetters;
|
|
||||||
var moreInfoGetters = window.hass.moreInfoGetters;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-group',
|
|
||||||
|
|
||||||
behaviors: [nuclearObserver],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
states: {
|
|
||||||
type: Array,
|
|
||||||
bindNuclear: [
|
|
||||||
moreInfoGetters.currentEntity,
|
|
||||||
entityGetters.entityMap,
|
|
||||||
function(currentEntity, entities) {
|
|
||||||
// weird bug??
|
|
||||||
if (!currentEntity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return currentEntity.attributes.entity_id.map(
|
|
||||||
entities.get.bind(entities));
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
updateStates: function() {
|
|
||||||
this.states = this.stateObj && this.stateObj.attributes.entity_id ?
|
|
||||||
stateStore.gets(this.stateObj.attributes.entity_id).toArray() : [];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,111 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
<link rel='import' href='../bower_components/paper-slider/paper-slider.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='../components/ha-color-picker.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-light'>
|
|
||||||
<style>
|
|
||||||
.brightness {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
max-height: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height .5s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-color-picker {
|
|
||||||
display: block;
|
|
||||||
width: 350px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
max-height: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height .5s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-brightness .brightness {
|
|
||||||
max-height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-xy_color ha-color-picker {
|
|
||||||
max-height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class$='[[computeClassNames(stateObj)]]'>
|
|
||||||
<div class='brightness center horizontal layout'>
|
|
||||||
<div>Brightness</div>
|
|
||||||
<paper-slider
|
|
||||||
max='255' id='brightness' value='{{brightnessSliderValue}}'
|
|
||||||
on-change='brightnessSliderChanged' class='flex'>
|
|
||||||
</paper-slider>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ha-color-picker on-colorselected='colorPicked' width='350' height='200'>
|
|
||||||
</color-picker>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var serviceActions = window.hass.serviceActions;
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
var ATTRIBUTE_CLASSES = ['brightness', 'xy_color'];
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-light',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
brightnessSliderValue: {
|
|
||||||
type: Number,
|
|
||||||
value: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function(newVal, oldVal) {
|
|
||||||
if (newVal && newVal.state === 'on') {
|
|
||||||
this.brightnessSliderValue = newVal.attributes.brightness;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.async(function() {
|
|
||||||
this.fire('iron-resize');
|
|
||||||
}.bind(this), 500);
|
|
||||||
},
|
|
||||||
|
|
||||||
computeClassNames: function(stateObj) {
|
|
||||||
return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
|
||||||
},
|
|
||||||
|
|
||||||
brightnessSliderChanged: function(ev) {
|
|
||||||
var bri = parseInt(ev.target.value);
|
|
||||||
|
|
||||||
if(isNaN(bri)) return;
|
|
||||||
|
|
||||||
if(bri === 0) {
|
|
||||||
serviceActions.callTurnOff(this.stateObj.entityId);
|
|
||||||
} else {
|
|
||||||
serviceActions.callService('light', 'turn_on', {
|
|
||||||
entity_id: this.stateObj.entityId,
|
|
||||||
brightness: bri
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
colorPicked: function(ev) {
|
|
||||||
var color = ev.detail.rgb;
|
|
||||||
|
|
||||||
serviceActions.callService('light', 'turn_on', {
|
|
||||||
entity_id: this.stateObj.entityId,
|
|
||||||
rgb_color: [color.r, color.g, color.b]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,210 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
|
|
||||||
<link rel='import' href='../bower_components/paper-icon-button/paper-icon-button.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-media_player'>
|
|
||||||
<style>
|
|
||||||
.media-state {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-icon-button[highlight] {
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.volume {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
max-height: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height .5s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-volume_level .volume {
|
|
||||||
max-height: 40px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class$='[[computeClassNames(stateObj)]]'>
|
|
||||||
<div class='layout horizontal'>
|
|
||||||
<div class='flex'>
|
|
||||||
<paper-icon-button icon='power-settings-new' highlight$='[[isOff]]'
|
|
||||||
on-tap='handleTogglePower'
|
|
||||||
hidden$='[[computeHidePowerButton(isOff, supportsTurnOn, supportsTurnOff)]]'></paper-icon-button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<template is='dom-if' if='[[!isOff]]'>
|
|
||||||
<paper-icon-button icon='av:skip-previous' on-tap='handlePrevious'
|
|
||||||
hidden$='[[!supportsPreviousTrack]]'></paper-icon-button>
|
|
||||||
<paper-icon-button icon='[[computePlaybackControlIcon(stateObj)]]'
|
|
||||||
on-tap='handlePlaybackControl' highlight></paper-icon-button>
|
|
||||||
<paper-icon-button icon='av:skip-next' on-tap='handleNext'
|
|
||||||
hidden$='[[!supportsNextTrack]]'></paper-icon-button>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class='volume center horizontal layout' hidden$='[[!supportsVolumeSet]]'>
|
|
||||||
<paper-icon-button on-tap="handleVolumeTap"
|
|
||||||
icon="[[computeMuteVolumeIcon(isMuted)]]"></paper-icon-button>
|
|
||||||
<paper-slider disabled$='[[isMuted]]'
|
|
||||||
min='0' max='100' value='[[volumeSliderValue]]'
|
|
||||||
on-change='volumeSliderChanged' class='flex'>
|
|
||||||
</paper-slider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var serviceActions = window.hass.serviceActions;
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
var ATTRIBUTE_CLASSES = ['volume_level'];
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-media_player',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
isOff: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
isPlaying: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
isMuted: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false
|
|
||||||
},
|
|
||||||
|
|
||||||
volumeSliderValue: {
|
|
||||||
type: Number,
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsPause: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsVolumeSet: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsVolumeMute: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsPreviousTrack: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsNextTrack: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsTurnOn: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsTurnOff: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function(newVal) {
|
|
||||||
if (newVal) {
|
|
||||||
this.isOff = newVal.state == 'off';
|
|
||||||
this.isPlaying = newVal.state == 'playing';
|
|
||||||
this.volumeSliderValue = newVal.attributes.volume_level * 100;
|
|
||||||
this.isMuted = newVal.attributes.is_volume_muted;
|
|
||||||
this.supportsPause = (newVal.attributes.supported_media_commands & 1) !== 0;
|
|
||||||
this.supportsVolumeSet = (newVal.attributes.supported_media_commands & 4) !== 0;
|
|
||||||
this.supportsVolumeMute = (newVal.attributes.supported_media_commands & 8) !== 0;
|
|
||||||
this.supportsPreviousTrack = (newVal.attributes.supported_media_commands & 16) !== 0;
|
|
||||||
this.supportsNextTrack = (newVal.attributes.supported_media_commands & 32) !== 0;
|
|
||||||
this.supportsTurnOn = (newVal.attributes.supported_media_commands & 128) !== 0;
|
|
||||||
this.supportsTurnOff = (newVal.attributes.supported_media_commands & 256) !== 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.async(function() { this.fire('iron-resize'); }.bind(this), 500);
|
|
||||||
},
|
|
||||||
|
|
||||||
computeClassNames: function(stateObj) {
|
|
||||||
return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
|
||||||
},
|
|
||||||
|
|
||||||
computeIsOff: function(stateObj) {
|
|
||||||
return stateObj.state == 'off';
|
|
||||||
},
|
|
||||||
|
|
||||||
computeMuteVolumeIcon: function(isMuted) {
|
|
||||||
return isMuted ? 'av:volume-off' : 'av:volume-up';
|
|
||||||
},
|
|
||||||
|
|
||||||
computePlaybackControlIcon: function(stateObj) {
|
|
||||||
if (this.isPlaying) {
|
|
||||||
return this.supportsPause ? 'av:pause' : 'av:stop';
|
|
||||||
}
|
|
||||||
return 'av:play-arrow';
|
|
||||||
},
|
|
||||||
|
|
||||||
computeHidePowerButton: function(isOff, supportsTurnOn, supportsTurnOff) {
|
|
||||||
return isOff ? !supportsTurnOn : !supportsTurnOff;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleTogglePower: function() {
|
|
||||||
this.callService(this.isOff ? 'turn_on' : 'turn_off');
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePrevious: function() {
|
|
||||||
this.callService('media_previous_track');
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePlaybackControl: function() {
|
|
||||||
if (this.isPlaying && !this.supportsPause) {
|
|
||||||
alert('This case is not supported yet');
|
|
||||||
}
|
|
||||||
this.callService('media_play_pause');
|
|
||||||
},
|
|
||||||
|
|
||||||
handleNext: function() {
|
|
||||||
this.callService('media_next_track');
|
|
||||||
},
|
|
||||||
|
|
||||||
handleVolumeTap: function() {
|
|
||||||
if (!this.supportsVolumeMute) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.callService('volume_mute', { is_volume_muted: !this.isMuted });
|
|
||||||
},
|
|
||||||
|
|
||||||
volumeSliderChanged: function(ev) {
|
|
||||||
var volPercentage = parseFloat(ev.target.value);
|
|
||||||
var vol = volPercentage > 0 ? volPercentage / 100 : 0;
|
|
||||||
this.callService('volume_set', { volume_level: vol });
|
|
||||||
},
|
|
||||||
|
|
||||||
callService: function(service, data) {
|
|
||||||
data = data || {};
|
|
||||||
data.entity_id = this.stateObj.entityId;
|
|
||||||
serviceActions.callService('media_player', service, data);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,26 +0,0 @@
|
|||||||
<link rel='import' href='../bower_components/polymer/polymer.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-script'>
|
|
||||||
<template>
|
|
||||||
<div class='layout vertical'>
|
|
||||||
<div class='data-entry layout justified horizontal'>
|
|
||||||
<div class='key'>Last Action</div>
|
|
||||||
<div class='value'>[[stateObj.attributes.last_action]]</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-script',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,71 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../components/relative-ha-datetime.html">
|
|
||||||
|
|
||||||
<dom-module id="more-info-sun">
|
|
||||||
<template>
|
|
||||||
<div class='data-entry layout justified horizontal' id='rising'>
|
|
||||||
<div class='key'>
|
|
||||||
Rising <relative-ha-datetime datetime-obj="[[risingDate]]"></relative-ha-datetime>
|
|
||||||
</div>
|
|
||||||
<div class='value'>[[risingTime]]</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='data-entry layout justified horizontal' id='setting'>
|
|
||||||
<div class='key'>
|
|
||||||
Setting <relative-ha-datetime datetime-obj="[[settingDate]]"></relative-ha-datetime>
|
|
||||||
</div>
|
|
||||||
<div class='value'>[[settingTime]]</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var parseDateTime = window.hass.util.parseDateTime;
|
|
||||||
var formatTime = window.hass.uiUtil.formatTime;
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-sun',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
risingDate: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
settingDate: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
risingTime: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
settingTime: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function() {
|
|
||||||
this.risingDate = parseDateTime(this.stateObj.attributes.next_rising);
|
|
||||||
this.risingTime = formatTime(this.risingDate);
|
|
||||||
|
|
||||||
this.settingDate = parseDateTime(this.stateObj.attributes.next_setting);
|
|
||||||
this.settingTime = formatTime(this.settingDate);
|
|
||||||
|
|
||||||
var root = Polymer.dom(this);
|
|
||||||
|
|
||||||
if(self.risingDate > self.settingDate) {
|
|
||||||
root.appendChild(this.$.rising);
|
|
||||||
} else {
|
|
||||||
root.appendChild(this.$.setting);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,129 +0,0 @@
|
|||||||
<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-toggle-button/paper-toggle-button.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-thermostat'>
|
|
||||||
<style>
|
|
||||||
paper-slider {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.away-mode-toggle {
|
|
||||||
display: none;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-away_mode .away-mode-toggle {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template>
|
|
||||||
<div class$='[[computeClassNames(stateObj)]]'>
|
|
||||||
<div>
|
|
||||||
<div>Target Temperature</div>
|
|
||||||
<paper-slider
|
|
||||||
min='[[tempMin]]' max='[[tempMax]]'
|
|
||||||
value='[[targetTemperatureSliderValue]]' pin
|
|
||||||
on-change='targetTemperatureSliderChanged'>
|
|
||||||
</paper-slider>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='away-mode-toggle'>
|
|
||||||
<div class='center horizontal layout'>
|
|
||||||
<div class='flex'>Away Mode</div>
|
|
||||||
<paper-toggle-button checked='[[awayToggleChecked]]' on-change='toggleChanged'>
|
|
||||||
</paper-toggle-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</dom-module>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var temperatureUnits = window.hass.util.temperatureUnits;
|
|
||||||
var serviceActions = window.hass.serviceActions;
|
|
||||||
var uiUtil = window.hass.uiUtil;
|
|
||||||
var ATTRIBUTE_CLASSES = ['away_mode'];
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'more-info-thermostat',
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: 'stateObjChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
tempMin: {
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
|
|
||||||
tempMax: {
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
|
|
||||||
targetTemperatureSliderValue: {
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
|
|
||||||
awayToggleChecked: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObjChanged: function(newVal, oldVal) {
|
|
||||||
this.targetTemperatureSliderValue = this.stateObj.state;
|
|
||||||
this.awayToggleChecked = this.stateObj.attributes.away_mode == 'on';
|
|
||||||
|
|
||||||
if (this.stateObj.attributes.unit_of_measurement ===
|
|
||||||
temperatureUnits.UNIT_TEMP_F) {
|
|
||||||
this.tempMin = 45;
|
|
||||||
this.tempMax = 95;
|
|
||||||
} else {
|
|
||||||
this.tempMin = 7;
|
|
||||||
this.tempMax = 35;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computeClassNames: function(stateObj) {
|
|
||||||
return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
|
||||||
},
|
|
||||||
|
|
||||||
targetTemperatureSliderChanged: function(ev) {
|
|
||||||
var temp = parseInt(ev.target.value);
|
|
||||||
|
|
||||||
if(isNaN(temp)) return;
|
|
||||||
|
|
||||||
serviceActions.callService('thermostat', 'set_temperature', {
|
|
||||||
entity_id: this.stateObj.entityId,
|
|
||||||
temperature: temp
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleChanged: function(ev) {
|
|
||||||
var newVal = ev.target.checked;
|
|
||||||
|
|
||||||
if(newVal && this.stateObj.attributes.away_mode === 'off') {
|
|
||||||
this.service_set_away(true);
|
|
||||||
} else if(!newVal && this.stateObj.attributes.away_mode === 'on') {
|
|
||||||
this.service_set_away(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
service_set_away: function(away_mode) {
|
|
||||||
// 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
|
|
||||||
// result in the entity to be turned on. Since the state is not changing,
|
|
||||||
// the resync is not called automatic.
|
|
||||||
serviceActions.callService(
|
|
||||||
'thermostat', 'set_away_mode',
|
|
||||||
{entity_id: this.stateObj.entityId, away_mode: away_mode})
|
|
||||||
|
|
||||||
.then(function() {
|
|
||||||
this.stateObjChanged(this.stateObj);
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
@ -1,91 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/iron-iconset-svg/iron-iconset-svg.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../bower_components/iron-icons/iron-icons.html">
|
|
||||||
<link rel="import" href="../bower_components/iron-icons/social-icons.html">
|
|
||||||
<link rel="import" href="../bower_components/iron-icons/image-icons.html">
|
|
||||||
<link rel="import" href="../bower_components/iron-icons/hardware-icons.html">
|
|
||||||
<link rel="import" href="../bower_components/iron-icons/av-icons.html">
|
|
||||||
|
|
||||||
<iron-iconset-svg name="homeassistant-100" size="100">
|
|
||||||
<svg><defs>
|
|
||||||
<!--
|
|
||||||
Thermostat icon created by Scott Lewis from the Noun Project
|
|
||||||
Licensed under CC BY 3.0 - http://creativecommons.org/licenses/by/3.0/us/
|
|
||||||
-->
|
|
||||||
<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>
|
|
||||||
</iron-iconset-svg>
|
|
||||||
|
|
||||||
<iron-iconset-svg name="homeassistant-24" size="24">
|
|
||||||
<svg><defs>
|
|
||||||
<!--
|
|
||||||
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
|
||||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
|
||||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
|
||||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
|
||||||
Code distributed by Google as part of the polymer project is also
|
|
||||||
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>
|
|
||||||
</defs></svg>
|
|
||||||
</iron-iconset-svg>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window.hass.uiUtil.domainIcon = function(domain, state) {
|
|
||||||
switch(domain) {
|
|
||||||
case "homeassistant":
|
|
||||||
return "home";
|
|
||||||
|
|
||||||
case "group":
|
|
||||||
return "homeassistant-24:group";
|
|
||||||
|
|
||||||
case "device_tracker":
|
|
||||||
return "social:person";
|
|
||||||
|
|
||||||
case "switch":
|
|
||||||
return "image:flash-on";
|
|
||||||
|
|
||||||
case "media_player":
|
|
||||||
var icon = "hardware:cast";
|
|
||||||
|
|
||||||
if (state && state !== "off" && state !== 'idle') {
|
|
||||||
icon += "-connected";
|
|
||||||
}
|
|
||||||
|
|
||||||
return icon;
|
|
||||||
|
|
||||||
case "sun":
|
|
||||||
return "image:wb-sunny";
|
|
||||||
|
|
||||||
case "light":
|
|
||||||
return "image:wb-incandescent";
|
|
||||||
|
|
||||||
case "simple_alarm":
|
|
||||||
return "social:notifications";
|
|
||||||
|
|
||||||
case "notify":
|
|
||||||
return "announcement";
|
|
||||||
|
|
||||||
case "thermostat":
|
|
||||||
return "homeassistant-100:thermostat";
|
|
||||||
|
|
||||||
case "sensor":
|
|
||||||
return "visibility";
|
|
||||||
|
|
||||||
case "configurator":
|
|
||||||
return "settings";
|
|
||||||
|
|
||||||
case "conversation":
|
|
||||||
return "av:hearing";
|
|
||||||
|
|
||||||
case "script":
|
|
||||||
return "description";
|
|
||||||
|
|
||||||
case 'scene':
|
|
||||||
return 'social:pages';
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "bookmark";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,54 +0,0 @@
|
|||||||
<script src="../home-assistant-js/dist/homeassistant.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
var DOMAINS_WITH_CARD = ['thermostat', 'configurator', 'scene', 'media_player'];
|
|
||||||
var DOMAINS_WITH_MORE_INFO = [
|
|
||||||
'light', 'group', 'sun', 'configurator', 'thermostat', 'script', 'media_player', 'camera'
|
|
||||||
];
|
|
||||||
|
|
||||||
var reactor = window.hass.reactor;
|
|
||||||
var serviceGetters = window.hass.serviceGetters;
|
|
||||||
var authActions = window.hass.authActions;
|
|
||||||
var preferences = window.hass.localStoragePreferences;
|
|
||||||
|
|
||||||
window.hass.uiActions = {
|
|
||||||
validateAuth: function(authToken, rememberAuth) {
|
|
||||||
authActions.validate(authToken, {
|
|
||||||
rememberAuth: rememberAuth,
|
|
||||||
useStreaming: preferences.useStreaming,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// UI specific util methods
|
|
||||||
window.hass.uiUtil = {
|
|
||||||
stateCardType: function(state) {
|
|
||||||
if(DOMAINS_WITH_CARD.indexOf(state.domain) !== -1) {
|
|
||||||
return state.domain;
|
|
||||||
} else if(reactor.evaluate(serviceGetters.canToggle(state.entityId))) {
|
|
||||||
return "toggle";
|
|
||||||
} else {
|
|
||||||
return "display";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stateMoreInfoType: function(state) {
|
|
||||||
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>
|
|
||||||
|
|
||||||
<link rel="import" href="./moment-js.html">
|
|
@ -1,46 +0,0 @@
|
|||||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
|
||||||
|
|
||||||
<style is="custom-style">
|
|
||||||
: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;
|
|
||||||
|
|
||||||
--paper-toggle-button-checked-ink-color: #039be5;
|
|
||||||
--paper-toggle-button-checked-button-color: #039be5;
|
|
||||||
--paper-toggle-button-checked-bar-color: #039be5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes ha-spin {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(359deg);
|
|
||||||
transform: rotate(359deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes ha-spin {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(359deg);
|
|
||||||
transform: rotate(359deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body /deep/ .ha-spin {
|
|
||||||
-webkit-animation: ha-spin 2s infinite linear;
|
|
||||||
animation: ha-spin 2s infinite linear;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,5 +0,0 @@
|
|||||||
<!--
|
|
||||||
Wrapping JS in an HTML file will prevent it from being loaded twice.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script src="../bower_components/lodash/lodash.min.js"></script>
|
|
@ -1,20 +0,0 @@
|
|||||||
<!--
|
|
||||||
Wrapping JS in an HTML file will prevent it from being loaded twice.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script src="../bower_components/moment/min/moment.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window.hass.uiUtil.formatTime = function(dateObj) {
|
|
||||||
return moment(dateObj).format('LT');
|
|
||||||
};
|
|
||||||
|
|
||||||
window.hass.uiUtil.formatDateTime = function(dateObj) {
|
|
||||||
return moment(dateObj).format('lll');
|
|
||||||
};
|
|
||||||
|
|
||||||
window.hass.uiUtil.formatDate = function(dateObj) {
|
|
||||||
return moment(dateObj).format('ll');
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
@ -1,2 +0,0 @@
|
|||||||
<script src="../bower_components/pikaday/pikaday.js"></script>
|
|
||||||
<link href="../bower_components/pikaday/css/pikaday.css" media="all" rel="stylesheet" />
|
|
@ -1,42 +0,0 @@
|
|||||||
<script>
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
var NuclearObserver = function NuclearObserver(reactor) {
|
|
||||||
return {
|
|
||||||
|
|
||||||
attached: function() {
|
|
||||||
var component = this;
|
|
||||||
this.__unwatchFns = Object.keys(component.properties).reduce(
|
|
||||||
function(unwatchFns, key) {
|
|
||||||
if (!('bindNuclear' in component.properties[key])) {
|
|
||||||
return unwatchFns;
|
|
||||||
}
|
|
||||||
var getter = component.properties[key].bindNuclear;
|
|
||||||
|
|
||||||
if (!getter) {
|
|
||||||
throw 'Undefined getter specified for key ' + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(key, getter);
|
|
||||||
|
|
||||||
component[key] = reactor.evaluate(getter);
|
|
||||||
|
|
||||||
return unwatchFns.concat(reactor.observe(getter, function(val) {
|
|
||||||
// console.log('New value for', key, val);
|
|
||||||
component[key] = val;
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function() {
|
|
||||||
while (this.__unwatchFns.length) {
|
|
||||||
this.__unwatchFns.shift()();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
|
||||||
window.nuclearObserver = NuclearObserver(window.hass.reactor);
|
|
||||||
})();
|
|
||||||
|
|
||||||
</script>
|
|
2
homeassistant/components/frontend/www_static/version.py
Normal file
2
homeassistant/components/frontend/www_static/version.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||||
|
VERSION = ""
|
@ -6,24 +6,15 @@ if [ ${PWD##*/} == "scripts" ]; then
|
|||||||
cd ..
|
cd ..
|
||||||
fi
|
fi
|
||||||
|
|
||||||
scripts/build_js $1
|
cd homeassistant/components/frontend/www_static/home-assistant-polymer
|
||||||
|
npm install
|
||||||
|
npm run frontend_prod
|
||||||
|
|
||||||
# To build the frontend, you need node, bower, vulcanize and html-minifier
|
cp bower_components/webcomponentsjs/webcomponents-lite.min.js ..
|
||||||
# npm install -g bower vulcanize html-minifier
|
cp build/frontend.html ..
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
cd homeassistant/components/frontend/www_static/polymer
|
|
||||||
bower install
|
|
||||||
cd ..
|
|
||||||
cp polymer/bower_components/webcomponentsjs/webcomponents-lite.min.js .
|
|
||||||
|
|
||||||
vulcanize --inline-css --inline-scripts --strip-comments polymer/home-assistant.html > frontend.html
|
|
||||||
|
|
||||||
# html-minifier crashes on frontend, minimize kills the CSS
|
|
||||||
# html-minifier --config-file polymer/html-minifier.conf -o frontend.html frontend.html
|
|
||||||
|
|
||||||
# Generate the MD5 hash of the new frontend
|
# Generate the MD5 hash of the new frontend
|
||||||
cd ..
|
cd ../..
|
||||||
echo '""" DO NOT MODIFY. Auto-generated by build_frontend script """' > version.py
|
echo '""" DO NOT MODIFY. Auto-generated by build_frontend script """' > version.py
|
||||||
if [ $(command -v md5) ]; then
|
if [ $(command -v md5) ]; then
|
||||||
echo 'VERSION = "'`md5 -q www_static/frontend.html`'"' >> version.py
|
echo 'VERSION = "'`md5 -q www_static/frontend.html`'"' >> version.py
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
# Builds the JS for production
|
|
||||||
|
|
||||||
# If current pwd is scripts, go 1 up.
|
|
||||||
if [ ${PWD##*/} == "scripts" ]; then
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd homeassistant/components/frontend/www_static/polymer/home-assistant-js
|
|
||||||
|
|
||||||
npm install
|
|
||||||
|
|
||||||
if [ "$1" = "demo" ]; then
|
|
||||||
echo "Building a demo mode build!"
|
|
||||||
npm run demo
|
|
||||||
else
|
|
||||||
npm run prod
|
|
||||||
fi
|
|
@ -1,11 +0,0 @@
|
|||||||
# Builds the JS for developing, rebuilds when files change
|
|
||||||
|
|
||||||
# If current pwd is scripts, go 1 up.
|
|
||||||
if [ ${PWD##*/} == "scripts" ]; then
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd homeassistant/components/frontend/www_static/polymer/home-assistant-js
|
|
||||||
|
|
||||||
npm install
|
|
||||||
npm run dev
|
|
Loading…
x
Reference in New Issue
Block a user