mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Add customization config panel (#413)
This commit is contained in:
parent
592591d879
commit
0508a6bf2e
@ -11,7 +11,7 @@
|
|||||||
<ha-config-section is-wide='[[isWide]]'>
|
<ha-config-section is-wide='[[isWide]]'>
|
||||||
<span slot='header'>Set a theme</span>
|
<span slot='header'>Set a theme</span>
|
||||||
<span slot='introduction'>
|
<span slot='introduction'>
|
||||||
Choose 'default' to use whatever theme the backend chooses or pick a theme for this device.
|
Choose 'Backend-selected' to use whatever theme the backend chooses or pick a theme for this device.
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<paper-card>
|
<paper-card>
|
||||||
|
89
panels/config/customize/ha-config-customize.html
Normal file
89
panels/config/customize/ha-config-customize.html
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
|
||||||
|
<link rel="import" href="../../../bower_components/app-layout/app-header-layout/app-header-layout.html">
|
||||||
|
<link rel="import" href="../../../bower_components/app-layout/app-header/app-header.html">
|
||||||
|
<link rel="import" href="../../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||||||
|
<link rel="import" href="../../../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../../../src/util/hass-util.html">
|
||||||
|
<link rel="import" href="../../../src/resources/ha-style.html">
|
||||||
|
|
||||||
|
<link rel="import" href="./ha-form-customize.html">
|
||||||
|
<link rel="import" href="../ha-config-section.html">
|
||||||
|
<link rel="import" href="../ha-entity-config.html">
|
||||||
|
|
||||||
|
<dom-module id="ha-config-customize">
|
||||||
|
<template>
|
||||||
|
<style include="ha-style">
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<app-header-layout has-scrolling-region>
|
||||||
|
<app-header slot="header" fixed>
|
||||||
|
<app-toolbar>
|
||||||
|
<paper-icon-button
|
||||||
|
icon='mdi:arrow-left'
|
||||||
|
on-tap='_backTapped'
|
||||||
|
></paper-icon-button>
|
||||||
|
<div main-title>Customization</div>
|
||||||
|
</app-toolbar>
|
||||||
|
</app-header>
|
||||||
|
|
||||||
|
<div class$='[[computeClasses(isWide)]]'>
|
||||||
|
<ha-config-section is-wide='[[isWide]]'>
|
||||||
|
<span slot='header'>Customization</span>
|
||||||
|
<span slot='introduction'>
|
||||||
|
Tweak per-entity attributes.<br>
|
||||||
|
Added/edited customizations will take effect immidiately. Removed customizations will take effect when the entity is updated.
|
||||||
|
</span>
|
||||||
|
<ha-entity-config
|
||||||
|
hass='[[hass]]'
|
||||||
|
label='Entity'
|
||||||
|
entities='[[entities]]'
|
||||||
|
config='[[entityConfig]]'>
|
||||||
|
</ha-entity-config>
|
||||||
|
</ha-config-section>
|
||||||
|
</div>
|
||||||
|
</app-header-layout>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaConfigCustomize extends Polymer.Element {
|
||||||
|
static get is() { return 'ha-config-customize'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
isWide: Boolean,
|
||||||
|
|
||||||
|
entities: {
|
||||||
|
type: Array,
|
||||||
|
computed: 'computeEntities(hass)',
|
||||||
|
},
|
||||||
|
|
||||||
|
entityConfig: {
|
||||||
|
type: Object,
|
||||||
|
value: {
|
||||||
|
component: 'ha-form-customize',
|
||||||
|
computeSelectCaption: stateObj =>
|
||||||
|
window.hassUtil.computeStateName(stateObj) + ' (' + window.hassUtil.computeDomain(stateObj) + ')'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
computeClasses(isWide) {
|
||||||
|
return isWide ? 'content' : 'content narrow';
|
||||||
|
}
|
||||||
|
|
||||||
|
_backTapped() {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
computeEntities(hass) {
|
||||||
|
return Object.keys(hass.states)
|
||||||
|
.map(key => hass.states[key])
|
||||||
|
.sort(window.hassUtil.sortByName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaConfigCustomize.is, HaConfigCustomize);
|
||||||
|
</script>
|
82
panels/config/customize/ha-customize-attribute.html
Normal file
82
panels/config/customize/ha-customize-attribute.html
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
|
||||||
|
<link rel='import' href="../../../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
|
<link rel="import" href="../ha-form-style.html">
|
||||||
|
<script src="../../../src/util/hass-attributes-util.js"></script>
|
||||||
|
<link rel="import" href="./types/ha-customize-array.html">
|
||||||
|
<link rel="import" href="./types/ha-customize-boolean.html">
|
||||||
|
<link rel="import" href="./types/ha-customize-icon.html">
|
||||||
|
<link rel="import" href="./types/ha-customize-key-value.html">
|
||||||
|
<link rel="import" href="./types/ha-customize-string.html">
|
||||||
|
|
||||||
|
<dom-module id="ha-customize-attribute">
|
||||||
|
<template>
|
||||||
|
<style include="ha-form-style">
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
position: absolute;
|
||||||
|
margin-top: -20px;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id='wrapper' class='form-group'></div>
|
||||||
|
<paper-icon-button class="button" icon="[[getIcon(item.secondary)]]" on-tap="tapButton"></paper-icon-button>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaCustomizeAttribute extends Polymer.Element {
|
||||||
|
|
||||||
|
static get is() { return 'ha-customize-attribute'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
notify: true,
|
||||||
|
observer: 'itemObserver',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tapButton() {
|
||||||
|
if (this.item.secondary) {
|
||||||
|
this.item = Object.assign({}, this.item, { secondary: false });
|
||||||
|
} else {
|
||||||
|
this.item = Object.assign({}, this.item, { closed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getIcon(secondary) {
|
||||||
|
return secondary ? 'mdi:pencil' : 'mdi:close';
|
||||||
|
}
|
||||||
|
|
||||||
|
itemObserver(item) {
|
||||||
|
const wrapper = this.$.wrapper;
|
||||||
|
const tag = window.hassAttributeUtil.TYPE_TO_TAG[item.type].toUpperCase();
|
||||||
|
let child;
|
||||||
|
if (wrapper.lastChild && wrapper.lastChild.tagName === tag) {
|
||||||
|
child = wrapper.lastChild;
|
||||||
|
} else {
|
||||||
|
if (wrapper.lastChild) {
|
||||||
|
wrapper.removeChild(wrapper.lastChild);
|
||||||
|
}
|
||||||
|
this.$.child = child = document.createElement(tag);
|
||||||
|
child.className = 'form-control';
|
||||||
|
child.addEventListener('item-changed', () => {
|
||||||
|
this.item = Object.assign({}, child.item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
child.setProperties({ item: this.item });
|
||||||
|
if (child.parentNode === null) {
|
||||||
|
wrapper.appendChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaCustomizeAttribute.is, HaCustomizeAttribute);
|
||||||
|
</script>
|
36
panels/config/customize/ha-form-customize-attributes.html
Normal file
36
panels/config/customize/ha-form-customize-attributes.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
|
||||||
|
|
||||||
|
<link rel="import" href="./ha-customize-attribute.html">
|
||||||
|
|
||||||
|
<dom-module id="ha-form-customize-attributes">
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template is='dom-repeat' items='{{attributes}}'>
|
||||||
|
<ha-customize-attribute
|
||||||
|
item="{{item}}"
|
||||||
|
hidden$="[[item.closed]]">
|
||||||
|
</ha-customize-attribute>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaFormCustomizeAttributes extends Polymer.Element {
|
||||||
|
|
||||||
|
static get is() { return 'ha-form-customize-attributes'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
attributes: {
|
||||||
|
type: Array,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaFormCustomizeAttributes.is, HaFormCustomizeAttributes);
|
||||||
|
</script>
|
277
panels/config/customize/ha-form-customize.html
Normal file
277
panels/config/customize/ha-form-customize.html
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
|
||||||
|
<link rel='import' href='../../../bower_components/paper-listbox/paper-listbox.html'>
|
||||||
|
<link rel='import' href='../../../bower_components/paper-item/paper-item.html'>
|
||||||
|
|
||||||
|
<link rel="import" href="../../../src/util/hass-util.html">
|
||||||
|
<script src="../../../src/util/hass-attributes-util.js"></script>
|
||||||
|
|
||||||
|
<link rel="import" href="./ha-form-customize-attributes.html">
|
||||||
|
|
||||||
|
<dom-module id="ha-form-customize">
|
||||||
|
<template>
|
||||||
|
<style include="iron-flex ha-style ha-form-style">
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributes-text {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template is='dom-if' if='[[computeShowWarning(localConfig, globalConfig)]]'>
|
||||||
|
<div class="warning">
|
||||||
|
It seems that your configuration.yaml doesn't properly include customize.yaml<br>
|
||||||
|
Changes made here won't affect your configuration.
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- <dom-if> would be more appropriate here, but due to
|
||||||
|
https://github.com/Polymer/polymer/issues/4818 array observations don't
|
||||||
|
propagate properly via <dom-if>.
|
||||||
|
-->
|
||||||
|
<div hidden$='[[!hasLocalAttributes]]'>
|
||||||
|
<h4 class="attributes-text">
|
||||||
|
The following attributes are already set in customize.yaml<br>
|
||||||
|
</h4>
|
||||||
|
<ha-form-customize-attributes attributes="{{localAttributes}}"></ha-form-customize-attributes>
|
||||||
|
</div>
|
||||||
|
<div hidden$='[[!hasGlobalAttributes]]'>
|
||||||
|
<h4 class="attributes-text">
|
||||||
|
The following attributes are customized from outside of customize.yaml<br>
|
||||||
|
Possibly via a domain, a glob or a different include.
|
||||||
|
</h4>
|
||||||
|
<ha-form-customize-attributes attributes="{{globalAttributes}}"></ha-form-customize-attributes>
|
||||||
|
</div>
|
||||||
|
<div hidden$='[[!hasExistingAttributes]]'>
|
||||||
|
<h4 class="attributes-text">
|
||||||
|
The following attributes of the entity are set programatically.<br>
|
||||||
|
You can override them if you like.
|
||||||
|
</h4>
|
||||||
|
<ha-form-customize-attributes attributes="{{existingAttributes}}"></ha-form-customize-attributes>
|
||||||
|
</div>
|
||||||
|
<div hidden$='[[!hasNewAttributes]]'>
|
||||||
|
<h4 class="attributes-text">
|
||||||
|
The following attributes weren't set. Set them if you like.
|
||||||
|
</h4>
|
||||||
|
<ha-form-customize-attributes attributes="{{newAttributes}}"></ha-form-customize-attributes>
|
||||||
|
</div>
|
||||||
|
<div class='form-group'>
|
||||||
|
<paper-dropdown-menu
|
||||||
|
label='Pick an attribute to override'
|
||||||
|
class='flex'
|
||||||
|
dynamic-align
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
selected='{{selectedNewAttribute}}'
|
||||||
|
>
|
||||||
|
<template is='dom-repeat' items='[[newAttributesOptions]]' as='option'>
|
||||||
|
<paper-item>[[option]]</paper-item>
|
||||||
|
</template>
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaFormCustomize extends Polymer.Element {
|
||||||
|
|
||||||
|
static get is() { return 'ha-form-customize'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
entity: Object,
|
||||||
|
|
||||||
|
localAttributes: {
|
||||||
|
type: Array,
|
||||||
|
computed: 'computeLocalAttributes(localConfig)',
|
||||||
|
},
|
||||||
|
hasLocalAttributes: Boolean,
|
||||||
|
|
||||||
|
globalAttributes: {
|
||||||
|
type: Array,
|
||||||
|
computed: 'computeGlobalAttributes(localConfig, globalConfig)',
|
||||||
|
},
|
||||||
|
hasGlobalAttributes: Boolean,
|
||||||
|
|
||||||
|
existingAttributes: {
|
||||||
|
type: Array,
|
||||||
|
computed: 'computeExistingAttributes(localConfig, globalConfig, entity)',
|
||||||
|
},
|
||||||
|
hasExistingAttributes: Boolean,
|
||||||
|
|
||||||
|
newAttributes: {
|
||||||
|
type: Array,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
hasNewAttributes: Boolean,
|
||||||
|
|
||||||
|
newAttributesOptions: Array,
|
||||||
|
selectedNewAttribute: {
|
||||||
|
type: Number,
|
||||||
|
value: -1,
|
||||||
|
observer: 'selectedNewAttributeObserver',
|
||||||
|
},
|
||||||
|
|
||||||
|
localConfig: Object,
|
||||||
|
globalConfig: Object,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observers() {
|
||||||
|
return [
|
||||||
|
'attributesObserver(localAttributes.*, globalAttributes.*, existingAttributes.*, newAttributes.*)',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
_initOpenObject(key, value, secondary, config) {
|
||||||
|
return Object.assign({
|
||||||
|
attribute: key,
|
||||||
|
value: value,
|
||||||
|
closed: false,
|
||||||
|
domain: window.hassUtil.computeDomain(this.entity),
|
||||||
|
secondary: secondary,
|
||||||
|
description: key,
|
||||||
|
}, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEntity(entity) {
|
||||||
|
this.entity = entity;
|
||||||
|
return this.hass.callApi('GET', 'config/customize/config/' + entity.entity_id)
|
||||||
|
.then((data) => {
|
||||||
|
this.localConfig = data.local;
|
||||||
|
this.globalConfig = data.global;
|
||||||
|
this.newAttributes = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
saveEntity() {
|
||||||
|
const data = {};
|
||||||
|
const attrs = this.localAttributes.concat(
|
||||||
|
this.globalAttributes, this.existingAttributes, this.newAttributes);
|
||||||
|
attrs.forEach((attr) => {
|
||||||
|
if (attr.closed || attr.secondary || !attr.attribute || !attr.value) return;
|
||||||
|
data[attr.attribute] = (attr.type === 'json' ? JSON.parse(attr.value) : attr.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const objectId = this.entity.entity_id;
|
||||||
|
return this.hass.callApi('POST', 'config/customize/config/' + objectId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeSingleAttribute(key, value, secondary) {
|
||||||
|
const config = window.hassAttributeUtil.LOGIC_STATE_ATTRIBUTES[key]
|
||||||
|
|| { type: window.hassAttributeUtil.UNKNOWN_TYPE };
|
||||||
|
return this._initOpenObject(key, config.type === 'json' ? JSON.stringify(value) : value, secondary, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeAttributes(config, keys, secondary) {
|
||||||
|
return keys.map(key => this._computeSingleAttribute(key, config[key], secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
computeLocalAttributes(localConfig) {
|
||||||
|
if (!localConfig) return [];
|
||||||
|
const localKeys = Object.keys(localConfig);
|
||||||
|
const result = this._computeAttributes(localConfig, localKeys, false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
computeGlobalAttributes(localConfig, globalConfig) {
|
||||||
|
if (!localConfig || !globalConfig) return [];
|
||||||
|
const localKeys = Object.keys(localConfig);
|
||||||
|
const globalKeys = Object.keys(globalConfig).filter(key => !localKeys.includes(key));
|
||||||
|
return this._computeAttributes(globalConfig, globalKeys, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
computeExistingAttributes(localConfig, globalConfig, entity) {
|
||||||
|
if (!localConfig || !globalConfig || !entity) return [];
|
||||||
|
const localKeys = Object.keys(localConfig);
|
||||||
|
const globalKeys = Object.keys(globalConfig);
|
||||||
|
const entityKeys = Object.keys(entity.attributes)
|
||||||
|
.filter(key => !localKeys.includes(key) && !globalKeys.includes(key));
|
||||||
|
return this._computeAttributes(entity.attributes, entityKeys, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
computeShowWarning(localConfig, globalConfig) {
|
||||||
|
if (!localConfig || !globalConfig) return false;
|
||||||
|
return Object.keys(localConfig).some(
|
||||||
|
key => JSON.stringify(globalConfig[key]) !== JSON.stringify(localConfig[key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
filterFromAttributes(attributes) {
|
||||||
|
return key => !attributes || attributes.every(attr => attr.attribute !== key || attr.closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNewAttributesOptions(localAttributes, globalAttributes, existingAttributes, newAttributes) {
|
||||||
|
const knownKeys =
|
||||||
|
Object.keys(window.hassAttributeUtil.LOGIC_STATE_ATTRIBUTES)
|
||||||
|
.filter(key => window.hassAttributeUtil.LOGIC_STATE_ATTRIBUTES[key])
|
||||||
|
.filter(this.filterFromAttributes(localAttributes))
|
||||||
|
.filter(this.filterFromAttributes(globalAttributes))
|
||||||
|
.filter(this.filterFromAttributes(existingAttributes))
|
||||||
|
.filter(this.filterFromAttributes(newAttributes));
|
||||||
|
return knownKeys.sort().concat('Other');
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedNewAttributeObserver(selected) {
|
||||||
|
if (selected < 0) return;
|
||||||
|
const option = this.newAttributesOptions[selected];
|
||||||
|
if (selected === this.newAttributesOptions.length - 1) {
|
||||||
|
// The "Other" option.
|
||||||
|
const attr = this._initOpenObject('', '', false /* secondary */, { type: window.hassAttributeUtil.ADD_TYPE });
|
||||||
|
this.push('newAttributes', attr);
|
||||||
|
this.selectedNewAttribute = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let result = this.localAttributes.findIndex(attr => attr.attribute === option);
|
||||||
|
if (result >= 0) {
|
||||||
|
this.set('localAttributes.' + result + '.closed', false);
|
||||||
|
this.selectedNewAttribute = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result = this.globalAttributes.findIndex(attr => attr.attribute === option);
|
||||||
|
if (result >= 0) {
|
||||||
|
this.set('globalAttributes.' + result + '.closed', false);
|
||||||
|
this.selectedNewAttribute = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result = this.existingAttributes.findIndex(attr => attr.attribute === option);
|
||||||
|
if (result >= 0) {
|
||||||
|
this.set('existingAttributes.' + result + '.closed', false);
|
||||||
|
this.selectedNewAttribute = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result = this.newAttributes.findIndex(attr => attr.attribute === option);
|
||||||
|
if (result >= 0) {
|
||||||
|
this.set('newAttributes.' + result + '.closed', false);
|
||||||
|
this.selectedNewAttribute = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const attr = this._computeSingleAttribute(option, '', false /* secondary */);
|
||||||
|
this.push('newAttributes', attr);
|
||||||
|
this.selectedNewAttribute = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributesObserver() {
|
||||||
|
this.hasLocalAttributes =
|
||||||
|
this.localAttributes && this.localAttributes.some(attr => !attr.closed);
|
||||||
|
this.hasGlobalAttributes =
|
||||||
|
this.globalAttributes && this.globalAttributes.some(attr => !attr.closed);
|
||||||
|
this.hasExistingAttributes =
|
||||||
|
this.existingAttributes && this.existingAttributes.some(attr => !attr.closed);
|
||||||
|
this.hasNewAttributes =
|
||||||
|
this.newAttributes && this.newAttributes.some(attr => !attr.closed);
|
||||||
|
this.newAttributesOptions = this.getNewAttributesOptions(
|
||||||
|
this.localAttributes, this.globalAttributes, this.existingAttributes, this.newAttributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaFormCustomize.is, HaFormCustomize);
|
||||||
|
</script>
|
69
panels/config/customize/types/ha-customize-array.html
Normal file
69
panels/config/customize/types/ha-customize-array.html
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<link rel='import' href='../../../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel="import" href="../../../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
|
||||||
|
<link rel='import' href='../../../../bower_components/paper-listbox/paper-listbox.html'>
|
||||||
|
<link rel='import' href='../../../../bower_components/paper-item/paper-item.html'>
|
||||||
|
<link rel="import" href="../../../../src/util/hass-mixins.html">
|
||||||
|
|
||||||
|
<dom-module id='ha-customize-array'>
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
paper-dropdown-menu {
|
||||||
|
margin: -9px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-dropdown-menu
|
||||||
|
label='[[item.description]]'
|
||||||
|
disabled='[[item.secondary]]'
|
||||||
|
selected-item-label="{{item.value}}"
|
||||||
|
dynamic-align
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
selected='[[computeSelected(item)]]'
|
||||||
|
>
|
||||||
|
<template is='dom-repeat' items='[[getOptions(item)]]' as='option'>
|
||||||
|
<paper-item>[[option]]</paper-item>
|
||||||
|
</template>
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaCustomizeArray extends window.hassMixins.EventsMixin(Polymer.Element) {
|
||||||
|
|
||||||
|
static get is() { return 'ha-customize-array'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
notifies: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptions(item) {
|
||||||
|
const domain = item.domain;
|
||||||
|
if (!domain) {
|
||||||
|
this.item.type = 'string';
|
||||||
|
this.fire('item-changed');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const options = item.options[domain];
|
||||||
|
if (!options) {
|
||||||
|
this.item.type = 'string';
|
||||||
|
this.fire('item-changed');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return options.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
computeSelected(item) {
|
||||||
|
const options = this.getOptions(item);
|
||||||
|
return options.indexOf(item.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
customElements.define(HaCustomizeArray.is, HaCustomizeArray);
|
||||||
|
</script>
|
30
panels/config/customize/types/ha-customize-boolean.html
Normal file
30
panels/config/customize/types/ha-customize-boolean.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<link rel='import' href='../../../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel='import' href='../../../../bower_components/paper-checkbox/paper-checkbox.html'>
|
||||||
|
|
||||||
|
<dom-module id='ha-customize-boolean'>
|
||||||
|
<template>
|
||||||
|
<paper-checkbox
|
||||||
|
disabled='[[item.secondary]]'
|
||||||
|
checked="[[item.value]]"
|
||||||
|
>
|
||||||
|
[[item.description]]
|
||||||
|
</paper-checkbox>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaCustomizeBoolean extends Polymer.Element {
|
||||||
|
|
||||||
|
static get is() { return 'ha-customize-boolean'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
notifies: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaCustomizeBoolean.is, HaCustomizeBoolean);
|
||||||
|
</script>
|
43
panels/config/customize/types/ha-customize-icon.html
Normal file
43
panels/config/customize/types/ha-customize-icon.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<link rel='import' href='../../../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel='import' href='../../../../bower_components/iron-icon/iron-icon.html'>
|
||||||
|
<link rel='import' href='../../../../bower_components/paper-input/paper-input.html'>
|
||||||
|
|
||||||
|
<dom-module id='ha-customize-icon'>
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
@apply(--layout-horizontal);
|
||||||
|
}
|
||||||
|
.icon-image {
|
||||||
|
border: 1px solid grey;
|
||||||
|
padding: 8px;
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<iron-icon class="icon-image" icon="mdi:[[item.value]]"></iron-icon>
|
||||||
|
<paper-input
|
||||||
|
disabled='[[item.secondary]]'
|
||||||
|
label='icon'
|
||||||
|
value='{{item.value}}'>
|
||||||
|
<span slot="prefix">mdi:</span>
|
||||||
|
</paper-input>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaCustomizeIcon extends Polymer.Element {
|
||||||
|
|
||||||
|
static get is() { return 'ha-customize-icon'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
notifies: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaCustomizeIcon.is, HaCustomizeIcon);
|
||||||
|
</script>
|
46
panels/config/customize/types/ha-customize-key-value.html
Normal file
46
panels/config/customize/types/ha-customize-key-value.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<link rel='import' href='../../../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel='import' href='../../../../bower_components/paper-input/paper-input.html'>
|
||||||
|
|
||||||
|
<dom-module id='ha-customize-key-value'>
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
@apply(--layout-horizontal);
|
||||||
|
}
|
||||||
|
paper-input {
|
||||||
|
@apply(--layout-flex);
|
||||||
|
}
|
||||||
|
.key {
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-input
|
||||||
|
disabled='[[item.secondary]]'
|
||||||
|
class='key'
|
||||||
|
label='Attribute name'
|
||||||
|
value='{{item.attribute}}'>
|
||||||
|
</paper-input>
|
||||||
|
<paper-input
|
||||||
|
disabled='[[item.secondary]]'
|
||||||
|
label='Attribute value'
|
||||||
|
value='{{item.value}}'>
|
||||||
|
</paper-input>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaCustomizeKeyValue extends Polymer.Element {
|
||||||
|
|
||||||
|
static get is() { return 'ha-customize-key-value'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
notifies: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaCustomizeKeyValue.is, HaCustomizeKeyValue);
|
||||||
|
</script>
|
33
panels/config/customize/types/ha-customize-string.html
Normal file
33
panels/config/customize/types/ha-customize-string.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<link rel='import' href='../../../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel='import' href='../../../../bower_components/paper-input/paper-input.html'>
|
||||||
|
|
||||||
|
<dom-module id='ha-customize-string'>
|
||||||
|
<template>
|
||||||
|
<paper-input
|
||||||
|
disabled='[[item.secondary]]'
|
||||||
|
label='[[getLabel(item)]]'
|
||||||
|
value='{{item.value}}'>
|
||||||
|
</paper-input>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaCustomizeString extends Polymer.Element {
|
||||||
|
|
||||||
|
static get is() { return 'ha-customize-string'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
notifies: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getLabel(item) {
|
||||||
|
return item.description + (item.type === 'json' ? ' (JSON formatted)' : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define(HaCustomizeString.is, HaCustomizeString);
|
||||||
|
</script>
|
@ -48,6 +48,12 @@ Polymer({
|
|||||||
description: 'Validate your configuration file and control the server.',
|
description: 'Validate your configuration file and control the server.',
|
||||||
loaded: true,
|
loaded: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
domain: 'customize',
|
||||||
|
caption: 'Customization',
|
||||||
|
description: 'Customize you entities.',
|
||||||
|
loaded: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
domain: 'automation',
|
domain: 'automation',
|
||||||
caption: 'Automation',
|
caption: 'Automation',
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-group .form-control {
|
.form-group .form-control {
|
||||||
@apply(--layout-flex-1);
|
@apply(--layout-flex);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group.vertical {
|
.form-group.vertical {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<link rel="import" href="./automation/ha-config-automation.html">
|
<link rel="import" href="./automation/ha-config-automation.html">
|
||||||
<link rel="import" href="./script/ha-config-script.html">
|
<link rel="import" href="./script/ha-config-script.html">
|
||||||
<link rel="import" href="./zwave/ha-config-zwave.html">
|
<link rel="import" href="./zwave/ha-config-zwave.html">
|
||||||
|
<link rel="import" href="./customize/ha-config-customize.html">
|
||||||
|
|
||||||
<dom-module id="ha-panel-config">
|
<dom-module id="ha-panel-config">
|
||||||
<template>
|
<template>
|
||||||
@ -78,6 +79,12 @@
|
|||||||
is-wide='[[isWide]]'
|
is-wide='[[isWide]]'
|
||||||
></ha-config-zwave>
|
></ha-config-zwave>
|
||||||
|
|
||||||
|
<ha-config-customize
|
||||||
|
page-name='customize'
|
||||||
|
hass='[[hass]]'
|
||||||
|
is-wide='[[isWide]]'
|
||||||
|
></ha-config-customize>
|
||||||
|
|
||||||
<hass-error-screen
|
<hass-error-screen
|
||||||
page-name='not-found'
|
page-name='not-found'
|
||||||
error='Page not found.'
|
error='Page not found.'
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
|
<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
|
||||||
<link rel="import" href="../util/hass-util.html">
|
<script src="../util/hass-attributes-util.js"></script>
|
||||||
|
|
||||||
<dom-module id="ha-attributes">
|
<dom-module id="ha-attributes">
|
||||||
<template>
|
<template>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computeFiltersArray: function (extraFilters) {
|
computeFiltersArray: function (extraFilters) {
|
||||||
return window.hassUtil.LOGIC_STATE_ATTRIBUTES + (extraFilters ? extraFilters.split(',') : []);
|
return Object.keys(window.hassAttributeUtil.LOGIC_STATE_ATTRIBUTES) + (extraFilters ? extraFilters.split(',') : []);
|
||||||
},
|
},
|
||||||
|
|
||||||
computeDisplayAttributes: function (stateObj, filtersArray) {
|
computeDisplayAttributes: function (stateObj, filtersArray) {
|
||||||
|
50
src/util/hass-attributes-util.js
Normal file
50
src/util/hass-attributes-util.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
window.hassAttributeUtil = window.hassAttributeUtil || {};
|
||||||
|
|
||||||
|
window.hassAttributeUtil.DOMAIN_DEVICE_CLASS = {
|
||||||
|
binary_sensor: [
|
||||||
|
'connectivity', 'light', 'moisture', 'motion', 'occupancy', 'opening',
|
||||||
|
'sound', 'vibration', 'gas', 'power', 'safety', 'smoke', 'cold', 'heat',
|
||||||
|
'moving'],
|
||||||
|
cover: ['garage'],
|
||||||
|
};
|
||||||
|
|
||||||
|
window.hassAttributeUtil.UNKNOWN_TYPE = 'json';
|
||||||
|
window.hassAttributeUtil.ADD_TYPE = 'key-value';
|
||||||
|
|
||||||
|
window.hassAttributeUtil.TYPE_TO_TAG = {
|
||||||
|
string: 'ha-customize-string',
|
||||||
|
json: 'ha-customize-string',
|
||||||
|
icon: 'ha-customize-icon',
|
||||||
|
boolean: 'ha-customize-boolean',
|
||||||
|
array: 'ha-customize-array',
|
||||||
|
'key-value': 'ha-customize-key-value',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attributes here serve dual purpose:
|
||||||
|
// 1) Any key of this object won't be shown in more-info window.
|
||||||
|
// 2) Any key which has value other than undefined will appear in customization
|
||||||
|
// config according to its value.
|
||||||
|
// TODO: Allow per-domain config, as some attributes are only relevant for some
|
||||||
|
// domains.
|
||||||
|
window.hassAttributeUtil.LOGIC_STATE_ATTRIBUTES = {
|
||||||
|
entity_picture: undefined,
|
||||||
|
friendly_name: { type: 'string', description: 'Name' },
|
||||||
|
icon: { type: 'icon' },
|
||||||
|
emulated_hue: { type: 'boolean' },
|
||||||
|
emulated_hue_name: { type: 'string' },
|
||||||
|
haaska_hidden: undefined,
|
||||||
|
haaska_name: undefined,
|
||||||
|
homebridge_hidden: { type: 'boolean' },
|
||||||
|
homebridge_name: { type: 'string' },
|
||||||
|
supported_features: undefined,
|
||||||
|
attribution: undefined,
|
||||||
|
custom_ui_state_card: { type: 'string' },
|
||||||
|
device_class: {
|
||||||
|
type: 'array',
|
||||||
|
options: window.hassAttributeUtil.DOMAIN_DEVICE_CLASS,
|
||||||
|
description: 'Device class' },
|
||||||
|
hidden: { type: 'boolean', description: 'Hide from UI' },
|
||||||
|
assumed_state: { type: 'boolean' },
|
||||||
|
initial_state: { type: 'string' },
|
||||||
|
unit_of_measurement: { type: 'string' },
|
||||||
|
};
|
@ -34,13 +34,6 @@ window.hassUtil.HIDE_MORE_INFO = [
|
|||||||
'input_select', 'scene', 'script', 'input_slider',
|
'input_select', 'scene', 'script', 'input_slider',
|
||||||
];
|
];
|
||||||
|
|
||||||
window.hassUtil.LOGIC_STATE_ATTRIBUTES = [
|
|
||||||
'entity_picture', 'friendly_name', 'icon', 'unit_of_measurement',
|
|
||||||
'emulated_hue', 'emulated_hue_name', 'haaska_hidden', 'haaska_name',
|
|
||||||
'homebridge_hidden', 'homebridge_name', 'supported_features', 'attribution',
|
|
||||||
'custom_ui_state_card', 'device_class',
|
|
||||||
];
|
|
||||||
|
|
||||||
window.hassUtil.LANGUAGE = navigator.languages ?
|
window.hassUtil.LANGUAGE = navigator.languages ?
|
||||||
navigator.languages[0] : navigator.language || navigator.userLanguage;
|
navigator.languages[0] : navigator.language || navigator.userLanguage;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user