Remove last ES6

This commit is contained in:
Paulus Schoutsen 2016-07-18 21:28:42 -07:00
parent 57f40725d9
commit 43940d067a
38 changed files with 1522 additions and 1491 deletions

View File

@ -16,13 +16,11 @@
"frontend_html": "script/vulcanize.js",
"frontend_prod": "npm run js_prod && npm run frontend_html",
"frontend_demo": "npm run js_demo && npm run frontend_html",
"ru_all": "npm run ru_core | npm run ru_ui | npm run ru_demo",
"ru_all": "npm run ru_core | npm run ru_demo",
"ru_core": "rollup --config rollup/core.js",
"ru_ui": "rollup --config rollup/ui.js",
"ru_demo": "rollup --config rollup/demo.js",
"watch_ru_all": "npm run watch_ru_core | npm run watch_ru_ui | npm run watch_ru_demo",
"watch_ru_all": "npm run watch_ru_core | npm run watch_ru_demo",
"watch_ru_core": "rollup --config rollup/core.js --watch",
"watch_ru_ui": "rollup --config rollup/ui.js --watch",
"watch_ru_demo": "rollup --config rollup/demo.js --watch",
"test": "eslint src panels --ext html"
},

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import HomeAssistant from '../../home-assistant-js/src/index';
import HomeAssistant from '../home-assistant-js/src/index';
const hass = new HomeAssistant();

View File

@ -1 +1,220 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<script>
(function () {
'use strict';
function range(start, end) {
var result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
}
function saveParseFloat(value) {
var parsed = parseFloat(value);
return !isNaN(parsed) && isFinite(parsed) ? parsed : null;
}
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',
},
chartEngine: {
type: Object,
},
},
created: function () {
this.style.display = 'block';
},
attached: function () {
this.isAttached = true;
},
dataChanged: function () {
this.drawChart();
},
drawChart: function () {
var unit = this.unit;
var deviceStates = this.data;
var options;
var startTime;
var endTime;
var dataTables;
var finalDataTable;
if (!this.isAttached) {
return;
}
if (!this.chartEngine) {
this.chartEngine = new window.google.visualization.LineChart(this);
}
if (deviceStates.length === 0) {
return;
}
options = {
legend: { position: 'top' },
interpolateNulls: true,
titlePosition: 'none',
vAxes: {
// Adds units to the left hand side of the graph
0: { title: unit },
},
hAxis: {
format: 'H:mm',
},
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;
}
startTime = new Date(Math.min.apply(null, deviceStates.map(function (states) {
return states[0].lastChangedAsDate;
})));
endTime = new Date(startTime);
endTime.setDate(endTime.getDate() + 1);
if (endTime > new Date()) {
endTime = new Date();
}
dataTables = deviceStates.map(function (states) {
var last = states[states.length - 1];
var domain = last.domain;
var name = last.entityDisplay;
var data = [];
var dataTable = new window.google.visualization.DataTable();
// array containing [time, value1, value2, etc]
var prevValues;
var hasTargetRange;
var processState;
var noInterpolations;
dataTable.addColumn({ type: 'datetime', id: 'Time' });
function pushData(values, noInterpolationValues) {
if (prevValues && noInterpolationValues) {
// if we have to prevent interpolation, we add an old value for each
// value that should not be interpolated at the same time that our new
// line will be published.
data.push([values[0]].concat(prevValues.slice(1).map(
function (val, index) {
return noInterpolationValues[index] ? val : null;
})));
}
data.push(values);
prevValues = values;
}
if (domain === 'thermostat') {
// We differentiate between thermostats that have a target temperature
// range versus ones that have just a target temperature
hasTargetRange = states.reduce(
(cum, cur) => cum || cur.attributes.target_temp_high !== cur.attributes.target_temp_low,
false);
dataTable.addColumn('number', `${name} current temperature`);
if (hasTargetRange) {
dataTable.addColumn('number', `${name} target temperature high`);
dataTable.addColumn('number', `${name} target temperature low`);
noInterpolations = [false, true, true];
processState = function (state) {
var curTemp = saveParseFloat(state.attributes.current_temperature);
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
var targetLow = saveParseFloat(state.attributes.target_temp_low);
pushData([state.lastUpdatedAsDate, curTemp, targetHigh, targetLow], noInterpolations);
};
} else {
dataTable.addColumn('number', `${name} target temperature`);
noInterpolations = [false, true];
processState = function (state) {
var curTemp = saveParseFloat(state.attributes.current_temperature);
var target = saveParseFloat(state.attributes.temperature);
pushData([state.lastUpdatedAsDate, curTemp, target], noInterpolations);
};
}
states.forEach(processState);
} else {
dataTable.addColumn('number', name);
// Only disable interpolation for sensors
noInterpolations = domain !== 'sensor' && [true];
states.forEach(function (state) {
var value = saveParseFloat(state.state);
pushData([state.lastChangedAsDate, value], noInterpolations);
});
}
// Add an entry for final values
pushData([endTime].concat(prevValues.slice(1)), false);
dataTable.addRows(data);
return dataTable;
});
if (dataTables.length === 1) {
finalDataTable = dataTables[0];
} else {
finalDataTable = dataTables.slice(1).reduce(
function (tot, cur) {
return window.google.visualization.data.join(
tot, cur, 'full', [[0, 0]],
range(1, tot.getNumberOfColumns()),
range(1, cur.getNumberOfColumns()));
},
dataTables[0]);
}
this.chartEngine.draw(finalDataTable, options);
},
});
}());
</script>

View File

