Reorg into panels (#75)

This commit is contained in:
Paulus Schoutsen 2016-07-16 23:19:49 -07:00 committed by GitHub
parent 5e7f2fdbe8
commit 4029f16e97
45 changed files with 755 additions and 605 deletions

View File

@ -4,6 +4,9 @@
"__DEV__": false,
"Polymer": true
},
"env": {
"browser": true
},
"rules": {
"new-cap": 0,
"prefer-template": 0,

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
build/*
build-temp/*
node_modules/*
bower_components/*
npm-debug.log

@ -1 +1 @@
Subproject commit 8ad1b80dac98aeefba73fb1081f51b9fb1cb6952
Subproject commit 0209bcdd0b7491d539f05887347d66a63e0da42c

View File

@ -8,7 +8,8 @@
},
"scripts": {
"setup_js_dev": "git submodule init && git submodule update && cd home-assistant-js && npm install",
"js_dev": "watch_ru_all",
"clean": "rm -rf build/* build-temp/*",
"js_dev": "npm run watch_ru_all",
"js_dev_demo": "BUILD_DEMO=1 npm run watch_ru_all",
"js_prod": "BUILD_DEV=0 npm run ru_all",
"js_demo": "BUILD_DEV=0 BUILD_DEMO=1 npm run ru_all",
@ -36,9 +37,9 @@
},
"devDependencies": {
"bower": "^1.7.9",
"eslint": "^3.0.1",
"eslint-config-airbnb-base": "^4.0.0",
"eslint-plugin-import": "^1.10.2",
"eslint": "^3.1.0",
"eslint-config-airbnb-base": "^4.0.2",
"eslint-plugin-import": "^1.10.3",
"html-minifier": "^3.0.1",
"rollup": "^0.34.1",
"rollup-plugin-babel": "^2.6.1",

View File

@ -1,4 +1,4 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<!-- <link rel="import" href="../../bower_components/polymer/polymer.html"> -->
<dom-module id="events-list">
<style>

View File

@ -1,3 +1,4 @@
<!--
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
@ -5,10 +6,10 @@
<link rel="import" href="../../bower_components/paper-input/paper-textarea.html">
<link rel="import" href="./partial-base.html">
-->
<link rel="import" href="./events-list.html">
<link rel="import" href="../components/events-list.html">
<dom-module id="partial-dev-fire-event">
<dom-module id="ha-panel-dev-event">
<style is="custom-style" include="iron-flex iron-positioning"></style>
<style>
.content {
@ -59,7 +60,7 @@
<script>
Polymer({
is: 'partial-dev-fire-event',
is: 'ha-panel-dev-event',
properties: {
hass: {

View File

@ -1,10 +1,11 @@
<!--
<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">
<dom-module id="partial-dev-info">
-->
<dom-module id="ha-panel-dev-info">
<style is="custom-style" include="iron-positioning"></style>
<style>
.content {
@ -56,7 +57,7 @@
<div class='content fit'>
<div class='about'>
<p class='version'>
<a href='https://home-assistant.io'><img src="/static/favicon-192x192.png" height="192" /></a><br />
<a href='https://home-assistant.io'><img src="/static/icons/favicon-192x192.png" height="192" /></a><br />
Home Assistant<br />
[[hassVersion]]
</p>
@ -92,7 +93,7 @@
<script>
Polymer({
is: 'partial-dev-info',
is: 'ha-panel-dev-info',
behaviors: [window.hassBehavior],

View File

@ -1,3 +1,4 @@
<!--
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
@ -5,10 +6,10 @@
<link rel="import" href="../../bower_components/paper-input/paper-textarea.html">
<link rel="import" href="./partial-base.html">
-->
<link rel="import" href="./services-list.html">
<link rel="import" href="../components/services-list.html">
<dom-module id="partial-dev-call-service">
<dom-module id="ha-panel-dev-service">
<style is="custom-style" include="iron-flex iron-positioning"></style>
<style>
.content {
@ -64,7 +65,7 @@
<script>
Polymer({
is: 'partial-dev-call-service',
is: 'ha-panel-dev-service',
properties: {
hass: {

View File

@ -1,4 +1,4 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<!-- <link rel="import" href="../../bower_components/polymer/polymer.html"> -->
<dom-module id="services-list">
<style>

View File

@ -1,4 +1,4 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<!-- <link rel="import" href="../../bower_components/polymer/polymer.html"> -->
<dom-module id="entity-list">
<style>

View File

@ -1,3 +1,4 @@
<!--
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
@ -5,10 +6,10 @@
<link rel="import" href="../../bower_components/paper-input/paper-textarea.html">
<link rel="import" href="./partial-base.html">
-->
<link rel="import" href="./entity-list.html">
<link rel="import" href="../components/entity-list.html">
<dom-module id="partial-dev-set-state">
<dom-module id="ha-panel-dev-state">
<style is="custom-style" include="iron-flex iron-positioning"></style>
<style>
.content {
@ -60,7 +61,7 @@
<script>
Polymer({
is: 'partial-dev-set-state',
is: 'ha-panel-dev-state',
properties: {
hass: {

View File

@ -1,13 +1,13 @@
<!--
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-input/paper-textarea.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="./partial-base.html">
-->
<link rel="import" href="../components/services-list.html">
<dom-module id="partial-dev-template">
<dom-module id="ha-panel-dev-template">
<style is="custom-style" include="iron-flex iron-positioning"></style>
<style>
.content {
@ -80,7 +80,7 @@
<script>
Polymer({
is: 'partial-dev-template',
is: 'ha-panel-dev-template',
behaviors: [window.hassBehavior],

View File

@ -0,0 +1,131 @@
<!--
<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="../../src/resources/pikaday-js.html">
<dom-module id="ha-panel-history">
<style is="custom-style" include="iron-flex"></style>
<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]]" show-menu='[[showMenu]]'>
<span header-title>History</span>
<paper-icon-button icon="mdi:refresh" header-buttons
on-tap="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>
Polymer({
is: 'ha-panel-history',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
narrow: {
type: Boolean,
},
showMenu: {
type: Boolean,
value: false,
},
isDataLoaded: {
type: Boolean,
bindNuclear: function (hass) {
return hass.entityHistoryGetters.hasDataForCurrentDate;
},
observer: 'isDataLoadedChanged',
},
stateHistory: {
type: Object,
bindNuclear: function (hass) {
return hass.entityHistoryGetters.entityHistoryForCurrentDate;
},
},
isLoadingData: {
type: Boolean,
bindNuclear: function (hass) {
return hass.entityHistoryGetters.isLoadingEntityHistory;
},
},
selectedDate: {
type: String,
value: null,
bindNuclear: function (hass) {
return hass.entityHistoryGetters.currentDate;
},
},
},
isDataLoadedChanged: function (newVal) {
if (!newVal) {
this.async(function () {
this.hass.entityHistoryActions.fetchSelectedDate();
}.bind(this), 1);
}
},
handleRefreshClick: function () {
this.hass.entityHistoryActions.fetchSelectedDate();
},
datepickerFocus: function () {
this.datePicker.adjustPosition();
},
attached: function () {
this.datePicker = new window.Pikaday({
field: this.$.datePicker.inputElement,
onSelect: this.hass.entityHistoryActions.changeCurrentDate,
});
},
detached: function () {
this.datePicker.destroy();
},
computeContentClasses: function (narrow) {
return narrow ? 'flex content narrow' : 'flex content wide';
},
});
</script>

View File

@ -0,0 +1,38 @@
<dom-module id='ha-panel-iframe'>
<style>
iframe {
border: 0;
width: 100%;
height: 100%;
}
</style>
<template>
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
<span header-title>[[panel.title]]</span>
<iframe
src='[[panel.config.url]]'
sandbox="allow-forms allow-popups allow-pointer-lock allow-same-origin allow-scripts"
></iframe>
</template>
</dom-module>
<script>
Polymer({
is: 'ha-panel-iframe',
properties: {
panel: {
type: Object,
},
narrow: {
type: Boolean,
},
showMenu: {
type: Boolean,
},
},
});
</script>

View File

@ -1,6 +1,8 @@
<!--
<link rel="import" href="../../bower_components/polymer/polymer.html">
-->
<link rel="import" href="../components/logbook-entry.html">
<link rel="import" href="./logbook-entry.html">
<dom-module id="ha-logbook">
<style>
@ -10,7 +12,7 @@
}
</style>
<template>
<template is='dom-if' if='[[noEntries(entries)]]'>
<template is='dom-if' if='[[!entries.length]]'>
No logbook entries found.
</template>
<template is='dom-repeat' items="[[entries]]">
@ -33,9 +35,5 @@ Polymer({
value: [],
},
},
noEntries: function (entries) {
return !entries.length;
},
});
</script>

View File

@ -0,0 +1,125 @@
<!--
<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/loading-box.html">
-->
<link rel="import" href="./ha-logbook.html">
<link rel="import" href="../../src/resources/pikaday-js.html">
<dom-module id="ha-panel-logbook">
<style>
.selected-date-container {
padding: 0 16px;
}
paper-input {
max-width: 200px;
}
</style>
<template>
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
<span header-title>Logbook</span>
<paper-icon-button icon="mdi:refresh" header-buttons
on-tap="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 hass='[[hass]]' entries="[[entries]]" hidden$='[[isLoading]]'></ha-logbook>
</div>
</partial-base>
</template>
</dom-module>
<script>
Polymer({
is: 'ha-panel-logbook',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
narrow: {
type: Boolean,
value: false,
},
showMenu: {
type: Boolean,
value: false,
},
selectedDate: {
type: String,
bindNuclear: function (hass) { return hass.logbookGetters.currentDate; },
},
isLoading: {
type: Boolean,
bindNuclear: function (hass) { return hass.logbookGetters.isLoadingEntries; },
},
isStale: {
type: Boolean,
bindNuclear: function (hass) { return hass.logbookGetters.isCurrentStale; },
observer: 'isStaleChanged',
},
entries: {
type: Array,
bindNuclear: function (hass) {
return [
hass.logbookGetters.currentEntries,
function (entries) { return entries.reverse().toArray(); },
];
},
},
datePicker: {
type: Object,
},
},
isStaleChanged: function (newVal) {
if (newVal) {
this.async(function () {
this.hass.logbookActions.fetchDate(this.selectedDate);
}.bind(this), 1);
}
},
handleRefresh: function () {
this.hass.logbookActions.fetchDate(this.selectedDate);
},
datepickerFocus: function () {
this.datePicker.adjustPosition();
},
attached: function () {
this.datePicker = new window.Pikaday({
field: this.$.datePicker.inputElement,
onSelect: this.hass.logbookActions.changeCurrentDate,
});
},
detached: function () {
this.datePicker.destroy();
},
});
</script>

View File

@ -1,8 +1,10 @@
<!--
<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 is="custom-style" include="iron-flex"></style>
@ -54,3 +56,20 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'logbook-entry',
properties: {
hass: {
type: Object,
},
},
entityClicked: function (ev) {
ev.preventDefault();
this.hass.moreInfoActions.selectEntity(this.entryObj.entityId);
},
});
</script>

View File

@ -1,6 +1,8 @@
<!--
<link rel='import' href='../../../bower_components/polymer/polymer.html'>
<link rel='import' href='../../../bower_components/iron-image/iron-image.html'>
<link rel='import' href='../../../bower_components/iron-icon/iron-icon.html'>
-->
<dom-module id='ha-entity-marker'>
<style is="custom-style" include="iron-positioning"></style>

View File

@ -1,14 +1,15 @@
<!--
<link rel="import" href="../../bower_components/polymer/polymer.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">
<link rel='import' href='../../bower_components/iron-icon/iron-icon.html'>
-->
<link rel="import" href="../../bower_components/leaflet-map/leaflet-map.html">
<!-- temp work around -->
<link rel="stylesheet" href="../../bower_components/leaflet/dist/leaflet.css" />
<link rel="import" href="../components/entity/ha-entity-marker.html">
<link rel="import" href="./ha-entity-marker.html">
<style>
/* Otherwise they go through overlays. */
@ -21,7 +22,7 @@
}
</style>
<dom-module id="partial-map">
<dom-module id="ha-panel-map">
<style is="custom-style" include="iron-flex iron-positioning"></style>
<style>
.map {
@ -87,7 +88,7 @@
window.L.Icon.Default.imagePath = '/static/images/leaflet';
Polymer({
is: 'partial-map',
is: 'ha-panel-map',
behaviors: [window.hassBehavior],

View File

@ -3,6 +3,6 @@ import config from './base-config';
export default Object.assign({}, config, {
entry: 'src/entry-points/app-core.js',
targets: [
{ dest: 'build/_core_compiled.js', format: 'iife' },
{ dest: 'build/core.js', format: 'iife' },
],
});

View File

@ -3,6 +3,6 @@ import config from './base-config';
export default Object.assign({}, config, {
entry: 'home-assistant-js/demo_data/expose_window.js',
targets: [
{ dest: 'build/_demo_data_compiled.js', format: 'iife' },
{ dest: 'build-temp/demo_data.js', format: 'iife' },
],
});

View File

@ -3,6 +3,6 @@ import config from './base-config';
export default Object.assign({}, config, {
entry: 'src/entry-points/home-assistant-main.js',
targets: [
{ dest: 'build/_ui_compiled.js', format: 'iife' },
{ dest: 'build-temp/ui.js', format: 'iife' },
],
});

View File

@ -11,53 +11,55 @@ var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var swPrecache = require('sw-precache');
var uglifyJS = require('uglify-js');
var rootDir = '..';
var panelDir = rootDir + '/panels';
// var panels = fs.readdirSync(panelDir);
var dynamicUrlToDependencies = {
'/': [rootDir + '/frontend.html', rootDir + '/core.js'],
};
var staticFingerprinted = [
'frontend.html',
'mdi.html',
'core.js',
];
// The panels that will always be loaded
var panelsFingerprinted = [
'map', 'dev-event', 'dev-info', 'dev-service', 'dev-state', 'dev-template',
];
function md5(filename) {
return crypto.createHash('md5')
.update(fs.readFileSync(filename)).digest('hex');
}
var appPaths = {
'/': [],
'/states': [],
'/logbook': [],
'/history': [],
'/map': ['/partial-map.html'],
'/devService': ['/dev-tools.html'],
'/devState': ['/dev-tools.html'],
'/devEvent': ['/dev-tools.html'],
'/devInfo': ['/dev-tools.html'],
'/devTemplate': ['/dev-tools.html'],
};
var fingerprinted = ['frontend.html', 'mdi.html', 'core.js', 'partial-map.html',
'dev-tools.html'];
var dynamicUrlToDependencies = {};
// Have all app paths be refreshed based on if their dependencies changed
Object.keys(appPaths).forEach(ap => {
dynamicUrlToDependencies[ap] = [
rootDir + '/frontend.html', rootDir + '/core.js',
].concat(appPaths[ap].map(val => rootDir + val));
});
// Create fingerprinted versions of our dependencies.
fingerprinted.forEach(fn => {
staticFingerprinted.forEach(fn => {
var parts = path.parse(fn);
var hash = md5(rootDir + '/' + parts.name + parts.ext);
var url = '/static/' + parts.name + '-' + hash + parts.ext;
var fpath = rootDir + '/' + parts.name + parts.ext;
dynamicUrlToDependencies[url] = [fpath];
});
swPrecache.write(path.join('build', 'service_worker.js'), {
panelsFingerprinted.forEach(panel => {
var fpath = panelDir + '/ha-panel-' + panel + '.html';
var hash = md5(fpath);
var url = '/frontend/panels/' + panel + '-' + hash + '.html';
dynamicUrlToDependencies[url] = [fpath];
});
var options = {
navigateFallback: '/',
navigateFallbackWhitelist: [/^((?!(static|api)).)*$/],
dynamicUrlToDependencies: dynamicUrlToDependencies,
staticFileGlobs: [
rootDir + '/favicon-192x192.png',
rootDir + '/icons/favicon.ico',
rootDir + '/icons/favicon-192x192.png',
rootDir + '/webcomponents-lite.min.js',
rootDir + '/fonts/roboto/Roboto-Light.ttf',
rootDir + '/fonts/roboto/Roboto-Medium.ttf',
@ -67,4 +69,17 @@ swPrecache.write(path.join('build', 'service_worker.js'), {
],
stripPrefix: '..',
replacePrefix: 'static',
}).catch(err => console.error(err));
verbose: true,
};
var genPromise = swPrecache.generate(options);
if (true) {
genPromise = genPromise.then(
swString => uglifyJS.minify(swString, { fromString: true }).code);
}
genPromise.then(
swString =>
fs.writeFileSync(path.join('build', 'service_worker.js'), swString)
).catch(err => console.error(err));

View File

@ -4,6 +4,13 @@ var Vulcanize = require('vulcanize');
var minify = require('html-minifier');
var fs = require('fs');
if (!fs.existsSync('build')) {
fs.mkdirSync('build');
}
if (!fs.existsSync('build/panels')) {
fs.mkdirSync('build/panels');
}
function minifyHTML(html) {
return minify.minify(html, {
customAttrAssign: [/\$=/],
@ -25,6 +32,16 @@ const baseVulcanOptions = {
stripComments: true,
};
const vulcan = new Vulcanize({
inlineScripts: true,
inlineCss: true,
implicitStrip: true,
stripComments: true,
stripExcludes: [
'bower_components/polymer/polymer.html',
],
});
const toProcess = [
{
source: 'src/home-assistant.html',
@ -35,41 +52,32 @@ const toProcess = [
],
})),
},
{
source: 'src/layouts/partial-map.html',
output: 'partial-map.html',
vulcan: new Vulcanize(Object.assign({}, baseVulcanOptions, {
stripExcludes: [
'bower_components/polymer/polymer.html',
'bower_components/paper-toolbar/paper-toolbar.html',
'bower_components/paper-icon-button/paper-icon-button.html',
'bower_components/iron-icon/iron-icon.html',
'bower_components/iron-image/iron-image.html',
],
})),
},
{
source: 'src/entry-points/dev-tools.html',
output: 'dev-tools.html',
vulcan: new Vulcanize(Object.assign({}, baseVulcanOptions, {
stripExcludes: [
'bower_components/polymer/polymer.html',
'bower_components/paper-button/paper-button.html',
'bower_components/paper-input/paper-input.html',
'bower_components/paper-spinner/paper-spinner.html',
'src/layouts/partial-base.html',
],
})),
},
];
toProcess.forEach(info => {
info.vulcan.process(info.source, (err, inlinedHtml) => {
fs.readdirSync('./panels').forEach(panel => {
toProcess.push({
source: `panels/${panel}/ha-panel-${panel}.html`,
output: `panels/ha-panel-${panel}.html`,
});
});
function process(entry) {
console.log('Processing', entry.source);
const vulc = entry.vulcan || vulcan;
vulc.process(entry.source, (err, inlinedHtml) => {
if (err !== null) {
console.error(info.source, err);
console.error(entry.source, err);
return;
}
fs.writeFileSync('build/' + info.output, minifyHTML(inlinedHtml));
const out = 'build/' + entry.output;
console.log('Writing', out);
fs.writeFileSync(out, minifyHTML(inlinedHtml));
if (toProcess.length) {
process(toProcess.pop());
}
});
});
}
process(toProcess.pop());

View File

@ -1,5 +1,19 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module id="display-time">
<template>[[computeTime(dateObj)]]</template>
</dom-module>
<script>
Polymer({
is: 'display-time',
properties: {
dateObj: {
type: Object,
observer: 'timeChanged',
},
},
timeChanged: function (dateObj) {
var root = Polymer.dom(this);
root.innerHTML = window.moment(dateObj).format('LT');
},
});
</script>

View File

@ -1,17 +0,0 @@
import Polymer from '../polymer';
import formatTime from '../util/format-time';
export default new Polymer({
is: 'display-time',
properties: {
dateObj: {
type: Object,
},
},
computeTime(dateObj) {
return dateObj ? formatTime(dateObj) : '';
},
});

View File

@ -7,3 +7,25 @@
<iron-icon icon="[[computeIcon(domain, state)]]"></iron-icon>
</template>
</dom-module>
<script>
Polymer({
is: 'domain-icon',
properties: {
domain: {
type: String,
value: '',
},
state: {
type: String,
value: '',
},
},
computeIcon: function (domain, state) {
return window.domainIcon(domain, state);
},
});
</script>

View File

@ -1,23 +0,0 @@
import Polymer from '../polymer';
import domainIcon from '../util/domain-icon';
export default new Polymer({
is: 'domain-icon',
properties: {
domain: {
type: String,
value: '',
},
state: {
type: String,
value: '',
},
},
computeIcon(domain, state) {
return domainIcon(domain, state);
},
});

View File

@ -104,28 +104,17 @@
<div class="title">Home Assistant</div>
<paper-icon-button icon='mdi:chevron-left' hidden$='[[narrow]]' on-tap='toggleMenu'></paper-icon-button>
</paper-toolbar>
<paper-menu attr-for-selected='data-panel' selected='[[selected]]' on-iron-select='menuSelect'>
<paper-icon-item on-tap='menuClicked' data-panel='states'>
<iron-icon item-icon icon='mdi:apps'></iron-icon>
<span class='item-text'>States</span>
</paper-icon-item>
<paper-icon-item on-tap='menuClicked' data-panel='map'>
<iron-icon item-icon icon='mdi:account-location'></iron-icon>
<span class='item-text'>Map</span>
</paper-icon-item>
<template is='dom-if' if='[[hasHistoryComponent]]'>
<paper-icon-item on-tap='menuClicked' data-panel='history'>
<iron-icon item-icon icon='mdi:poll-box'></iron-icon>
<span class='item-text'>History</span>
</paper-icon-item>
</template>
<template is='dom-if' if='[[hasLogbookComponent]]'>
<paper-icon-item on-tap='menuClicked' data-panel='logbook'>
<iron-icon item-icon icon='mdi:format-list-bulleted-type'></iron-icon>
<span class='item-text'>Logbook</span>
<template is='dom-repeat' items='[[computePanels(panels)]]'>
<paper-icon-item on-tap='menuClicked' data-panel$='[[item.url_name]]'>
<iron-icon item-icon icon='[[item.icon]]'></iron-icon>
<span class='item-text'>[[item.title]]</span>
</paper-icon-item>
</template>
@ -149,19 +138,19 @@
<div class='dev-tools layout horizontal justified'>
<paper-icon-button
icon='mdi:remote' data-panel='devService'
icon='mdi:remote' data-panel='dev-service'
on-tap='menuClicked'></paper-icon-button>
<paper-icon-button
icon='mdi:code-tags' data-panel='devState'
icon='mdi:code-tags' data-panel='dev-state'
on-tap='menuClicked'></paper-icon-button>
<paper-icon-button
icon='mdi:radio-tower' data-panel='devEvent'
icon='mdi:radio-tower' data-panel='dev-event'
on-tap='menuClicked'></paper-icon-button>
<paper-icon-button
icon='mdi:file-xml' data-panel='devTemplate'
icon='mdi:file-xml' data-panel='dev-template'
on-tap='menuClicked'></paper-icon-button>
<paper-icon-button
icon='mdi:information-outline' data-panel='devInfo'
icon='mdi:information-outline' data-panel='dev-info'
on-tap='menuClicked'></paper-icon-button>
</div>
</div>
@ -195,17 +184,33 @@ Polymer({
selected: {
type: String,
bindNuclear: function (hass) { return hass.navigationGetters.activePane; },
bindNuclear: function (hass) {
return hass.navigationGetters.activePane;
},
},
hasHistoryComponent: {
type: Boolean,
bindNuclear: function (hass) { return hass.configGetters.isComponentLoaded('history'); },
bindNuclear: function (hass) {
return hass.configGetters.isComponentLoaded('history');
},
},
hasLogbookComponent: {
type: Boolean,
bindNuclear: function (hass) { return hass.configGetters.isComponentLoaded('logbook'); },
bindNuclear: function (hass) {
return hass.configGetters.isComponentLoaded('logbook');
},
},
panels: {
type: Array,
bindNuclear: function (hass) {
return [
hass.panelGetters.panels,
function (res) { return res.toJS(); },
];
},
},
},
@ -213,6 +218,44 @@ Polymer({
this._boundUpdateStyles = this.updateStyles.bind(this);
},
computePanels: function (panels) {
var sortValue = {
map: 1,
logbook: 2,
history: 3,
};
var result = [];
Object.keys(panels).forEach(function (key) {
if (panels[key].title) {
result.push(panels[key]);
}
});
result.sort(function (a, b) {
var aBuiltIn = (a.component_name in sortValue);
var bBuiltIn = (b.component_name in sortValue);
if (aBuiltIn && bBuiltIn) {
return sortValue[a.component_name] - sortValue[b.component_name];
} else if (aBuiltIn) {
return -1;
} else if (bBuiltIn) {
return 1;
}
// both not built in, sort by title
if (a.title > b.title) {
return 1;
}
if (a.title < b.title) {
return -1;
}
return 0;
});
return result;
},
menuSelect: function () {
this.debounce('updateStyles', this._boundUpdateStyles, 1);
},
@ -220,15 +263,17 @@ Polymer({
menuClicked: function (ev) {
var target = ev.target;
var checks = 5;
var attr = target.getAttribute('data-panel');
// find panel to select
while (checks && !target.getAttribute('data-panel')) {
while (checks && !attr) {
target = target.parentElement;
attr = target.getAttribute('data-panel');
checks--;
}
if (checks) {
this.selectPanel(target.getAttribute('data-panel'));
this.selectPanel(attr);
}
},

View File

@ -1,19 +0,0 @@
import Polymer from '../polymer';
import './domain-icon';
import './display-time';
export default new Polymer({
is: 'logbook-entry',
properties: {
hass: {
type: Object,
},
},
entityClicked(ev) {
ev.preventDefault();
this.hass.moreInfoActions.selectEntity(this.entryObj.entityId);
},
});

View File

@ -15,12 +15,6 @@
font-size: 14px;
}
paper-dialog-scrollable {
--paper-dialog-scrollable: {
padding: 0;
};
}
state-history-charts {
position: relative;
z-index: 1;
@ -53,16 +47,14 @@
state-obj="[[stateObj]]"
hass='[[hass]]' in-dialog></state-card-content>
</h2>
<div>
<paper-dialog-scrollable id='scrollable'>
<template is='dom-if' if="[[showHistoryComponent]]">
<state-history-charts state-history="[[stateHistory]]"
is-loading-data="[[isLoadingHistoryData]]"></state-history-charts>
</template>
<paper-dialog-scrollable id='scrollable'>
<more-info-content
state-obj="[[stateObj]]" hass='[[hass]]'></more-info-content>
</paper-dialog-scrollable>
</div>
<more-info-content
state-obj="[[stateObj]]" hass='[[hass]]'></more-info-content>
</paper-dialog-scrollable>
</paper-dialog>
</template>
</dom-module>

View File

@ -12,6 +12,10 @@ export default new Polymer({
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
bindNuclear: hass => hass.moreInfoGetters.currentEntity,

View File

@ -1,5 +0,0 @@
<link rel='import' href='../layouts/partial-dev-call-service.html'>
<link rel='import' href='../layouts/partial-dev-fire-event.html'>
<link rel='import' href='../layouts/partial-dev-set-state.html'>
<link rel='import' href='../layouts/partial-dev-template.html'>
<link rel='import' href='../layouts/partial-dev-info.html'>

View File

@ -1,9 +1,10 @@
<link rel='import' href='../../bower_components/polymer/polymer.html'>
<link rel='import' href='../../bower_components/paper-drawer-panel/paper-drawer-panel.html'>
<link rel='import' href='../../bower_components/iron-media-query/iron-media-query.html'>
<link rel='import' href='../../bower_components/iron-pages/iron-pages.html'>
<link rel='import' href='../layouts/partial-cards.html'>
<link rel='import' href='../layouts/partial-logbook.html'>
<link rel='import' href='../layouts/partial-history.html'>
<link rel='import' href='../layouts/partial-panel-resolver.html'>
<link rel='import' href='../managers/notification-manager.html'>
<link rel="import" href="../dialogs/more-info-dialog.html">
<link rel="import" href="../dialogs/ha-voice-command-dialog.html">
@ -24,34 +25,102 @@
disable-edge-swipe='[[isSelectedMap]]'>
<ha-sidebar drawer narrow='[[narrow]]' hass='[[hass]]'></ha-sidebar>
<template is='dom-if' if='[[isSelectedStates]]'>
<partial-cards main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-cards>
</template>
<template is='dom-if' if='[[isSelectedLogbook]]'>
<partial-logbook main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-logbook>
</template>
<template is='dom-if' if='[[isSelectedHistory]]'>
<partial-history main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-history>
</template>
<template is='dom-if' if='[[isSelectedMap]]'>
<partial-map main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-map>
</template>
<template is='dom-if' if='[[isSelectedDevService]]'>
<partial-dev-call-service main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-dev-call-service>
</template>
<template is='dom-if' if='[[isSelectedDevEvent]]'>
<partial-dev-fire-event main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-dev-fire-event>
</template>
<template is='dom-if' if='[[isSelectedDevState]]'>
<partial-dev-set-state main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-dev-set-state>
</template>
<template is='dom-if' if='[[isSelectedDevTemplate]]'>
<partial-dev-template main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-dev-template>
</template>
<template is='dom-if' if='[[isSelectedDevInfo]]'>
<partial-dev-info main narrow='[[narrow]]' hass='[[hass]]' show-menu='[[showSidebar]]'></partial-dev-info>
</template>
<iron-pages
main
attr-for-selected='id'
fallback-selection='panel-resolver'
selected='[[activePane]]'
>
<partial-cards
id='states'
narrow='[[narrow]]'
hass='[[hass]]'
show-menu='[[showSidebar]]'
></partial-cards>
<partial-panel-resolver
id='panel-resolver'
narrow='[[narrow]]'
hass='[[hass]]'
show-menu='[[showSidebar]]'
></partial-panel-resolver>
</iron-pages>
</paper-drawer-panel>
</template>
</dom-module>
<script>
Polymer({
is: 'home-assistant-main',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
narrow: {
type: Boolean,
value: true,
},
activePane: {
type: String,
bindNuclear: function (hass) {
return hass.navigationGetters.activePane;
},
observer: 'activePaneChanged',
},
showSidebar: {
type: Boolean,
value: false,
bindNuclear: function (hass) {
return hass.navigationGetters.showSidebar;
},
},
},
listeners: {
'open-menu': 'openMenu',
'close-menu': 'closeMenu',
},
openMenu: function () {
if (this.narrow) {
this.$.drawer.openDrawer();
} else {
this.hass.navigationActions.showSidebar(true);
}
},
closeMenu: function () {
this.$.drawer.closeDrawer();
if (this.showSidebar) {
this.hass.navigationActions.showSidebar(false);
}
},
activePaneChanged: function () {
if (this.narrow) {
this.$.drawer.closeDrawer();
}
},
attached: function () {
window.removeInitMsg();
this.hass.startUrlSync();
},
computeForceNarrow: function (narrow, showSidebar) {
return narrow || !showSidebar;
},
detached: function () {
this.hass.stopUrlSync();
},
});
</script>

View File

@ -1,126 +1,4 @@
import Polymer from '../polymer';
import '../layouts/partial-cards';
import '../layouts/partial-logbook';
import '../layouts/partial-history';
import '../managers/notification-manager';
import '../dialogs/more-info-dialog';
import '../dialogs/ha-voice-command-dialog';
Polymer({
is: 'home-assistant-main',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
narrow: {
type: Boolean,
value: true,
},
activePane: {
type: String,
bindNuclear: hass => hass.navigationGetters.activePane,
observer: 'activePaneChanged',
},
isSelectedStates: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('states'),
},
isSelectedHistory: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('history'),
},
isSelectedMap: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('map'),
},
isSelectedLogbook: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('logbook'),
},
isSelectedDevEvent: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('devEvent'),
},
isSelectedDevState: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('devState'),
},
isSelectedDevTemplate: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('devTemplate'),
},
isSelectedDevService: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('devService'),
},
isSelectedDevInfo: {
type: Boolean,
bindNuclear: hass => hass.navigationGetters.isActivePane('devInfo'),
},
showSidebar: {
type: Boolean,
value: false,
bindNuclear: hass => hass.navigationGetters.showSidebar,
},
},
listeners: {
'open-menu': 'openMenu',
'close-menu': 'closeMenu',
},
openMenu() {
if (this.narrow) {
this.$.drawer.openDrawer();
} else {
this.hass.navigationActions.showSidebar(true);
}
},
closeMenu() {
this.$.drawer.closeDrawer();
if (this.showSidebar) {
this.hass.navigationActions.showSidebar(false);
}
},
activePaneChanged(newValue) {
if (this.narrow) {
this.$.drawer.closeDrawer();
}
const key = newValue.substr(0, 3) === 'dev' ? 'dev' : newValue;
if (window.deferredLoading[key]) {
this.importHref(window.deferredLoading[key]);
window.deferredLoading[key] = false;
}
},
attached() {
window.removeInitMsg();
this.hass.startUrlSync();
},
computeForceNarrow(narrow, showSidebar) {
return narrow || !showSidebar;
},
detached() {
this.hass.stopUrlSync();
},
});

View File

@ -8,9 +8,10 @@
<link rel="import" href="../bower_components/iron-iconset-svg/iron-iconset-svg.html">
<link rel='import' href='./util/hass-behavior.html'>
<link rel='import' href='./entry-points/login-form.html'>
<link rel='import' href='./layouts/login-form.html'>
<link rel='import' href='./entry-points/home-assistant-main.html'>
<link rel='import' href='./resources/home-assistant-style.html'>
<link rel='import' href='./resources/panel-imports.html'>
<dom-module id='home-assistant'>
<style>
@ -102,5 +103,4 @@ Polymer({
});
</script>
<script src='../build/_ui_compiled.js'></script>
<!-- <script src='../build/ui_rollup.js'></script> -->
<script src='../build-temp/ui.js'></script>

View File

@ -43,7 +43,7 @@
<template>
<div class="layout vertical center center-center fit">
<img src="/static/favicon-192x192.png" height="192" />
<img src="/static/icons/favicon-192x192.png" height="192" />
<a href="#" id="hideKeyboardOnFocus"></a>
<div class='interact'>
<div id='loginform' hidden$="[[showLoading]]">

View File

@ -11,12 +11,17 @@
<style>
:host {
display: block;
background-color: #E5E5E5;
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
paper-scroll-header-panel {
--paper-scroll-header-panel-container: {
background-color: #E5E5E5;
};
}
paper-scroll-header-panel[has-views] paper-toolbar {
height: 104px;
}

View File

@ -1,46 +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 is="custom-style" include="iron-flex"></style>
<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]]" show-menu='[[showMenu]]'>
<span header-title>History</span>
<paper-icon-button icon="mdi:refresh" header-buttons
on-tap="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>

View File

@ -1,75 +0,0 @@
import Polymer from '../polymer';
import '../components/state-history-charts';
export default new Polymer({
is: 'partial-history',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
narrow: {
type: Boolean,
},
showMenu: {
type: Boolean,
value: false,
},
isDataLoaded: {
type: Boolean,
bindNuclear: hass => hass.entityHistoryGetters.hasDataForCurrentDate,
observer: 'isDataLoadedChanged',
},
stateHistory: {
type: Object,
bindNuclear: hass => hass.entityHistoryGetters.entityHistoryForCurrentDate,
},
isLoadingData: {
type: Boolean,
bindNuclear: hass => hass.entityHistoryGetters.isLoadingEntityHistory,
},
selectedDate: {
type: String,
value: null,
bindNuclear: hass => hass.entityHistoryGetters.currentDate,
},
},
isDataLoadedChanged(newVal) {
if (!newVal) {
this.async(() => this.hass.entityHistoryActions.fetchSelectedDate(), 1);
}
},
handleRefreshClick() {
this.hass.entityHistoryActions.fetchSelectedDate();
},
datepickerFocus() {
this.datePicker.adjustPosition();
},
attached() {
this.datePicker = new window.Pikaday({
field: this.$.datePicker.inputElement,
onSelect: this.hass.entityHistoryActions.changeCurrentDate,
});
},
detached() {
this.datePicker.destroy();
},
computeContentClasses(narrow) {
return `flex content ${narrow ? 'narrow' : 'wide'}`;
},
});

View File

@ -1,41 +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]]" show-menu='[[showMenu]]'>
<span header-title>Logbook</span>
<paper-icon-button icon="mdi:refresh" header-buttons
on-tap="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 hass='[[hass]]' entries="[[entries]]" hidden$='[[isLoading]]'></ha-logbook>
</div>
</partial-base>
</template>
</dom-module>

View File

@ -1,78 +0,0 @@
import Polymer from '../polymer';
import '../components/logbook-entry';
export default new Polymer({
is: 'partial-logbook',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
narrow: {
type: Boolean,
value: false,
},
showMenu: {
type: Boolean,
value: false,
},
selectedDate: {
type: String,
bindNuclear: hass => hass.logbookGetters.currentDate,
},
isLoading: {
type: Boolean,
bindNuclear: hass => hass.logbookGetters.isLoadingEntries,
},
isStale: {
type: Boolean,
bindNuclear: hass => hass.logbookGetters.isCurrentStale,
observer: 'isStaleChanged',
},
entries: {
type: Array,
bindNuclear: hass => [
hass.logbookGetters.currentEntries,
(entries) => entries.reverse().toArray(),
],
},
datePicker: {
type: Object,
},
},
isStaleChanged(newVal) {
if (newVal) {
this.async(() => this.hass.logbookActions.fetchDate(this.selectedDate), 1);
}
},
handleRefresh() {
this.hass.logbookActions.fetchDate(this.selectedDate);
},
datepickerFocus() {
this.datePicker.adjustPosition();
},
attached() {
this.datePicker = new window.Pikaday({
field: this.$.datePicker.inputElement,
onSelect: this.hass.logbookActions.changeCurrentDate,
});
},
detached() {
this.datePicker.destroy();
},
});

View File

@ -0,0 +1,68 @@
<script>
Polymer({
is: 'partial-panel-resolver',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
observer: 'updateAttributes',
},
narrow: {
type: Boolean,
value: false,
observer: 'updateAttributes',
},
showMenu: {
type: Boolean,
value: false,
observer: 'updateAttributes',
},
panel: {
type: Object,
bindNuclear: function (hass) {
return hass.panelGetters.activePanel;
},
observer: 'panelChanged',
},
},
panelChanged: function (panel) {
if (!panel) {
// while (rootEl.lastChild) {
// rootEl.removeChild(rootEl.lastChild);
// }
return;
}
const rootEl = Polymer.dom(this);
const tag = 'ha-panel-' + panel.get('component_name');
this.importHref(panel.get('url'));
while (rootEl.lastChild) {
rootEl.removeChild(rootEl.lastChild);
}
const customEl = document.createElement(tag);
customEl.hass = this.hass;
customEl.narrow = this.narrow;
customEl.showMenu = this.showMenu;
customEl.panel = panel.toJS();
rootEl.appendChild(customEl);
},
updateAttributes: function () {
const customEl = Polymer.dom(this).lastChild;
if (!customEl) return;
customEl.hass = this.hass;
customEl.narrow = this.narrow;
customEl.showMenu = this.showMenu;
},
});
</script>

View File

@ -0,0 +1,9 @@
<!--
Components that are used by panels.
Temporary while we figure imports out.
-->
<link rel="import" href="../layouts/partial-base.html">
<link rel="import" href="../components/display-time.html">
<link rel="import" href="../components/domain-icon.html">
<link rel="import" href="../../bower_components/paper-input/paper-textarea.html">

View File

@ -1,6 +1,6 @@
import defaultIcon from './default-icon';
export default function domainIcon(domain, state) {
window.domainIcon = function (domain, state) {
switch (domain) {
case 'alarm_control_panel':
return state && state === 'disarmed' ? 'mdi:bell-outline' : 'mdi:bell';
@ -98,4 +98,6 @@ export default function domainIcon(domain, state) {
}
return defaultIcon;
}
}
};
export default window.domainIcon;