mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 19:26:36 +00:00
Remove customize UI (#10632)
This commit is contained in:
parent
7d94615f47
commit
1e851e0e8c
@ -1,88 +0,0 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { property } from "lit/decorators";
|
|
||||||
import "../../../components/ha-card";
|
|
||||||
import "../../../layouts/hass-loading-screen";
|
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
|
||||||
import { haStyle } from "../../../resources/styles";
|
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
|
||||||
import "../ha-config-section";
|
|
||||||
import "../ha-entity-config";
|
|
||||||
import { configSections } from "../ha-panel-config";
|
|
||||||
import "./ha-form-customize";
|
|
||||||
|
|
||||||
class HaConfigCustomize extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() public isWide?: boolean;
|
|
||||||
|
|
||||||
@property() public narrow?: boolean;
|
|
||||||
|
|
||||||
@property() public route!: Route;
|
|
||||||
|
|
||||||
@property() private _selectedEntityId = "";
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<hass-tabs-subpage
|
|
||||||
.hass=${this.hass}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.route=${this.route}
|
|
||||||
back-path="/config"
|
|
||||||
.tabs=${configSections.advanced}
|
|
||||||
>
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
|
||||||
<span slot="header">
|
|
||||||
${this.hass.localize("ui.panel.config.customize.picker.header")}
|
|
||||||
</span>
|
|
||||||
<span slot="introduction">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.customize.picker.introduction"
|
|
||||||
)}
|
|
||||||
<br />
|
|
||||||
<a
|
|
||||||
href=${documentationUrl(
|
|
||||||
this.hass,
|
|
||||||
"/docs/configuration/customizing-devices/#customization-using-the-ui"
|
|
||||||
)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.customize.picker.documentation"
|
|
||||||
)}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<ha-entity-config
|
|
||||||
.hass=${this.hass}
|
|
||||||
.selectedEntityId=${this._selectedEntityId}
|
|
||||||
>
|
|
||||||
</ha-entity-config>
|
|
||||||
</ha-config-section>
|
|
||||||
</div>
|
|
||||||
</hass-tabs-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
|
|
||||||
if (!this.route.path.includes("/edit/")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const routeSegments = this.route.path.split("/edit/");
|
|
||||||
this._selectedEntityId = routeSegments.length > 1 ? routeSegments[1] : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return [
|
|
||||||
haStyle,
|
|
||||||
css`
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("ha-config-customize", HaConfigCustomize);
|
|
@ -1,84 +0,0 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../../components/ha-icon";
|
|
||||||
import "../../../components/ha-icon-button";
|
|
||||||
import hassAttributeUtil from "../../../util/hass-attributes-util";
|
|
||||||
import "../ha-form-style";
|
|
||||||
import "./types/ha-customize-array";
|
|
||||||
import "./types/ha-customize-boolean";
|
|
||||||
import "./types/ha-customize-icon";
|
|
||||||
import "./types/ha-customize-key-value";
|
|
||||||
import "./types/ha-customize-string";
|
|
||||||
|
|
||||||
class HaCustomizeAttribute extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<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>
|
|
||||||
<ha-icon-button class="button" on-click="tapButton">
|
|
||||||
<ha-icon icon="[[getIcon(item.secondary)]]"></ha-icon>
|
|
||||||
</ha-icon-button>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
notify: true,
|
|
||||||
observer: "itemObserver",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
tapButton() {
|
|
||||||
if (this.item.secondary) {
|
|
||||||
this.item = { ...this.item, secondary: false };
|
|
||||||
} else {
|
|
||||||
this.item = { ...this.item, closed: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getIcon(secondary) {
|
|
||||||
return secondary ? "hass:pencil" : "hass:close";
|
|
||||||
}
|
|
||||||
|
|
||||||
itemObserver(item) {
|
|
||||||
const wrapper = this.$.wrapper;
|
|
||||||
const tag = 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);
|
|
||||||
}
|
|
||||||
// Creating an element with upper case works fine in Chrome, but in FF it doesn't immediately
|
|
||||||
// become a defined Custom Element. Polymer does that in some later pass.
|
|
||||||
this.$.child = child = document.createElement(tag.toLowerCase());
|
|
||||||
child.className = "form-control";
|
|
||||||
child.addEventListener("item-changed", () => {
|
|
||||||
this.item = { ...child.item };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
child.setProperties({ item: this.item });
|
|
||||||
if (child.parentNode === null) {
|
|
||||||
wrapper.appendChild(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("ha-customize-attribute", HaCustomizeAttribute);
|
|
@ -1,34 +0,0 @@
|
|||||||
import { MutableData } from "@polymer/polymer/lib/mixins/mutable-data";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "./ha-customize-attribute";
|
|
||||||
|
|
||||||
class HaFormCustomizeAttributes extends MutableData(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template is="dom-repeat" items="{{attributes}}" mutable-data="">
|
|
||||||
<ha-customize-attribute item="{{item}}" hidden$="[[item.closed]]">
|
|
||||||
</ha-customize-attribute>
|
|
||||||
</template>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
attributes: {
|
|
||||||
type: Array,
|
|
||||||
notify: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define(
|
|
||||||
"ha-form-customize-attributes",
|
|
||||||
HaFormCustomizeAttributes
|
|
||||||
);
|
|
@ -1,362 +0,0 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
|
||||||
import "../../../styles/polymer-ha-style";
|
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
|
||||||
import hassAttributeUtil from "../../../util/hass-attributes-util";
|
|
||||||
import "../ha-form-style";
|
|
||||||
import "./ha-form-customize-attributes";
|
|
||||||
|
|
||||||
export class HaFormCustomize extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex ha-style ha-form-style">
|
|
||||||
.warning {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.attributes-text {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if="[[computeShowWarning(localConfig, globalConfig)]]"
|
|
||||||
>
|
|
||||||
<div class="warning">
|
|
||||||
[[localize('ui.panel.config.customize.warning.include_sentence')]]
|
|
||||||
<a
|
|
||||||
href="[[_computeDocumentationUrl(hass)]]"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>[[localize('ui.panel.config.customize.warning.include_link')]]</a
|
|
||||||
>.<br />
|
|
||||||
[[localize('ui.panel.config.customize.warning.not_applied')]]
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[hasLocalAttributes]]">
|
|
||||||
<p class="attributes-text">
|
|
||||||
[[localize('ui.panel.config.customize.attributes_customize')]]<br />
|
|
||||||
</p>
|
|
||||||
<ha-form-customize-attributes
|
|
||||||
attributes="{{localAttributes}}"
|
|
||||||
></ha-form-customize-attributes>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[hasGlobalAttributes]]">
|
|
||||||
<p class="attributes-text">
|
|
||||||
[[localize('ui.panel.config.customize.attributes_outside')]]<br />
|
|
||||||
[[localize('ui.panel.config.customize.different_include')]]
|
|
||||||
</p>
|
|
||||||
<ha-form-customize-attributes
|
|
||||||
attributes="{{globalAttributes}}"
|
|
||||||
></ha-form-customize-attributes>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[hasExistingAttributes]]">
|
|
||||||
<p class="attributes-text">
|
|
||||||
[[localize('ui.panel.config.customize.attributes_set')]]<br />
|
|
||||||
[[localize('ui.panel.config.customize.attributes_override')]]
|
|
||||||
</p>
|
|
||||||
<ha-form-customize-attributes
|
|
||||||
attributes="{{existingAttributes}}"
|
|
||||||
></ha-form-customize-attributes>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[hasNewAttributes]]">
|
|
||||||
<p class="attributes-text">
|
|
||||||
[[localize('ui.panel.config.customize.attributes_not_set')]]
|
|
||||||
</p>
|
|
||||||
<ha-form-customize-attributes
|
|
||||||
attributes="{{newAttributes}}"
|
|
||||||
></ha-form-customize-attributes>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[entity]]">
|
|
||||||
<div class="form-group">
|
|
||||||
<paper-dropdown-menu
|
|
||||||
label="[[localize('ui.panel.config.customize.pick_attribute')]]"
|
|
||||||
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>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
attribute: key,
|
|
||||||
value: value,
|
|
||||||
closed: false,
|
|
||||||
domain: computeStateDomain(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 === null ||
|
|
||||||
attr.value === undefined
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
const value = attr.type === "json" ? JSON.parse(attr.value) : attr.value;
|
|
||||||
if (value === null || value === undefined) return;
|
|
||||||
data[attr.attribute] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
const objectId = this.entity.entity_id;
|
|
||||||
return this.hass.callApi(
|
|
||||||
"POST",
|
|
||||||
"config/customize/config/" + objectId,
|
|
||||||
data
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeSingleAttribute(key, value, secondary) {
|
|
||||||
const config = hassAttributeUtil.LOGIC_STATE_ATTRIBUTES[key] || {
|
|
||||||
type: 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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeDocumentationUrl(hass) {
|
|
||||||
return documentationUrl(
|
|
||||||
hass,
|
|
||||||
"/docs/configuration/customizing-devices/#customization-using-the-ui"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(hassAttributeUtil.LOGIC_STATE_ATTRIBUTES)
|
|
||||||
.filter((key) => {
|
|
||||||
const conf = hassAttributeUtil.LOGIC_STATE_ATTRIBUTES[key];
|
|
||||||
return (
|
|
||||||
conf &&
|
|
||||||
(!conf.domains ||
|
|
||||||
!this.entity ||
|
|
||||||
conf.domains.includes(computeStateDomain(this.entity)))
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.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: 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("ha-form-customize", HaFormCustomize);
|
|
@ -1,63 +0,0 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { EventsMixin } from "../../../../mixins/events-mixin";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
*/
|
|
||||||
class HaCustomizeArray extends EventsMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<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>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
notifies: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getOptions(item) {
|
|
||||||
const domain = item.domain || "*";
|
|
||||||
const options = item.options[domain] || item.options["*"];
|
|
||||||
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("ha-customize-array", HaCustomizeArray);
|
|
@ -1,34 +0,0 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../../../components/ha-checkbox";
|
|
||||||
import "../../../../components/ha-formfield";
|
|
||||||
|
|
||||||
class HaCustomizeBoolean extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<ha-formfield label="[[item.description]]">
|
|
||||||
<ha-checkbox
|
|
||||||
disabled="[[item.secondary]]"
|
|
||||||
checked="[[item.value]]"
|
|
||||||
on-change="checkedChanged"
|
|
||||||
>
|
|
||||||
</ha-checkbox
|
|
||||||
></ha-formfield>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
notifies: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
checkedChanged(ev) {
|
|
||||||
this.item.value = ev.target.checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("ha-customize-boolean", HaCustomizeBoolean);
|
|
@ -1,40 +0,0 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../../../components/ha-icon";
|
|
||||||
|
|
||||||
class HaCustomizeIcon extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
@apply --layout-horizontal;
|
|
||||||
}
|
|
||||||
.icon-image {
|
|
||||||
border: 1px solid grey;
|
|
||||||
padding: 8px;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<ha-icon class="icon-image" icon="[[item.value]]"></ha-icon>
|
|
||||||
<paper-input
|
|
||||||
disabled="[[item.secondary]]"
|
|
||||||
label="Icon"
|
|
||||||
value="{{item.value}}"
|
|
||||||
>
|
|
||||||
</paper-input>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
notifies: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("ha-customize-icon", HaCustomizeIcon);
|
|
@ -1,45 +0,0 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
|
|
||||||
class HaCustomizeKeyValue extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<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>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
notifies: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("ha-customize-key-value", HaCustomizeKeyValue);
|
|
@ -1,35 +0,0 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { formatAttributeName } from "../../../../util/hass-attributes-util";
|
|
||||||
|
|
||||||
class HaCustomizeString extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<paper-input
|
|
||||||
disabled="[[item.secondary]]"
|
|
||||||
label="[[getLabel(item)]]"
|
|
||||||
value="{{item.value}}"
|
|
||||||
>
|
|
||||||
</paper-input>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
notifies: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getLabel(item) {
|
|
||||||
return (
|
|
||||||
formatAttributeName(item.description) +
|
|
||||||
(item.type === "json" ? " (JSON formatted)" : "")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("ha-customize-string", HaCustomizeString);
|
|
@ -165,21 +165,6 @@ export class DialogEntityEditor extends LitElement {
|
|||||||
>${this.hass.localize("ui.dialogs.entity_registry.faq")}</a
|
>${this.hass.localize("ui.dialogs.entity_registry.faq")}</a
|
||||||
>`
|
>`
|
||||||
)}
|
)}
|
||||||
${this.hass.userData?.showAdvanced
|
|
||||||
? html`<br /><br />
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.dialogs.entity_registry.info_customize",
|
|
||||||
"customize_link",
|
|
||||||
html`<a
|
|
||||||
href=${"/config/customize/edit/" +
|
|
||||||
this._params!.entity_id}
|
|
||||||
rel="noreferrer"
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.dialogs.entity_registry.customize_link"
|
|
||||||
)}</a
|
|
||||||
>`
|
|
||||||
)}`
|
|
||||||
: ""}
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
case "tab-related":
|
case "tab-related":
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, query } from "lit/decorators";
|
|
||||||
import "../../components/buttons/ha-progress-button";
|
|
||||||
import "../../components/entity/ha-entity-picker";
|
|
||||||
import "../../components/ha-card";
|
|
||||||
import "../../components/ha-circular-progress";
|
|
||||||
import { haStyle } from "../../resources/styles";
|
|
||||||
import "../../styles/polymer-ha-style";
|
|
||||||
import type { HomeAssistant } from "../../types";
|
|
||||||
import { HaFormCustomize } from "./customize/ha-form-customize";
|
|
||||||
|
|
||||||
@customElement("ha-entity-config")
|
|
||||||
export class HaEntityConfig extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() public selectedEntityId!: string;
|
|
||||||
|
|
||||||
// False if no entity is selected or currently saving or loading
|
|
||||||
@property() private _formEditState = false;
|
|
||||||
|
|
||||||
@query("#form") private _form!: HaFormCustomize;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-content">
|
|
||||||
<ha-entity-picker
|
|
||||||
.hass=${this.hass}
|
|
||||||
.value=${this.selectedEntityId}
|
|
||||||
.configValue=${"entity"}
|
|
||||||
@change=${this._selectedEntityChanged}
|
|
||||||
allow-custom-entity
|
|
||||||
hideClearIcon
|
|
||||||
>
|
|
||||||
</ha-entity-picker>
|
|
||||||
|
|
||||||
<div class="form-container">
|
|
||||||
<ha-form-customize .hass=${this.hass} .id=${"form"}>
|
|
||||||
</ha-form-customize>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<ha-progress-button
|
|
||||||
@click=${this._saveEntity}
|
|
||||||
.disabled=${!this._formEditState}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.common.save")}
|
|
||||||
</ha-progress-button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
|
||||||
super.updated(changedProps);
|
|
||||||
if (
|
|
||||||
changedProps.has("selectedEntityId") &&
|
|
||||||
changedProps.get("selectedEntityId") !== this.selectedEntityId
|
|
||||||
) {
|
|
||||||
this._selectEntity(this.selectedEntityId);
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _selectedEntityChanged(ev) {
|
|
||||||
this._selectEntity(ev.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _selectEntity(entityId?: string) {
|
|
||||||
if (!this._form || !entityId) return;
|
|
||||||
const entity = this.hass.states[entityId];
|
|
||||||
if (!entity) return;
|
|
||||||
|
|
||||||
this._formEditState = false;
|
|
||||||
await this._form.loadEntity(entity);
|
|
||||||
this._formEditState = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _saveEntity(ev) {
|
|
||||||
if (!this._formEditState) return;
|
|
||||||
this._formEditState = false;
|
|
||||||
const button = ev.target;
|
|
||||||
button.progress = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this._form.saveEntity();
|
|
||||||
this._formEditState = true;
|
|
||||||
button.actionSuccess();
|
|
||||||
} catch {
|
|
||||||
button.actionError();
|
|
||||||
} finally {
|
|
||||||
button.progress = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return [
|
|
||||||
haStyle,
|
|
||||||
css`
|
|
||||||
ha-card {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-placeholder {
|
|
||||||
height: 96px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ import {
|
|||||||
mdiNfcVariant,
|
mdiNfcVariant,
|
||||||
mdiPalette,
|
mdiPalette,
|
||||||
mdiPaletteSwatch,
|
mdiPaletteSwatch,
|
||||||
mdiPencil,
|
|
||||||
mdiPuzzle,
|
mdiPuzzle,
|
||||||
mdiRobot,
|
mdiRobot,
|
||||||
mdiScriptText,
|
mdiScriptText,
|
||||||
@ -180,16 +179,6 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
|||||||
core: true,
|
core: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
advanced: [
|
|
||||||
{
|
|
||||||
component: "customize",
|
|
||||||
path: "/config/customize",
|
|
||||||
translationKey: "ui.panel.config.customize.caption",
|
|
||||||
iconPath: mdiPencil,
|
|
||||||
core: true,
|
|
||||||
advancedOnly: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("ha-panel-config")
|
@customElement("ha-panel-config")
|
||||||
@ -243,10 +232,8 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
tag: "ha-config-info",
|
tag: "ha-config-info",
|
||||||
load: () => import("./info/ha-config-info"),
|
load: () => import("./info/ha-config-info"),
|
||||||
},
|
},
|
||||||
customize: {
|
// customize was removed in 2021.12, fallback to dashboard
|
||||||
tag: "ha-config-customize",
|
customize: "dashboard",
|
||||||
load: () => import("./customize/ha-config-customize"),
|
|
||||||
},
|
|
||||||
dashboard: {
|
dashboard: {
|
||||||
tag: "ha-config-dashboard",
|
tag: "ha-config-dashboard",
|
||||||
load: () => import("./dashboard/ha-config-dashboard"),
|
load: () => import("./dashboard/ha-config-dashboard"),
|
||||||
|
@ -141,7 +141,8 @@ const REDIRECTS: Redirects = {
|
|||||||
redirect: "/config/info",
|
redirect: "/config/info",
|
||||||
},
|
},
|
||||||
customize: {
|
customize: {
|
||||||
redirect: "/config/customize",
|
// customize was removed in 2021.12, fallback to dashboard
|
||||||
|
redirect: "/config/dashboard",
|
||||||
},
|
},
|
||||||
profile: {
|
profile: {
|
||||||
redirect: "/profile/dashboard",
|
redirect: "/profile/dashboard",
|
||||||
|
@ -601,7 +601,6 @@
|
|||||||
"zone": "[%key:ui::panel::config::zone::caption%]",
|
"zone": "[%key:ui::panel::config::zone::caption%]",
|
||||||
"users": "[%key:ui::panel::config::users::caption%]",
|
"users": "[%key:ui::panel::config::users::caption%]",
|
||||||
"info": "[%key:ui::panel::config::info::caption%]",
|
"info": "[%key:ui::panel::config::info::caption%]",
|
||||||
"customize": "[%key:ui::panel::config::customize::caption%]",
|
|
||||||
"blueprint": "[%key:ui::panel::config::blueprint::caption%]",
|
"blueprint": "[%key:ui::panel::config::blueprint::caption%]",
|
||||||
"server_control": "[%key:ui::panel::config::server_control::caption%]"
|
"server_control": "[%key:ui::panel::config::server_control::caption%]"
|
||||||
}
|
}
|
||||||
@ -687,8 +686,6 @@
|
|||||||
"dismiss": "Dismiss",
|
"dismiss": "Dismiss",
|
||||||
"no_unique_id": "This entity (''{entity_id}'') does not have a unique ID, therefore its settings cannot be managed from the UI. See the {faq_link} for more detail.",
|
"no_unique_id": "This entity (''{entity_id}'') does not have a unique ID, therefore its settings cannot be managed from the UI. See the {faq_link} for more detail.",
|
||||||
"faq": "documentation",
|
"faq": "documentation",
|
||||||
"info_customize": "You can overwrite some attributes in the {customize_link} section.",
|
|
||||||
"customize_link": "entity customizations",
|
|
||||||
"editor": {
|
"editor": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"icon": "Icon",
|
"icon": "Icon",
|
||||||
@ -1404,27 +1401,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"customize": {
|
|
||||||
"caption": "Customizations",
|
|
||||||
"description": "Customize your entities",
|
|
||||||
"picker": {
|
|
||||||
"header": "Customizations",
|
|
||||||
"introduction": "Tweak per-entity attributes. Added/edited customizations will take effect immediately. Removed customizations will take effect when the entity is updated.",
|
|
||||||
"documentation": "Customization documentation"
|
|
||||||
},
|
|
||||||
"warning": {
|
|
||||||
"include_sentence": "It seems that your configuration.yaml doesn't properly",
|
|
||||||
"include_link": "include customize.yaml",
|
|
||||||
"not_applied": "Changes made here are written in it, but will not be applied after a configuration reload unless the include is in place."
|
|
||||||
},
|
|
||||||
"attributes_customize": "The following attributes are already set in customize.yaml",
|
|
||||||
"attributes_outside": "The following attributes are customized from outside of customize.yaml",
|
|
||||||
"different_include": "Possibly via a domain, a glob or a different include.",
|
|
||||||
"attributes_set": "The following attributes of the entity are set programmatically.",
|
|
||||||
"attributes_override": "You can override them if you like.",
|
|
||||||
"attributes_not_set": "The following attributes weren't set. Set them if you like.",
|
|
||||||
"pick_attribute": "Pick an attribute to override"
|
|
||||||
},
|
|
||||||
"automation": {
|
"automation": {
|
||||||
"caption": "Automations",
|
"caption": "Automations",
|
||||||
"description": "Create custom behavior rules for your home",
|
"description": "Create custom behavior rules for your home",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user