@ -1,206 +0,0 @@
import Polymer from '../polymer';
function range(start, end) {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
}
function saveParseFloat(value) {
const parsed = parseFloat(value);
return !isNaN(parsed) && isFinite(parsed) ? parsed : null;
}
export default new 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',
},
chartEngine: {
type: Object,
},
},
created() {
this.style.display = 'block';
},
attached() {
this.isAttached = true;
},
dataChanged() {
this.drawChart();
},
drawChart() {
if (!this.isAttached) {
return;
}
if (!this.chartEngine) {
this.chartEngine = new window.google.visualization.LineChart(this);
}
const unit = this.unit;
const deviceStates = this.data;
if (deviceStates.length === 0) {
return;
}
const options = {
legend: { position: 'top' },
interpolateNulls: true,
titlePosition: 'none',
vAxes: {
// Adds units to the left hand side of the graph
0: { title: unit },
},
hAxis: {
format: 'H:mm',
},
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;
}
const startTime = new Date(Math.min.apply(
null, deviceStates.map(states => states[0].lastChangedAsDate)));
let endTime = new Date(startTime);
endTime.setDate(endTime.getDate() + 1);
if (endTime > new Date()) {
endTime = new Date();
}
const dataTables = deviceStates.map(states => {
const last = states[states.length - 1];
const domain = last.domain;
const name = last.entityDisplay;
const dataTable = new window.google.visualization.DataTable();
dataTable.addColumn({ type: 'datetime', id: 'Time' });
const data = [];
// array containing [time, value1, value2, etc]
let prevValues;
function pushData(values, noInterpolations) {
if (prevValues && noInterpolations) {
// if we have to prevent interpolation, we add an old value for each
// value that should not be interpolated at the same time that our new
// line will be published.
data.push([values[0]].concat(prevValues.slice(1).map(
(val, index) => (noInterpolations[index] ? val : null))));
}
data.push(values);
prevValues = values;
}
if (domain === 'thermostat') {
// We differentiate between thermostats that have a target temperature
// range versus ones that have just a target temperature
const hasTargetRange = states.reduce(
(cum, cur) => cum || cur.attributes.target_temp_high !== cur.attributes.target_temp_low,
false);
dataTable.addColumn('number', `${name} current temperature`);
let processState;
if (hasTargetRange) {
dataTable.addColumn('number', `${name} target temperature high`);
dataTable.addColumn('number', `${name} target temperature low`);
const noInterpolations = [false, true, true];
processState = state => {
const curTemp = saveParseFloat(state.attributes.current_temperature);
const targetHigh = saveParseFloat(state.attributes.target_temp_high);
const targetLow = saveParseFloat(state.attributes.target_temp_low);
pushData([state.lastUpdatedAsDate, curTemp, targetHigh, targetLow], noInterpolations);
};
} else {
dataTable.addColumn('number', `${name} target temperature`);
const noInterpolations = [false, true];
processState = state => {
const curTemp = saveParseFloat(state.attributes.current_temperature);
const target = saveParseFloat(state.attributes.temperature);
pushData([state.lastUpdatedAsDate, curTemp, target], noInterpolations);
};
}
states.forEach(processState);
} else {
dataTable.addColumn('number', name);
// Only disable interpolation for sensors
const noInterpolation = domain !== 'sensor' && [true];
states.forEach(state => {
const value = saveParseFloat(state.state);
pushData([state.lastChangedAsDate, value], noInterpolation);
});
}
// Add an entry for final values
pushData([endTime].concat(prevValues.slice(1)), false);
dataTable.addRows(data);
return dataTable;
});
let finalDataTable;
if (dataTables.length === 1) {
finalDataTable = dataTables[0];
} else {
finalDataTable = dataTables.slice(1).reduce(
(tot, cur) => window.google.visualization.data.join(
tot, cur, 'full', [[0, 0]],
range(1, tot.getNumberOfColumns()),
range(1, cur.getNumberOfColumns())),
dataTables[0]);
}
this.chartEngine.draw(finalDataTable, options);
},
});

View File

@ -1,10 +1,122 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module is='state-history-chart-timeline'>
<style>
:host {
display: block;
<script>
Polymer({
is: 'state-history-chart-timeline',
properties: {
data: {
type: Object,
observer: 'dataChanged',
},
isAttached: {
type: Boolean,
value: false,
observer: 'dataChanged',
},
},
created: function () {
this.style.display = 'block';
},
attached: function () {
this.isAttached = true;
},
dataChanged: function () {
this.drawChart();
},
drawChart: function () {
var root = Polymer.dom(this);
var stateHistory = this.data;
var chart;
var dataTable;
var startTime;
var endTime;
var numTimelines;
if (!this.isAttached) {
return;
}
</style>
<template></template>
</dom-module>
while (root.node.lastChild) {
root.node.removeChild(root.node.lastChild);
}
if (!stateHistory || stateHistory.length === 0) {
return;
}
chart = new window.google.visualization.Timeline(this);
dataTable = new window.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' });
function addRow(entityDisplay, stateStr, start, end) {
var stateDisplay = stateStr.replace(/_/g, ' ');
dataTable.addRow([entityDisplay, stateDisplay, start, end]);
}
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)
endTime = new Date(startTime);
endTime.setDate(endTime.getDate() + 1);
if (endTime > new Date()) {
endTime = new Date();
}
numTimelines = 0;
// stateHistory is a list of lists of sorted state objects
stateHistory.forEach(function (stateInfo) {
var entityDisplay;
var newLastChanged;
var prevState = null;
var prevLastChanged = null;
if (stateInfo.length === 0) return;
entityDisplay = stateInfo[0].entityDisplay;
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++;
});
chart.draw(dataTable, {
height: 55 + (numTimelines * 42),
timeline: {
showRowLabels: stateHistory.length > 1,
},
hAxis: {
format: 'H:mm',
},
});
},
});
</script>

View File

@ -1,109 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'state-history-chart-timeline',
properties: {
data: {
type: Object,
observer: 'dataChanged',
},
isAttached: {
type: Boolean,
value: false,
observer: 'dataChanged',
},
},
attached() {
this.isAttached = true;
},
dataChanged() {
this.drawChart();
},
drawChart() {
if (!this.isAttached) {
return;
}
const root = Polymer.dom(this);
const stateHistory = this.data;
while (root.node.lastChild) {
root.node.removeChild(root.node.lastChild);
}
if (!stateHistory || stateHistory.length === 0) {
return;
}
const chart = new window.google.visualization.Timeline(this);
const dataTable = new window.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' });
function addRow(entityDisplay, stateStr, start, end) {
const stateDisplay = stateStr.replace(/_/g, ' ');
dataTable.addRow([entityDisplay, stateDisplay, start, end]);
}
const startTime = new Date(
stateHistory.reduce((minTime, stateInfo) => Math.min(
minTime, stateInfo[0].lastChangedAsDate), new Date())
);
// end time is Math.min(curTime, start time + 1 day)
let endTime = new Date(startTime);
endTime.setDate(endTime.getDate() + 1);
if (endTime > new Date()) {
endTime = new Date();
}
let numTimelines = 0;
// stateHistory is a list of lists of sorted state objects
stateHistory.forEach((stateInfo) => {
if (stateInfo.length === 0) return;
const entityDisplay = stateInfo[0].entityDisplay;
/* eslint-disable prefer-const */
let newLastChanged;
/* eslint-enable prefer-const */
let prevState = null;
let prevLastChanged = null;
stateInfo.forEach((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++;
});
chart.draw(dataTable, {
height: 55 + (numTimelines * 42),
timeline: {
showRowLabels: stateHistory.length > 1,
},
hAxis: {
format: 'H:mm',
},
});
},
});

View File

@ -25,7 +25,7 @@
<template>
<google-legacy-loader on-api-load="googleApiLoaded"></google-legacy-loader>
<div hidden$="{{!isLoading}}" class='loading-container'>
<div hidden$="[[!isLoading]]" class='loading-container'>
<loading-box>Updating history data</loading-box>
</div>
@ -40,10 +40,116 @@
</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
unit='[[item.unit]]'
data='[[item.data]]'
is-single-device='[[isSingleDevice]]'>
</state-history-chart-line>
</template>
</div>
</template>
</dom-module>
<script>
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) {
var lineChartDevices = {};
var timelineDevices = [];
var unitStates;
if (isLoading || !stateHistory) {
return { line: [], timeline: [] };
}
stateHistory.forEach(function (stateInfo) {
var stateWithUnit;
var unit;
if (!stateInfo || stateInfo.size === 0) {
return;
}
stateWithUnit = stateInfo.find(
(state) => 'unit_of_measurement' in state.attributes);
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;
unitStates = Object.keys(lineChartDevices).map(
function (unit) {
return { unit: unit, data: lineChartDevices[unit] };
});
return { line: unitStates, timeline: timelineDevices };
},
googleApiLoaded: function () {
window.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;
},
});
</script>

View File

