mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
More ES6->ES5 conversion
This commit is contained in:
parent
ff0e24fecb
commit
111b6c6f48
@ -16,5 +16,8 @@
|
||||
"no-underscore-dangle": 0,
|
||||
"no-var": 0,
|
||||
"strict": 0
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
"html"
|
||||
]
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
"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"
|
||||
"test": "eslint src panels --ext html"
|
||||
},
|
||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||
"license": "MIT",
|
||||
@ -39,8 +39,10 @@
|
||||
"bower": "^1.7.9",
|
||||
"eslint": "^3.1.0",
|
||||
"eslint-config-airbnb-base": "^4.0.2",
|
||||
"eslint-plugin-import": "^1.10.3",
|
||||
"eslint-plugin-html": "^1.5.1",
|
||||
"eslint-plugin-import": "^1.11.0",
|
||||
"html-minifier": "^3.0.1",
|
||||
"polymer-cli": "^0.12.0",
|
||||
"rollup": "^0.34.1",
|
||||
"rollup-plugin-babel": "^2.6.1",
|
||||
"rollup-plugin-buble": "^0.12.1",
|
||||
|
@ -1,6 +1,7 @@
|
||||
<!-- <link rel="import" href="../../bower_components/polymer/polymer.html"> -->
|
||||
|
||||
<dom-module id="events-list">
|
||||
<template>
|
||||
<style>
|
||||
ul {
|
||||
margin: 0;
|
||||
@ -17,7 +18,6 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<ul>
|
||||
<template is='dom-repeat' items='[[events]]' as='event'>
|
||||
<li>
|
||||
|
@ -10,6 +10,7 @@
|
||||
<link rel="import" href="./events-list.html">
|
||||
|
||||
<dom-module id="ha-panel-dev-event">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
.content {
|
||||
@ -31,7 +32,6 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<partial-base narrow="{{narrow}}" show-menu='[[showMenu]]'>
|
||||
<span header-title>Events</span>
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
<link rel="import" href="./partial-base.html">
|
||||
-->
|
||||
<dom-module id="ha-panel-dev-info">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-positioning"></style>
|
||||
<style>
|
||||
.content {
|
||||
@ -50,7 +51,7 @@
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
|
||||
<span header-title>About</span>
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
<link rel="import" href="./services-list.html">
|
||||
|
||||
<dom-module id="ha-panel-dev-service">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
.content {
|
||||
@ -35,7 +36,7 @@
|
||||
@apply(--paper-font-title);
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
|
||||
<span header-title>Services</span>
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<!-- <link rel="import" href="../../bower_components/polymer/polymer.html"> -->
|
||||
|
||||
<dom-module id="services-list">
|
||||
<template>
|
||||
<style>
|
||||
ul {
|
||||
margin: 0;
|
||||
@ -17,7 +18,6 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<ul>
|
||||
<template is='dom-repeat' items="[[computeDomains(serviceDomains)]]" as="domain">
|
||||
<template is='dom-repeat' items="[[computeServices(serviceDomains, domain)]]" as="service">
|
||||
|
@ -1,6 +1,7 @@
|
||||
<!-- <link rel="import" href="../../bower_components/polymer/polymer.html"> -->
|
||||
|
||||
<dom-module id="entity-list">
|
||||
<template>
|
||||
<style>
|
||||
ul {
|
||||
margin: 0;
|
||||
@ -17,7 +18,6 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<ul>
|
||||
<template is='dom-repeat' items='[[entities]]' as='entity'>
|
||||
<li><a href='#' on-click='entitySelected'>[[entity.entityId]]</a></li>
|
||||
|
@ -10,6 +10,7 @@
|
||||
<link rel="import" href="./entity-list.html">
|
||||
|
||||
<dom-module id="ha-panel-dev-state">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
.content {
|
||||
@ -31,7 +32,6 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
|
||||
<span header-title>States</span>
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
-->
|
||||
|
||||
<dom-module id="ha-panel-dev-template">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
.content {
|
||||
@ -53,7 +54,7 @@
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
|
||||
<span header-title>Template Editor</span>
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
<link rel="import" href="../../src/resources/pikaday-js.html">
|
||||
|
||||
<dom-module id="ha-panel-history">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-flex"></style>
|
||||
<style>
|
||||
.content {
|
||||
@ -28,7 +29,7 @@
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
|
||||
<span header-title>History</span>
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<dom-module id='ha-panel-iframe'>
|
||||
<template>
|
||||
<style>
|
||||
iframe {
|
||||
border: 0;
|
||||
@ -6,7 +7,7 @@
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
|
||||
<span header-title>[[panel.title]]</span>
|
||||
|
||||
|
@ -5,13 +5,14 @@
|
||||
<link rel="import" href="./logbook-entry.html">
|
||||
|
||||
<dom-module id="ha-logbook">
|
||||
<template>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<template is='dom-if' if='[[!entries.length]]'>
|
||||
No logbook entries found.
|
||||
</template>
|
||||
|
@ -14,6 +14,7 @@
|
||||
<link rel="import" href="../../src/resources/pikaday-js.html">
|
||||
|
||||
<dom-module id="ha-panel-logbook">
|
||||
<template>
|
||||
<style>
|
||||
.selected-date-container {
|
||||
padding: 0 16px;
|
||||
@ -23,7 +24,7 @@
|
||||
max-width: 200px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<partial-base narrow="[[narrow]]" show-menu='[[showMenu]]'>
|
||||
<span header-title>Logbook</span>
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
-->
|
||||
|
||||
<dom-module id="logbook-entry">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-flex"></style>
|
||||
<style>
|
||||
:host {
|
||||
@ -39,7 +40,7 @@
|
||||
color: var(--primary-color);
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<div class='horizontal layout'>
|
||||
<display-time date-obj="[[entryObj.when]]"></display-time>
|
||||
<domain-icon domain="[[entryObj.domain]]" class='icon'></domain-icon>
|
||||
|
@ -5,6 +5,7 @@
|
||||
-->
|
||||
|
||||
<dom-module id='ha-entity-marker'>
|
||||
<template>
|
||||
<style is="custom-style" include="iron-positioning"></style>
|
||||
<style>
|
||||
.marker {
|
||||
@ -26,7 +27,7 @@
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<div class='marker'>
|
||||
<template is='dom-if' if='[[icon]]'>
|
||||
<iron-icon icon='[[icon]]'></iron-icon>
|
||||
|
@ -16,20 +16,21 @@
|
||||
.leaflet-top, .leaflet-bottom {
|
||||
z-index: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<dom-module id="ha-panel-map">
|
||||
<template>
|
||||
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
.map {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
margin-right: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<dom-module id="ha-panel-map">
|
||||
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
.map {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class='layout vertical fit'>
|
||||
<paper-toolbar>
|
||||
<paper-icon-button icon='mdi:menu' class$='[[computeMenuButtonClass(narrow, showMenu)]]' on-tap='toggleMenu'></paper-icon-button>
|
||||
|
@ -11,3 +11,19 @@
|
||||
</template>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-badges-card',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,17 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import '../components/entity/ha-state-label-badge';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-badges-card',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
});
|
@ -46,3 +46,75 @@
|
||||
</div>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-camera-card',
|
||||
UPDATE_INTERVAL: 10000, // ms
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
observer: 'updateCameraFeedSrc',
|
||||
},
|
||||
|
||||
cameraFeedSrc: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
imageLoaded: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* The z-depth of the card, from 0-5.
|
||||
*/
|
||||
elevation: {
|
||||
type: Number,
|
||||
value: 1,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
},
|
||||
|
||||
listeners: {
|
||||
tap: 'cardTapped',
|
||||
},
|
||||
|
||||
attached: function () {
|
||||
this.timer = setInterval(
|
||||
function () {
|
||||
this.updateCameraFeedSrc(this.stateObj);
|
||||
}.bind(this),
|
||||
this.UPDATE_INTERVAL);
|
||||
},
|
||||
|
||||
detached: function () {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
|
||||
cardTapped: function () {
|
||||
this.async(function () {
|
||||
this.hass.moreInfoActions.selectEntity(this.stateObj.entityId);
|
||||
}.bind(this), 1);
|
||||
},
|
||||
|
||||
updateCameraFeedSrc: function (stateObj) {
|
||||
const attr = stateObj.attributes;
|
||||
const time = (new Date()).getTime();
|
||||
this.cameraFeedSrc = attr.entity_picture + '&time=' + time;
|
||||
},
|
||||
|
||||
imageLoadSuccess: function () {
|
||||
this.imageLoaded = true;
|
||||
},
|
||||
|
||||
imageLoadFail: function () {
|
||||
this.imageLoaded = false;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,67 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
const UPDATE_INTERVAL = 10000; // ms
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-camera-card',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
observer: 'updateCameraFeedSrc',
|
||||
},
|
||||
|
||||
cameraFeedSrc: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
imageLoaded: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* The z-depth of the card, from 0-5.
|
||||
*/
|
||||
elevation: {
|
||||
type: Number,
|
||||
value: 1,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
},
|
||||
|
||||
listeners: {
|
||||
tap: 'cardTapped',
|
||||
},
|
||||
|
||||
attached() {
|
||||
this.timer = setInterval(() => this.updateCameraFeedSrc(this.stateObj),
|
||||
UPDATE_INTERVAL);
|
||||
},
|
||||
|
||||
detached() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
|
||||
cardTapped() {
|
||||
this.async(() => this.hass.moreInfoActions.selectEntity(this.stateObj.entityId), 1);
|
||||
},
|
||||
|
||||
updateCameraFeedSrc(stateObj) {
|
||||
const attr = stateObj.attributes;
|
||||
const time = (new Date()).getTime();
|
||||
this.cameraFeedSrc = `${attr.entity_picture}&time=${time}`;
|
||||
},
|
||||
|
||||
imageLoadSuccess() {
|
||||
this.imageLoaded = true;
|
||||
},
|
||||
|
||||
imageLoadFail() {
|
||||
this.imageLoaded = false;
|
||||
},
|
||||
});
|
@ -5,3 +5,23 @@
|
||||
<link rel='import' href='./ha-introduction-card.html'>
|
||||
<link rel='import' href='./ha-media_player-card.html'>
|
||||
<link rel='import' href='./ha-persistent_notification-card.html'>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-card-chooser',
|
||||
|
||||
properties: {
|
||||
cardData: {
|
||||
type: Object,
|
||||
observer: 'cardDataChanged',
|
||||
},
|
||||
},
|
||||
|
||||
cardDataChanged: function (newData) {
|
||||
if (!newData) return;
|
||||
|
||||
window.hassUtil.dynamicContentUpdater(this, 'HA-' + newData.cardType.toUpperCase() + '-CARD',
|
||||
newData);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,25 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import dynamicContentUpdater from '../util/dynamic-content-updater';
|
||||
|
||||
import './ha-camera-card';
|
||||
import './ha-entities-card';
|
||||
import './ha-media_player-card';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-card-chooser',
|
||||
|
||||
properties: {
|
||||
cardData: {
|
||||
type: Object,
|
||||
observer: 'cardDataChanged',
|
||||
},
|
||||
},
|
||||
|
||||
cardDataChanged(newData) {
|
||||
if (!newData) return;
|
||||
|
||||
dynamicContentUpdater(this, `HA-${newData.cardType.toUpperCase()}-CARD`,
|
||||
newData);
|
||||
},
|
||||
});
|
@ -56,3 +56,69 @@
|
||||
</ha-card>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-entities-card',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Array,
|
||||
},
|
||||
groupEntity: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
computeTitle: function (states, groupEntity) {
|
||||
return groupEntity ? groupEntity.entityDisplay :
|
||||
states[0].domain.replace(/_/g, ' ');
|
||||
},
|
||||
|
||||
computeTitleClass: function (groupEntity) {
|
||||
var classes = 'header horizontal layout center ';
|
||||
if (groupEntity) {
|
||||
classes += 'header-more-info';
|
||||
}
|
||||
return classes;
|
||||
},
|
||||
|
||||
entityTapped: function (ev) {
|
||||
var entityId;
|
||||
if (ev.target.classList.contains('paper-toggle-button') ||
|
||||
ev.target.classList.contains('paper-icon-button') ||
|
||||
(!ev.model && !this.groupEntity)) {
|
||||
return;
|
||||
}
|
||||
ev.stopPropagation();
|
||||
|
||||
if (ev.model) {
|
||||
entityId = ev.model.item.entityId;
|
||||
} else {
|
||||
entityId = this.groupEntity.entityId;
|
||||
}
|
||||
this.async(function () { this.hass.moreInfoActions.selectEntity(entityId); }.bind(this), 1);
|
||||
},
|
||||
|
||||
showGroupToggle: function (groupEntity, states) {
|
||||
var canToggleCount;
|
||||
|
||||
if (!groupEntity || !states ||
|
||||
(groupEntity.state !== 'on' && groupEntity.state !== 'off')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only show if we can toggle 2+ entities in group
|
||||
canToggleCount = states.reduce(
|
||||
function (sum, state) {
|
||||
return sum + window.hassUtil.canToggle(this.hass, state.entityId);
|
||||
}, 0);
|
||||
|
||||
return canToggleCount > 1;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,60 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
import canToggle from '../util/can-toggle';
|
||||
|
||||
import '../components/entity/ha-entity-toggle';
|
||||
import '../state-summary/state-card-content';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-entities-card',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Array,
|
||||
},
|
||||
groupEntity: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
computeTitle(states, groupEntity) {
|
||||
return groupEntity ? groupEntity.entityDisplay :
|
||||
states[0].domain.replace(/_/g, ' ');
|
||||
},
|
||||
computeTitleClass(groupEntity) {
|
||||
let classes = 'header horizontal layout center ';
|
||||
if (groupEntity) {
|
||||
classes += 'header-more-info';
|
||||
}
|
||||
return classes;
|
||||
},
|
||||
entityTapped(ev) {
|
||||
if (ev.target.classList.contains('paper-toggle-button') ||
|
||||
ev.target.classList.contains('paper-icon-button') ||
|
||||
(!ev.model && !this.groupEntity)) {
|
||||
return;
|
||||
}
|
||||
ev.stopPropagation();
|
||||
|
||||
let entityId;
|
||||
if (ev.model) {
|
||||
entityId = ev.model.item.entityId;
|
||||
} else {
|
||||
entityId = this.groupEntity.entityId;
|
||||
}
|
||||
this.async(() => this.hass.moreInfoActions.selectEntity(entityId), 1);
|
||||
},
|
||||
|
||||
showGroupToggle(groupEntity, states) {
|
||||
if (!groupEntity || !states ||
|
||||
(groupEntity.state !== 'on' && groupEntity.state !== 'off')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only show if we can toggle 2+ entities in group
|
||||
return states.reduce((sum, state) => sum + canToggle(this.hass, state.entityId), 0) > 1;
|
||||
},
|
||||
});
|
@ -178,3 +178,108 @@
|
||||
</div>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-media_player-card',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
playerObj: {
|
||||
type: Object,
|
||||
computed: 'computePlayerObj(stateObj)',
|
||||
observer: 'playerObjChanged',
|
||||
},
|
||||
|
||||
playbackControlIcon: {
|
||||
type: String,
|
||||
computed: 'computePlaybackControlIcon(playerObj)',
|
||||
},
|
||||
|
||||
/**
|
||||
* The z-depth of the card, from 0-5.
|
||||
*/
|
||||
elevation: {
|
||||
type: Number,
|
||||
value: 1,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
},
|
||||
|
||||
playerObjChanged: function (playerObj) {
|
||||
if (!playerObj.isOff && !playerObj.isIdle) {
|
||||
this.$.cover.style.backgroundImage = playerObj.stateObj.attributes.entity_picture ?
|
||||
'url(' + playerObj.stateObj.attributes.entity_picture + ')' : '';
|
||||
}
|
||||
},
|
||||
|
||||
computeBannerClasses: function (playerObj) {
|
||||
var cls = 'banner';
|
||||
|
||||
if (playerObj.isOff || playerObj.isIdle) {
|
||||
cls += ' is-off';
|
||||
}
|
||||
|
||||
if (!playerObj.stateObj.attributes.entity_picture) {
|
||||
cls += ' no-cover';
|
||||
}
|
||||
|
||||
return cls;
|
||||
},
|
||||
|
||||
computeHidePowerOnButton: function (playerObj) {
|
||||
return !playerObj.isOff || !playerObj.supportsTurnOn;
|
||||
},
|
||||
|
||||
computePlayerObj: function (stateObj) {
|
||||
return stateObj.domainModel(this.hass);
|
||||
},
|
||||
|
||||
computePlaybackControlIcon: function (playerObj) {
|
||||
if (playerObj.isPlaying) {
|
||||
return playerObj.supportsPause ? 'mdi:pause' : 'mdi:stop';
|
||||
} else if (playerObj.isPaused || playerObj.isOff) {
|
||||
return 'mdi:play';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
computeShowControls: function (playerObj) {
|
||||
return !playerObj.isOff;
|
||||
},
|
||||
|
||||
handleNext: function (ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.nextTrack();
|
||||
},
|
||||
|
||||
handleOpenMoreInfo: function (ev) {
|
||||
ev.stopPropagation();
|
||||
this.async(function () {
|
||||
this.hass.moreInfoActions.selectEntity(this.stateObj.entityId);
|
||||
}, 1);
|
||||
},
|
||||
|
||||
handlePlaybackControl: function (ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.mediaPlayPause();
|
||||
},
|
||||
|
||||
handlePrevious: function (ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.previousTrack();
|
||||
},
|
||||
|
||||
handleTogglePower: function (ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.togglePower();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,98 +0,0 @@
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Polymer from '../polymer';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-media_player-card',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
playerObj: {
|
||||
type: Object,
|
||||
computed: 'computePlayerObj(stateObj)',
|
||||
observer: 'playerObjChanged',
|
||||
},
|
||||
|
||||
playbackControlIcon: {
|
||||
type: String,
|
||||
computed: 'computePlaybackControlIcon(playerObj)',
|
||||
},
|
||||
|
||||
/**
|
||||
* The z-depth of the card, from 0-5.
|
||||
*/
|
||||
elevation: {
|
||||
type: Number,
|
||||
value: 1,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
},
|
||||
|
||||
playerObjChanged(playerObj) {
|
||||
if (!playerObj.isOff && !playerObj.isIdle) {
|
||||
this.$.cover.style.backgroundImage = playerObj.stateObj.attributes.entity_picture ?
|
||||
`url(${playerObj.stateObj.attributes.entity_picture})` : '';
|
||||
}
|
||||
},
|
||||
|
||||
computeBannerClasses(playerObj) {
|
||||
return classnames({
|
||||
banner: true,
|
||||
'is-off': playerObj.isOff || playerObj.isIdle,
|
||||
'no-cover': !playerObj.stateObj.attributes.entity_picture,
|
||||
});
|
||||
},
|
||||
|
||||
computeHidePowerOnButton(playerObj) {
|
||||
return !playerObj.isOff || !playerObj.supportsTurnOn;
|
||||
},
|
||||
|
||||
computePlayerObj(stateObj) {
|
||||
return stateObj.domainModel(this.hass);
|
||||
},
|
||||
|
||||
computePlaybackControlIcon(playerObj) {
|
||||
if (playerObj.isPlaying) {
|
||||
return playerObj.supportsPause ? 'mdi:pause' : 'mdi:stop';
|
||||
} else if (playerObj.isPaused || playerObj.isOff) {
|
||||
return 'mdi:play';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
computeShowControls(playerObj) {
|
||||
return !playerObj.isOff;
|
||||
},
|
||||
|
||||
handleNext(ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.nextTrack();
|
||||
},
|
||||
|
||||
handleOpenMoreInfo(ev) {
|
||||
ev.stopPropagation();
|
||||
this.async(() => this.hass.moreInfoActions.selectEntity(this.stateObj.entityId), 1);
|
||||
},
|
||||
|
||||
handlePlaybackControl(ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.mediaPlayPause();
|
||||
},
|
||||
|
||||
handlePrevious(ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.previousTrack();
|
||||
},
|
||||
|
||||
handleTogglePower(ev) {
|
||||
ev.stopPropagation();
|
||||
this.playerObj.togglePower();
|
||||
},
|
||||
});
|
@ -25,7 +25,7 @@ Polymer({
|
||||
},
|
||||
|
||||
computeIcon: function (domain, state) {
|
||||
return window.domainIcon(domain, state);
|
||||
return window.hassUtil.domainIcon(domain, state);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -32,3 +32,109 @@
|
||||
</template>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-entity-toggle',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
toggleChecked: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
isOn: {
|
||||
type: Boolean,
|
||||
computed: 'computeIsOn(stateObj)',
|
||||
observer: 'isOnChanged',
|
||||
},
|
||||
},
|
||||
|
||||
listeners: {
|
||||
tap: 'onTap',
|
||||
},
|
||||
|
||||
onTap: function (ev) {
|
||||
ev.stopPropagation();
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
this.forceStateChange();
|
||||
},
|
||||
|
||||
toggleChanged: function (ev) {
|
||||
var newVal = ev.target.checked;
|
||||
|
||||
if (newVal && !this.isOn) {
|
||||
this.callService(true);
|
||||
} else if (!newVal && this.isOn) {
|
||||
this.callService(false);
|
||||
}
|
||||
},
|
||||
|
||||
isOnChanged: function (newVal) {
|
||||
this.toggleChecked = newVal;
|
||||
},
|
||||
|
||||
forceStateChange: function () {
|
||||
if (this.toggleChecked === this.isOn) {
|
||||
this.toggleChecked = !this.toggleChecked;
|
||||
}
|
||||
this.toggleChecked = this.isOn;
|
||||
},
|
||||
|
||||
turnOn: function () {
|
||||
this.callService(true);
|
||||
},
|
||||
|
||||
turnOff: function () {
|
||||
this.callService(false);
|
||||
},
|
||||
|
||||
computeIsOn: function (stateObj) {
|
||||
return stateObj && window.hassUtil.OFF_STATES.indexOf(stateObj.state) === -1;
|
||||
},
|
||||
|
||||
// We call updateToggle after a successful call to re-sync the toggle
|
||||
// with the state. It will be out of sync if our service call did not
|
||||
// result in the entity to be turned on. Since the state is not changing,
|
||||
// the resync is not called automatic.
|
||||
callService: function (turnOn) {
|
||||
var domain;
|
||||
var service;
|
||||
var currentState;
|
||||
|
||||
if (this.stateObj.domain === 'lock') {
|
||||
domain = 'lock';
|
||||
service = turnOn ? 'lock' : 'unlock';
|
||||
} else if (this.stateObj.domain === 'garage_door') {
|
||||
domain = 'garage_door';
|
||||
service = turnOn ? 'open' : 'close';
|
||||
} else {
|
||||
domain = 'homeassistant';
|
||||
service = turnOn ? 'turn_on' : 'turn_off';
|
||||
}
|
||||
|
||||
currentState = this.stateObj;
|
||||
this.hass.serviceActions.callService(domain, service,
|
||||
{ entity_id: this.stateObj.entityId })
|
||||
.then(function () {
|
||||
setTimeout(function () {
|
||||
// If after 2 seconds we have not received a state update
|
||||
// reset the switch to it's original state.
|
||||
if (this.stateObj === currentState) {
|
||||
this.forceStateChange();
|
||||
}
|
||||
}.bind(this), 2000);
|
||||
}.bind(this));
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,104 +0,0 @@
|
||||
import Polymer from '../../polymer';
|
||||
|
||||
import OFF_STATES from '../../util/off-states';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-entity-toggle',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
toggleChecked: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
isOn: {
|
||||
type: Boolean,
|
||||
computed: 'computeIsOn(stateObj)',
|
||||
observer: 'isOnChanged',
|
||||
},
|
||||
},
|
||||
|
||||
listeners: {
|
||||
tap: 'onTap',
|
||||
},
|
||||
|
||||
onTap(ev) {
|
||||
ev.stopPropagation();
|
||||
},
|
||||
|
||||
ready() {
|
||||
this.forceStateChange();
|
||||
},
|
||||
|
||||
toggleChanged(ev) {
|
||||
const newVal = ev.target.checked;
|
||||
|
||||
if (newVal && !this.isOn) {
|
||||
this.callService(true);
|
||||
} else if (!newVal && this.isOn) {
|
||||
this.callService(false);
|
||||
}
|
||||
},
|
||||
|
||||
isOnChanged(newVal) {
|
||||
this.toggleChecked = newVal;
|
||||
},
|
||||
|
||||
forceStateChange() {
|
||||
if (this.toggleChecked === this.isOn) {
|
||||
this.toggleChecked = !this.toggleChecked;
|
||||
}
|
||||
this.toggleChecked = this.isOn;
|
||||
},
|
||||
|
||||
turnOn() {
|
||||
this.callService(true);
|
||||
},
|
||||
|
||||
turnOff() {
|
||||
this.callService(false);
|
||||
},
|
||||
|
||||
computeIsOn(stateObj) {
|
||||
return stateObj && OFF_STATES.indexOf(stateObj.state) === -1;
|
||||
},
|
||||
|
||||
// We call updateToggle after a successful call to re-sync the toggle
|
||||
// with the state. It will be out of sync if our service call did not
|
||||
// result in the entity to be turned on. Since the state is not changing,
|
||||
// the resync is not called automatic.
|
||||
callService(turnOn) {
|
||||
let domain;
|
||||
let service;
|
||||
|
||||
if (this.stateObj.domain === 'lock') {
|
||||
domain = 'lock';
|
||||
service = turnOn ? 'lock' : 'unlock';
|
||||
} else if (this.stateObj.domain === 'garage_door') {
|
||||
domain = 'garage_door';
|
||||
service = turnOn ? 'open' : 'close';
|
||||
} else {
|
||||
domain = 'homeassistant';
|
||||
service = turnOn ? 'turn_on' : 'turn_off';
|
||||
}
|
||||
|
||||
const currentState = this.stateObj;
|
||||
this.hass.serviceActions.callService(domain, service,
|
||||
{ entity_id: this.stateObj.entityId })
|
||||
.then(() => setTimeout(() => {
|
||||
// If after 2 seconds we have not received a state update
|
||||
// reset the switch to it's original state.
|
||||
if (this.stateObj === currentState) {
|
||||
this.forceStateChange();
|
||||
}
|
||||
}, 2000));
|
||||
},
|
||||
});
|
@ -7,3 +7,19 @@
|
||||
<iron-icon icon="[[computeIcon(stateObj)]]"></iron-icon>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-state-icon',
|
||||
|
||||
properties: {
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
computeIcon: function (stateObj) {
|
||||
return window.hassUtil.stateIcon(stateObj);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,17 +0,0 @@
|
||||
import Polymer from '../../polymer';
|
||||
|
||||
import stateIcon from '../../util/state-icon';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-state-icon',
|
||||
|
||||
properties: {
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
computeIcon(stateObj) {
|
||||
return stateIcon(stateObj);
|
||||
},
|
||||
});
|
@ -34,3 +34,114 @@
|
||||
></ha-label-badge>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-state-label-badge',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
state: {
|
||||
type: Object,
|
||||
observer: 'stateChanged',
|
||||
},
|
||||
},
|
||||
|
||||
listeners: {
|
||||
tap: 'badgeTap',
|
||||
},
|
||||
|
||||
badgeTap: function (ev) {
|
||||
ev.stopPropagation();
|
||||
this.async(function () {
|
||||
this.hass.moreInfoActions.selectEntity(this.state.entityId);
|
||||
}, 1);
|
||||
},
|
||||
|
||||
computeClasses: function (state) {
|
||||
switch (state.domain) {
|
||||
case 'binary_sensor':
|
||||
case 'updater':
|
||||
return 'blue';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
computeValue: function (state) {
|
||||
switch (state.domain) {
|
||||
case 'binary_sensor':
|
||||
case 'device_tracker':
|
||||
case 'updater':
|
||||
case 'sun':
|
||||
case 'alarm_control_panel':
|
||||
return null;
|
||||
case 'sensor':
|
||||
default:
|
||||
return state.state === 'unknown' ? '-' : state.state;
|
||||
}
|
||||
},
|
||||
|
||||
computeIcon: function (state) {
|
||||
if (state.state === 'unavailable') {
|
||||
return null;
|
||||
}
|
||||
switch (state.domain) {
|
||||
case 'alarm_control_panel':
|
||||
if (state.state === 'pending') {
|
||||
return 'mdi:clock-fast';
|
||||
} else if (state.state === 'armed_away') {
|
||||
return 'mdi:nature';
|
||||
} else if (state.state === 'armed_home') {
|
||||
return 'mdi:home-variant';
|
||||
}
|
||||
// state == 'disarmed'
|
||||
return window.hassUtil.domainIcon(state.domain, state.state);
|
||||
case 'binary_sensor':
|
||||
case 'device_tracker':
|
||||
case 'updater':
|
||||
return window.hassUtil.stateIcon(state);
|
||||
case 'sun':
|
||||
return state.state === 'above_horizon' ?
|
||||
window.hassUtil.domainIcon(state.domain) : 'mdi:brightness-3';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
computeImage: function (state) {
|
||||
return state.attributes.entity_picture || null;
|
||||
},
|
||||
|
||||
computeLabel: function (state) {
|
||||
if (state.state === 'unavailable') {
|
||||
return 'unavai';
|
||||
}
|
||||
switch (state.domain) {
|
||||
case 'device_tracker':
|
||||
return state.state === 'not_home' ? 'Away' : state.state;
|
||||
case 'alarm_control_panel':
|
||||
if (state.state === 'pending') {
|
||||
return 'pend';
|
||||
} else if (state.state === 'armed_away' || state.state === 'armed_home') {
|
||||
return 'armed';
|
||||
}
|
||||
// state == 'disarmed'
|
||||
return 'disarm';
|
||||
default:
|
||||
return state.attributes.unit_of_measurement || null;
|
||||
}
|
||||
},
|
||||
|
||||
computeDescription: function (state) {
|
||||
return state.entityDisplay;
|
||||
},
|
||||
|
||||
stateChanged: function () {
|
||||
this.updateStyles();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,110 +0,0 @@
|
||||
import Polymer from '../../polymer';
|
||||
import domainIcon from '../../util/domain-icon';
|
||||
import stateIcon from '../../util/state-icon';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-state-label-badge',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
state: {
|
||||
type: Object,
|
||||
observer: 'stateChanged',
|
||||
},
|
||||
},
|
||||
|
||||
listeners: {
|
||||
tap: 'badgeTap',
|
||||
},
|
||||
|
||||
badgeTap(ev) {
|
||||
ev.stopPropagation();
|
||||
this.async(() => this.hass.moreInfoActions.selectEntity(this.state.entityId), 1);
|
||||
},
|
||||
|
||||
computeClasses(state) {
|
||||
switch (state.domain) {
|
||||
case 'binary_sensor':
|
||||
case 'updater':
|
||||
return 'blue';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
computeValue(state) {
|
||||
switch (state.domain) {
|
||||
case 'binary_sensor':
|
||||
case 'device_tracker':
|
||||
case 'updater':
|
||||
case 'sun':
|
||||
case 'alarm_control_panel':
|
||||
return null;
|
||||
case 'sensor':
|
||||
default:
|
||||
return state.state === 'unknown' ? '-' : state.state;
|
||||
}
|
||||
},
|
||||
|
||||
computeIcon(state) {
|
||||
if (state.state === 'unavailable') {
|
||||
return null;
|
||||
}
|
||||
switch (state.domain) {
|
||||
case 'alarm_control_panel':
|
||||
if (state.state === 'pending') {
|
||||
return 'mdi:clock-fast';
|
||||
} else if (state.state === 'armed_away') {
|
||||
return 'mdi:nature';
|
||||
} else if (state.state === 'armed_home') {
|
||||
return 'mdi:home-variant';
|
||||
}
|
||||
// state == 'disarmed'
|
||||
return domainIcon(state.domain, state.state);
|
||||
case 'binary_sensor':
|
||||
case 'device_tracker':
|
||||
case 'updater':
|
||||
return stateIcon(state);
|
||||
case 'sun':
|
||||
return state.state === 'above_horizon' ?
|
||||
domainIcon(state.domain) : 'mdi:brightness-3';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
computeImage(state) {
|
||||
return state.attributes.entity_picture || null;
|
||||
},
|
||||
|
||||
computeLabel(state) {
|
||||
if (state.state === 'unavailable') {
|
||||
return 'unavai';
|
||||
}
|
||||
switch (state.domain) {
|
||||
case 'device_tracker':
|
||||
return state.state === 'not_home' ? 'Away' : state.state;
|
||||
case 'alarm_control_panel':
|
||||
if (state.state === 'pending') {
|
||||
return 'pend';
|
||||
} else if (state.state === 'armed_away' || state.state === 'armed_home') {
|
||||
return 'armed';
|
||||
}
|
||||
// state == 'disarmed'
|
||||
return 'disarm';
|
||||
default:
|
||||
return state.attributes.unit_of_measurement || null;
|
||||
}
|
||||
},
|
||||
|
||||
computeDescription(state) {
|
||||
return state.entityDisplay;
|
||||
},
|
||||
|
||||
stateChanged() {
|
||||
this.updateStyles();
|
||||
},
|
||||
});
|
@ -40,3 +40,42 @@
|
||||
</ha-state-icon>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'state-badge',
|
||||
|
||||
properties: {
|
||||
stateObj: {
|
||||
type: Object,
|
||||
observer: 'updateIconColor',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when an attribute changes that influences the color of the icon.
|
||||
*/
|
||||
updateIconColor: function (newVal) {
|
||||
// hide icon if we have entity picture
|
||||
if (newVal.attributes.entity_picture) {
|
||||
this.style.backgroundImage = 'url(' + newVal.attributes.entity_picture + ')';
|
||||
this.$.icon.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.style.backgroundImage = '';
|
||||
this.$.icon.style.display = 'inline';
|
||||
|
||||
// for domain light, set color of icon to light color if available and it is
|
||||
// not very white (sum rgb colors < 730)
|
||||
if (newVal.domain === 'light' && newVal.state === 'on' &&
|
||||
newVal.attributes.rgb_color &&
|
||||
newVal.attributes.rgb_color.reduce(function (cur, tot) { return cur + tot; }, 0) < 730) {
|
||||
this.$.icon.style.color = 'rgb(' + newVal.attributes.rgb_color.join(',') + ')';
|
||||
} else {
|
||||
this.$.icon.style.color = null;
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
</script>
|
||||
|
@ -1,40 +0,0 @@
|
||||
import Polymer from '../../polymer';
|
||||
|
||||
import './ha-state-icon';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'state-badge',
|
||||
|
||||
properties: {
|
||||
stateObj: {
|
||||
type: Object,
|
||||
observer: 'updateIconColor',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when an attribute changes that influences the color of the icon.
|
||||
*/
|
||||
updateIconColor(newVal) {
|
||||
// hide icon if we have entity picture
|
||||
if (newVal.attributes.entity_picture) {
|
||||
this.style.backgroundImage = `url(${newVal.attributes.entity_picture})`;
|
||||
this.$.icon.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.style.backgroundImage = '';
|
||||
this.$.icon.style.display = 'inline';
|
||||
|
||||
// for domain light, set color of icon to light color if available and it is
|
||||
// not very white (sum rgb colors < 730)
|
||||
if (newVal.domain === 'light' && newVal.state === 'on' &&
|
||||
newVal.attributes.rgb_color &&
|
||||
newVal.attributes.rgb_color.reduce((cur, tot) => cur + tot, 0) < 730) {
|
||||
this.$.icon.style.color = `rgb(${newVal.attributes.rgb_color.join(',')})`;
|
||||
} else {
|
||||
this.$.icon.style.color = null;
|
||||
}
|
||||
},
|
||||
|
||||
});
|
@ -80,3 +80,209 @@
|
||||
</template>
|
||||
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
// mapping domain to size of the card.
|
||||
var DOMAINS_WITH_CARD = {
|
||||
camera: 4,
|
||||
media_player: 3,
|
||||
persistent_notification: 0,
|
||||
};
|
||||
|
||||
var PRIORITY = {
|
||||
configurator: -20,
|
||||
persistent_notification: -15,
|
||||
group: -10,
|
||||
a: -1,
|
||||
updater: 0,
|
||||
sun: 1,
|
||||
device_tracker: 2,
|
||||
alarm_control_panel: 3,
|
||||
sensor: 5,
|
||||
binary_sensor: 6,
|
||||
};
|
||||
|
||||
function getPriority(domain) {
|
||||
return (domain in PRIORITY) ? PRIORITY[domain] : 30;
|
||||
}
|
||||
|
||||
function entitySortBy(entity) {
|
||||
return entity.domain === 'group' ? entity.attributes.order :
|
||||
entity.entityDisplay.toLowerCase();
|
||||
}
|
||||
|
||||
Polymer({
|
||||
is: 'ha-cards',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
showIntroduction: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
columns: {
|
||||
type: Number,
|
||||
value: 2,
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
cards: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
observers: [
|
||||
'updateCards(columns, states, showIntroduction)',
|
||||
],
|
||||
|
||||
updateCards: function (columns, states, showIntroduction) {
|
||||
this.debounce(
|
||||
'updateCards',
|
||||
function () { this.cards = this.computeCards(columns, states, showIntroduction); },
|
||||
0
|
||||
);
|
||||
},
|
||||
|
||||
computeCards: function (columns, states, showIntroduction) {
|
||||
var hass = this.hass;
|
||||
var byDomain = states.groupBy(function (entity) { return entity.domain; });
|
||||
var hasGroup = {};
|
||||
|
||||
var cards = {
|
||||
demo: false,
|
||||
badges: [],
|
||||
columns: [],
|
||||
};
|
||||
var entityCount = [];
|
||||
var expandGroup;
|
||||
var i;
|
||||
for (i = 0; i < columns; i++) {
|
||||
cards.columns.push([]);
|
||||
entityCount.push(0);
|
||||
}
|
||||
|
||||
function filterGrouped(entities) {
|
||||
return entities.filter(function (entity) { return !(entity.entityId in hasGroup); });
|
||||
}
|
||||
|
||||
// Find column with < 5 entities, else column with lowest count
|
||||
function getIndex(size) {
|
||||
var minIndex = 0;
|
||||
for (i = minIndex; i < entityCount.length; i++) {
|
||||
if (entityCount[i] < 5) {
|
||||
minIndex = i;
|
||||
break;
|
||||
}
|
||||
if (entityCount[i] < entityCount[minIndex]) {
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
entityCount[minIndex] += size;
|
||||
|
||||
return minIndex;
|
||||
}
|
||||
if (showIntroduction) {
|
||||
cards.columns[getIndex(5)].push({
|
||||
hass: hass,
|
||||
cardType: 'introduction',
|
||||
showHideInstruction: states.size > 0 && !hass.demo,
|
||||
});
|
||||
}
|
||||
|
||||
function addEntitiesCard(name, entities, groupEntity) {
|
||||
var owncard;
|
||||
var other;
|
||||
var size;
|
||||
var curIndex;
|
||||
if (entities.length === 0) return;
|
||||
|
||||
owncard = [];
|
||||
other = [];
|
||||
|
||||
size = 0;
|
||||
|
||||
entities.forEach(function (entity) {
|
||||
if (entity.domain in DOMAINS_WITH_CARD) {
|
||||
owncard.push(entity);
|
||||
size += DOMAINS_WITH_CARD[entity.domain];
|
||||
} else {
|
||||
other.push(entity);
|
||||
size++;
|
||||
}
|
||||
});
|
||||
|
||||
// Add 1 to the size if we're rendering entities card
|
||||
size += other.length > 1;
|
||||
|
||||
curIndex = getIndex(size);
|
||||
|
||||
if (other.length > 0) {
|
||||
cards.columns[curIndex].push({
|
||||
hass: hass,
|
||||
cardType: 'entities',
|
||||
states: other,
|
||||
groupEntity: groupEntity || false,
|
||||
});
|
||||
}
|
||||
|
||||
owncard.forEach(function (entity) {
|
||||
cards.columns[curIndex].push({
|
||||
hass: hass,
|
||||
cardType: entity.domain,
|
||||
stateObj: entity,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
expandGroup = this.hass.util.expandGroup;
|
||||
|
||||
byDomain.keySeq().sortBy(function (domain) { return getPriority(domain); })
|
||||
.forEach(function (domain) {
|
||||
var priority;
|
||||
|
||||
if (domain === 'a') {
|
||||
cards.demo = true;
|
||||
return;
|
||||
}
|
||||
|
||||
priority = getPriority(domain);
|
||||
|
||||
if (priority >= 0 && priority < 10) {
|
||||
cards.badges.push.apply(
|
||||
cards.badges, filterGrouped(byDomain.get(domain)).sortBy(
|
||||
entitySortBy).toArray());
|
||||
} else if (domain === 'group') {
|
||||
byDomain.get(domain).sortBy(entitySortBy)
|
||||
.forEach(function (groupState) {
|
||||
var entities = expandGroup(groupState, states);
|
||||
entities.forEach(function (entity) { hasGroup[entity.entityId] = true; });
|
||||
addEntitiesCard(groupState.entityId, entities.toArray(), groupState);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addEntitiesCard(
|
||||
domain, filterGrouped(byDomain.get(domain)).sortBy(entitySortBy).toArray());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Remove empty columns
|
||||
cards.columns = cards.columns.filter(function (val) {
|
||||
return val.length > 0;
|
||||
});
|
||||
|
||||
return cards;
|
||||
},
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
|
@ -1,195 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import '../cards/ha-badges-card';
|
||||
import '../cards/ha-card-chooser';
|
||||
|
||||
// mapping domain to size of the card.
|
||||
const DOMAINS_WITH_CARD = {
|
||||
camera: 4,
|
||||
media_player: 3,
|
||||
persistent_notification: 0,
|
||||
};
|
||||
|
||||
const PRIORITY = {
|
||||
configurator: -20,
|
||||
persistent_notification: -15,
|
||||
group: -10,
|
||||
a: -1,
|
||||
updater: 0,
|
||||
sun: 1,
|
||||
device_tracker: 2,
|
||||
alarm_control_panel: 3,
|
||||
sensor: 5,
|
||||
binary_sensor: 6,
|
||||
};
|
||||
|
||||
function getPriority(domain) {
|
||||
return (domain in PRIORITY) ? PRIORITY[domain] : 30;
|
||||
}
|
||||
|
||||
function entitySortBy(entity) {
|
||||
return entity.domain === 'group' ? entity.attributes.order :
|
||||
entity.entityDisplay.toLowerCase();
|
||||
}
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-cards',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
showIntroduction: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
columns: {
|
||||
type: Number,
|
||||
value: 2,
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
cards: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
observers: [
|
||||
'updateCards(columns, states, showIntroduction)',
|
||||
],
|
||||
|
||||
updateCards(columns, states, showIntroduction) {
|
||||
this.debounce(
|
||||
'updateCards',
|
||||
() => { this.cards = this.computeCards(columns, states, showIntroduction); },
|
||||
0
|
||||
);
|
||||
},
|
||||
|
||||
computeCards(columns, states, showIntroduction) {
|
||||
const hass = this.hass;
|
||||
const byDomain = states.groupBy(entity => entity.domain);
|
||||
const hasGroup = {};
|
||||
|
||||
const cards = {
|
||||
demo: false,
|
||||
badges: [],
|
||||
columns: [],
|
||||
};
|
||||
const entityCount = [];
|
||||
for (let idx = 0; idx < columns; idx++) {
|
||||
cards.columns.push([]);
|
||||
entityCount.push(0);
|
||||
}
|
||||
|
||||
function filterGrouped(entities) {
|
||||
return entities.filter(entity => !(entity.entityId in hasGroup));
|
||||
}
|
||||
|
||||
// Find column with < 5 entities, else column with lowest count
|
||||
function getIndex(size) {
|
||||
let minIndex = 0;
|
||||
for (let i = minIndex; i < entityCount.length; i++) {
|
||||
if (entityCount[i] < 5) {
|
||||
minIndex = i;
|
||||
break;
|
||||
}
|
||||
if (entityCount[i] < entityCount[minIndex]) {
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
entityCount[minIndex] += size;
|
||||
|
||||
return minIndex;
|
||||
}
|
||||
if (showIntroduction) {
|
||||
cards.columns[getIndex(5)].push({
|
||||
hass,
|
||||
cardType: 'introduction',
|
||||
showHideInstruction: states.size > 0 && !hass.demo,
|
||||
});
|
||||
}
|
||||
|
||||
function addEntitiesCard(name, entities, groupEntity = false) {
|
||||
if (entities.length === 0) return;
|
||||
|
||||
const owncard = [];
|
||||
const other = [];
|
||||
|
||||
let size = 0;
|
||||
|
||||
entities.forEach(entity => {
|
||||
if (entity.domain in DOMAINS_WITH_CARD) {
|
||||
owncard.push(entity);
|
||||
size += DOMAINS_WITH_CARD[entity.domain];
|
||||
} else {
|
||||
other.push(entity);
|
||||
size++;
|
||||
}
|
||||
});
|
||||
|
||||
// Add 1 to the size if we're rendering entities card
|
||||
size += other.length > 1;
|
||||
|
||||
const curIndex = getIndex(size);
|
||||
|
||||
if (other.length > 0) {
|
||||
cards.columns[curIndex].push({
|
||||
hass,
|
||||
cardType: 'entities',
|
||||
states: other,
|
||||
groupEntity,
|
||||
});
|
||||
}
|
||||
|
||||
owncard.forEach(entity => {
|
||||
cards.columns[curIndex].push({
|
||||
hass,
|
||||
cardType: entity.domain,
|
||||
stateObj: entity,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const expandGroup = this.hass.util.expandGroup;
|
||||
|
||||
byDomain.keySeq().sortBy(domain => getPriority(domain))
|
||||
.forEach(domain => {
|
||||
if (domain === 'a') {
|
||||
cards.demo = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const priority = getPriority(domain);
|
||||
|
||||
if (priority >= 0 && priority < 10) {
|
||||
cards.badges.push.apply(
|
||||
cards.badges, filterGrouped(byDomain.get(domain)).sortBy(
|
||||
entitySortBy).toArray());
|
||||
} else if (domain === 'group') {
|
||||
byDomain.get(domain).sortBy(entitySortBy)
|
||||
.forEach(groupState => {
|
||||
const entities = expandGroup(groupState, states);
|
||||
entities.forEach(entity => { hasGroup[entity.entityId] = true; });
|
||||
addEntitiesCard(groupState.entityId, entities.toArray(), groupState);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addEntitiesCard(
|
||||
domain, filterGrouped(byDomain.get(domain)).sortBy(entitySortBy).toArray());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Remove empty columns
|
||||
cards.columns = cards.columns.filter(val => val.length > 0);
|
||||
|
||||
return cards;
|
||||
},
|
||||
});
|
@ -113,13 +113,17 @@ Polymer({
|
||||
|
||||
drawGradient: function () {
|
||||
var style;
|
||||
var width;
|
||||
var height;
|
||||
var colorGradient;
|
||||
var bwGradient;
|
||||
if (!this.width || !this.height) {
|
||||
style = getComputedStyle(this);
|
||||
}
|
||||
var width = this.width || parseInt(style.width, 10);
|
||||
var height = this.height || parseInt(style.height, 10);
|
||||
width = this.width || parseInt(style.width, 10);
|
||||
height = this.height || parseInt(style.height, 10);
|
||||
|
||||
var colorGradient = this.context.createLinearGradient(0, 0, width, 0);
|
||||
colorGradient = this.context.createLinearGradient(0, 0, width, 0);
|
||||
colorGradient.addColorStop(0, 'rgb(255,0,0)');
|
||||
colorGradient.addColorStop(0.16, 'rgb(255,0,255)');
|
||||
colorGradient.addColorStop(0.32, 'rgb(0,0,255)');
|
||||
@ -130,7 +134,7 @@ Polymer({
|
||||
this.context.fillStyle = colorGradient;
|
||||
this.context.fillRect(0, 0, width, height);
|
||||
|
||||
var bwGradient = this.context.createLinearGradient(0, 0, 0, height);
|
||||
bwGradient = this.context.createLinearGradient(0, 0, 0, height);
|
||||
bwGradient.addColorStop(0, 'rgba(255,255,255,1)');
|
||||
bwGradient.addColorStop(0.5, 'rgba(255,255,255,0)');
|
||||
bwGradient.addColorStop(0.5, 'rgba(0,0,0,0)');
|
||||
|
@ -52,3 +52,20 @@
|
||||
</div>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'state-info',
|
||||
|
||||
properties: {
|
||||
detailed: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,18 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import './entity/state-badge';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'state-info',
|
||||
|
||||
properties: {
|
||||
detailed: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
});
|
@ -62,3 +62,75 @@
|
||||
</template>
|
||||
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ha-voice-command-dialog',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
dialogOpen: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: 'dialogOpenChanged',
|
||||
},
|
||||
|
||||
finalTranscript: {
|
||||
type: String,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.voiceGetters.finalTranscript;
|
||||
},
|
||||
},
|
||||
|
||||
interimTranscript: {
|
||||
type: String,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.voiceGetters.extraInterimTranscript;
|
||||
},
|
||||
},
|
||||
|
||||
isTransmitting: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.voiceGetters.isTransmitting;
|
||||
},
|
||||
},
|
||||
|
||||
isListening: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.voiceGetters.isListening;
|
||||
},
|
||||
},
|
||||
|
||||
showListenInterface: {
|
||||
type: Boolean,
|
||||
computed: 'computeShowListenInterface(isListening, isTransmitting)',
|
||||
observer: 'showListenInterfaceChanged',
|
||||
},
|
||||
},
|
||||
|
||||
computeShowListenInterface: function (isListening, isTransmitting) {
|
||||
return isListening || isTransmitting;
|
||||
},
|
||||
|
||||
dialogOpenChanged: function (newVal) {
|
||||
if (!newVal && this.isListening) {
|
||||
this.hass.voiceActions.stop();
|
||||
}
|
||||
},
|
||||
|
||||
showListenInterfaceChanged: function (newVal) {
|
||||
if (!newVal && this.dialogOpen) {
|
||||
this.dialogOpen = false;
|
||||
} else if (newVal) {
|
||||
this.dialogOpen = true;
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,63 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'ha-voice-command-dialog',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
dialogOpen: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: 'dialogOpenChanged',
|
||||
},
|
||||
|
||||
finalTranscript: {
|
||||
type: String,
|
||||
bindNuclear: hass => hass.voiceGetters.finalTranscript,
|
||||
},
|
||||
|
||||
interimTranscript: {
|
||||
type: String,
|
||||
bindNuclear: hass => hass.voiceGetters.extraInterimTranscript,
|
||||
},
|
||||
|
||||
isTransmitting: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.voiceGetters.isTransmitting,
|
||||
},
|
||||
|
||||
isListening: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.voiceGetters.isListening,
|
||||
},
|
||||
|
||||
showListenInterface: {
|
||||
type: Boolean,
|
||||
computed: 'computeShowListenInterface(isListening, isTransmitting)',
|
||||
observer: 'showListenInterfaceChanged',
|
||||
},
|
||||
},
|
||||
|
||||
computeShowListenInterface(isListening, isTransmitting) {
|
||||
return isListening || isTransmitting;
|
||||
},
|
||||
|
||||
dialogOpenChanged(newVal) {
|
||||
if (!newVal && this.isListening) {
|
||||
this.hass.voiceActions.stop();
|
||||
}
|
||||
},
|
||||
|
||||
showListenInterfaceChanged(newVal) {
|
||||
if (!newVal && this.dialogOpen) {
|
||||
this.dialogOpen = false;
|
||||
} else if (newVal) {
|
||||
this.dialogOpen = true;
|
||||
}
|
||||
},
|
||||
});
|
@ -58,3 +58,130 @@
|
||||
</paper-dialog>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'more-info-dialog',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.moreInfoGetters.currentEntity;
|
||||
},
|
||||
observer: 'stateObjChanged',
|
||||
},
|
||||
|
||||
stateHistory: {
|
||||
type: Object,
|
||||
bindNuclear: function (hass) {
|
||||
return [
|
||||
hass.moreInfoGetters.currentEntityHistory,
|
||||
function (history) { return history ? [history] : false; },
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
isLoadingHistoryData: {
|
||||
type: Boolean,
|
||||
computed: 'computeIsLoadingHistoryData(delayedDialogOpen, isLoadingEntityHistoryData)',
|
||||
},
|
||||
|
||||
isLoadingEntityHistoryData: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.entityHistoryGetters.isLoadingEntityHistory;
|
||||
},
|
||||
},
|
||||
|
||||
hasHistoryComponent: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.configGetters.isComponentLoaded('history');
|
||||
},
|
||||
observer: 'fetchHistoryData',
|
||||
},
|
||||
|
||||
shouldFetchHistory: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.moreInfoGetters.isCurrentEntityHistoryStale;
|
||||
},
|
||||
observer: 'fetchHistoryData',
|
||||
},
|
||||
|
||||
showHistoryComponent: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
computed: 'computeShowHistoryComponent(hasHistoryComponent, stateObj)',
|
||||
},
|
||||
|
||||
dialogOpen: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: 'dialogOpenChanged',
|
||||
},
|
||||
|
||||
delayedDialogOpen: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
this.$.scrollable.dialogElement = this.$.dialog;
|
||||
},
|
||||
|
||||
/**
|
||||
* We depend on a delayed dialogOpen value to tell the chart component
|
||||
* that the data is there. Otherwise the chart component will render
|
||||
* before the dialog is attached to the screen and is unable to determine
|
||||
* graph size resulting in scroll bars.
|
||||
*/
|
||||
computeIsLoadingHistoryData: function (delayedDialogOpen, isLoadingEntityHistoryData) {
|
||||
return !delayedDialogOpen || isLoadingEntityHistoryData;
|
||||
},
|
||||
|
||||
computeShowHistoryComponent: function (hasHistoryComponent, stateObj) {
|
||||
return this.hasHistoryComponent && stateObj &&
|
||||
window.hassUtil.DOMAINS_WITH_NO_HISTORY.indexOf(stateObj.domain) === -1;
|
||||
},
|
||||
|
||||
fetchHistoryData: function () {
|
||||
if (this.stateObj && this.hasHistoryComponent &&
|
||||
this.shouldFetchHistory) {
|
||||
this.hass.entityHistoryActions.fetchRecent(this.stateObj.entityId);
|
||||
}
|
||||
},
|
||||
|
||||
stateObjChanged: function (newVal) {
|
||||
if (!newVal) {
|
||||
this.dialogOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.async(function () {
|
||||
// Firing action while other action is happening confuses nuclear
|
||||
this.fetchHistoryData();
|
||||
// allow dialog to render content before showing it so it is
|
||||
// positioned correctly.
|
||||
this.dialogOpen = true;
|
||||
}.bind(this), 10);
|
||||
},
|
||||
|
||||
dialogOpenChanged: function (newVal) {
|
||||
if (newVal) {
|
||||
this.async(function () { this.delayedDialogOpen = true; }.bind(this), 10);
|
||||
} else if (!newVal && this.stateObj) {
|
||||
this.async(function () { this.hass.moreInfoActions.deselectEntity(); }.bind(this), 10);
|
||||
this.delayedDialogOpen = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,122 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import '../state-summary/state-card-content';
|
||||
import '../components/state-history-charts';
|
||||
import '../more-infos/more-info-content';
|
||||
|
||||
const DOMAINS_WITH_NO_HISTORY = ['camera', 'configurator', 'scene'];
|
||||
|
||||
export default new Polymer({
|
||||
is: 'more-info-dialog',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
bindNuclear: hass => hass.moreInfoGetters.currentEntity,
|
||||
observer: 'stateObjChanged',
|
||||
},
|
||||
|
||||
stateHistory: {
|
||||
type: Object,
|
||||
bindNuclear: hass => [
|
||||
hass.moreInfoGetters.currentEntityHistory,
|
||||
(history) => (history ? [history] : false),
|
||||
],
|
||||
},
|
||||
|
||||
isLoadingHistoryData: {
|
||||
type: Boolean,
|
||||
computed: 'computeIsLoadingHistoryData(delayedDialogOpen, isLoadingEntityHistoryData)',
|
||||
},
|
||||
|
||||
isLoadingEntityHistoryData: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.entityHistoryGetters.isLoadingEntityHistory,
|
||||
},
|
||||
|
||||
hasHistoryComponent: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.configGetters.isComponentLoaded('history'),
|
||||
observer: 'fetchHistoryData',
|
||||
},
|
||||
|
||||
shouldFetchHistory: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.moreInfoGetters.isCurrentEntityHistoryStale,
|
||||
observer: 'fetchHistoryData',
|
||||
},
|
||||
|
||||
showHistoryComponent: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
computed: 'computeShowHistoryComponent(hasHistoryComponent, stateObj)',
|
||||
},
|
||||
|
||||
dialogOpen: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: 'dialogOpenChanged',
|
||||
},
|
||||
|
||||
delayedDialogOpen: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
|
||||
ready() {
|
||||
this.$.scrollable.dialogElement = this.$.dialog;
|
||||
},
|
||||
|
||||
/**
|
||||
* We depend on a delayed dialogOpen value to tell the chart component
|
||||
* that the data is there. Otherwise the chart component will render
|
||||
* before the dialog is attached to the screen and is unable to determine
|
||||
* graph size resulting in scroll bars.
|
||||
*/
|
||||
computeIsLoadingHistoryData(delayedDialogOpen, isLoadingEntityHistoryData) {
|
||||
return !delayedDialogOpen || isLoadingEntityHistoryData;
|
||||
},
|
||||
|
||||
computeShowHistoryComponent(hasHistoryComponent, stateObj) {
|
||||
return this.hasHistoryComponent && stateObj &&
|
||||
DOMAINS_WITH_NO_HISTORY.indexOf(stateObj.domain) === -1;
|
||||
},
|
||||
|
||||
fetchHistoryData() {
|
||||
if (this.stateObj && this.hasHistoryComponent &&
|
||||
this.shouldFetchHistory) {
|
||||
this.hass.entityHistoryActions.fetchRecent(this.stateObj.entityId);
|
||||
}
|
||||
},
|
||||
|
||||
stateObjChanged(newVal) {
|
||||
if (!newVal) {
|
||||
this.dialogOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.async(() => {
|
||||
// Firing action while other action is happening confuses nuclear
|
||||
this.fetchHistoryData();
|
||||
// allow dialog to render content before showing it so it is
|
||||
// positioned correctly.
|
||||
this.dialogOpen = true;
|
||||
}, 10);
|
||||
},
|
||||
|
||||
dialogOpenChanged(newVal) {
|
||||
if (newVal) {
|
||||
this.async(() => { this.delayedDialogOpen = true; }, 10);
|
||||
} else if (!newVal && this.stateObj) {
|
||||
this.async(() => this.hass.moreInfoActions.deselectEntity(), 10);
|
||||
this.delayedDialogOpen = false;
|
||||
}
|
||||
},
|
||||
});
|
@ -1,4 +1,3 @@
|
||||
import '../layouts/partial-cards';
|
||||
import '../managers/notification-manager';
|
||||
import '../dialogs/more-info-dialog';
|
||||
import '../dialogs/ha-voice-command-dialog';
|
||||
// components that still require update
|
||||
import '../components/state-history-charts';
|
||||
import '../more-infos/more-info-content';
|
||||
|
@ -7,6 +7,7 @@
|
||||
<link rel='import' href='../bower_components/iron-flex-layout/iron-flex-layout-classes.html'>
|
||||
<link rel="import" href="../bower_components/iron-iconset-svg/iron-iconset-svg.html">
|
||||
|
||||
<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'>
|
||||
|
@ -105,3 +105,203 @@
|
||||
</template>
|
||||
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'partial-cards',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
narrow: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
isFetching: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.syncGetters.isFetching;
|
||||
},
|
||||
},
|
||||
|
||||
isStreaming: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.streamGetters.isStreamingEvents;
|
||||
},
|
||||
},
|
||||
|
||||
canListen: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return [
|
||||
hass.voiceGetters.isVoiceSupported,
|
||||
hass.configGetters.isComponentLoaded('conversation'),
|
||||
function (isVoiceSupported, componentLoaded) {
|
||||
return isVoiceSupported && componentLoaded;
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
introductionLoaded: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.configGetters.isComponentLoaded('introduction');
|
||||
},
|
||||
},
|
||||
|
||||
locationName: {
|
||||
type: String,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.configGetters.locationName;
|
||||
},
|
||||
},
|
||||
|
||||
showMenu: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: 'windowChange',
|
||||
},
|
||||
|
||||
currentView: {
|
||||
type: String,
|
||||
bindNuclear: function (hass) {
|
||||
return [
|
||||
hass.viewGetters.currentView,
|
||||
function (view) { return view || ''; },
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
hasViews: {
|
||||
type: Boolean,
|
||||
bindNuclear: function (hass) {
|
||||
return [
|
||||
hass.viewGetters.views,
|
||||
function (views) { return views.size > 0; },
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Object,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.viewGetters.currentViewEntities;
|
||||
},
|
||||
},
|
||||
|
||||
columns: {
|
||||
type: Number,
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
|
||||
created: function () {
|
||||
var sizes = [];
|
||||
var col;
|
||||
this.windowChange = this.windowChange.bind(this);
|
||||
for (col = 0; col < 5; col++) {
|
||||
sizes.push(300 + (col * 300));
|
||||
}
|
||||
this.mqls = sizes.map(function (width) {
|
||||
var mql = window.matchMedia('(min-width: ' + width + 'px)');
|
||||
mql.addListener(this.windowChange);
|
||||
return mql;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
detached: function () {
|
||||
this.mqls.forEach(function (mql) {
|
||||
mql.removeListener(this.windowChange);
|
||||
});
|
||||
},
|
||||
|
||||
windowChange: function () {
|
||||
var matchColumns = this.mqls.reduce(function (cols, mql) { return cols + mql.matches; }, 0);
|
||||
// Do -1 column if the menu is docked and open
|
||||
this.columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
|
||||
},
|
||||
|
||||
scrollToTop: function () {
|
||||
this.$.panel.scrollToTop(true);
|
||||
},
|
||||
|
||||
handleRefresh: function () {
|
||||
this.hass.syncActions.fetchAll();
|
||||
},
|
||||
|
||||
handleListenClick: function () {
|
||||
this.hass.voiceActions.listen();
|
||||
},
|
||||
|
||||
contentScroll: function () {
|
||||
if (this.debouncedContentScroll) return;
|
||||
|
||||
this.debouncedContentScroll = this.async(function () {
|
||||
this.checkRaised();
|
||||
this.debouncedContentScroll = false;
|
||||
}.bind(this), 100);
|
||||
},
|
||||
|
||||
checkRaised: function () {
|
||||
this.toggleClass(
|
||||
'raised',
|
||||
this.$.panel.scroller.scrollTop > (this.hasViews ? 56 : 0),
|
||||
this.$.panel);
|
||||
},
|
||||
|
||||
headerScrollAdjust: function (ev) {
|
||||
if (!this.hasViews) return;
|
||||
this.translate3d('0', '-' + ev.detail.y + 'px', '0', this.$.menu);
|
||||
// this.toggleClass('condensed', ev.detail.y === 56, this.$.panel);
|
||||
},
|
||||
|
||||
computeHeaderHeight: function (hasViews, narrow) {
|
||||
if (hasViews) {
|
||||
return 104;
|
||||
} else if (narrow) {
|
||||
return 56;
|
||||
}
|
||||
return 64;
|
||||
},
|
||||
|
||||
computeCondensedHeaderHeight: function (hasViews, narrow) {
|
||||
if (hasViews) {
|
||||
return 48;
|
||||
} else if (narrow) {
|
||||
return 56;
|
||||
}
|
||||
return 64;
|
||||
},
|
||||
|
||||
computeMenuButtonClass: function (narrow, showMenu) {
|
||||
return !narrow && showMenu ? 'menu-icon invisible' : 'menu-icon';
|
||||
},
|
||||
|
||||
computeRefreshButtonClass: function (isFetching) {
|
||||
return isFetching ? 'ha-spin' : '';
|
||||
},
|
||||
|
||||
computeTitle: function (hasViews, locationName) {
|
||||
return hasViews ? 'Home Assistant' : locationName;
|
||||
},
|
||||
|
||||
computeShowIntroduction: function (currentView, introductionLoaded, states) {
|
||||
return currentView === '' && (introductionLoaded || states.size === 0);
|
||||
},
|
||||
|
||||
computeHasViews: function (views) {
|
||||
return views.length > 0;
|
||||
},
|
||||
|
||||
toggleMenu: function () {
|
||||
this.fire('open-menu');
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,180 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import '../components/ha-cards';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'partial-cards',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
narrow: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
isFetching: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.syncGetters.isFetching,
|
||||
},
|
||||
|
||||
isStreaming: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.streamGetters.isStreamingEvents,
|
||||
},
|
||||
|
||||
canListen: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => [
|
||||
hass.voiceGetters.isVoiceSupported,
|
||||
hass.configGetters.isComponentLoaded('conversation'),
|
||||
(isVoiceSupported, componentLoaded) => isVoiceSupported && componentLoaded,
|
||||
],
|
||||
},
|
||||
|
||||
introductionLoaded: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => hass.configGetters.isComponentLoaded('introduction'),
|
||||
},
|
||||
|
||||
locationName: {
|
||||
type: String,
|
||||
bindNuclear: hass => hass.configGetters.locationName,
|
||||
},
|
||||
|
||||
showMenu: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: 'windowChange',
|
||||
},
|
||||
|
||||
currentView: {
|
||||
type: String,
|
||||
bindNuclear: hass => [
|
||||
hass.viewGetters.currentView,
|
||||
view => view || '',
|
||||
],
|
||||
},
|
||||
|
||||
hasViews: {
|
||||
type: Boolean,
|
||||
bindNuclear: hass => [
|
||||
hass.viewGetters.views,
|
||||
views => views.size > 0,
|
||||
],
|
||||
},
|
||||
|
||||
states: {
|
||||
type: Object,
|
||||
bindNuclear: hass => hass.viewGetters.currentViewEntities,
|
||||
},
|
||||
|
||||
columns: {
|
||||
type: Number,
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.windowChange = this.windowChange.bind(this);
|
||||
const sizes = [];
|
||||
for (let col = 0; col < 5; col++) {
|
||||
sizes.push(300 + (col * 300));
|
||||
}
|
||||
this.mqls = sizes.map(width => {
|
||||
const mql = window.matchMedia(`(min-width: ${width}px)`);
|
||||
mql.addListener(this.windowChange);
|
||||
return mql;
|
||||
});
|
||||
},
|
||||
|
||||
detached() {
|
||||
this.mqls.forEach(mql => mql.removeListener(this.windowChange));
|
||||
},
|
||||
|
||||
windowChange() {
|
||||
const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
|
||||
// Do -1 column if the menu is docked and open
|
||||
this.columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
|
||||
},
|
||||
|
||||
scrollToTop() {
|
||||
this.$.panel.scrollToTop(true);
|
||||
},
|
||||
|
||||
handleRefresh() {
|
||||
this.hass.syncActions.fetchAll();
|
||||
},
|
||||
|
||||
handleListenClick() {
|
||||
this.hass.voiceActions.listen();
|
||||
},
|
||||
|
||||
contentScroll() {
|
||||
if (this.debouncedContentScroll) return;
|
||||
|
||||
this.debouncedContentScroll = this.async(() => {
|
||||
this.checkRaised();
|
||||
this.debouncedContentScroll = false;
|
||||
}, 100);
|
||||
},
|
||||
|
||||
checkRaised() {
|
||||
this.toggleClass(
|
||||
'raised',
|
||||
this.$.panel.scroller.scrollTop > (this.hasViews ? 56 : 0),
|
||||
this.$.panel);
|
||||
},
|
||||
|
||||
headerScrollAdjust(ev) {
|
||||
if (!this.hasViews) return;
|
||||
this.translate3d('0', `-${ev.detail.y}px`, '0', this.$.menu);
|
||||
// this.toggleClass('condensed', ev.detail.y === 56, this.$.panel);
|
||||
},
|
||||
|
||||
computeHeaderHeight(hasViews, narrow) {
|
||||
if (hasViews) {
|
||||
return 104;
|
||||
} else if (narrow) {
|
||||
return 56;
|
||||
}
|
||||
return 64;
|
||||
},
|
||||
|
||||
computeCondensedHeaderHeight(hasViews, narrow) {
|
||||
if (hasViews) {
|
||||
return 48;
|
||||
} else if (narrow) {
|
||||
return 56;
|
||||
}
|
||||
return 64;
|
||||
},
|
||||
|
||||
computeMenuButtonClass(narrow, showMenu) {
|
||||
return !narrow && showMenu ? 'menu-icon invisible' : 'menu-icon';
|
||||
},
|
||||
|
||||
computeRefreshButtonClass(isFetching) {
|
||||
return isFetching ? 'ha-spin' : '';
|
||||
},
|
||||
|
||||
computeTitle(hasViews, locationName) {
|
||||
return hasViews ? 'Home Assistant' : locationName;
|
||||
},
|
||||
|
||||
computeShowIntroduction(currentView, introductionLoaded, states) {
|
||||
return currentView === '' && (introductionLoaded || states.size === 0);
|
||||
},
|
||||
|
||||
computeHasViews(views) {
|
||||
return views.length > 0;
|
||||
},
|
||||
|
||||
toggleMenu() {
|
||||
this.fire('open-menu');
|
||||
},
|
||||
});
|
@ -12,3 +12,37 @@
|
||||
<paper-toast id="toast" text='{{text}}' no-cancel-on-outside-click='[[neg]]'></paper-toast>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'notification-manager',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
// Otherwise we cannot close a modal when a notification is being shown.
|
||||
neg: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
text: {
|
||||
type: String,
|
||||
bindNuclear: function (hass) {
|
||||
return hass.notificationGetters.lastNotificationMessage;
|
||||
},
|
||||
observer: 'showNotification',
|
||||
},
|
||||
},
|
||||
|
||||
showNotification: function (newText) {
|
||||
if (newText) {
|
||||
this.$.toast.show();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,31 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'notification-manager',
|
||||
|
||||
behaviors: [window.hassBehavior],
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
// Otherwise we cannot close a modal when a notification is being shown.
|
||||
neg: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
text: {
|
||||
type: String,
|
||||
bindNuclear: hass => hass.notificationGetters.lastNotificationMessage,
|
||||
observer: 'showNotification',
|
||||
},
|
||||
},
|
||||
|
||||
showNotification(newText) {
|
||||
if (newText) {
|
||||
this.$.toast.show();
|
||||
}
|
||||
},
|
||||
});
|
@ -1,8 +1,5 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import dynamicContentUpdater from '../util/dynamic-content-updater';
|
||||
import stateMoreInfoType from '../util/state-more-info-type';
|
||||
|
||||
import './more-info-group';
|
||||
import './more-info-sun';
|
||||
import './more-info-configurator';
|
||||
@ -32,8 +29,8 @@ export default new Polymer({
|
||||
stateObjChanged(stateObj) {
|
||||
if (!stateObj) return;
|
||||
|
||||
dynamicContentUpdater(
|
||||
this, `MORE-INFO-${stateMoreInfoType(stateObj).toUpperCase()}`,
|
||||
window.hassUtil.dynamicContentUpdater(
|
||||
this, `MORE-INFO-${window.hassUtil.stateMoreInfoType(stateObj).toUpperCase()}`,
|
||||
{ hass: this.hass, stateObj });
|
||||
},
|
||||
});
|
||||
|
@ -1,8 +1,4 @@
|
||||
import Polymer from '../polymer';
|
||||
import dynamicContentUpdater from '../util/dynamic-content-updater';
|
||||
import stateMoreInfoType from '../util/state-more-info-type';
|
||||
|
||||
import '../state-summary/state-card-content';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'more-info-group',
|
||||
@ -64,9 +60,9 @@ export default new Polymer({
|
||||
el.removeChild(el.lastChild);
|
||||
}
|
||||
} else {
|
||||
dynamicContentUpdater(
|
||||
window.hassUtil.dynamicContentUpdater(
|
||||
this.$.groupedControlDetails,
|
||||
`MORE-INFO-${stateMoreInfoType(groupDomainStateObj).toUpperCase()}`,
|
||||
`MORE-INFO-${window.hassUtil.stateMoreInfoType(groupDomainStateObj).toUpperCase()}`,
|
||||
{ stateObj: groupDomainStateObj });
|
||||
}
|
||||
},
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Polymer from '../polymer';
|
||||
import attributeClassNames from '../util/attribute-class-names';
|
||||
|
||||
const ATTRIBUTE_CLASSES = [
|
||||
'away_mode',
|
||||
@ -80,7 +79,7 @@ export default new Polymer({
|
||||
},
|
||||
|
||||
computeClassNames(stateObj) {
|
||||
return `more-info-hvac ${attributeClassNames(stateObj, ATTRIBUTE_CLASSES)}`;
|
||||
return `more-info-hvac ${window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES)}`;
|
||||
},
|
||||
|
||||
targetTemperatureSliderChanged(ev) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Polymer from '../polymer';
|
||||
import attributeClassNames from '../util/attribute-class-names';
|
||||
|
||||
const ATTRIBUTE_CLASSES = ['brightness', 'rgb_color', 'color_temp'];
|
||||
|
||||
@ -44,7 +43,7 @@ export default new Polymer({
|
||||
},
|
||||
|
||||
computeClassNames(stateObj) {
|
||||
return attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||
return window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||
},
|
||||
|
||||
brightnessSliderChanged(ev) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Polymer from '../polymer';
|
||||
import attributeClassNames from '../util/attribute-class-names';
|
||||
|
||||
const ATTRIBUTE_CLASSES = ['volume_level'];
|
||||
|
||||
@ -128,7 +127,7 @@ export default new Polymer({
|
||||
},
|
||||
|
||||
computeClassNames(stateObj) {
|
||||
return attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||
return window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||
},
|
||||
|
||||
computeIsOff(stateObj) {
|
||||
|
@ -1,7 +1,5 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import formatTime from '../util/format-time';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'more-info-sun',
|
||||
|
||||
@ -42,6 +40,6 @@ export default new Polymer({
|
||||
},
|
||||
|
||||
itemValue(type) {
|
||||
return formatTime(this.itemDate(type));
|
||||
return window.hassUtil.formatTime(this.itemDate(type));
|
||||
},
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Polymer from '../polymer';
|
||||
import attributeClassNames from '../util/attribute-class-names';
|
||||
|
||||
const ATTRIBUTE_CLASSES = ['away_mode'];
|
||||
|
||||
@ -42,7 +41,7 @@ export default new Polymer({
|
||||
},
|
||||
|
||||
computeClassNames(stateObj) {
|
||||
return attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||
return window.hassUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
|
||||
},
|
||||
|
||||
targetTemperatureSliderChanged(ev) {
|
||||
|
@ -12,3 +12,42 @@
|
||||
<link rel="import" href="state-card-thermostat.html">
|
||||
<link rel="import" href="state-card-toggle.html">
|
||||
<link rel="import" href="state-card-weblink.html">
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'state-card-content',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
inDialog: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
observers: [
|
||||
'inputChanged(hass, inDialog, stateObj)',
|
||||
],
|
||||
|
||||
inputChanged: function (hass, inDialog, stateObj) {
|
||||
if (!stateObj) return;
|
||||
|
||||
window.hassUtil.dynamicContentUpdater(
|
||||
this,
|
||||
('STATE-CARD-' +
|
||||
window.hassUtil.stateCardType(this.hass, stateObj).toUpperCase()),
|
||||
{
|
||||
hass: hass,
|
||||
stateObj: stateObj,
|
||||
inDialog: inDialog,
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,37 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
import stateCardType from '../util/state-card-type';
|
||||
import dynamicContentUpdater from '../util/dynamic-content-updater';
|
||||
|
||||
import '../components/state-info';
|
||||
|
||||
export default new Polymer({
|
||||
is: 'state-card-content',
|
||||
|
||||
properties: {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
inDialog: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
observers: [
|
||||
'inputChanged(hass, inDialog, stateObj)',
|
||||
],
|
||||
|
||||
inputChanged(hass, inDialog, stateObj) {
|
||||
if (!stateObj) return;
|
||||
|
||||
dynamicContentUpdater(
|
||||
this, `STATE-CARD-${stateCardType(this.hass, stateObj).toUpperCase()}`,
|
||||
{ hass, stateObj, inDialog });
|
||||
},
|
||||
});
|
@ -1,6 +0,0 @@
|
||||
export default function attributeClassNames(stateObj, attributes) {
|
||||
if (!stateObj) return '';
|
||||
return attributes.map(
|
||||
(attribute) => (attribute in stateObj.attributes ? `has-${attribute}` : '')
|
||||
).join(' ');
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
// Return boolean if entity can be toggled.
|
||||
export default function canToggle(hass, entityId) {
|
||||
return hass.reactor.evaluate(hass.serviceGetters.canToggleEntity(entityId));
|
||||
}
|
@ -1 +0,0 @@
|
||||
export default 'mdi:bookmark';
|
@ -1,103 +0,0 @@
|
||||
import defaultIcon from './default-icon';
|
||||
|
||||
window.domainIcon = function (domain, state) {
|
||||
switch (domain) {
|
||||
case 'alarm_control_panel':
|
||||
return state && state === 'disarmed' ? 'mdi:bell-outline' : 'mdi:bell';
|
||||
|
||||
case 'automation':
|
||||
return 'mdi:playlist-play';
|
||||
|
||||
case 'binary_sensor':
|
||||
return state && state === 'off' ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
|
||||
case 'camera':
|
||||
return 'mdi:video';
|
||||
|
||||
case 'configurator':
|
||||
return 'mdi:settings';
|
||||
|
||||
case 'conversation':
|
||||
return 'mdi:text-to-speech';
|
||||
|
||||
case 'device_tracker':
|
||||
return 'mdi:account';
|
||||
|
||||
case 'garage_door':
|
||||
return 'mdi:glassdoor';
|
||||
|
||||
case 'group':
|
||||
return 'mdi:google-circles-communities';
|
||||
|
||||
case 'homeassistant':
|
||||
return 'mdi:home';
|
||||
|
||||
case 'hvac':
|
||||
return 'mdi:air-conditioner';
|
||||
|
||||
case 'input_boolean':
|
||||
return 'mdi:drawing';
|
||||
|
||||
case 'input_select':
|
||||
return 'mdi:format-list-bulleted';
|
||||
|
||||
case 'input_slider':
|
||||
return 'mdi:ray-vertex';
|
||||
|
||||
case 'light':
|
||||
return 'mdi:lightbulb';
|
||||
|
||||
case 'lock':
|
||||
return state && state === 'unlocked' ? 'mdi:lock-open' : 'mdi:lock';
|
||||
|
||||
case 'media_player':
|
||||
return state && state !== 'off' && state !== 'idle' ?
|
||||
'mdi:cast-connected' : 'mdi:cast';
|
||||
|
||||
case 'notify':
|
||||
return 'mdi:comment-alert';
|
||||
|
||||
case 'proximity':
|
||||
return 'mdi:apple-safari';
|
||||
|
||||
case 'rollershutter':
|
||||
return state && state === 'open' ? 'mdi:window-open' : 'mdi:window-closed';
|
||||
|
||||
case 'scene':
|
||||
return 'mdi:google-pages';
|
||||
|
||||
case 'script':
|
||||
return 'mdi:file-document';
|
||||
|
||||
case 'sensor':
|
||||
return 'mdi:eye';
|
||||
|
||||
case 'simple_alarm':
|
||||
return 'mdi:bell';
|
||||
|
||||
case 'sun':
|
||||
return 'mdi:white-balance-sunny';
|
||||
|
||||
case 'switch':
|
||||
return 'mdi:flash';
|
||||
|
||||
case 'thermostat':
|
||||
return 'mdi:nest-thermostat';
|
||||
|
||||
case 'updater':
|
||||
return 'mdi:cloud-upload';
|
||||
|
||||
case 'weblink':
|
||||
return 'mdi:open-in-new';
|
||||
|
||||
default:
|
||||
if (__DEV__) {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(`Unable to find icon for domain ${domain} (${state})`);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
return defaultIcon;
|
||||
}
|
||||
};
|
||||
|
||||
export default window.domainIcon;
|
@ -1,22 +0,0 @@
|
||||
import Polymer from '../polymer';
|
||||
|
||||
export default function dynamicContentUpdater(root, newElementTag, attributes) {
|
||||
const rootEl = Polymer.dom(root);
|
||||
|
||||
let customEl;
|
||||
|
||||
if (rootEl.lastChild && rootEl.lastChild.tagName === newElementTag) {
|
||||
customEl = rootEl.lastChild;
|
||||
} else {
|
||||
if (rootEl.lastChild) {
|
||||
rootEl.removeChild(rootEl.lastChild);
|
||||
}
|
||||
customEl = document.createElement(newElementTag);
|
||||
}
|
||||
|
||||
Object.keys(attributes).forEach(key => { customEl[key] = attributes[key]; });
|
||||
|
||||
if (customEl.parentNode === null) {
|
||||
rootEl.appendChild(customEl);
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export default function formatDateTime(dateObj) {
|
||||
return window.moment(dateObj).format('lll');
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export default function formatDate(dateObj) {
|
||||
return window.moment(dateObj).format('ll');
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export default function formatTime(dateObj) {
|
||||
return window.moment(dateObj).format('LT');
|
||||
}
|
@ -9,11 +9,13 @@ window.hassBehavior = {
|
||||
|
||||
this.nuclearUnwatchFns = Object.keys(this.properties).reduce(
|
||||
function bindGetters(unwatchFns, key) {
|
||||
var getter;
|
||||
|
||||
if (!('bindNuclear' in this.properties[key])) {
|
||||
return unwatchFns;
|
||||
}
|
||||
|
||||
var getter = this.properties[key].bindNuclear(hass);
|
||||
getter = this.properties[key].bindNuclear(hass);
|
||||
|
||||
if (!getter) {
|
||||
throw new Error('Undefined getter specified for key ' + key);
|
||||
|
255
src/util/hass-util.html
Normal file
255
src/util/hass-util.html
Normal file
@ -0,0 +1,255 @@
|
||||
<!--
|
||||
collection of utility functions.
|
||||
-->
|
||||
<script>
|
||||
window.hassUtil = window.hassUtil || {};
|
||||
|
||||
window.hassUtil.DEFAULT_ICON = 'mdi:bookmark';
|
||||
|
||||
window.hassUtil.OFF_STATES = ['off', 'closed', 'unlocked'];
|
||||
|
||||
window.hassUtil.DOMAINS_WITH_CARD = [
|
||||
'configurator',
|
||||
'hvac',
|
||||
'input_select',
|
||||
'input_slider',
|
||||
'media_player',
|
||||
'rollershutter',
|
||||
'scene',
|
||||
'script',
|
||||
'thermostat',
|
||||
'weblink',
|
||||
];
|
||||
|
||||
window.hassUtil.DOMAINS_WITH_MORE_INFO = [
|
||||
'light', 'group', 'sun', 'configurator', 'thermostat', 'script',
|
||||
'media_player', 'camera', 'updater', 'alarm_control_panel', 'lock',
|
||||
'hvac',
|
||||
];
|
||||
|
||||
window.hassUtil.DOMAINS_WITH_NO_HISTORY = ['camera', 'configurator', 'scene'];
|
||||
|
||||
window.hassUtil.HIDE_MORE_INFO = [
|
||||
'input_select', 'scene', 'script', 'input_slider',
|
||||
];
|
||||
|
||||
window.hassUtil.attributeClassNames = function (stateObj, attributes) {
|
||||
if (!stateObj) return '';
|
||||
return attributes.map(
|
||||
function (attribute) {
|
||||
return attribute in stateObj.attributes ? 'has-' + attribute : '';
|
||||
}
|
||||
).join(' ');
|
||||
};
|
||||
|
||||
window.hassUtil.canToggle = function (hass, entityId) {
|
||||
return hass.reactor.evaluate(hass.serviceGetters.canToggleEntity(entityId));
|
||||
};
|
||||
|
||||
window.hassUtil.dynamicContentUpdater = function (root, newElementTag, attributes) {
|
||||
var rootEl = Polymer.dom(root);
|
||||
var customEl;
|
||||
|
||||
if (rootEl.lastChild && rootEl.lastChild.tagName === newElementTag) {
|
||||
customEl = rootEl.lastChild;
|
||||
} else {
|
||||
if (rootEl.lastChild) {
|
||||
rootEl.removeChild(rootEl.lastChild);
|
||||
}
|
||||
customEl = document.createElement(newElementTag);
|
||||
}
|
||||
|
||||
Object.keys(attributes).forEach(function (key) {
|
||||
customEl[key] = attributes[key];
|
||||
});
|
||||
|
||||
if (customEl.parentNode === null) {
|
||||
rootEl.appendChild(customEl);
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.formatDateTime = function (dateObj) {
|
||||
return window.moment(dateObj).format('lll');
|
||||
};
|
||||
|
||||
window.hassUtil.formatDate = function (dateObj) {
|
||||
return window.moment(dateObj).format('ll');
|
||||
};
|
||||
|
||||
window.hassUtil.formatTime = function (dateObj) {
|
||||
return window.moment(dateObj).format('LT');
|
||||
};
|
||||
|
||||
window.hassUtil.stateCardType = function (hass, state) {
|
||||
if (state.state === 'unavailable') {
|
||||
return 'display';
|
||||
} else if (window.hassUtil.DOMAINS_WITH_CARD.indexOf(state.domain) !== -1) {
|
||||
return state.domain;
|
||||
} else if (window.hassUtil.canToggle(hass, state.entityId)) {
|
||||
return 'toggle';
|
||||
}
|
||||
return 'display';
|
||||
};
|
||||
|
||||
window.hassUtil.stateMoreInfoType = function (state) {
|
||||
if (window.hassUtil.DOMAINS_WITH_MORE_INFO.indexOf(state.domain) !== -1) {
|
||||
return state.domain;
|
||||
}
|
||||
if (window.hassUtil.HIDE_MORE_INFO.indexOf(state.domain) !== -1) {
|
||||
return 'hidden';
|
||||
}
|
||||
return 'default';
|
||||
};
|
||||
|
||||
window.hassUtil.domainIcon = function (domain, state) {
|
||||
switch (domain) {
|
||||
case 'alarm_control_panel':
|
||||
return state && state === 'disarmed' ? 'mdi:bell-outline' : 'mdi:bell';
|
||||
|
||||
case 'automation':
|
||||
return 'mdi:playlist-play';
|
||||
|
||||
case 'binary_sensor':
|
||||
return state && state === 'off' ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
|
||||
case 'camera':
|
||||
return 'mdi:video';
|
||||
|
||||
case 'configurator':
|
||||
return 'mdi:settings';
|
||||
|
||||
case 'conversation':
|
||||
return 'mdi:text-to-speech';
|
||||
|
||||
case 'device_tracker':
|
||||
return 'mdi:account';
|
||||
|
||||
case 'garage_door':
|
||||
return 'mdi:glassdoor';
|
||||
|
||||
case 'group':
|
||||
return 'mdi:google-circles-communities';
|
||||
|
||||
case 'homeassistant':
|
||||
return 'mdi:home';
|
||||
|
||||
case 'hvac':
|
||||
return 'mdi:air-conditioner';
|
||||
|
||||
case 'input_boolean':
|
||||
return 'mdi:drawing';
|
||||
|
||||
case 'input_select':
|
||||
return 'mdi:format-list-bulleted';
|
||||
|
||||
case 'input_slider':
|
||||
return 'mdi:ray-vertex';
|
||||
|
||||
case 'light':
|
||||
return 'mdi:lightbulb';
|
||||
|
||||
case 'lock':
|
||||
return state && state === 'unlocked' ? 'mdi:lock-open' : 'mdi:lock';
|
||||
|
||||
case 'media_player':
|
||||
return state && state !== 'off' && state !== 'idle' ?
|
||||
'mdi:cast-connected' : 'mdi:cast';
|
||||
|
||||
case 'notify':
|
||||
return 'mdi:comment-alert';
|
||||
|
||||
case 'proximity':
|
||||
return 'mdi:apple-safari';
|
||||
|
||||
case 'rollershutter':
|
||||
return state && state === 'open' ? 'mdi:window-open' : 'mdi:window-closed';
|
||||
|
||||
case 'scene':
|
||||
return 'mdi:google-pages';
|
||||
|
||||
case 'script':
|
||||
return 'mdi:file-document';
|
||||
|
||||
case 'sensor':
|
||||
return 'mdi:eye';
|
||||
|
||||
case 'simple_alarm':
|
||||
return 'mdi:bell';
|
||||
|
||||
case 'sun':
|
||||
return 'mdi:white-balance-sunny';
|
||||
|
||||
case 'switch':
|
||||
return 'mdi:flash';
|
||||
|
||||
case 'thermostat':
|
||||
return 'mdi:nest-thermostat';
|
||||
|
||||
case 'updater':
|
||||
return 'mdi:cloud-upload';
|
||||
|
||||
case 'weblink':
|
||||
return 'mdi:open-in-new';
|
||||
|
||||
default:
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
'Unable to find icon for domain ' + domain + ' (' + state + ')');
|
||||
/* eslint-enable no-console */
|
||||
return window.hassUtil.DEFAULT_ICON;
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.binarySensorIcon = function (state) {
|
||||
var activated = state.state && state.state === 'off';
|
||||
switch (state.attributes.sensor_class) {
|
||||
case 'opening':
|
||||
return activated ? 'mdi:crop-square' : 'mdi:exit-to-app';
|
||||
case 'moisture':
|
||||
return activated ? 'mdi:water-off' : 'mdi:water';
|
||||
case 'light':
|
||||
return activated ? 'mdi:brightness-5' : 'mdi:brightness-7';
|
||||
case 'sound':
|
||||
return activated ? 'mdi:music-note-off' : 'mdi:music-note';
|
||||
case 'vibration':
|
||||
return activated ? 'mdi:crop-portrait' : 'mdi:vibrate';
|
||||
case 'connectivity':
|
||||
return activated ? 'mdi:server-network-off' : 'mdi:server-network';
|
||||
case 'safety':
|
||||
case 'gas':
|
||||
case 'smoke':
|
||||
case 'power':
|
||||
return activated ? 'mdi:verified' : 'mdi:alert';
|
||||
case 'motion':
|
||||
return activated ? 'mdi:walk' : 'mdi:run';
|
||||
case 'digital':
|
||||
default:
|
||||
return activated ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
}
|
||||
};
|
||||
|
||||
window.hassUtil.stateIcon = function (state) {
|
||||
var unit;
|
||||
|
||||
if (!state) {
|
||||
return window.hassUtil.DEFAULT_ICON;
|
||||
} else if (state.attributes.icon) {
|
||||
return state.attributes.icon;
|
||||
}
|
||||
|
||||
unit = state.attributes.unit_of_measurement;
|
||||
|
||||
if (unit && state.domain === 'sensor') {
|
||||
if (unit === '°C' || unit === '°F') {
|
||||
return 'mdi:thermometer';
|
||||
} else if (unit === 'Mice') {
|
||||
return 'mdi:mouse-variant';
|
||||
}
|
||||
} else if (state.domain === 'binary_sensor') {
|
||||
return window.hassUtil.binarySensorIcon(state);
|
||||
}
|
||||
|
||||
return window.hassUtil.domainIcon(state.domain, state.state);
|
||||
};
|
||||
|
||||
</script>
|
@ -1 +0,0 @@
|
||||
export default ['off', 'closed', 'unlocked'];
|
@ -1,25 +0,0 @@
|
||||
import canToggle from './can-toggle';
|
||||
|
||||
const DOMAINS_WITH_CARD = [
|
||||
'configurator',
|
||||
'hvac',
|
||||
'input_select',
|
||||
'input_slider',
|
||||
'media_player',
|
||||
'rollershutter',
|
||||
'scene',
|
||||
'script',
|
||||
'thermostat',
|
||||
'weblink',
|
||||
];
|
||||
|
||||
export default function stateCardType(hass, state) {
|
||||
if (state.state === 'unavailable') {
|
||||
return 'display';
|
||||
} else if (DOMAINS_WITH_CARD.indexOf(state.domain) !== -1) {
|
||||
return state.domain;
|
||||
} else if (canToggle(hass, state.entityId)) {
|
||||
return 'toggle';
|
||||
}
|
||||
return 'display';
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
import defaultIcon from './default-icon';
|
||||
import domainIcon from './domain-icon.js';
|
||||
|
||||
function binarySensorIcon(state) {
|
||||
const activated = state.state && state.state === 'off';
|
||||
switch (state.attributes.sensor_class) {
|
||||
case 'opening':
|
||||
return activated ? 'mdi:crop-square' : 'mdi:exit-to-app';
|
||||
case 'moisture':
|
||||
return activated ? 'mdi:water-off' : 'mdi:water';
|
||||
case 'light':
|
||||
return activated ? 'mdi:brightness-5' : 'mdi:brightness-7';
|
||||
case 'sound':
|
||||
return activated ? 'mdi:music-note-off' : 'mdi:music-note';
|
||||
case 'vibration':
|
||||
return activated ? 'mdi:crop-portrait' : 'mdi:vibrate';
|
||||
case 'connectivity':
|
||||
return activated ? 'mdi:server-network-off' : 'mdi:server-network';
|
||||
case 'safety':
|
||||
case 'gas':
|
||||
case 'smoke':
|
||||
case 'power':
|
||||
return activated ? 'mdi:verified' : 'mdi:alert';
|
||||
case 'motion':
|
||||
return activated ? 'mdi:walk' : 'mdi:run';
|
||||
case 'digital':
|
||||
default:
|
||||
return activated ? 'mdi:radiobox-blank' : 'mdi:checkbox-marked-circle';
|
||||
}
|
||||
}
|
||||
|
||||
export default function stateIcon(state) {
|
||||
if (!state) {
|
||||
return defaultIcon;
|
||||
} else if (state.attributes.icon) {
|
||||
return state.attributes.icon;
|
||||
}
|
||||
|
||||
const unit = state.attributes.unit_of_measurement;
|
||||
|
||||
if (unit && state.domain === 'sensor') {
|
||||
if (unit === '°C' || unit === '°F') {
|
||||
return 'mdi:thermometer';
|
||||
} else if (unit === 'Mice') {
|
||||
return 'mdi:mouse-variant';
|
||||
}
|
||||
} else if (state.domain === 'binary_sensor') {
|
||||
return binarySensorIcon(state);
|
||||
}
|
||||
|
||||
return domainIcon(state.domain, state.state);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
const DOMAINS_WITH_MORE_INFO = [
|
||||
'light', 'group', 'sun', 'configurator', 'thermostat', 'script',
|
||||
'media_player', 'camera', 'updater', 'alarm_control_panel', 'lock',
|
||||
'hvac',
|
||||
];
|
||||
|
||||
const HIDE_MORE_INFO = [
|
||||
'input_select', 'scene', 'script', 'input_slider',
|
||||
];
|
||||
|
||||
export default function stateMoreInfoType(state) {
|
||||
if (DOMAINS_WITH_MORE_INFO.indexOf(state.domain) !== -1) {
|
||||
return state.domain;
|
||||
}
|
||||
if (HIDE_MORE_INFO.indexOf(state.domain) !== -1) {
|
||||
return 'hidden';
|
||||
}
|
||||
return 'default';
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user