@ -1,106 +0,0 @@
import Polymer from '../polymer';
import './state-history-chart-timeline';
import './state-history-chart-line';
export default new 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(stateHistory) {
return stateHistory && stateHistory.size === 1;
},
computeGroupedStateHistory(isLoading, stateHistory) {
if (isLoading || !stateHistory) {
return { line: [], timeline: [] };
}
const lineChartDevices = {};
let timelineDevices = [];
stateHistory.forEach((stateInfo) => {
if (!stateInfo || stateInfo.size === 0) {
return;
}
const stateWithUnit = stateInfo.find(
(state) => 'unit_of_measurement' in state.attributes);
const 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;
const unitStates = Object.keys(lineChartDevices).map(
(unit) => [unit, lineChartDevices[unit]]);
return { line: unitStates, timeline: timelineDevices };
},
googleApiLoaded() {
window.google.load('visualization', '1', {
packages: ['timeline', 'corechart'],
callback: () => { this.apiLoaded = true; },
});
},
computeContentClasses(isLoading) {
return isLoading ? 'loading' : '';
},
computeIsLoading(isLoadingData, apiLoaded) {
return isLoadingData || !apiLoaded;
},
computeIsEmpty(stateHistory) {
return stateHistory && stateHistory.size === 0;
},
extractUnit(arr) {
return arr[0];
},
extractData(arr) {
return arr[1];
},
});

View File

@ -1,3 +0,0 @@
// components that still require update
import '../components/state-history-charts';
import '../more-infos/more-info-content';

View File

@ -10,7 +10,7 @@
<link rel='import' href='./util/hass-util.html'>
<link rel='import' href='./util/hass-behavior.html'>
<link rel='import' href='./layouts/login-form.html'>
<link rel='import' href='./entry-points/home-assistant-main.html'>
<link rel='import' href='./layouts/home-assistant-main.html'>
<link rel='import' href='./resources/home-assistant-style.html'>
<link rel='import' href='./resources/panel-imports.html'>
@ -103,5 +103,3 @@ Polymer({
},
});
</script>
<script src='../build-temp/ui.js'></script>

View File

@ -7,63 +7,63 @@
<link rel="import" href="../components/ha-view-tabs.html">
<dom-module id="partial-cards">
<style is="custom-style" include="iron-flex iron-positioning"></style>
<style>
:host {
display: block;
-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;
}
paper-toolbar:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
bottom: -5px;
width: 100%;
height: 5px;
pointer-events: none;
opacity: 0;
box-shadow: inset 0px 5px 6px -3px rgba(0, 0, 0, 0.4);
will-change: opacity;
transition: opacity 0.4s;
}
paper-scroll-header-panel.raised paper-toolbar:after {
opacity: 1;
}
.menu-icon {
margin-right: 24px;
}
#menu > .title {
line-height: 40px;
}
#menu > .title > span {
pointer-events: auto;
}
.views {
padding-left: 12px;
--paper-tabs-selection-bar-color: #FFF;
}
</style>
<template>
<style is="custom-style" include="iron-flex iron-positioning"></style>
<style>
:host {
display: block;
-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;
}
paper-toolbar:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
bottom: -5px;
width: 100%;
height: 5px;
pointer-events: none;
opacity: 0;
box-shadow: inset 0px 5px 6px -3px rgba(0, 0, 0, 0.4);
will-change: opacity;
transition: opacity 0.4s;
}
paper-scroll-header-panel.raised paper-toolbar:after {
opacity: 1;
}
.menu-icon {
margin-right: 24px;
}
#menu > .title {
line-height: 40px;
}
#menu > .title > span {
pointer-events: auto;
}
.views {
padding-left: 12px;
--paper-tabs-selection-bar-color: #FFF;
}
</style>
<paper-scroll-header-panel id='panel'
condenses keep-condensed-header class='fit' has-views$='[[hasViews]]'
header-height="[[computeHeaderHeight(hasViews, narrow)]]"

View File

@ -16,3 +16,104 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-alarm_control_panel',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
enteredCode: {
type: String,
value: '',
},
disarmButtonVisible: {
type: Boolean,
value: false,
},
armHomeButtonVisible: {
type: Boolean,
value: false,
},
armAwayButtonVisible: {
type: Boolean,
value: false,
},
codeInputVisible: {
type: Boolean,
value: false,
},
codeInputEnabled: {
type: Boolean,
value: false,
},
codeFormat: {
type: String,
value: '',
},
codeValid: {
type: Boolean,
computed: 'validateCode(enteredCode, codeFormat)',
},
},
validateCode: function (code, format) {
var re = new RegExp(format);
if (format === null) {
return true;
}
return re.test(code);
},
stateObjChanged: function (newVal) {
if (newVal) {
this.codeFormat = newVal.attributes.code_format;
this.codeInputVisible = this.codeFormat !== null;
this.codeInputEnabled = (
newVal.state === 'armed_home' ||
newVal.state === 'armed_away' ||
newVal.state === 'disarmed' ||
newVal.state === 'pending' ||
newVal.state === 'triggered');
this.disarmButtonVisible = (
newVal.state === 'armed_home' ||
newVal.state === 'armed_away' ||
newVal.state === 'pending' ||
newVal.state === 'triggered');
this.armHomeButtonVisible = newVal.state === 'disarmed';
this.armAwayButtonVisible = newVal.state === 'disarmed';
}
this.async(function () {
this.fire('iron-resize');
}.bind(this), 500);
},
handleDisarmTap: function () {
this.callService('alarm_disarm', { code: this.enteredCode });
},
handleHomeTap: function () {
this.callService('alarm_arm_home', { code: this.enteredCode });
},
handleAwayTap: function () {
this.callService('alarm_arm_away', { code: this.enteredCode });
},
callService: function (service, data) {
var serviceData = data || {};
serviceData.entity_id = this.stateObj.entityId;
this.hass.serviceActions.callService('alarm_control_panel', service, serviceData)
.then(function () {
this.enteredCode = '';
}.bind(this));
},
});
</script>

View File

@ -1,96 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'more-info-alarm_control_panel',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
enteredCode: {
type: String,
value: '',
},
disarmButtonVisible: {
type: Boolean,
value: false,
},
armHomeButtonVisible: {
type: Boolean,
value: false,
},
armAwayButtonVisible: {
type: Boolean,
value: false,
},
codeInputVisible: {
type: Boolean,
value: false,
},
codeInputEnabled: {
type: Boolean,
value: false,
},
codeFormat: {
type: String,
value: '',
},
codeValid: {
type: Boolean,
computed: 'validateCode(enteredCode, codeFormat)',
},
},
validateCode(code, format) {
const re = new RegExp(format);
if (format === null) {
return true;
}
return re.test(code);
},
stateObjChanged(newVal) {
if (newVal) {
this.codeFormat = newVal.attributes.code_format;
this.codeInputVisible = this.codeFormat !== null;
this.codeInputEnabled = (
newVal.state === 'armed_home' ||
newVal.state === 'armed_away' ||
newVal.state === 'disarmed' ||
newVal.state === 'pending' ||
newVal.state === 'triggered');
this.disarmButtonVisible = (
newVal.state === 'armed_home' ||
newVal.state === 'armed_away' ||
newVal.state === 'pending' ||
newVal.state === 'triggered');
this.armHomeButtonVisible = newVal.state === 'disarmed';
this.armAwayButtonVisible = newVal.state === 'disarmed';
}
this.async(() => this.fire('iron-resize'), 500);
},
handleDisarmTap() {
this.callService('alarm_disarm', { code: this.enteredCode });
},
handleHomeTap() {
this.callService('alarm_arm_home', { code: this.enteredCode });
},
handleAwayTap() {
this.callService('alarm_arm_away', { code: this.enteredCode });
},
callService(service, data) {
const serviceData = data || {};
serviceData.entity_id = this.stateObj.entityId;
this.hass.serviceActions.callService('alarm_control_panel', service, serviceData)
.then(() => { this.enteredCode = ''; });
},
});

View File

@ -4,38 +4,48 @@
<link rel='import' href='../components/loading-box.html'>
<dom-module id='more-info-configurator'>
<style is="custom-style" include="iron-flex"></style>
<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>
<style is="custom-style" include="iron-flex"></style>
<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>
<div class='layout vertical'>
<template is='dom-if' if='[[isConfigurable]]'>
<p hidden$='[[!stateObj.attributes.description]]'>[[stateObj.attributes.description]]
<a hidden$='[[!stateObj.attributes.link_url]]' href='[[stateObj.attributes.link_url]]' target='_blank'>[[stateObj.attributes.link_name]]</a>
<p hidden$='[[!stateObj.attributes.description]]'>
[[stateObj.attributes.description]]
<a
hidden$='[[!stateObj.attributes.link_url]]'
href='[[stateObj.attributes.link_url]]'
target='_blank'
>
[[stateObj.attributes.link_name]]
</a>
</p>
<p class='error' hidden$='[[!stateObj.attributes.errors]]'>[[stateObj.attributes.errors]]</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]]' />
@ -43,14 +53,19 @@
<template is='dom-repeat' items='[[stateObj.attributes.fields]]'>
<paper-input-container id='paper-input-fields-{{item.id}}'>
<label>{{item.name}}</label>
<input is="iron-input" type="{{item.type}}" id='{{item.id}}' on-change="fieldChanged"/>
<label>[[item.name]]</label>
<input is="iron-input" type="[[item.type]]" id='[[item.id]]' on-change="fieldChanged"/>
</paper-input-container>
</template>
<p class='submit'>
<paper-button raised on-tap='submitClicked'
hidden$='[[isConfiguring]]'>[[submitCaption]]</paper-button>
<paper-button
raised
on-tap='submitClicked'
hidden$='[[isConfiguring]]'
>
[[submitCaption]]
</paper-button>
<loading-box hidden$='[[!isConfiguring]]'>Configuring</loading-box>
</p>
@ -59,3 +74,83 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-configurator',
behaviors: [window.hassBehavior],
properties: {
stateObj: {
type: Object,
},
action: {
type: String,
value: 'display',
},
isStreaming: {
type: Boolean,
bindNuclear: function (hass) {
return hass.streamGetters.isStreamingEvents;
},
},
isConfigurable: {
type: Boolean,
computed: 'computeIsConfigurable(stateObj)',
},
isConfiguring: {
type: Boolean,
value: false,
},
submitCaption: {
type: String,
computed: 'computeSubmitCaption(stateObj)',
},
fieldInput: {
type: Object, value: {},
},
},
computeIsConfigurable: function (stateObj) {
return stateObj.state === 'configure';
},
computeSubmitCaption: function (stateObj) {
return stateObj.attributes.submit_caption || 'Set configuration';
},
fieldChanged: function (ev) {
var el = ev.target;
this.fieldInput[el.id] = el.value;
},
submitClicked: function () {
var data = {
configure_id: this.stateObj.attributes.configure_id,
fields: this.fieldInput,
};
this.isConfiguring = true;
this.hass.serviceActions.callService('configurator', 'configure', data).then(
function () {
this.isConfiguring = false;
if (!this.isStreaming) {
this.hass.syncActions.fetchAll();
}
}.bind(this),
function () {
this.isConfiguring = false;
}.bind(this)
);
},
});
</script>

View File

@ -1,77 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'more-info-configurator',
behaviors: [window.hassBehavior],
properties: {
stateObj: {
type: Object,
},
action: {
type: String,
value: 'display',
},
isStreaming: {
type: Boolean,
bindNuclear: hass => hass.streamGetters.isStreamingEvents,
},
isConfigurable: {
type: Boolean,
computed: 'computeIsConfigurable(stateObj)',
},
isConfiguring: {
type: Boolean,
value: false,
},
submitCaption: {
type: String,
computed: 'computeSubmitCaption(stateObj)',
},
fieldInput: {
type: Object, value: {},
},
},
computeIsConfigurable(stateObj) {
return stateObj.state === 'configure';
},
computeSubmitCaption(stateObj) {
return stateObj.attributes.submit_caption || 'Set configuration';
},
fieldChanged(ev) {
const el = ev.target;
this.fieldInput[el.id] = el.value;
},
submitClicked() {
this.isConfiguring = true;
const data = {
configure_id: this.stateObj.attributes.configure_id,
fields: this.fieldInput,
};
this.hass.serviceActions.callService('configurator', 'configure', data).then(
() => {
this.isConfiguring = false;
if (!this.isStreaming) {
this.hass.syncActions.fetchAll();
}
},
() => {
this.isConfiguring = false;
}
);
},
});

View File

@ -21,3 +21,28 @@
}
</style>
</dom-module>
<script>
Polymer({
is: 'more-info-content',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
},
stateObjChanged: function (stateObj) {
if (!stateObj) return;
window.hassUtil.dynamicContentUpdater(
this, 'MORE-INFO-' + window.hassUtil.stateMoreInfoType(stateObj).toUpperCase(),
{ hass: this.hass, stateObj: stateObj });
},
});
</script>

View File

@ -1,36 +0,0 @@
import Polymer from '../polymer';
import './more-info-group';
import './more-info-sun';
import './more-info-configurator';
import './more-info-thermostat';
import './more-info-script';
import './more-info-light';
import './more-info-media_player';
import './more-info-updater';
import './more-info-alarm_control_panel';
import './more-info-lock';
import './more-info-hvac';
export default new Polymer({
is: 'more-info-content',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
},
stateObjChanged(stateObj) {
if (!stateObj) return;
window.hassUtil.dynamicContentUpdater(
this, `MORE-INFO-${window.hassUtil.stateMoreInfoType(stateObj).toUpperCase()}`,
{ hass: this.hass, stateObj });
},
});

View File

@ -22,3 +22,77 @@
</template>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-group',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
},
states: {
type: Array,
bindNuclear: hass => [
hass.moreInfoGetters.currentEntity,
hass.entityGetters.entityMap,
(currentEntity, entities) => {
// weird bug??
if (!currentEntity) {
return [];
}
return currentEntity.attributes.entity_id.map(
entities.get.bind(entities));
},
],
},
},
observers: [
'statesChanged(stateObj, states)',
],
statesChanged: function (stateObj, states) {
var groupDomainStateObj = false;
var baseStateObj;
var i;
var state;
var el;
if (states && states.length > 0) {
baseStateObj = states[0];
groupDomainStateObj = baseStateObj.set('entityId', stateObj.entityId).set(
'attributes', Object.assign({}, baseStateObj.attributes));
for (i = 0; i < states.length; i++) {
state = states[i];
if (state && state.domain) {
if (groupDomainStateObj.domain !== state.domain) {
groupDomainStateObj = false;
}
}
}
}
if (!groupDomainStateObj) {
el = Polymer.dom(this.$.groupedControlDetails);
if (el.lastChild) {
el.removeChild(el.lastChild);
}
} else {
window.hassUtil.dynamicContentUpdater(
this.$.groupedControlDetails,
'MORE-INFO-' + window.hassUtil.stateMoreInfoType(groupDomainStateObj).toUpperCase(),
{ stateObj: groupDomainStateObj });
}
},
});
</script>

View File

@ -1,69 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'more-info-group',
behaviors: [window.hassBehavior],
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
},
states: {
type: Array,
bindNuclear: hass => [
hass.moreInfoGetters.currentEntity,
hass.entityGetters.entityMap,
(currentEntity, entities) => {
// weird bug??
if (!currentEntity) {
return [];
}
return currentEntity.attributes.entity_id.map(
entities.get.bind(entities));
},
],
},
},
observers: [
'statesChanged(stateObj, states)',
],
statesChanged(stateObj, states) {
let groupDomainStateObj = false;
if (states && states.length > 0) {
const baseStateObj = states[0];
groupDomainStateObj = baseStateObj.set('entityId', stateObj.entityId).set(
'attributes', Object.assign({}, baseStateObj.attributes));
for (let i = 0; i < states.length; i++) {
const s = states[i];
if (s && s.domain) {
if (groupDomainStateObj.domain !== s.domain) {
groupDomainStateObj = false;
}
}
}
}
if (!groupDomainStateObj) {
const el = Polymer.dom(this.$.groupedControlDetails);
if (el.lastChild) {
el.removeChild(el.lastChild);
}
} else {
window.hassUtil.dynamicContentUpdater(
this.$.groupedControlDetails,
`MORE-INFO-${window.hassUtil.stateMoreInfoType(groupDomainStateObj).toUpperCase()}`,
{ stateObj: groupDomainStateObj });
}
},
});

View File

@ -129,3 +129,165 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-hvac',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
operationIndex: {
type: Number,
value: -1,
observer: 'handleOperationmodeChanged',
},
fanIndex: {
type: Number,
value: -1,
observer: 'handleFanmodeChanged',
},
swingIndex: {
type: Number,
value: -1,
observer: 'handleSwingmodeChanged',
},
awayToggleChecked: {
type: Boolean,
},
auxToggleChecked: {
type: Boolean,
},
},
stateObjChanged: function (newVal) {
this.awayToggleChecked = newVal.attributes.away_mode === 'on';
this.auxheatToggleChecked = newVal.attributes.aux_heat === 'on';
if (newVal.attributes.fan_list) {
this.fanIndex = newVal.attributes.fan_list.indexOf(
newVal.attributes.fan_mode);
} else {
this.fanIndex = -1;
}
if (newVal.attributes.operation_list) {
this.operationIndex = newVal.attributes.operation_list.indexOf(
newVal.attributes.operation_mode);
} else {
this.operationIndex = -1;
}
if (newVal.attributes.swing_list) {
this.swingIndex = newVal.attributes.swing_list.indexOf(
newVal.attributes.swing_mode);
} else {
this.swingIndex = -1;
}
this.async(function () {
this.fire('iron-resize');
}.bind(this), 500);
},
computeClassNames: function (stateObj) {
return 'more-info-hvac' + window.hassUtil.attributeClassNames(
stateObj,
['away_mode', 'aux_heat', 'temperature', 'humidity', 'operation_list',
'fan_list', 'swing_list']
);
},
targetTemperatureSliderChanged: function (ev) {
var temperature = ev.target.value;
if (temperature === this.stateObj.attributes.temperature) return;
this.callServiceHelper('set_temperature', { temperature });
},
targetHumiditySliderChanged: function (ev) {
var humidity = ev.target.value;
if (humidity === this.stateObj.attributes.humidity) return;
this.callServiceHelper('set_humidity', { humidity });
},
awayToggleChanged: function (ev) {
var oldVal = this.stateObj.attributes.away_mode === 'on';
var newVal = ev.target.checked;
if (oldVal === newVal) return;
this.callServiceHelper('set_away_mode', { away_mode: newVal });
},
auxToggleChanged: function (ev) {
var oldVal = this.stateObj.attributes.aux_heat === 'on';
var newVal = ev.target.checked;
if (oldVal === newVal) return;
this.callServiceHelper('set_aux_heat', { aux_heat: newVal });
},
handleFanmodeChanged: function (fanIndex) {
var fanInput;
// Selected Option will transition to '' before transitioning to new value
if (fanIndex === '' || fanIndex === -1) return;
fanInput = this.stateObj.attributes.fan_list[fanIndex];
if (fanInput === this.stateObj.attributes.fan_mode) return;
this.callServiceHelper('set_fan_mode', { fan_mode: fanInput });
},
handleOperationmodeChanged: function (operationIndex) {
var operationInput;
// Selected Option will transition to '' before transitioning to new value
if (operationIndex === '' || operationIndex === -1) return;
operationInput = this.stateObj.attributes.operation_list[operationIndex];
if (operationInput === this.stateObj.attributes.operation_mode) return;
this.callServiceHelper('set_operation_mode', { operation_mode: operationInput });
},
handleSwingmodeChanged: function (swingIndex) {
var swingInput;
// Selected Option will transition to '' before transitioning to new value
if (swingIndex === '' || swingIndex === -1) return;
swingInput = this.stateObj.attributes.swing_list[swingIndex];
if (swingInput === this.stateObj.attributes.swing_mode) return;
this.callServiceHelper('set_swing_mode', { swing_mode: swingInput });
},
callServiceHelper: function (service, data) {
// We call stateChanged after a successful call to re-sync the inputs
// 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.
/* eslint-disable no-param-reassign */
data.entity_id = this.stateObj.entityId;
/* eslint-enable no-param-reassign */
this.hass.serviceActions.callService('hvac', service, data)
.then(function () {
this.stateObjChanged(this.stateObj);
});
},
});
</script>

View File

@ -1,160 +0,0 @@
import Polymer from '../polymer';
const ATTRIBUTE_CLASSES = [
'away_mode',
'aux_heat',
'temperature',
'humidity',
'operation_list',
'fan_list',
'swing_list',
];
export default new Polymer({
is: 'more-info-hvac',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
operationIndex: {
type: Number,
value: -1,
observer: 'handleOperationmodeChanged',
},
fanIndex: {
type: Number,
value: -1,
observer: 'handleFanmodeChanged',
},
swingIndex: {
type: Number,
value: -1,
observer: 'handleSwingmodeChanged',
},
awayToggleChecked: {
type: Boolean,
},
auxToggleChecked: {
type: Boolean,
},
},
stateObjChanged(newVal) {
this.awayToggleChecked = newVal.attributes.away_mode === 'on';
this.auxheatToggleChecked = newVal.attributes.aux_heat === 'on';
if (newVal.attributes.fan_list) {
this.fanIndex = newVal.attributes.fan_list.indexOf(
newVal.attributes.fan_mode);
} else {
this.fanIndex = -1;
}
if (newVal.attributes.operation_list) {
this.operationIndex = newVal.attributes.operation_list.indexOf(
newVal.attributes.operation_mode);
} else {
this.operationIndex = -1;
}
if (newVal.attributes.swing_list) {
this.swingIndex = newVal.attributes.swing_list.indexOf(
newVal.attributes.swing_mode);
} else {
this.swingIndex = -1;
}
this.async(() => this.fire('iron-resize'), 500);
},
computeClassNames(stateObj) {
return `more-info-hvac ${window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES)}`;
},
targetTemperatureSliderChanged(ev) {
const temperature = ev.target.value;
if (temperature === this.stateObj.attributes.temperature) return;
this.callServiceHelper('set_temperature', { temperature });
},
targetHumiditySliderChanged(ev) {
const humidity = ev.target.value;
if (humidity === this.stateObj.attributes.humidity) return;
this.callServiceHelper('set_humidity', { humidity });
},
awayToggleChanged(ev) {
const oldVal = this.stateObj.attributes.away_mode === 'on';
const newVal = ev.target.checked;
if (oldVal === newVal) return;
this.callServiceHelper('set_away_mode', { away_mode: newVal });
},
auxToggleChanged(ev) {
const oldVal = this.stateObj.attributes.aux_heat === 'on';
const newVal = ev.target.checked;
if (oldVal === newVal) return;
this.callServiceHelper('set_aux_heat', { aux_heat: newVal });
},
handleFanmodeChanged(fanIndex) {
// Selected Option will transition to '' before transitioning to new value
if (fanIndex === '' || fanIndex === -1) return;
const fanInput = this.stateObj.attributes.fan_list[fanIndex];
if (fanInput === this.stateObj.attributes.fan_mode) return;
this.callServiceHelper('set_fan_mode', { fan_mode: fanInput });
},
handleOperationmodeChanged(operationIndex) {
// Selected Option will transition to '' before transitioning to new value
if (operationIndex === '' || operationIndex === -1) return;
const operationInput = this.stateObj.attributes.operation_list[operationIndex];
if (operationInput === this.stateObj.attributes.operation_mode) return;
this.callServiceHelper('set_operation_mode', { operation_mode: operationInput });
},
handleSwingmodeChanged(swingIndex) {
// Selected Option will transition to '' before transitioning to new value
if (swingIndex === '' || swingIndex === -1) return;
const swingInput = this.stateObj.attributes.swing_list[swingIndex];
if (swingInput === this.stateObj.attributes.swing_mode) return;
this.callServiceHelper('set_swing_mode', { swing_mode: swingInput });
},
callServiceHelper(service, data) {
// We call stateChanged after a successful call to re-sync the inputs
// 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.
/* eslint-disable no-param-reassign */
data.entity_id = this.stateObj.entityId;
/* eslint-enable no-param-reassign */
this.hass.serviceActions.callService('hvac', service, data)
.then(() => this.stateObjChanged(this.stateObj));
},
});

View File

@ -54,3 +54,104 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-light',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
brightnessSliderValue: {
type: Number,
value: 0,
},
ctSliderValue: {
type: Number,
value: 0,
},
},
stateObjChanged: function (newVal) {
if (newVal && newVal.state === 'on') {
this.brightnessSliderValue = newVal.attributes.brightness;
this.ctSliderValue = newVal.attributes.color_temp;
}
this.async(function () {
this.fire('iron-resize');
}.bind(this), 500);
},
computeClassNames: function (stateObj) {
return window.hassUtil.attributeClassNames(
stateObj, ['brightness', 'rgb_color', 'color_temp']);
},
brightnessSliderChanged: function (ev) {
var bri = parseInt(ev.target.value, 10);
if (isNaN(bri)) return;
if (bri === 0) {
this.hass.serviceActions.callTurnOff(this.stateObj.entityId);
} else {
this.hass.serviceActions.callService('light', 'turn_on', {
entity_id: this.stateObj.entityId,
brightness: bri,
});
}
},
ctSliderChanged: function (ev) {
var ct = parseInt(ev.target.value, 10);
if (isNaN(ct)) return;
this.hass.serviceActions.callService('light', 'turn_on', {
entity_id: this.stateObj.entityId,
color_temp: ct,
});
},
serviceChangeColor: function (hass, entityId, color) {
hass.serviceActions.callService('light', 'turn_on', {
entity_id: entityId,
rgb_color: [color.r, color.g, color.b],
});
},
/**
* Called when a new color has been picked. We will not respond to every
* color pick event but have a pause between requests.
*/
colorPicked: function (ev) {
if (this.skipColorPicked) {
this.colorChanged = true;
return;
}
this.color = ev.detail.rgb;
this.serviceChangeColor(this.hass, this.stateObj.entityId, this.color);
this.colorChanged = false;
this.skipColorPicked = true;
this.colorDebounce = setTimeout(function () {
if (this.colorChanged) {
this.serviceChangeColor(this.hass, this.stateObj.entityId, this.color);
}
this.skipColorPicked = false;
}.bind(this), 500);
},
});
</script>

View File

@ -1,100 +0,0 @@
import Polymer from '../polymer';
const ATTRIBUTE_CLASSES = ['brightness', 'rgb_color', 'color_temp'];
function pickColor(hass, entityId, color) {
hass.serviceActions.callService('light', 'turn_on', {
entity_id: entityId,
rgb_color: [color.r, color.g, color.b],
});
}
export default new Polymer({
is: 'more-info-light',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
brightnessSliderValue: {
type: Number,
value: 0,
},
ctSliderValue: {
type: Number,
value: 0,
},
},
stateObjChanged(newVal) {
if (newVal && newVal.state === 'on') {
this.brightnessSliderValue = newVal.attributes.brightness;
this.ctSliderValue = newVal.attributes.color_temp;
}
this.async(() => this.fire('iron-resize'), 500);
},
computeClassNames(stateObj) {
return window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
},
brightnessSliderChanged(ev) {
const bri = parseInt(ev.target.value, 10);
if (isNaN(bri)) return;
if (bri === 0) {
this.hass.serviceActions.callTurnOff(this.stateObj.entityId);
} else {
this.hass.serviceActions.callService('light', 'turn_on', {
entity_id: this.stateObj.entityId,
brightness: bri,
});
}
},
ctSliderChanged(ev) {
const ct = parseInt(ev.target.value, 10);
if (isNaN(ct)) return;
this.hass.serviceActions.callService('light', 'turn_on', {
entity_id: this.stateObj.entityId,
color_temp: ct,
});
},
/**
* Called when a new color has been picked. We will not respond to every
* color pick event but have a pause between requests.
*/
colorPicked(ev) {
if (this.skipColorPicked) {
this.colorChanged = true;
return;
}
this.color = ev.detail.rgb;
pickColor(this.hass, this.stateObj.entityId, this.color);
this.colorChanged = false;
this.skipColorPicked = true;
this.colorDebounce = setTimeout(() => {
if (this.colorChanged) {
pickColor(this.hass, this.stateObj.entityId, this.color);
}
this.skipColorPicked = false;
}, 500);
},
});

View File

@ -17,3 +17,46 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-lock',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
enteredCode: {
type: String,
value: '',
},
},
handleUnlockTap: function () {
this.callService('unlock', { code: this.enteredCode });
},
handleLockTap: function () {
this.callService('lock', { code: this.enteredCode });
},
stateObjChanged: function (newVal) {
if (newVal) {
this.isLocked = newVal.state === 'locked';
}
this.async(function () {
this.fire('iron-resize');
}.bind(this), 500);
},
callService: function (service, data) {
var serviceData = data || {};
serviceData.entity_id = this.stateObj.entityId;
this.hass.serviceActions.callService('lock', service, serviceData);
},
});
</script>

View File

@ -1,40 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'more-info-lock',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
enteredCode: {
type: String,
value: '',
},
},
handleUnlockTap() {
this.callService('unlock', { code: this.enteredCode });
},
handleLockTap() {
this.callService('lock', { code: this.enteredCode });
},
stateObjChanged(newVal) {
if (newVal) {
this.isLocked = newVal.state === 'locked';
}
this.async(() => this.fire('iron-resize'), 500);
},
callService(service, data) {
const serviceData = data || {};
serviceData.entity_id = this.stateObj.entityId;
this.hass.serviceActions.callService('lock', service, serviceData);
},
});

View File

@ -86,3 +86,245 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-media_player',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
isOff: {
type: Boolean,
value: false,
},
isPlaying: {
type: Boolean,
value: false,
},
isMuted: {
type: Boolean,
value: false,
},
source: {
type: String,
value: '',
},
sourceIndex: {
type: Number,
value: 0,
observer: 'handleSourceChanged',
},
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,
},
supportsVolumeButtons: {
type: Boolean,
value: false,
},
supportsSelectSource: {
type: Boolean,
value: false,
},
hasMediaControl: {
type: Boolean,
value: false,
},
},
HAS_MEDIA_STATES: ['playing', 'paused', 'unknown'],
stateObjChanged: function (newVal) {
if (newVal) {
this.isOff = newVal.state === 'off';
this.isPlaying = newVal.state === 'playing';
this.hasMediaControl = this.HAS_MEDIA_STATES.indexOf(newVal.state) !== -1;
this.volumeSliderValue = newVal.attributes.volume_level * 100;
this.isMuted = newVal.attributes.is_volume_muted;
this.source = newVal.attributes.source;
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.supportsVolumeButtons = (newVal.attributes.supported_media_commands & 1024) !== 0;
this.supportsSelectSource = (newVal.attributes.supported_media_commands & 2048) !== 0;
if (newVal.attributes.source_list !== undefined) {
this.sourceIndex = newVal.attributes.source_list.indexOf(this.source);
}
}
this.async(function () {
this.fire('iron-resize');
}.bind(this), 500);
},
computeClassNames: function (stateObj) {
return window.hassUtil.attributeClassNames(stateObj, ['volume_level']);
},
computeIsOff: function (stateObj) {
return stateObj.state === 'off';
},
computeMuteVolumeIcon: function (isMuted) {
return isMuted ? 'mdi:volume-off' : 'mdi:volume-high';
},
computeHideVolumeButtons: function (isOff, supportsVolumeButtons) {
return !supportsVolumeButtons || isOff;
},
computeShowPlaybackControls: function (isOff, hasMedia) {
return !isOff && hasMedia;
},
computePlaybackControlIcon: function () {
if (this.isPlaying) {
return this.supportsPause ? 'mdi:pause' : 'mdi:stop';
}
return 'mdi:play';
},
computeHidePowerButton: function (isOff, supportsTurnOn, supportsTurnOff) {
return isOff ? !supportsTurnOn : !supportsTurnOff;
},
computeHideSelectSource: function (isOff, supportsSelectSource) {
return !isOff && supportsSelectSource;
},
computeSelectedSource: function (stateObj) {
return stateObj.attributes.source_list.indexOf(stateObj.attributes.source);
},
handleTogglePower: function () {
this.callService(this.isOff ? 'turn_on' : 'turn_off');
},
handlePrevious: function () {
this.callService('media_previous_track');
},
handlePlaybackControl: function () {
this.callService('media_play_pause');
},
handleNext: function () {
this.callService('media_next_track');
},
handleSourceChanged: function (sourceIndex) {
var sourceInput;
// Selected Option will transition to '' before transitioning to new value
if (!this.stateObj
|| this.stateObj.attributes.source_list === undefined
|| sourceIndex < 0
|| sourceIndex >= this.stateObj.attributes.source_list.length
) {
return;
}
sourceInput = this.stateObj.attributes.source_list[sourceIndex];
if (sourceInput === this.stateObj.attributes.source) {
return;
}
this.callService('select_source', { source: sourceInput });
},
handleVolumeTap: function () {
if (!this.supportsVolumeMute) {
return;
}
this.callService('volume_mute', { is_volume_muted: !this.isMuted });
},
handleVolumeUp: function () {
var obj = this.$.volumeUp;
this.handleVolumeWorker('volume_up', obj, true);
},
handleVolumeDown: function () {
var obj = this.$.volumeDown;
this.handleVolumeWorker('volume_down', obj, true);
},
handleVolumeWorker: function (service, obj, force) {
if (force || (obj !== undefined && obj.pointerDown)) {
this.callService(service);
this.async(function () {
this.handleVolumeWorker(service, obj, false);
}.bind(this), 500);
}
},
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) {
var serviceData = data || {};
serviceData.entity_id = this.stateObj.entityId;
this.hass.serviceActions.callService('media_player', service, serviceData);
},
});
</script>

View File

@ -1,237 +0,0 @@
import Polymer from '../polymer';
const ATTRIBUTE_CLASSES = ['volume_level'];
export default new Polymer({
is: 'more-info-media_player',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
isOff: {
type: Boolean,
value: false,
},
isPlaying: {
type: Boolean,
value: false,
},
isMuted: {
type: Boolean,
value: false,
},
source: {
type: String,
value: '',
},
sourceIndex: {
type: Number,
value: 0,
observer: 'handleSourceChanged',
},
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,
},
supportsVolumeButtons: {
type: Boolean,
value: false,
},
supportsSelectSource: {
type: Boolean,
value: false,
},
hasMediaControl: {
type: Boolean,
value: false,
},
},
stateObjChanged(newVal) {
if (newVal) {
const hasMediaStates = ['playing', 'paused', 'unknown'];
this.isOff = newVal.state === 'off';
this.isPlaying = newVal.state === 'playing';
this.hasMediaControl = hasMediaStates.indexOf(newVal.state) !== -1;
this.volumeSliderValue = newVal.attributes.volume_level * 100;
this.isMuted = newVal.attributes.is_volume_muted;
this.source = newVal.attributes.source;
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.supportsVolumeButtons = (newVal.attributes.supported_media_commands & 1024) !== 0;
this.supportsSelectSource = (newVal.attributes.supported_media_commands & 2048) !== 0;
if (newVal.attributes.source_list !== undefined) {
this.sourceIndex = newVal.attributes.source_list.indexOf(this.source);
}
}
this.async(() => this.fire('iron-resize'), 500);
},
computeClassNames(stateObj) {
return window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
},
computeIsOff(stateObj) {
return stateObj.state === 'off';
},
computeMuteVolumeIcon(isMuted) {
return isMuted ? 'mdi:volume-off' : 'mdi:volume-high';
},
computeHideVolumeButtons(isOff, supportsVolumeButtons) {
return !supportsVolumeButtons || isOff;
},
computeShowPlaybackControls(isOff, hasMedia) {
return !isOff && hasMedia;
},
computePlaybackControlIcon() {
if (this.isPlaying) {
return this.supportsPause ? 'mdi:pause' : 'mdi:stop';
}
return 'mdi:play';
},
computeHidePowerButton(isOff, supportsTurnOn, supportsTurnOff) {
return isOff ? !supportsTurnOn : !supportsTurnOff;
},
computeHideSelectSource(isOff, supportsSelectSource) {
return !isOff && supportsSelectSource;
},
computeSelectedSource(stateObj) {
return stateObj.attributes.source_list.indexOf(stateObj.attributes.source);
},
handleTogglePower() {
this.callService(this.isOff ? 'turn_on' : 'turn_off');
},
handlePrevious() {
this.callService('media_previous_track');
},
handlePlaybackControl() {
this.callService('media_play_pause');
},
handleNext() {
this.callService('media_next_track');
},
handleSourceChanged(sourceIndex) {
// Selected Option will transition to '' before transitioning to new value
if (!this.stateObj
|| this.stateObj.attributes.source_list === undefined
|| sourceIndex < 0
|| sourceIndex >= this.stateObj.attributes.source_list.length
) {
return;
}
const sourceInput = this.stateObj.attributes.source_list[sourceIndex];
if (sourceInput === this.stateObj.attributes.source) {
return;
}
this.callService('select_source', { source: sourceInput });
},
handleVolumeTap() {
if (!this.supportsVolumeMute) {
return;
}
this.callService('volume_mute', { is_volume_muted: !this.isMuted });
},
handleVolumeUp() {
const obj = this.$.volumeUp;
this.handleVolumeWorker('volume_up', obj, true);
},
handleVolumeDown() {
const obj = this.$.volumeDown;
this.handleVolumeWorker('volume_down', obj, true);
},
handleVolumeWorker(service, obj, force) {
if (force || (obj !== undefined && obj.pointerDown)) {
this.callService(service);
this.async(() => this.handleVolumeWorker(service, obj, false), 500);
}
},
volumeSliderChanged(ev) {
const volPercentage = parseFloat(ev.target.value);
const vol = volPercentage > 0 ? volPercentage / 100 : 0;
this.callService('volume_set', { volume_level: vol });
},
callService(service, data) {
const serviceData = data || {};
serviceData.entity_id = this.stateObj.entityId;
this.hass.serviceActions.callService('media_player', service, serviceData);
},
});

View File

@ -11,3 +11,15 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-script',
properties: {
stateObj: {
type: Object,
},
},
});
</script>

View File

@ -1,11 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'more-info-script',
properties: {
stateObj: {
type: Object,
},
},
});

View File

@ -20,3 +20,49 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-sun',
properties: {
stateObj: {
type: Object,
},
risingDate: {
type: Object,
computed: 'computeRising(stateObj)',
},
settingDate: {
type: Object,
computed: 'computeSetting(stateObj)',
},
},
computeRising: function (stateObj) {
return new Date(stateObj.attributes.next_rising);
},
computeSetting: function (stateObj) {
return new Date(stateObj.attributes.next_setting);
},
computeOrder: function (risingDate, settingDate) {
return risingDate > settingDate ? ['set', 'ris'] : ['ris', 'set'];
},
itemCaption: function (type) {
return type === 'ris' ? 'Rising ' : 'Setting ';
},
itemDate: function (type) {
return type === 'ris' ? this.risingDate : this.settingDate;
},
itemValue: function (type) {
return window.hassUtil.formatTime(this.itemDate(type));
},
});
</script>

View File

@ -1,45 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'more-info-sun',
properties: {
stateObj: {
type: Object,
},
risingDate: {
type: Object,
computed: 'computeRising(stateObj)',
},
settingDate: {
type: Object,
computed: 'computeSetting(stateObj)',
},
},
computeRising(stateObj) {
return new Date(stateObj.attributes.next_rising);
},
computeSetting(stateObj) {
return new Date(stateObj.attributes.next_setting);
},
computeOrder(risingDate, settingDate) {
return risingDate > settingDate ? ['set', 'ris'] : ['ris', 'set'];
},
itemCaption(type) {
return type === 'ris' ? 'Rising ' : 'Setting ';
},
itemDate(type) {
return type === 'ris' ? this.risingDate : this.settingDate;
},
itemValue(type) {
return window.hassUtil.formatTime(this.itemDate(type));
},
});

View File

@ -39,3 +39,77 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-thermostat',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
tempMin: {
type: Number,
},
tempMax: {
type: Number,
},
targetTemperatureSliderValue: {
type: Number,
},
awayToggleChecked: {
type: Boolean,
},
},
stateObjChanged: function (newVal) {
this.targetTemperatureSliderValue = newVal.attributes.temperature;
this.awayToggleChecked = newVal.attributes.away_mode === 'on';
this.tempMin = newVal.attributes.min_temp;
this.tempMax = newVal.attributes.max_temp;
},
computeClassNames: function (stateObj) {
return window.hassUtil.attributeClassNames(stateObj, ['away_mode']);
},
targetTemperatureSliderChanged: function (ev) {
this.hass.serviceActions.callService('thermostat', 'set_temperature', {
entity_id: this.stateObj.entityId,
temperature: ev.target.value,
});
},
toggleChanged: function (ev) {
const 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 (awayMode) {
// 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.
this.hass.serviceActions.callService(
'thermostat', 'set_away_mode',
{ away_mode: awayMode, entity_id: this.stateObj.entityId })
.then(function () { this.stateObjChanged(this.stateObj); }.bind(this));
},
});
</script>

View File

@ -1,75 +0,0 @@
import Polymer from '../polymer';
const ATTRIBUTE_CLASSES = ['away_mode'];
export default new Polymer({
is: 'more-info-thermostat',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
observer: 'stateObjChanged',
},
tempMin: {
type: Number,
},
tempMax: {
type: Number,
},
targetTemperatureSliderValue: {
type: Number,
},
awayToggleChecked: {
type: Boolean,
},
},
stateObjChanged(newVal) {
this.targetTemperatureSliderValue = newVal.attributes.temperature;
this.awayToggleChecked = newVal.attributes.away_mode === 'on';
this.tempMin = newVal.attributes.min_temp;
this.tempMax = newVal.attributes.max_temp;
},
computeClassNames(stateObj) {
return window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
},
targetTemperatureSliderChanged(ev) {
this.hass.serviceActions.callService('thermostat', 'set_temperature', {
entity_id: this.stateObj.entityId,
temperature: ev.target.value,
});
},
toggleChanged(ev) {
const 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(awayMode) {
// 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.
this.hass.serviceActions.callService(
'thermostat', 'set_away_mode',
{ away_mode: awayMode, entity_id: this.stateObj.entityId })
.then(() => this.stateObjChanged(this.stateObj));
},
});

View File

@ -15,3 +15,12 @@
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'more-info-updater',
properties: {
},
});
</script>

View File

@ -1,8 +0,0 @@
import Polymer from '../polymer';
export default new Polymer({
is: 'more-info-updater',
properties: {
},
});