mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-21 00:06:35 +00:00
Add helper UI (#4940)
* Add helper UI * Oops * Update * Update * Update * Lint * Add all input forms * Return extended entity registry entry from update * Comments Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
52ded635ff
commit
b229071248
@ -452,7 +452,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
.disabled=${!this.addon.available}
|
.disabled=${!this.addon.available || this._installing}
|
||||||
.progress=${this._installing}
|
.progress=${this._installing}
|
||||||
@click=${this._installClicked}
|
@click=${this._installClicked}
|
||||||
>
|
>
|
||||||
|
@ -4,7 +4,7 @@ export const dynamicElement = directive(
|
|||||||
(tag: string, properties?: { [key: string]: any }) => (part: Part): void => {
|
(tag: string, properties?: { [key: string]: any }) => (part: Part): void => {
|
||||||
if (!(part instanceof NodePart)) {
|
if (!(part instanceof NodePart)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"dynamicContentDirective can only be used in content bindings"
|
"dynamicElementDirective can only be used in content bindings"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ const fixedIcons = {
|
|||||||
homeassistant: "hass:home-assistant",
|
homeassistant: "hass:home-assistant",
|
||||||
homekit: "hass:home-automation",
|
homekit: "hass:home-automation",
|
||||||
image_processing: "hass:image-filter-frames",
|
image_processing: "hass:image-filter-frames",
|
||||||
input_boolean: "hass:drawing",
|
input_boolean: "hass:toggle-switch-outline",
|
||||||
input_datetime: "hass:calendar-clock",
|
input_datetime: "hass:calendar-clock",
|
||||||
input_number: "hass:ray-vertex",
|
input_number: "hass:ray-vertex",
|
||||||
input_select: "hass:format-list-bulleted",
|
input_select: "hass:format-list-bulleted",
|
||||||
|
65
src/components/ha-icon-input.ts
Normal file
65
src/components/ha-icon-input.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "./ha-icon";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
|
||||||
|
@customElement("ha-icon-input")
|
||||||
|
export class HaIconInput extends LitElement {
|
||||||
|
@property() public value?: string;
|
||||||
|
@property() public label?: string;
|
||||||
|
@property() public placeholder?: string;
|
||||||
|
@property({ attribute: "error-message" }) public errorMessage?: string;
|
||||||
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.value=${this.value}
|
||||||
|
.label=${this.label}
|
||||||
|
.placeholder=${this.placeholder}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
auto-validate
|
||||||
|
.errorMessage=${this.errorMessage}
|
||||||
|
pattern="^\\S+:\\S+$"
|
||||||
|
>
|
||||||
|
${this.value || this.placeholder
|
||||||
|
? html`
|
||||||
|
<ha-icon .icon=${this.value || this.placeholder} slot="suffix">
|
||||||
|
</ha-icon>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
this.value = ev.detail.value;
|
||||||
|
fireEvent(
|
||||||
|
this,
|
||||||
|
"value-changed",
|
||||||
|
{ value: ev.detail.value },
|
||||||
|
{
|
||||||
|
bubbles: false,
|
||||||
|
composed: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-icon {
|
||||||
|
position: relative;
|
||||||
|
bottom: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
@ -70,9 +70,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
if (Object.keys(this._related).length === 0) {
|
if (Object.keys(this._related).length === 0) {
|
||||||
return html`
|
return html`
|
||||||
<p>
|
${this.hass.localize("ui.components.related-items.no_related_found")}
|
||||||
${this.hass.localize("ui.components.related-items.no_related_found")}
|
|
||||||
</p>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
|
@ -6,14 +6,23 @@ import { debounce } from "../common/util/debounce";
|
|||||||
export interface EntityRegistryEntry {
|
export interface EntityRegistryEntry {
|
||||||
entity_id: string;
|
entity_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
icon?: string;
|
||||||
platform: string;
|
platform: string;
|
||||||
config_entry_id?: string;
|
config_entry_id?: string;
|
||||||
device_id?: string;
|
device_id?: string;
|
||||||
disabled_by: string | null;
|
disabled_by: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExtEntityRegistryEntry extends EntityRegistryEntry {
|
||||||
|
unique_id: string;
|
||||||
|
capabilities: object;
|
||||||
|
original_name?: string;
|
||||||
|
original_icon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface EntityRegistryEntryUpdateParams {
|
export interface EntityRegistryEntryUpdateParams {
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
|
icon?: string | null;
|
||||||
disabled_by?: string | null;
|
disabled_by?: string | null;
|
||||||
new_entity_id?: string;
|
new_entity_id?: string;
|
||||||
}
|
}
|
||||||
@ -29,12 +38,21 @@ export const computeEntityRegistryName = (
|
|||||||
return state ? computeStateName(state) : null;
|
return state ? computeStateName(state) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getExtendedEntityRegistryEntry = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entityId: string
|
||||||
|
): Promise<ExtEntityRegistryEntry> =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "config/entity_registry/get",
|
||||||
|
entity_id: entityId,
|
||||||
|
});
|
||||||
|
|
||||||
export const updateEntityRegistryEntry = (
|
export const updateEntityRegistryEntry = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityId: string,
|
entityId: string,
|
||||||
updates: Partial<EntityRegistryEntryUpdateParams>
|
updates: Partial<EntityRegistryEntryUpdateParams>
|
||||||
): Promise<EntityRegistryEntry> =>
|
): Promise<ExtEntityRegistryEntry> =>
|
||||||
hass.callWS<EntityRegistryEntry>({
|
hass.callWS({
|
||||||
type: "config/entity_registry/update",
|
type: "config/entity_registry/update",
|
||||||
entity_id: entityId,
|
entity_id: entityId,
|
||||||
...updates,
|
...updates,
|
||||||
|
@ -59,3 +59,12 @@ export const getOptimisticFrontendUserDataCollection = <
|
|||||||
`_frontendUserData-${userDataKey}`,
|
`_frontendUserData-${userDataKey}`,
|
||||||
() => fetchFrontendUserData(conn, userDataKey)
|
() => fetchFrontendUserData(conn, userDataKey)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const subscribeFrontendUserData = <UserDataKey extends ValidUserDataKey>(
|
||||||
|
conn: Connection,
|
||||||
|
userDataKey: UserDataKey,
|
||||||
|
onChange: (state: FrontendUserData[UserDataKey] | null) => void
|
||||||
|
) =>
|
||||||
|
getOptimisticFrontendUserDataCollection(conn, userDataKey).subscribe(
|
||||||
|
onChange
|
||||||
|
);
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import { HomeAssistant } from "../types";
|
|
||||||
|
|
||||||
export const setInputSelectOption = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
entity: string,
|
|
||||||
option: string
|
|
||||||
) =>
|
|
||||||
hass.callService("input_select", "select_option", {
|
|
||||||
option,
|
|
||||||
entity_id: entity,
|
|
||||||
});
|
|
43
src/data/input_boolean.ts
Normal file
43
src/data/input_boolean.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export interface InputBoolean {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
initial?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputBooleanMutableParams {
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
initial: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchInputBoolean = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<InputBoolean[]>({ type: "input_boolean/list" });
|
||||||
|
|
||||||
|
export const createInputBoolean = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
values: InputBooleanMutableParams
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputBoolean>({
|
||||||
|
type: "input_boolean/create",
|
||||||
|
...values,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateInputBoolean = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
id: string,
|
||||||
|
updates: Partial<InputBooleanMutableParams>
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputBoolean>({
|
||||||
|
type: "input_boolean/update",
|
||||||
|
input_boolean_id: id,
|
||||||
|
...updates,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteInputBoolean = (hass: HomeAssistant, id: string) =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "input_boolean/delete",
|
||||||
|
input_boolean_id: id,
|
||||||
|
});
|
@ -1,5 +1,22 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export interface InputDateTime {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
initial?: string;
|
||||||
|
has_time: boolean;
|
||||||
|
has_date: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputDateTimeMutableParams {
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
initial: string;
|
||||||
|
has_time: boolean;
|
||||||
|
has_date: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const setInputDateTimeValue = (
|
export const setInputDateTimeValue = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityId: string,
|
entityId: string,
|
||||||
@ -9,3 +26,32 @@ export const setInputDateTimeValue = (
|
|||||||
const param = { entity_id: entityId, time, date };
|
const param = { entity_id: entityId, time, date };
|
||||||
hass.callService(entityId.split(".", 1)[0], "set_datetime", param);
|
hass.callService(entityId.split(".", 1)[0], "set_datetime", param);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchInputDateTime = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<InputDateTime[]>({ type: "input_datetime/list" });
|
||||||
|
|
||||||
|
export const createInputDateTime = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
values: InputDateTimeMutableParams
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputDateTime>({
|
||||||
|
type: "input_datetime/create",
|
||||||
|
...values,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateInputDateTime = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
id: string,
|
||||||
|
updates: Partial<InputDateTimeMutableParams>
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputDateTime>({
|
||||||
|
type: "input_datetime/update",
|
||||||
|
input_datetime_id: id,
|
||||||
|
...updates,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteInputDateTime = (hass: HomeAssistant, id: string) =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "input_datetime/delete",
|
||||||
|
input_datetime_id: id,
|
||||||
|
});
|
||||||
|
53
src/data/input_number.ts
Normal file
53
src/data/input_number.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export interface InputNumber {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
icon?: string;
|
||||||
|
initial?: number;
|
||||||
|
step?: number;
|
||||||
|
mode?: "box" | "slider";
|
||||||
|
unit_of_measurement?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputNumberMutableParams {
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
initial: number;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
step: number;
|
||||||
|
mode: "box" | "slider";
|
||||||
|
unit_of_measurement?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchInputNumber = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<InputNumber[]>({ type: "input_number/list" });
|
||||||
|
|
||||||
|
export const createInputNumber = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
values: InputNumberMutableParams
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputNumber>({
|
||||||
|
type: "input_number/create",
|
||||||
|
...values,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateInputNumber = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
id: string,
|
||||||
|
updates: Partial<InputNumberMutableParams>
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputNumber>({
|
||||||
|
type: "input_number/update",
|
||||||
|
input_number_id: id,
|
||||||
|
...updates,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteInputNumber = (hass: HomeAssistant, id: string) =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "input_number/delete",
|
||||||
|
input_number_id: id,
|
||||||
|
});
|
55
src/data/input_select.ts
Normal file
55
src/data/input_select.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export interface InputSelect {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
options: string[];
|
||||||
|
icon?: string;
|
||||||
|
initial?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputSelectMutableParams {
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
initial: string;
|
||||||
|
options: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setInputSelectOption = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity: string,
|
||||||
|
option: string
|
||||||
|
) =>
|
||||||
|
hass.callService("input_select", "select_option", {
|
||||||
|
option,
|
||||||
|
entity_id: entity,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchInputSelect = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<InputSelect[]>({ type: "input_select/list" });
|
||||||
|
|
||||||
|
export const createInputSelect = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
values: InputSelectMutableParams
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputSelect>({
|
||||||
|
type: "input_select/create",
|
||||||
|
...values,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateInputSelect = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
id: string,
|
||||||
|
updates: Partial<InputSelectMutableParams>
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputSelect>({
|
||||||
|
type: "input_select/update",
|
||||||
|
input_select_id: id,
|
||||||
|
...updates,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteInputSelect = (hass: HomeAssistant, id: string) =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "input_select/delete",
|
||||||
|
input_select_id: id,
|
||||||
|
});
|
@ -1,7 +1,57 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export interface InputText {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
initial?: string;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
pattern?: string;
|
||||||
|
mode?: "text" | "password";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputTextMutableParams {
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
initial: string;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
pattern: string;
|
||||||
|
mode: "text" | "password";
|
||||||
|
}
|
||||||
|
|
||||||
export const setValue = (hass: HomeAssistant, entity: string, value: string) =>
|
export const setValue = (hass: HomeAssistant, entity: string, value: string) =>
|
||||||
hass.callService(entity.split(".", 1)[0], "set_value", {
|
hass.callService(entity.split(".", 1)[0], "set_value", {
|
||||||
value,
|
value,
|
||||||
entity_id: entity,
|
entity_id: entity,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const fetchInputText = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<InputText[]>({ type: "input_text/list" });
|
||||||
|
|
||||||
|
export const createInputText = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
values: InputTextMutableParams
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputText>({
|
||||||
|
type: "input_text/create",
|
||||||
|
...values,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateInputText = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
id: string,
|
||||||
|
updates: Partial<InputTextMutableParams>
|
||||||
|
) =>
|
||||||
|
hass.callWS<InputText>({
|
||||||
|
type: "input_text/update",
|
||||||
|
input_text_id: id,
|
||||||
|
...updates,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteInputText = (hass: HomeAssistant, id: string) =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "input_text/delete",
|
||||||
|
input_text_id: id,
|
||||||
|
});
|
||||||
|
@ -129,6 +129,10 @@ class DialogBox extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
|
:host([inert]) {
|
||||||
|
pointer-events: initial !important;
|
||||||
|
cursor: initial !important;
|
||||||
|
}
|
||||||
ha-paper-dialog {
|
ha-paper-dialog {
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
|
@ -8,7 +8,6 @@ import "../resources/ha-style";
|
|||||||
import "./more-info/more-info-controls";
|
import "./more-info/more-info-controls";
|
||||||
|
|
||||||
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
|
||||||
|
|
||||||
import DialogMixin from "../mixins/dialog-mixin";
|
import DialogMixin from "../mixins/dialog-mixin";
|
||||||
|
|
||||||
@ -81,7 +80,6 @@ class HaMoreInfoDialog extends DialogMixin(PolymerElement) {
|
|||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
state-obj="[[stateObj]]"
|
state-obj="[[stateObj]]"
|
||||||
dialog-element="[[_dialogElement()]]"
|
dialog-element="[[_dialogElement()]]"
|
||||||
registry-entry="[[_registryInfo]]"
|
|
||||||
large="{{large}}"
|
large="{{large}}"
|
||||||
></more-info-controls>
|
></more-info-controls>
|
||||||
`;
|
`;
|
||||||
@ -102,8 +100,6 @@ class HaMoreInfoDialog extends DialogMixin(PolymerElement) {
|
|||||||
observer: "_largeChanged",
|
observer: "_largeChanged",
|
||||||
},
|
},
|
||||||
|
|
||||||
_registryInfo: Object,
|
|
||||||
|
|
||||||
dataDomain: {
|
dataDomain: {
|
||||||
computed: "_computeDomain(stateObj)",
|
computed: "_computeDomain(stateObj)",
|
||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
@ -127,11 +123,10 @@ class HaMoreInfoDialog extends DialogMixin(PolymerElement) {
|
|||||||
return hass.states[hass.moreInfoEntityId] || null;
|
return hass.states[hass.moreInfoEntityId] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _stateObjChanged(newVal, oldVal) {
|
async _stateObjChanged(newVal) {
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
opened: false,
|
opened: false,
|
||||||
_registryInfo: null,
|
|
||||||
large: false,
|
large: false,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -144,25 +139,6 @@ class HaMoreInfoDialog extends DialogMixin(PolymerElement) {
|
|||||||
this.opened = true;
|
this.opened = true;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
|
||||||
!isComponentLoaded(this.hass, "config") ||
|
|
||||||
(oldVal && oldVal.entity_id === newVal.entity_id)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hass.user.is_admin) {
|
|
||||||
try {
|
|
||||||
const info = await this.hass.callWS({
|
|
||||||
type: "config/entity_registry/get",
|
|
||||||
entity_id: newVal.entity_id,
|
|
||||||
});
|
|
||||||
this._registryInfo = info;
|
|
||||||
} catch (err) {
|
|
||||||
this._registryInfo = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_dialogOpenChanged(newVal) {
|
_dialogOpenChanged(newVal) {
|
||||||
|
@ -39,7 +39,8 @@ class MoreInfoPerson extends LitElement {
|
|||||||
></ha-map>
|
></ha-map>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.hass.user?.is_admin &&
|
${!__DEMO__ &&
|
||||||
|
this.hass.user?.is_admin &&
|
||||||
this.stateObj.state === "not_home" &&
|
this.stateObj.state === "not_home" &&
|
||||||
this.stateObj.attributes.latitude &&
|
this.stateObj.attributes.latitude &&
|
||||||
this.stateObj.attributes.longitude
|
this.stateObj.attributes.longitude
|
||||||
|
@ -22,7 +22,7 @@ import LocalizeMixin from "../../mixins/localize-mixin";
|
|||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
import { removeEntityRegistryEntry } from "../../data/entity_registry";
|
import { removeEntityRegistryEntry } from "../../data/entity_registry";
|
||||||
import { showConfirmationDialog } from "../generic/show-dialog-box";
|
import { showConfirmationDialog } from "../generic/show-dialog-box";
|
||||||
import { showEntityRegistryDetailDialog } from "../../panels/config/entities/show-dialog-entity-registry-detail";
|
import { showEntityEditorDialog } from "../../panels/config/entities/show-dialog-entity-editor";
|
||||||
|
|
||||||
const DOMAINS_NO_INFO = ["camera", "configurator", "history_graph"];
|
const DOMAINS_NO_INFO = ["camera", "configurator", "history_graph"];
|
||||||
const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"];
|
const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"];
|
||||||
@ -88,7 +88,7 @@ class MoreInfoControls extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
<div class="main-title" main-title="" on-click="enlarge">
|
<div class="main-title" main-title="" on-click="enlarge">
|
||||||
[[_computeStateName(stateObj)]]
|
[[_computeStateName(stateObj)]]
|
||||||
</div>
|
</div>
|
||||||
<template is="dom-if" if="[[registryEntry]]">
|
<template is="dom-if" if="[[_computeConfig(hass)]]">
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
aria-label$="[[localize('ui.dialogs.more_info_control.settings')]]"
|
aria-label$="[[localize('ui.dialogs.more_info_control.settings')]]"
|
||||||
icon="hass:settings"
|
icon="hass:settings"
|
||||||
@ -221,6 +221,10 @@ class MoreInfoControls extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
return stateObj ? computeStateName(stateObj) : "";
|
return stateObj ? computeStateName(stateObj) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_computeConfig(hass) {
|
||||||
|
return hass.user.is_admin && isComponentLoaded(hass, "config");
|
||||||
|
}
|
||||||
|
|
||||||
_computeEdit(hass, stateObj) {
|
_computeEdit(hass, stateObj) {
|
||||||
const domain = this._computeDomain(stateObj);
|
const domain = this._computeDomain(stateObj);
|
||||||
return (
|
return (
|
||||||
@ -260,7 +264,9 @@ class MoreInfoControls extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_gotoSettings() {
|
_gotoSettings() {
|
||||||
showEntityRegistryDetailDialog(this, { entry: this.registryEntry });
|
showEntityEditorDialog(this, {
|
||||||
|
entity_id: this.stateObj.entity_id,
|
||||||
|
});
|
||||||
this.fire("hass-more-info", { entityId: null });
|
this.fire("hass-more-info", { entityId: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import { subscribeThemes } from "../data/ws-themes";
|
|||||||
import { subscribeUser } from "../data/ws-user";
|
import { subscribeUser } from "../data/ws-user";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { hassUrl } from "../data/auth";
|
import { hassUrl } from "../data/auth";
|
||||||
|
import { subscribeFrontendUserData } from "../data/frontend";
|
||||||
import {
|
import {
|
||||||
fetchConfig,
|
fetchConfig,
|
||||||
fetchResources,
|
fetchResources,
|
||||||
@ -92,6 +93,7 @@ window.hassConnection.then(({ conn }) => {
|
|||||||
subscribePanels(conn, noop);
|
subscribePanels(conn, noop);
|
||||||
subscribeThemes(conn, noop);
|
subscribeThemes(conn, noop);
|
||||||
subscribeUser(conn, noop);
|
subscribeUser(conn, noop);
|
||||||
|
subscribeFrontendUserData(conn, "core", noop);
|
||||||
|
|
||||||
if (location.pathname === "/" || location.pathname.startsWith("/lovelace/")) {
|
if (location.pathname === "/" || location.pathname.startsWith("/lovelace/")) {
|
||||||
(window as WindowWithLovelaceProm).llConfProm = fetchConfig(
|
(window as WindowWithLovelaceProm).llConfProm = fetchConfig(
|
||||||
|
@ -20,7 +20,7 @@ import "@polymer/paper-item/paper-item-body";
|
|||||||
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-icon";
|
import "../../../../components/ha-icon";
|
||||||
import { showEntityRegistryDetailDialog } from "../../entities/show-dialog-entity-registry-detail";
|
import { showEntityEditorDialog } from "../../entities/show-dialog-entity-editor";
|
||||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||||
import { EntityRegistryStateEntry } from "../ha-config-device-page";
|
import { EntityRegistryStateEntry } from "../ha-config-device-page";
|
||||||
@ -150,7 +150,7 @@ export class HaDeviceEntitiesCard extends LitElement {
|
|||||||
private _openEditEntry(ev: Event): void {
|
private _openEditEntry(ev: Event): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const entry = (ev.currentTarget! as any).entry;
|
const entry = (ev.currentTarget! as any).entry;
|
||||||
showEntityRegistryDetailDialog(this, {
|
showEntityEditorDialog(this, {
|
||||||
entry,
|
entry,
|
||||||
entity_id: entry.entity_id,
|
entity_id: entry.entity_id,
|
||||||
});
|
});
|
||||||
|
@ -6,8 +6,6 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
property,
|
property,
|
||||||
customElement,
|
customElement,
|
||||||
CSSResult,
|
|
||||||
css,
|
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import {
|
import {
|
||||||
@ -253,17 +251,6 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
const deviceId = (ev.detail as RowClickedEvent).id;
|
const deviceId = (ev.detail as RowClickedEvent).id;
|
||||||
navigate(this, `/config/devices/device/${deviceId}`);
|
navigate(this, `/config/devices/device/${deviceId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
|
||||||
return css`
|
|
||||||
.content {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
ha-devices-data-table {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
8
src/panels/config/entities/const.ts
Normal file
8
src/panels/config/entities/const.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/** Platforms that have a settings tab. */
|
||||||
|
export const PLATFORMS_WITH_SETTINGS_TAB = {
|
||||||
|
input_number: "entity-settings-helper-tab",
|
||||||
|
input_select: "entity-settings-helper-tab",
|
||||||
|
input_text: "entity-settings-helper-tab",
|
||||||
|
input_boolean: "entity-settings-helper-tab",
|
||||||
|
input_datetime: "entity-settings-helper-tab",
|
||||||
|
};
|
@ -13,25 +13,45 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { cache } from "lit-html/directives/cache";
|
import { cache } from "lit-html/directives/cache";
|
||||||
|
import { PLATFORMS_WITH_SETTINGS_TAB } from "./const";
|
||||||
|
import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import "../../../components/dialog/ha-paper-dialog";
|
import "../../../components/dialog/ha-paper-dialog";
|
||||||
// tslint:disable-next-line: no-duplicate-imports
|
// tslint:disable-next-line: no-duplicate-imports
|
||||||
import { HaPaperDialog } from "../../../components/dialog/ha-paper-dialog";
|
import { HaPaperDialog } from "../../../components/dialog/ha-paper-dialog";
|
||||||
import "../../../components/ha-related-items";
|
import "../../../components/ha-related-items";
|
||||||
import "../../../dialogs/more-info/controls/more-info-content";
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
ExtEntityRegistryEntry,
|
||||||
|
getExtendedEntityRegistryEntry,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
import { haStyleDialog } from "../../../resources/styles";
|
import { haStyleDialog } from "../../../resources/styles";
|
||||||
import "../../../state-summary/state-card-content";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "./entity-registry-settings";
|
import "./entity-registry-settings";
|
||||||
import { EntityRegistryDetailDialogParams } from "./show-dialog-entity-registry-detail";
|
import { EntityRegistryDetailDialogParams } from "./show-dialog-entity-editor";
|
||||||
|
|
||||||
@customElement("dialog-entity-registry-detail")
|
interface Tabs {
|
||||||
export class DialogEntityRegistryDetail extends LitElement {
|
[key: string]: Tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Tab {
|
||||||
|
component: string;
|
||||||
|
translationKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("dialog-entity-editor")
|
||||||
|
export class DialogEntityEditor extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
@property() private _params?: EntityRegistryDetailDialogParams;
|
@property() private _params?: EntityRegistryDetailDialogParams;
|
||||||
|
@property() private _entry?:
|
||||||
|
| EntityRegistryEntry
|
||||||
|
| ExtEntityRegistryEntry
|
||||||
|
| null;
|
||||||
@property() private _curTab?: string;
|
@property() private _curTab?: string;
|
||||||
|
@property() private _extraTabs: Tabs = {};
|
||||||
|
@property() private _settingsElementTag?: string;
|
||||||
@query("ha-paper-dialog") private _dialog!: HaPaperDialog;
|
@query("ha-paper-dialog") private _dialog!: HaPaperDialog;
|
||||||
private _curTabIndex = 0;
|
private _curTabIndex = 0;
|
||||||
|
|
||||||
@ -39,6 +59,10 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
params: EntityRegistryDetailDialogParams
|
params: EntityRegistryDetailDialogParams
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
|
this._entry = undefined;
|
||||||
|
this._settingsElementTag = undefined;
|
||||||
|
this._extraTabs = {};
|
||||||
|
this._getEntityReg();
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,11 +71,11 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._params) {
|
if (!this._params || this._entry === undefined) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const entry = this._params.entry;
|
|
||||||
const entityId = this._params.entity_id;
|
const entityId = this._params.entity_id;
|
||||||
|
const entry = this._entry;
|
||||||
const stateObj: HassEntity | undefined = this.hass.states[entityId];
|
const stateObj: HassEntity | undefined = this.hass.states[entityId];
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -59,6 +83,7 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
with-backdrop
|
with-backdrop
|
||||||
opened
|
opened
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
|
@close-dialog=${this.closeDialog}
|
||||||
>
|
>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
@ -92,6 +117,13 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
<paper-tab id="tab-settings">
|
<paper-tab id="tab-settings">
|
||||||
${this.hass.localize("ui.dialogs.entity_registry.settings")}
|
${this.hass.localize("ui.dialogs.entity_registry.settings")}
|
||||||
</paper-tab>
|
</paper-tab>
|
||||||
|
${Object.entries(this._extraTabs).map(
|
||||||
|
([key, tab]) => html`
|
||||||
|
<paper-tab id=${key}>
|
||||||
|
${this.hass.localize(tab.translationKey) || key}
|
||||||
|
</paper-tab>
|
||||||
|
`
|
||||||
|
)}
|
||||||
<paper-tab id="tab-related">
|
<paper-tab id="tab-related">
|
||||||
${this.hass.localize("ui.dialogs.entity_registry.related")}
|
${this.hass.localize("ui.dialogs.entity_registry.related")}
|
||||||
</paper-tab>
|
</paper-tab>
|
||||||
@ -99,14 +131,16 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
${cache(
|
${cache(
|
||||||
this._curTab === "tab-settings"
|
this._curTab === "tab-settings"
|
||||||
? entry
|
? entry
|
||||||
? html`
|
? this._settingsElementTag
|
||||||
<entity-registry-settings
|
? html`
|
||||||
.hass=${this.hass}
|
${dynamicElement(this._settingsElementTag, {
|
||||||
.entry=${entry}
|
hass: this.hass,
|
||||||
.dialogElement=${this._dialog}
|
entry,
|
||||||
@close-dialog=${this._closeDialog}
|
entityId,
|
||||||
></entity-registry-settings>
|
dialogElement: this._dialog,
|
||||||
`
|
})}
|
||||||
|
`
|
||||||
|
: ""
|
||||||
: html`
|
: html`
|
||||||
<paper-dialog-scrollable>
|
<paper-dialog-scrollable>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -121,7 +155,6 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.itemId=${entityId}
|
.itemId=${entityId}
|
||||||
itemType="entity"
|
itemType="entity"
|
||||||
@close-dialog=${this._closeDialog}
|
|
||||||
></ha-related-items>
|
></ha-related-items>
|
||||||
</paper-dialog-scrollable>
|
</paper-dialog-scrollable>
|
||||||
`
|
`
|
||||||
@ -131,6 +164,18 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _getEntityReg() {
|
||||||
|
try {
|
||||||
|
this._entry = await getExtendedEntityRegistryEntry(
|
||||||
|
this.hass,
|
||||||
|
this._params!.entity_id
|
||||||
|
);
|
||||||
|
this._loadPlatformSettingTabs();
|
||||||
|
} catch {
|
||||||
|
this._entry = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _handleTabSelected(ev: CustomEvent): void {
|
private _handleTabSelected(ev: CustomEvent): void {
|
||||||
if (!ev.detail.value) {
|
if (!ev.detail.value) {
|
||||||
return;
|
return;
|
||||||
@ -144,15 +189,26 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
fireEvent(this._dialog as HTMLElement, "iron-resize");
|
fireEvent(this._dialog as HTMLElement, "iron-resize");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _loadPlatformSettingTabs(): Promise<void> {
|
||||||
|
if (!this._entry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!Object.keys(PLATFORMS_WITH_SETTINGS_TAB).includes(this._entry.platform)
|
||||||
|
) {
|
||||||
|
this._settingsElementTag = "entity-registry-settings";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tag = PLATFORMS_WITH_SETTINGS_TAB[this._entry.platform];
|
||||||
|
await import(`./editor-tabs/settings/${tag}`);
|
||||||
|
this._settingsElementTag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
private _openMoreInfo(): void {
|
private _openMoreInfo(): void {
|
||||||
fireEvent(this, "hass-more-info", {
|
fireEvent(this, "hass-more-info", {
|
||||||
entityId: this._params!.entity_id,
|
entityId: this._params!.entity_id,
|
||||||
});
|
});
|
||||||
this._params = undefined;
|
this.closeDialog();
|
||||||
}
|
|
||||||
|
|
||||||
private _closeDialog(): void {
|
|
||||||
this._params = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||||
@ -250,6 +306,6 @@ export class DialogEntityRegistryDetail extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"dialog-entity-registry-detail": DialogEntityRegistryDetail;
|
"dialog-entity-editor": DialogEntityEditor;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,260 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
|
||||||
|
import { dynamicElement } from "../../../../../common/dom/dynamic-element-directive";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { HaPaperDialog } from "../../../../../components/dialog/ha-paper-dialog";
|
||||||
|
import { ExtEntityRegistryEntry } from "../../../../../data/entity_registry";
|
||||||
|
import {
|
||||||
|
deleteInputBoolean,
|
||||||
|
fetchInputBoolean,
|
||||||
|
updateInputBoolean,
|
||||||
|
} from "../../../../../data/input_boolean";
|
||||||
|
import {
|
||||||
|
deleteInputDateTime,
|
||||||
|
fetchInputDateTime,
|
||||||
|
updateInputDateTime,
|
||||||
|
} from "../../../../../data/input_datetime";
|
||||||
|
import {
|
||||||
|
deleteInputNumber,
|
||||||
|
fetchInputNumber,
|
||||||
|
updateInputNumber,
|
||||||
|
} from "../../../../../data/input_number";
|
||||||
|
import {
|
||||||
|
deleteInputSelect,
|
||||||
|
fetchInputSelect,
|
||||||
|
updateInputSelect,
|
||||||
|
} from "../../../../../data/input_select";
|
||||||
|
import {
|
||||||
|
deleteInputText,
|
||||||
|
fetchInputText,
|
||||||
|
updateInputText,
|
||||||
|
} from "../../../../../data/input_text";
|
||||||
|
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import "../../../helpers/forms/ha-input_boolean-form";
|
||||||
|
import "../../../helpers/forms/ha-input_text-form";
|
||||||
|
import "../../../helpers/forms/ha-input_datetime-form";
|
||||||
|
import "../../../helpers/forms/ha-input_select-form";
|
||||||
|
import "../../../helpers/forms/ha-input_number-form";
|
||||||
|
import { Helper } from "../../../helpers/const";
|
||||||
|
import "../../entity-registry-basic-editor";
|
||||||
|
// tslint:disable-next-line: no-duplicate-imports
|
||||||
|
import { HaEntityRegistryBasicEditor } from "../../entity-registry-basic-editor";
|
||||||
|
|
||||||
|
const HELPERS = {
|
||||||
|
input_boolean: {
|
||||||
|
fetch: fetchInputBoolean,
|
||||||
|
update: updateInputBoolean,
|
||||||
|
delete: deleteInputBoolean,
|
||||||
|
},
|
||||||
|
input_text: {
|
||||||
|
fetch: fetchInputText,
|
||||||
|
update: updateInputText,
|
||||||
|
delete: deleteInputText,
|
||||||
|
},
|
||||||
|
input_number: {
|
||||||
|
fetch: fetchInputNumber,
|
||||||
|
update: updateInputNumber,
|
||||||
|
delete: deleteInputNumber,
|
||||||
|
},
|
||||||
|
input_datetime: {
|
||||||
|
fetch: fetchInputDateTime,
|
||||||
|
update: updateInputDateTime,
|
||||||
|
delete: deleteInputDateTime,
|
||||||
|
},
|
||||||
|
input_select: {
|
||||||
|
fetch: fetchInputSelect,
|
||||||
|
update: updateInputSelect,
|
||||||
|
delete: deleteInputSelect,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("entity-settings-helper-tab")
|
||||||
|
export class EntityRegistrySettingsHelper extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public entry!: ExtEntityRegistryEntry;
|
||||||
|
@property() public dialogElement!: HaPaperDialog;
|
||||||
|
@property() private _error?: string;
|
||||||
|
@property() private _item?: Helper | null;
|
||||||
|
@property() private _submitting?: boolean;
|
||||||
|
@property() private _componentLoaded?: boolean;
|
||||||
|
@query("ha-registry-basic-editor")
|
||||||
|
private _registryEditor?: HaEntityRegistryBasicEditor;
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
this._componentLoaded = isComponentLoaded(this.hass, this.entry.platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProperties: PropertyValues) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
if (changedProperties.has("entry")) {
|
||||||
|
this._error = undefined;
|
||||||
|
this._item = undefined;
|
||||||
|
this._getItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (this._item === undefined) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
if (!this._componentLoaded) {
|
||||||
|
return html`
|
||||||
|
<paper-dialog-scrollable .dialogElement=${this.dialogElement}>
|
||||||
|
The ${this.entry.platform} component is not loaded, please add it your
|
||||||
|
configuration. Either by adding 'default_config:' or
|
||||||
|
'${this.entry.platform}:'.
|
||||||
|
</paper-dialog-scrollable>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
if (this._item === null) {
|
||||||
|
return html`
|
||||||
|
<paper-dialog-scrollable .dialogElement=${this.dialogElement}>
|
||||||
|
This entity can not be edited from the UI. Only entities setup from
|
||||||
|
the UI are editable.
|
||||||
|
</paper-dialog-scrollable>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<paper-dialog-scrollable .dialogElement=${this.dialogElement}>
|
||||||
|
${this._error
|
||||||
|
? html`
|
||||||
|
<div class="error">${this._error}</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<div class="form">
|
||||||
|
<div @value-changed=${this._valueChanged}>
|
||||||
|
${dynamicElement(`ha-${this.entry.platform}-form`, {
|
||||||
|
hass: this.hass,
|
||||||
|
item: this._item,
|
||||||
|
entry: this.entry,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<ha-registry-basic-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entry=${this.entry}
|
||||||
|
></ha-registry-basic-editor>
|
||||||
|
</div>
|
||||||
|
</paper-dialog-scrollable>
|
||||||
|
<div class="buttons">
|
||||||
|
<mwc-button
|
||||||
|
class="warning"
|
||||||
|
@click=${this._confirmDeleteItem}
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.dialogs.entity_registry.editor.delete")}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${this._updateItem}
|
||||||
|
.disabled=${this._submitting || !this._item.name}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.dialogs.entity_registry.editor.update")}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
this._error = undefined;
|
||||||
|
this._item = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getItem() {
|
||||||
|
const items = await HELPERS[this.entry.platform].fetch(this.hass!);
|
||||||
|
this._item = items.find((item) => item.id === this.entry.unique_id) || null;
|
||||||
|
await this.updateComplete;
|
||||||
|
fireEvent(this.dialogElement as HTMLElement, "iron-resize");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateItem(): Promise<void> {
|
||||||
|
if (!this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._submitting = true;
|
||||||
|
try {
|
||||||
|
await HELPERS[this.entry.platform].update(
|
||||||
|
this.hass!,
|
||||||
|
this._item.id,
|
||||||
|
this._item
|
||||||
|
);
|
||||||
|
await this._registryEditor?.updateEntry();
|
||||||
|
fireEvent(this, "close-dialog");
|
||||||
|
} catch (err) {
|
||||||
|
this._error = err.message || "Unknown error";
|
||||||
|
} finally {
|
||||||
|
this._submitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _confirmDeleteItem(): Promise<void> {
|
||||||
|
if (!this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!(await showConfirmationDialog(this, {
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.confirm_delete"
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._submitting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await HELPERS[this.entry.platform].delete(this.hass!, this._item.id);
|
||||||
|
fireEvent(this, "close-dialog");
|
||||||
|
} finally {
|
||||||
|
this._submitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: -20px;
|
||||||
|
}
|
||||||
|
mwc-button.warning {
|
||||||
|
--mdc-theme-primary: var(--google-red-500);
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--google-red-500);
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.secondary {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"entity-platform-helper-tab": EntityRegistrySettingsHelper;
|
||||||
|
}
|
||||||
|
}
|
138
src/panels/config/entities/entity-registry-basic-editor.ts
Normal file
138
src/panels/config/entities/entity-registry-basic-editor.ts
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import {
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
PropertyValues,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "../../../components/ha-switch";
|
||||||
|
import {
|
||||||
|
ExtEntityRegistryEntry,
|
||||||
|
EntityRegistryEntryUpdateParams,
|
||||||
|
updateEntityRegistryEntry,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
|
// tslint:disable-next-line: no-duplicate-imports
|
||||||
|
import { HaSwitch } from "../../../components/ha-switch";
|
||||||
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
|
|
||||||
|
@customElement("ha-registry-basic-editor")
|
||||||
|
export class HaEntityRegistryBasicEditor extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public entry!: ExtEntityRegistryEntry;
|
||||||
|
@property() private _origEntityId!: string;
|
||||||
|
@property() private _entityId!: string;
|
||||||
|
@property() private _disabledBy!: string | null;
|
||||||
|
@property() private _submitting?: boolean;
|
||||||
|
|
||||||
|
public async updateEntry(): Promise<void> {
|
||||||
|
this._submitting = true;
|
||||||
|
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
||||||
|
new_entity_id: this._entityId.trim(),
|
||||||
|
};
|
||||||
|
if (this._disabledBy === null || this._disabledBy === "user") {
|
||||||
|
params.disabled_by = this._disabledBy;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await updateEntityRegistryEntry(this.hass!, this._origEntityId, params);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
this._submitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProperties: PropertyValues) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
if (!changedProperties.has("entry")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.entry) {
|
||||||
|
this._origEntityId = this.entry.entity_id;
|
||||||
|
this._entityId = this.entry.entity_id;
|
||||||
|
this._disabledBy = this.entry.disabled_by;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (
|
||||||
|
!this.hass ||
|
||||||
|
!this.entry ||
|
||||||
|
this.entry.entity_id !== this._origEntityId
|
||||||
|
) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const invalidDomainUpdate =
|
||||||
|
computeDomain(this._entityId.trim()) !==
|
||||||
|
computeDomain(this.entry.entity_id);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.value=${this._entityId}
|
||||||
|
@value-changed=${this._entityIdChanged}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.entity_id"
|
||||||
|
)}
|
||||||
|
error-message="Domain needs to stay the same"
|
||||||
|
.invalid=${invalidDomainUpdate}
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
></paper-input>
|
||||||
|
<div class="row">
|
||||||
|
<ha-switch
|
||||||
|
.checked=${!this._disabledBy}
|
||||||
|
@change=${this._disabledByChanged}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.enabled_label"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="secondary">
|
||||||
|
${this._disabledBy && this._disabledBy !== "user"
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.enabled_cause",
|
||||||
|
"cause",
|
||||||
|
this.hass.localize(
|
||||||
|
`config_entry.disabled_by.${this._disabledBy}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: ""}
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.enabled_description"
|
||||||
|
)}
|
||||||
|
<br />${this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.note"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ha-switch>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityIdChanged(ev: PolymerChangedEvent<string>): void {
|
||||||
|
this._entityId = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _disabledByChanged(ev: Event): void {
|
||||||
|
this._disabledBy = (ev.target as HaSwitch).checked ? null : "user";
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
.row {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.secondary {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
@ -12,26 +12,28 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
|
||||||
import "../../../components/ha-switch";
|
import "../../../components/ha-switch";
|
||||||
|
import "../../../components/ha-icon-input";
|
||||||
// tslint:disable-next-line: no-duplicate-imports
|
// tslint:disable-next-line: no-duplicate-imports
|
||||||
import { HaSwitch } from "../../../components/ha-switch";
|
import { HaSwitch } from "../../../components/ha-switch";
|
||||||
import {
|
import {
|
||||||
EntityRegistryEntry,
|
|
||||||
removeEntityRegistryEntry,
|
removeEntityRegistryEntry,
|
||||||
updateEntityRegistryEntry,
|
updateEntityRegistryEntry,
|
||||||
EntityRegistryEntryUpdateParams,
|
EntityRegistryEntryUpdateParams,
|
||||||
|
ExtEntityRegistryEntry,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
|
||||||
@customElement("entity-registry-settings")
|
@customElement("entity-registry-settings")
|
||||||
export class EntityRegistrySettings extends LitElement {
|
export class EntityRegistrySettings extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
@property() public entry!: EntityRegistryEntry;
|
@property() public entry!: ExtEntityRegistryEntry;
|
||||||
@property() public dialogElement!: HTMLElement;
|
@property() public dialogElement!: HTMLElement;
|
||||||
@property() private _name!: string;
|
@property() private _name!: string;
|
||||||
|
@property() private _icon!: string;
|
||||||
@property() private _entityId!: string;
|
@property() private _entityId!: string;
|
||||||
@property() private _disabledBy!: string | null;
|
@property() private _disabledBy!: string | null;
|
||||||
@property() private _error?: string;
|
@property() private _error?: string;
|
||||||
@ -43,6 +45,7 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
if (changedProperties.has("entry")) {
|
if (changedProperties.has("entry")) {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._name = this.entry.name || "";
|
this._name = this.entry.name || "";
|
||||||
|
this._icon = this.entry.icon || "";
|
||||||
this._origEntityId = this.entry.entity_id;
|
this._origEntityId = this.entry.entity_id;
|
||||||
this._entityId = this.entry.entity_id;
|
this._entityId = this.entry.entity_id;
|
||||||
this._disabledBy = this.entry.disabled_by;
|
this._disabledBy = this.entry.disabled_by;
|
||||||
@ -59,7 +62,6 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
const invalidDomainUpdate =
|
const invalidDomainUpdate =
|
||||||
computeDomain(this._entityId.trim()) !==
|
computeDomain(this._entityId.trim()) !==
|
||||||
computeDomain(this.entry.entity_id);
|
computeDomain(this.entry.entity_id);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<paper-dialog-scrollable .dialogElement=${this.dialogElement}>
|
<paper-dialog-scrollable .dialogElement=${this.dialogElement}>
|
||||||
${!stateObj
|
${!stateObj
|
||||||
@ -83,9 +85,21 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.entity_registry.editor.name"
|
"ui.dialogs.entity_registry.editor.name"
|
||||||
)}
|
)}
|
||||||
.placeholder=${stateObj ? computeStateName(stateObj) : ""}
|
.placeholder=${this.entry.original_name}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
<ha-icon-input
|
||||||
|
.value=${this._icon}
|
||||||
|
@value-changed=${this._iconChanged}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.icon"
|
||||||
|
)}
|
||||||
|
.placeholder=${this.entry.original_icon}
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
.errorMessage=${this.hass.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.icon_error"
|
||||||
|
)}
|
||||||
|
></ha-icon-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
.value=${this._entityId}
|
.value=${this._entityId}
|
||||||
@value-changed=${this._entityIdChanged}
|
@value-changed=${this._entityIdChanged}
|
||||||
@ -153,6 +167,11 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
this._name = ev.detail.value;
|
this._name = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _iconChanged(ev: PolymerChangedEvent<string>): void {
|
||||||
|
this._error = undefined;
|
||||||
|
this._icon = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
private _entityIdChanged(ev: PolymerChangedEvent<string>): void {
|
private _entityIdChanged(ev: PolymerChangedEvent<string>): void {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._entityId = ev.detail.value;
|
this._entityId = ev.detail.value;
|
||||||
@ -162,6 +181,7 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
||||||
name: this._name.trim() || null,
|
name: this._name.trim() || null,
|
||||||
|
icon: this._icon.trim() || null,
|
||||||
new_entity_id: this._entityId.trim(),
|
new_entity_id: this._entityId.trim(),
|
||||||
};
|
};
|
||||||
if (this._disabledBy === null || this._disabledBy === "user") {
|
if (this._disabledBy === null || this._disabledBy === "user") {
|
||||||
@ -192,7 +212,7 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await removeEntityRegistryEntry(this.hass!, this._origEntityId);
|
await removeEntityRegistryEntry(this.hass!, this._origEntityId);
|
||||||
fireEvent(this as HTMLElement, "close-dialog");
|
fireEvent(this, "close-dialog");
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
@ -202,36 +222,39 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
this._disabledBy = (ev.target as HaSwitch).checked ? null : "user";
|
this._disabledBy = (ev.target as HaSwitch).checked ? null : "user";
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult[] {
|
||||||
return css`
|
return [
|
||||||
:host {
|
haStyle,
|
||||||
display: block;
|
css`
|
||||||
margin-bottom: 0 !important;
|
:host {
|
||||||
padding: 0 !important;
|
display: block;
|
||||||
}
|
margin-bottom: 0 !important;
|
||||||
.form {
|
padding: 0 !important;
|
||||||
padding-bottom: 24px;
|
}
|
||||||
}
|
.form {
|
||||||
.buttons {
|
padding-bottom: 24px;
|
||||||
display: flex;
|
}
|
||||||
justify-content: flex-end;
|
.buttons {
|
||||||
padding: 8px;
|
display: flex;
|
||||||
}
|
justify-content: flex-end;
|
||||||
mwc-button.warning {
|
padding: 8px;
|
||||||
margin-right: auto;
|
}
|
||||||
--mdc-theme-primary: var(--google-red-500);
|
mwc-button.warning {
|
||||||
}
|
margin-right: auto;
|
||||||
.error {
|
--mdc-theme-primary: var(--google-red-500);
|
||||||
color: var(--google-red-500);
|
}
|
||||||
}
|
.error {
|
||||||
.row {
|
color: var(--google-red-500);
|
||||||
margin-top: 8px;
|
}
|
||||||
color: var(--primary-text-color);
|
.row {
|
||||||
}
|
margin-top: 8px;
|
||||||
.secondary {
|
color: var(--primary-text-color);
|
||||||
color: var(--secondary-text-color);
|
}
|
||||||
}
|
.secondary {
|
||||||
`;
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,11 +39,11 @@ import "../../../layouts/hass-loading-screen";
|
|||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { DialogEntityRegistryDetail } from "./dialog-entity-registry-detail";
|
import { DialogEntityEditor } from "./dialog-entity-editor";
|
||||||
import {
|
import {
|
||||||
loadEntityRegistryDetailDialog,
|
loadEntityEditorDialog,
|
||||||
showEntityRegistryDetailDialog,
|
showEntityEditorDialog,
|
||||||
} from "./show-dialog-entity-registry-detail";
|
} from "./show-dialog-entity-editor";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
@ -75,7 +75,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
@property() private _selectedEntities: string[] = [];
|
@property() private _selectedEntities: string[] = [];
|
||||||
@query("hass-tabs-subpage-data-table")
|
@query("hass-tabs-subpage-data-table")
|
||||||
private _dataTable!: HaTabsSubpageDataTable;
|
private _dataTable!: HaTabsSubpageDataTable;
|
||||||
private getDialog?: () => DialogEntityRegistryDetail | undefined;
|
private getDialog?: () => DialogEntityEditor | undefined;
|
||||||
|
|
||||||
private _columns = memoize(
|
private _columns = memoize(
|
||||||
(narrow, _language): DataTableColumnContainer => {
|
(narrow, _language): DataTableColumnContainer => {
|
||||||
@ -387,33 +387,35 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.integrations}
|
.tabs=${configSections.integrations}
|
||||||
.columns=${this._columns(this.narrow, this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.language)}
|
||||||
.data=${this._filteredEntities(
|
.data=${this._filteredEntities(
|
||||||
this._entities,
|
this._entities,
|
||||||
this.hass.states,
|
this.hass.states,
|
||||||
this._showDisabled,
|
this._showDisabled,
|
||||||
this._showUnavailable,
|
this._showUnavailable,
|
||||||
this._showReadOnly
|
this._showReadOnly
|
||||||
)}
|
)}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
selectable
|
selectable
|
||||||
@selection-changed=${this._handleSelectionChanged}
|
@selection-changed=${this._handleSelectionChanged}
|
||||||
@row-click=${this._openEditEntry}
|
@row-click=${this._openEditEntry}
|
||||||
id="entity_id"
|
id="entity_id"
|
||||||
>
|
>
|
||||||
<div class=${classMap({
|
<div
|
||||||
"search-toolbar": this.narrow,
|
class=${classMap({
|
||||||
"table-header": !this.narrow,
|
"search-toolbar": this.narrow,
|
||||||
})} slot="header">
|
"table-header": !this.narrow,
|
||||||
${headerToolbar}
|
})}
|
||||||
</div>
|
slot="header"
|
||||||
</ha-data-table>
|
>
|
||||||
|
${headerToolbar}
|
||||||
|
</div>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps): void {
|
protected firstUpdated(changedProps): void {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
loadEntityRegistryDetailDialog();
|
loadEntityEditorDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showDisabledChanged() {
|
private _showDisabledChanged() {
|
||||||
@ -524,7 +526,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
const entry = this._entities!.find(
|
const entry = this._entities!.find(
|
||||||
(entity) => entity.entity_id === entityId
|
(entity) => entity.entity_id === entityId
|
||||||
);
|
);
|
||||||
this.getDialog = showEntityRegistryDetailDialog(this, {
|
this.getDialog = showEntityEditorDialog(this, {
|
||||||
entry,
|
entry,
|
||||||
entity_id: entityId,
|
entity_id: entityId,
|
||||||
});
|
});
|
||||||
|
@ -1,32 +1,33 @@
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||||
import { DialogEntityRegistryDetail } from "./dialog-entity-registry-detail";
|
import { DialogEntityEditor } from "./dialog-entity-editor";
|
||||||
|
|
||||||
export interface EntityRegistryDetailDialogParams {
|
export interface EntityRegistryDetailDialogParams {
|
||||||
entry?: EntityRegistryEntry;
|
entry?: EntityRegistryEntry;
|
||||||
entity_id: string;
|
entity_id: string;
|
||||||
|
tab?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadEntityRegistryDetailDialog = () =>
|
export const loadEntityEditorDialog = () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "entity-registry-detail-dialog" */ "./dialog-entity-registry-detail"
|
/* webpackChunkName: "entity-editor-dialog" */ "./dialog-entity-editor"
|
||||||
);
|
);
|
||||||
|
|
||||||
const getDialog = () => {
|
const getDialog = () => {
|
||||||
return document
|
return document
|
||||||
.querySelector("home-assistant")!
|
.querySelector("home-assistant")!
|
||||||
.shadowRoot!.querySelector("dialog-entity-registry-detail") as
|
.shadowRoot!.querySelector("dialog-entity-editor") as
|
||||||
| DialogEntityRegistryDetail
|
| DialogEntityEditor
|
||||||
| undefined;
|
| undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const showEntityRegistryDetailDialog = (
|
export const showEntityEditorDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
entityDetailParams: EntityRegistryDetailDialogParams
|
entityDetailParams: EntityRegistryDetailDialogParams
|
||||||
): (() => DialogEntityRegistryDetail | undefined) => {
|
): (() => DialogEntityEditor | undefined) => {
|
||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "dialog-entity-registry-detail",
|
dialogTag: "dialog-entity-editor",
|
||||||
dialogImport: loadEntityRegistryDetailDialog,
|
dialogImport: loadEntityEditorDialog,
|
||||||
dialogParams: entityDetailParams,
|
dialogParams: entityDetailParams,
|
||||||
});
|
});
|
||||||
return getDialog;
|
return getDialog;
|
@ -6,10 +6,6 @@ import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
|||||||
import { HomeAssistant, Route } from "../../types";
|
import { HomeAssistant, Route } from "../../types";
|
||||||
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
||||||
import { listenMediaQuery } from "../../common/dom/media_query";
|
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||||
import {
|
|
||||||
getOptimisticFrontendUserDataCollection,
|
|
||||||
CoreFrontendUserData,
|
|
||||||
} from "../../data/frontend";
|
|
||||||
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||||
import { PolymerElement } from "@polymer/polymer";
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||||
@ -71,6 +67,13 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
|||||||
translationKey: "ui.panel.config.script.caption",
|
translationKey: "ui.panel.config.script.caption",
|
||||||
icon: "hass:script-text",
|
icon: "hass:script-text",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: "helpers",
|
||||||
|
path: "/config/helpers",
|
||||||
|
translationKey: "ui.panel.config.helpers.caption",
|
||||||
|
icon: "hass:tools",
|
||||||
|
core: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
persons: [
|
persons: [
|
||||||
{
|
{
|
||||||
@ -235,6 +238,13 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
/* webpackChunkName: "panel-config-scene" */ "./scene/ha-config-scene"
|
/* webpackChunkName: "panel-config-scene" */ "./scene/ha-config-scene"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
helpers: {
|
||||||
|
tag: "ha-config-helpers",
|
||||||
|
load: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-helpers" */ "./helpers/ha-config-helpers"
|
||||||
|
),
|
||||||
|
},
|
||||||
users: {
|
users: {
|
||||||
tag: "ha-config-users",
|
tag: "ha-config-users",
|
||||||
load: () =>
|
load: () =>
|
||||||
@ -268,8 +278,6 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
|
|
||||||
@property() private _wideSidebar: boolean = false;
|
@property() private _wideSidebar: boolean = false;
|
||||||
@property() private _wide: boolean = false;
|
@property() private _wide: boolean = false;
|
||||||
@property() private _coreUserData?: CoreFrontendUserData;
|
|
||||||
@property() private _showAdvanced = false;
|
|
||||||
@property() private _cloudStatus?: CloudStatus;
|
@property() private _cloudStatus?: CloudStatus;
|
||||||
|
|
||||||
private _listeners: Array<() => void> = [];
|
private _listeners: Array<() => void> = [];
|
||||||
@ -286,17 +294,6 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
this._wideSidebar = matches;
|
this._wideSidebar = matches;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this._listeners.push(
|
|
||||||
getOptimisticFrontendUserDataCollection(
|
|
||||||
this.hass.connection,
|
|
||||||
"core"
|
|
||||||
).subscribe((coreUserData) => {
|
|
||||||
this._coreUserData = coreUserData || {};
|
|
||||||
this._showAdvanced = !!(
|
|
||||||
this._coreUserData && this._coreUserData.showAdvanced
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
@ -337,7 +334,7 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
(el as PolymerElement).setProperties({
|
(el as PolymerElement).setProperties({
|
||||||
route: this.routeTail,
|
route: this.routeTail,
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
showAdvanced: this._showAdvanced,
|
showAdvanced: Boolean(this.hass.userData?.showAdvanced),
|
||||||
isWide,
|
isWide,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
cloudStatus: this._cloudStatus,
|
cloudStatus: this._cloudStatus,
|
||||||
@ -345,7 +342,7 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
} else {
|
} else {
|
||||||
el.route = this.routeTail;
|
el.route = this.routeTail;
|
||||||
el.hass = this.hass;
|
el.hass = this.hass;
|
||||||
el.showAdvanced = this._showAdvanced;
|
el.showAdvanced = Boolean(this.hass.userData?.showAdvanced);
|
||||||
el.isWide = isWide;
|
el.isWide = isWide;
|
||||||
el.narrow = this.narrow;
|
el.narrow = this.narrow;
|
||||||
el.cloudStatus = this._cloudStatus;
|
el.cloudStatus = this._cloudStatus;
|
||||||
|
24
src/panels/config/helpers/const.ts
Normal file
24
src/panels/config/helpers/const.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { InputBoolean } from "../../../data/input_boolean";
|
||||||
|
|
||||||
|
import { InputText } from "../../../data/input_text";
|
||||||
|
|
||||||
|
import { InputNumber } from "../../../data/input_number";
|
||||||
|
|
||||||
|
import { InputSelect } from "../../../data/input_select";
|
||||||
|
|
||||||
|
import { InputDateTime } from "../../../data/input_datetime";
|
||||||
|
|
||||||
|
export const HELPER_DOMAINS = [
|
||||||
|
"input_boolean",
|
||||||
|
"input_text",
|
||||||
|
"input_number",
|
||||||
|
"input_datetime",
|
||||||
|
"input_select",
|
||||||
|
];
|
||||||
|
|
||||||
|
export type Helper =
|
||||||
|
| InputBoolean
|
||||||
|
| InputText
|
||||||
|
| InputNumber
|
||||||
|
| InputSelect
|
||||||
|
| InputDateTime;
|
197
src/panels/config/helpers/dialog-helper-detail.ts
Normal file
197
src/panels/config/helpers/dialog-helper-detail.ts
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../components/ha-dialog";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
|
||||||
|
import { createInputBoolean } from "../../../data/input_boolean";
|
||||||
|
import { createInputText } from "../../../data/input_text";
|
||||||
|
import { createInputNumber } from "../../../data/input_number";
|
||||||
|
import { createInputDateTime } from "../../../data/input_datetime";
|
||||||
|
import { createInputSelect } from "../../../data/input_select";
|
||||||
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
|
import { Helper } from "./const";
|
||||||
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
|
import "./forms/ha-input_boolean-form";
|
||||||
|
import "./forms/ha-input_text-form";
|
||||||
|
import "./forms/ha-input_datetime-form";
|
||||||
|
import "./forms/ha-input_select-form";
|
||||||
|
import "./forms/ha-input_number-form";
|
||||||
|
import { domainIcon } from "../../../common/entity/domain_icon";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
|
||||||
|
const HELPERS = {
|
||||||
|
input_boolean: createInputBoolean,
|
||||||
|
input_text: createInputText,
|
||||||
|
input_number: createInputNumber,
|
||||||
|
input_datetime: createInputDateTime,
|
||||||
|
input_select: createInputSelect,
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("dialog-helper-detail")
|
||||||
|
export class DialogHelperDetail extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() private _item?: Helper;
|
||||||
|
@property() private _opened = false;
|
||||||
|
@property() private _platform?: string;
|
||||||
|
@property() private _error?: string;
|
||||||
|
@property() private _submitting = false;
|
||||||
|
|
||||||
|
public async showDialog(): Promise<void> {
|
||||||
|
this._platform = undefined;
|
||||||
|
this._item = undefined;
|
||||||
|
this._opened = true;
|
||||||
|
await this.updateComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._opened = false;
|
||||||
|
this._error = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
.open=${this._opened}
|
||||||
|
@closing=${this.closeDialog}
|
||||||
|
class=${classMap({ "button-left": !this._platform })}
|
||||||
|
.heading=${this._platform
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.helpers.dialog.add_platform",
|
||||||
|
"platform",
|
||||||
|
this.hass.localize(
|
||||||
|
`ui.panel.config.helpers.types.${this._platform}`
|
||||||
|
) || this._platform
|
||||||
|
)
|
||||||
|
: this.hass.localize("ui.panel.config.helpers.dialog.add_helper")}
|
||||||
|
>
|
||||||
|
${this._platform
|
||||||
|
? html`
|
||||||
|
<div class="form" @value-changed=${this._valueChanged}>
|
||||||
|
${this._error
|
||||||
|
? html`
|
||||||
|
<div class="error">${this._error}</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${dynamicElement(`ha-${this._platform}-form`, {
|
||||||
|
hass: this.hass,
|
||||||
|
item: this._item,
|
||||||
|
new: true,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
@click="${this._createItem}"
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
|
${this.hass!.localize("ui.panel.config.helpers.dialog.create")}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
@click="${this._goBack}"
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
${Object.keys(HELPERS).map((platform: string) => {
|
||||||
|
return html`
|
||||||
|
<paper-icon-item
|
||||||
|
.disabled=${!isComponentLoaded(this.hass, platform)}
|
||||||
|
@click="${this._platformPicked}"
|
||||||
|
.platform="${platform}"
|
||||||
|
>
|
||||||
|
<ha-icon
|
||||||
|
slot="item-icon"
|
||||||
|
.icon=${domainIcon(platform)}
|
||||||
|
></ha-icon>
|
||||||
|
<span class="item-text">
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.helpers.types.${platform}`
|
||||||
|
) || platform}
|
||||||
|
</span>
|
||||||
|
</paper-icon-item>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
<mwc-button slot="primaryAction" @click="${this.closeDialog}">
|
||||||
|
${this.hass!.localize("ui.common.cancel")}
|
||||||
|
</mwc-button>
|
||||||
|
`}
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
this._item = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _createItem(): Promise<void> {
|
||||||
|
if (!this._platform || !this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._submitting = true;
|
||||||
|
this._error = "";
|
||||||
|
try {
|
||||||
|
await HELPERS[this._platform](this.hass, this._item);
|
||||||
|
this.closeDialog();
|
||||||
|
} catch (err) {
|
||||||
|
this._error = err.message || "Unknown error";
|
||||||
|
} finally {
|
||||||
|
this._submitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _platformPicked(ev: Event): void {
|
||||||
|
this._platform = (ev.currentTarget! as any).platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _goBack() {
|
||||||
|
this._platform = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-title-ink-color: var(--primary-text-color);
|
||||||
|
--justify-action-buttons: space-between;
|
||||||
|
}
|
||||||
|
ha-dialog.button-left {
|
||||||
|
--justify-action-buttons: flex-start;
|
||||||
|
}
|
||||||
|
@media only screen and (min-width: 600px) {
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-min-width: 600px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make dialog fullscreen on small screens */
|
||||||
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-min-width: 100vw;
|
||||||
|
--mdc-dialog-max-height: 100vh;
|
||||||
|
--mdc-dialog-shape-radius: 0px;
|
||||||
|
--vertial-align-dialog: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--google-red-500);
|
||||||
|
}
|
||||||
|
paper-icon-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-helper-detail": DialogHelperDetail;
|
||||||
|
}
|
||||||
|
}
|
137
src/panels/config/helpers/forms/ha-input_boolean-form.ts
Normal file
137
src/panels/config/helpers/forms/ha-input_boolean-form.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
|
import "../../../../components/ha-switch";
|
||||||
|
import "../../../../components/ha-icon-input";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { InputBoolean } from "../../../../data/input_boolean";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
|
||||||
|
@customElement("ha-input_boolean-form")
|
||||||
|
class HaInputBooleanForm extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public new?: boolean;
|
||||||
|
private _item?: InputBoolean;
|
||||||
|
@property() private _name!: string;
|
||||||
|
@property() private _icon!: string;
|
||||||
|
@property() private _initial?: boolean;
|
||||||
|
|
||||||
|
set item(item: InputBoolean) {
|
||||||
|
this._item = item;
|
||||||
|
if (item) {
|
||||||
|
this._name = item.name || "";
|
||||||
|
this._icon = item.icon || "";
|
||||||
|
this._initial = item.initial;
|
||||||
|
} else {
|
||||||
|
this._name = "";
|
||||||
|
this._icon = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const nameInvalid = !this._name || this._name.trim() === "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="form">
|
||||||
|
<paper-input
|
||||||
|
.value=${this._name}
|
||||||
|
.configValue=${"name"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.name"
|
||||||
|
)}
|
||||||
|
.errorMessage="${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.required_error_msg"
|
||||||
|
)}"
|
||||||
|
.invalid=${nameInvalid}
|
||||||
|
></paper-input>
|
||||||
|
<ha-icon-input
|
||||||
|
.value=${this._icon}
|
||||||
|
.configValue=${"icon"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.icon"
|
||||||
|
)}
|
||||||
|
></ha-icon-input>
|
||||||
|
<br />
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value_explain"
|
||||||
|
)}
|
||||||
|
${this.hass.userData?.showAdvanced
|
||||||
|
? html`
|
||||||
|
<div class="row layout horizontal justified">
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value"
|
||||||
|
)}:
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._initial}
|
||||||
|
@change=${this._initialChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _initialChanged(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, initial: ev.target.checked },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
if (!this.new && !this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.stopPropagation();
|
||||||
|
const configValue = (ev.target as any).configValue;
|
||||||
|
const value = ev.detail.value;
|
||||||
|
if (this[`_${configValue}`] === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newValue = { ...this._item };
|
||||||
|
if (!value) {
|
||||||
|
delete newValue[configValue];
|
||||||
|
} else {
|
||||||
|
newValue[configValue] = ev.detail.value;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: newValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.form {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-input_boolean-form": HaInputBooleanForm;
|
||||||
|
}
|
||||||
|
}
|
165
src/panels/config/helpers/forms/ha-input_datetime-form.ts
Normal file
165
src/panels/config/helpers/forms/ha-input_datetime-form.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
|
import "../../../../components/ha-switch";
|
||||||
|
import "../../../../components/ha-icon-input";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
import { InputDateTime } from "../../../../data/input_datetime";
|
||||||
|
|
||||||
|
@customElement("ha-input_datetime-form")
|
||||||
|
class HaInputDateTimeForm extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public new?: boolean;
|
||||||
|
private _item?: InputDateTime;
|
||||||
|
@property() private _name!: string;
|
||||||
|
@property() private _icon!: string;
|
||||||
|
@property() private _initial?: string;
|
||||||
|
@property() private _hasTime?: boolean;
|
||||||
|
@property() private _hasDate?: boolean;
|
||||||
|
|
||||||
|
set item(item: InputDateTime) {
|
||||||
|
this._item = item;
|
||||||
|
if (item) {
|
||||||
|
this._name = item.name || "";
|
||||||
|
this._icon = item.icon || "";
|
||||||
|
this._initial = item.initial;
|
||||||
|
this._hasTime = item.has_time;
|
||||||
|
this._hasDate = item.has_date;
|
||||||
|
} else {
|
||||||
|
this._name = "";
|
||||||
|
this._icon = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const nameInvalid = !this._name || this._name.trim() === "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="form">
|
||||||
|
<paper-input
|
||||||
|
.value=${this._name}
|
||||||
|
.configValue=${"name"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.name"
|
||||||
|
)}
|
||||||
|
.errorMessage="${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.required_error_msg"
|
||||||
|
)}"
|
||||||
|
.invalid=${nameInvalid}
|
||||||
|
></paper-input>
|
||||||
|
<ha-icon-input
|
||||||
|
.value=${this._icon}
|
||||||
|
.configValue=${"icon"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.icon"
|
||||||
|
)}
|
||||||
|
></ha-icon-input>
|
||||||
|
<div class="row layout horizontal justified">
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_datetime.has_time"
|
||||||
|
)}:
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._hasTime}
|
||||||
|
@change=${this._hasTimeChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
<div class="row layout horizontal justified">
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_datetime.has_date"
|
||||||
|
)}:
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._hasDate}
|
||||||
|
@change=${this._hasDateChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
${this.hass.userData?.showAdvanced
|
||||||
|
? html`
|
||||||
|
<br />
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value_explain"
|
||||||
|
)}
|
||||||
|
<paper-input
|
||||||
|
.value=${this._initial}
|
||||||
|
.configValue=${"initial"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hasTimeChanged(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, has_time: ev.target.checked },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hasDateChanged(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, has_date: ev.target.checked },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
if (!this.new && !this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.stopPropagation();
|
||||||
|
const configValue = (ev.target as any).configValue;
|
||||||
|
const value = ev.detail.value;
|
||||||
|
if (this[`_${configValue}`] === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newValue = { ...this._item };
|
||||||
|
if (!value) {
|
||||||
|
delete newValue[configValue];
|
||||||
|
} else {
|
||||||
|
newValue[configValue] = ev.detail.value;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: newValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.form {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-input_datetime-form": HaInputDateTimeForm;
|
||||||
|
}
|
||||||
|
}
|
214
src/panels/config/helpers/forms/ha-input_number-form.ts
Normal file
214
src/panels/config/helpers/forms/ha-input_number-form.ts
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
|
import "../../../../components/ha-switch";
|
||||||
|
import "../../../../components/ha-icon-input";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
import { InputNumber } from "../../../../data/input_number";
|
||||||
|
|
||||||
|
@customElement("ha-input_number-form")
|
||||||
|
class HaInputNumberForm extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public new?: boolean;
|
||||||
|
private _item?: Partial<InputNumber>;
|
||||||
|
@property() private _name!: string;
|
||||||
|
@property() private _icon!: string;
|
||||||
|
@property() private _initial?: number;
|
||||||
|
@property() private _max?: number;
|
||||||
|
@property() private _min?: number;
|
||||||
|
@property() private _mode?: string;
|
||||||
|
@property() private _step?: number;
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
@property() private _unit_of_measurement?: string;
|
||||||
|
|
||||||
|
set item(item: InputNumber) {
|
||||||
|
this._item = item;
|
||||||
|
if (item) {
|
||||||
|
this._name = item.name || "";
|
||||||
|
this._icon = item.icon || "";
|
||||||
|
this._max = item.max ?? 100;
|
||||||
|
this._min = item.min ?? 0;
|
||||||
|
this._initial = item.initial;
|
||||||
|
this._mode = item.mode || "slider";
|
||||||
|
this._step = item.step || 1;
|
||||||
|
this._unit_of_measurement = item.unit_of_measurement;
|
||||||
|
} else {
|
||||||
|
this._item = {
|
||||||
|
min: 0,
|
||||||
|
max: 0,
|
||||||
|
};
|
||||||
|
this._name = "";
|
||||||
|
this._icon = "";
|
||||||
|
this._max = 100;
|
||||||
|
this._min = 0;
|
||||||
|
this._mode = "slider";
|
||||||
|
this._step = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const nameInvalid = !this._name || this._name.trim() === "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="form">
|
||||||
|
<paper-input
|
||||||
|
.value=${this._name}
|
||||||
|
.configValue=${"name"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.name"
|
||||||
|
)}
|
||||||
|
.errorMessage="${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.required_error_msg"
|
||||||
|
)}"
|
||||||
|
.invalid=${nameInvalid}
|
||||||
|
></paper-input>
|
||||||
|
<ha-icon-input
|
||||||
|
.value=${this._icon}
|
||||||
|
.configValue=${"icon"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.icon"
|
||||||
|
)}
|
||||||
|
></ha-icon-input>
|
||||||
|
<paper-input
|
||||||
|
.value=${this._min}
|
||||||
|
.configValue=${"min"}
|
||||||
|
type="number"
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_number.min"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.value=${this._max}
|
||||||
|
.configValue=${"max"}
|
||||||
|
type="number"
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_number.max"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
${this.hass.userData?.showAdvanced
|
||||||
|
? html`
|
||||||
|
<div class="layout horizontal center justified">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_number.mode"
|
||||||
|
)}
|
||||||
|
<paper-radio-group
|
||||||
|
.selected=${this._mode}
|
||||||
|
@selected-changed=${this._modeChanged}
|
||||||
|
>
|
||||||
|
<paper-radio-button name="slider">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_number.slider"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
<paper-radio-button name="box">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_number.box"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
</paper-radio-group>
|
||||||
|
</div>
|
||||||
|
${this._mode === "slider"
|
||||||
|
? html`
|
||||||
|
<paper-input
|
||||||
|
.value=${this._step}
|
||||||
|
.configValue=${"step"}
|
||||||
|
type="number"
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_number.step"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<paper-input
|
||||||
|
.value=${this._unit_of_measurement}
|
||||||
|
.configValue=${"unit_of_measurement"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_number.unit_of_measurement"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
<br />
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value_explain"
|
||||||
|
)}
|
||||||
|
<paper-input
|
||||||
|
.value=${this._initial}
|
||||||
|
.configValue=${"initial"}
|
||||||
|
type="number"
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _modeChanged(ev: CustomEvent) {
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, mode: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
if (!this.new && !this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.stopPropagation();
|
||||||
|
const configValue = (ev.target as any).configValue;
|
||||||
|
const value = ev.detail.value;
|
||||||
|
if (this[`_${configValue}`] === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newValue = { ...this._item };
|
||||||
|
if (value === undefined || value === "") {
|
||||||
|
delete newValue[configValue];
|
||||||
|
} else {
|
||||||
|
newValue[configValue] = ev.detail.value;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: newValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.form {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
ha-paper-dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-input_number-form": HaInputNumberForm;
|
||||||
|
}
|
||||||
|
}
|
240
src/panels/config/helpers/forms/ha-input_select-form.ts
Normal file
240
src/panels/config/helpers/forms/ha-input_select-form.ts
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
query,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
|
import "../../../../components/ha-switch";
|
||||||
|
import "../../../../components/ha-icon-input";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
import { InputSelect } from "../../../../data/input_select";
|
||||||
|
// tslint:disable-next-line: no-duplicate-imports
|
||||||
|
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
|
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
|
|
||||||
|
@customElement("ha-input_select-form")
|
||||||
|
class HaInputSelectForm extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public new?: boolean;
|
||||||
|
private _item?: InputSelect;
|
||||||
|
@property() private _name!: string;
|
||||||
|
@property() private _icon!: string;
|
||||||
|
@property() private _options: string[] = [];
|
||||||
|
@property() private _initial?: string;
|
||||||
|
@query("#option_input") private _optionInput?: PaperInputElement;
|
||||||
|
|
||||||
|
set item(item: InputSelect) {
|
||||||
|
this._item = item;
|
||||||
|
if (item) {
|
||||||
|
this._name = item.name || "";
|
||||||
|
this._icon = item.icon || "";
|
||||||
|
this._initial = item.initial;
|
||||||
|
this._options = item.options || [];
|
||||||
|
} else {
|
||||||
|
this._name = "";
|
||||||
|
this._icon = "";
|
||||||
|
this._options = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const nameInvalid = !this._name || this._name.trim() === "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="form">
|
||||||
|
<paper-input
|
||||||
|
.value=${this._name}
|
||||||
|
.configValue=${"name"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.name"
|
||||||
|
)}
|
||||||
|
.errorMessage="${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.required_error_msg"
|
||||||
|
)}"
|
||||||
|
.invalid=${nameInvalid}
|
||||||
|
></paper-input>
|
||||||
|
<ha-icon-input
|
||||||
|
.value=${this._icon}
|
||||||
|
.configValue=${"icon"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.icon"
|
||||||
|
)}
|
||||||
|
></ha-icon-input>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_select.options"
|
||||||
|
)}:
|
||||||
|
${this._options.length
|
||||||
|
? this._options.map((option, index) => {
|
||||||
|
return html`
|
||||||
|
<paper-item class="option">
|
||||||
|
<paper-item-body> ${option} </paper-item-body>
|
||||||
|
<paper-icon-button
|
||||||
|
.index=${index}
|
||||||
|
.title=${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_select.remove_option"
|
||||||
|
)}
|
||||||
|
@click=${this._removeOption}
|
||||||
|
icon="hass:delete"
|
||||||
|
></paper-icon-button>
|
||||||
|
</paper-item>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
: html`
|
||||||
|
<paper-item>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_select.no_options"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
`}
|
||||||
|
<div class="layout horizontal bottom">
|
||||||
|
<paper-input
|
||||||
|
class="flex-auto"
|
||||||
|
id="option_input"
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_select.add_option"
|
||||||
|
)}
|
||||||
|
@keydown=${this._handleKeyAdd}
|
||||||
|
></paper-input>
|
||||||
|
<mwc-button @click=${this._addOption}
|
||||||
|
>${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_select.add"
|
||||||
|
)}</mwc-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
${this.hass.userData?.showAdvanced
|
||||||
|
? html`
|
||||||
|
<br />
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value_explain"
|
||||||
|
)}
|
||||||
|
<ha-paper-dropdown-menu
|
||||||
|
label-float
|
||||||
|
dynamic-align
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
attr-for-selected="item-initial"
|
||||||
|
.selected=${this._initial}
|
||||||
|
@selected-changed=${this._initialChanged}
|
||||||
|
>
|
||||||
|
${this._options.map(
|
||||||
|
(option) => html`
|
||||||
|
<paper-item item-initial=${option}>${option}</paper-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</paper-listbox>
|
||||||
|
</ha-paper-dropdown-menu>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _initialChanged(ev: CustomEvent) {
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, initial: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleKeyAdd(ev: KeyboardEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (ev.keyCode !== 13) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._addOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addOption() {
|
||||||
|
const input = this._optionInput;
|
||||||
|
if (!input || !input.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, options: [...this._options, input.value] },
|
||||||
|
});
|
||||||
|
input.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _removeOption(ev: Event) {
|
||||||
|
if (
|
||||||
|
!(await showConfirmationDialog(this, {
|
||||||
|
title: "Delete this item?",
|
||||||
|
text: "Are you sure you want to delete this item?",
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const index = (ev.target as any).index;
|
||||||
|
const options = [...this._options];
|
||||||
|
options.splice(index, 1);
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, options },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
if (!this.new && !this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.stopPropagation();
|
||||||
|
const configValue = (ev.target as any).configValue;
|
||||||
|
const value = ev.detail.value;
|
||||||
|
if (this[`_${configValue}`] === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newValue = { ...this._item };
|
||||||
|
if (!value) {
|
||||||
|
delete newValue[configValue];
|
||||||
|
} else {
|
||||||
|
newValue[configValue] = ev.detail.value;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: newValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.form {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.option {
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
mwc-button {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
ha-paper-dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-input_select-form": HaInputSelectForm;
|
||||||
|
}
|
||||||
|
}
|
199
src/panels/config/helpers/forms/ha-input_text-form.ts
Normal file
199
src/panels/config/helpers/forms/ha-input_text-form.ts
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
|
import "../../../../components/ha-switch";
|
||||||
|
import "../../../../components/ha-icon-input";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { InputText } from "../../../../data/input_text";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
|
||||||
|
@customElement("ha-input_text-form")
|
||||||
|
class HaInputTextForm extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public new?: boolean;
|
||||||
|
private _item?: InputText;
|
||||||
|
@property() private _name!: string;
|
||||||
|
@property() private _icon!: string;
|
||||||
|
@property() private _initial?: string;
|
||||||
|
@property() private _max?: number;
|
||||||
|
@property() private _min?: number;
|
||||||
|
@property() private _mode?: string;
|
||||||
|
@property() private _pattern?: string;
|
||||||
|
|
||||||
|
set item(item: InputText) {
|
||||||
|
this._item = item;
|
||||||
|
if (item) {
|
||||||
|
this._name = item.name || "";
|
||||||
|
this._icon = item.icon || "";
|
||||||
|
this._max = item.max || 100;
|
||||||
|
this._min = item.min || 0;
|
||||||
|
this._initial = item.initial;
|
||||||
|
this._mode = item.mode || "text";
|
||||||
|
this._pattern = item.pattern;
|
||||||
|
} else {
|
||||||
|
this._name = "";
|
||||||
|
this._icon = "";
|
||||||
|
this._max = 100;
|
||||||
|
this._min = 0;
|
||||||
|
this._mode = "text";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const nameInvalid = !this._name || this._name.trim() === "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="form">
|
||||||
|
<paper-input
|
||||||
|
.value=${this._name}
|
||||||
|
.configValue=${"name"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.name"
|
||||||
|
)}
|
||||||
|
.errorMessage="${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.required_error_msg"
|
||||||
|
)}"
|
||||||
|
.invalid=${nameInvalid}
|
||||||
|
></paper-input>
|
||||||
|
<ha-icon-input
|
||||||
|
.value=${this._icon}
|
||||||
|
.configValue=${"icon"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.icon"
|
||||||
|
)}
|
||||||
|
></ha-icon-input>
|
||||||
|
${this.hass.userData?.showAdvanced
|
||||||
|
? html`
|
||||||
|
<paper-input
|
||||||
|
.value=${this._min}
|
||||||
|
.configValue=${"min"}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="255"
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_text.min"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.value=${this._max}
|
||||||
|
.configValue=${"max"}
|
||||||
|
min="0"
|
||||||
|
max="255"
|
||||||
|
type="number"
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_text.max"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
<div class="layout horizontal center justified">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_text.mode"
|
||||||
|
)}
|
||||||
|
<paper-radio-group
|
||||||
|
.selected=${this._mode}
|
||||||
|
@selected-changed=${this._modeChanged}
|
||||||
|
>
|
||||||
|
<paper-radio-button name="text">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_text.text"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
<paper-radio-button name="password">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_text.password"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
</paper-radio-group>
|
||||||
|
</div>
|
||||||
|
<paper-input
|
||||||
|
.value=${this._pattern}
|
||||||
|
.configValue=${"pattern"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.input_text.pattern"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
<br />
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value_explain"
|
||||||
|
)}
|
||||||
|
<paper-input
|
||||||
|
.value=${this._initial}
|
||||||
|
.configValue=${"initial"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.dialogs.helper_settings.generic.initial_value"
|
||||||
|
)}
|
||||||
|
></paper-input>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _modeChanged(ev: CustomEvent) {
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this._item, mode: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
if (!this.new && !this._item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.stopPropagation();
|
||||||
|
const configValue = (ev.target as any).configValue;
|
||||||
|
const value = ev.detail.value;
|
||||||
|
if (this[`_${configValue}`] === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newValue = { ...this._item };
|
||||||
|
if (!value) {
|
||||||
|
delete newValue[configValue];
|
||||||
|
} else {
|
||||||
|
newValue[configValue] = ev.detail.value;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: newValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.form {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
ha-paper-dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-input_text-form": HaInputTextForm;
|
||||||
|
}
|
||||||
|
}
|
184
src/panels/config/helpers/ha-config-helpers.ts
Normal file
184
src/panels/config/helpers/ha-config-helpers.ts
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import "@polymer/paper-checkbox/paper-checkbox";
|
||||||
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
|
} from "lit-element";
|
||||||
|
import memoize from "memoize-one";
|
||||||
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
|
import "../../../common/search/search-input";
|
||||||
|
import {
|
||||||
|
DataTableColumnContainer,
|
||||||
|
RowClickedEvent,
|
||||||
|
} from "../../../components/data-table/ha-data-table";
|
||||||
|
import "../../../components/ha-icon";
|
||||||
|
import "../../../layouts/hass-loading-screen";
|
||||||
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
|
import { configSections } from "../ha-panel-config";
|
||||||
|
import { showEntityEditorDialog } from "../entities/show-dialog-entity-editor";
|
||||||
|
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
|
||||||
|
import { HELPER_DOMAINS } from "./const";
|
||||||
|
|
||||||
|
@customElement("ha-config-helpers")
|
||||||
|
export class HaConfigHelpers extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public isWide!: boolean;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
@property() public route!: Route;
|
||||||
|
@property() private _stateItems: HassEntity[] = [];
|
||||||
|
|
||||||
|
private _columns = memoize(
|
||||||
|
(_language): DataTableColumnContainer => {
|
||||||
|
return {
|
||||||
|
icon: {
|
||||||
|
title: "",
|
||||||
|
type: "icon",
|
||||||
|
template: (icon) => html`
|
||||||
|
<ha-icon slot="item-icon" .icon=${icon}></ha-icon>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.helpers.picker.headers.name"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
direction: "asc",
|
||||||
|
template: (name, item: any) =>
|
||||||
|
html`
|
||||||
|
${name}
|
||||||
|
<div style="color: var(--secondary-text-color)">
|
||||||
|
${item.entity_id}
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.helpers.picker.headers.type"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
template: (type) =>
|
||||||
|
html`
|
||||||
|
${this.hass.localize(`ui.panel.config.helpers.types.${type}`) ||
|
||||||
|
type}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _getItems = memoize((stateItems: HassEntity[]) => {
|
||||||
|
return stateItems.map((state) => {
|
||||||
|
return {
|
||||||
|
id: state.entity_id,
|
||||||
|
icon: state.attributes.icon,
|
||||||
|
name: state.attributes.friendly_name || "",
|
||||||
|
entity_id: state.entity_id,
|
||||||
|
editable: state.attributes.editable,
|
||||||
|
type: computeStateDomain(state),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass || this._stateItems === undefined) {
|
||||||
|
return html`
|
||||||
|
<hass-loading-screen></hass-loading-screen>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hass-tabs-subpage-data-table
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
back-path="/config"
|
||||||
|
.route=${this.route}
|
||||||
|
.tabs=${configSections.automation}
|
||||||
|
.columns=${this._columns(this.hass.language)}
|
||||||
|
.data=${this._getItems(this._stateItems)}
|
||||||
|
@row-click=${this._openEditDialog}
|
||||||
|
>
|
||||||
|
</hass-tabs-subpage-data-table>
|
||||||
|
<ha-fab
|
||||||
|
?is-wide=${this.isWide}
|
||||||
|
?narrow=${this.narrow}
|
||||||
|
icon="hass:plus"
|
||||||
|
title="${this.hass.localize(
|
||||||
|
"ui.panel.config.helpers.picker.add_helper"
|
||||||
|
)}"
|
||||||
|
@click=${this._createHelpler}
|
||||||
|
></ha-fab>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._getStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
|
if (oldHass && this._stateItems) {
|
||||||
|
this._getStates(oldHass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getStates(oldHass?: HomeAssistant) {
|
||||||
|
let changed = false;
|
||||||
|
const tempStates = Object.values(this.hass!.states).filter((entity) => {
|
||||||
|
if (!HELPER_DOMAINS.includes(computeStateDomain(entity))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (oldHass?.states[entity.entity_id] !== entity) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changed || this._stateItems.length !== tempStates.length) {
|
||||||
|
this._stateItems = tempStates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _openEditDialog(ev: CustomEvent): Promise<void> {
|
||||||
|
const entityId = (ev.detail as RowClickedEvent).id;
|
||||||
|
showEntityEditorDialog(this, {
|
||||||
|
entity_id: entityId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createHelpler() {
|
||||||
|
showHelperDetailDialog(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-fab {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
ha-fab[is-wide] {
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
}
|
||||||
|
ha-fab[narrow] {
|
||||||
|
bottom: 84px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
14
src/panels/config/helpers/show-dialog-helper-detail.ts
Normal file
14
src/panels/config/helpers/show-dialog-helper-detail.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
export const loadHelperDetailDialog = () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "helper-detail-dialog" */ "./dialog-helper-detail"
|
||||||
|
);
|
||||||
|
|
||||||
|
export const showHelperDetailDialog = (element: HTMLElement) => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-helper-detail",
|
||||||
|
dialogImport: loadHelperDetailDialog,
|
||||||
|
dialogParams: {},
|
||||||
|
});
|
||||||
|
};
|
@ -148,6 +148,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
${this._config.show_icon
|
${this._config.show_icon
|
||||||
? html`
|
? html`
|
||||||
<ha-icon
|
<ha-icon
|
||||||
|
tabindex="-1"
|
||||||
data-domain=${ifDefined(
|
data-domain=${ifDefined(
|
||||||
this._config.state_color && stateObj
|
this._config.state_color && stateObj
|
||||||
? computeStateDomain(stateObj)
|
? computeStateDomain(stateObj)
|
||||||
@ -170,7 +171,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
: ""}
|
: ""}
|
||||||
${this._config.show_name
|
${this._config.show_name
|
||||||
? html`
|
? html`
|
||||||
<span>
|
<span tabindex="-1">
|
||||||
${this._config.name ||
|
${this._config.name ||
|
||||||
(stateObj ? computeStateName(stateObj) : "")}
|
(stateObj ? computeStateName(stateObj) : "")}
|
||||||
</span>
|
</span>
|
||||||
@ -224,6 +225,11 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
color: var(--paper-item-icon-color, #44739e);
|
color: var(--paper-item-icon-color, #44739e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-icon,
|
||||||
|
span {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
${iconColorCSS}
|
${iconColorCSS}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import {
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
import "../../components/hui-theme-select-editor";
|
import "../../components/hui-theme-select-editor";
|
||||||
|
|
||||||
|
import "../../../../components/ha-icon-input";
|
||||||
import "../../components/hui-entity-editor";
|
import "../../components/hui-entity-editor";
|
||||||
|
|
||||||
import { struct } from "../../common/structs/struct";
|
import { struct } from "../../common/structs/struct";
|
||||||
@ -17,6 +19,7 @@ import { LovelaceCardEditor } from "../../types";
|
|||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import { LightCardConfig } from "../../cards/types";
|
import { LightCardConfig } from "../../cards/types";
|
||||||
|
import { stateIcon } from "../../../../common/entity/state_icon";
|
||||||
|
|
||||||
const cardConfigStruct = struct({
|
const cardConfigStruct = struct({
|
||||||
type: "string",
|
type: "string",
|
||||||
@ -34,8 +37,7 @@ export class HuiLightCardEditor extends LitElement
|
|||||||
@property() private _config?: LightCardConfig;
|
@property() private _config?: LightCardConfig;
|
||||||
|
|
||||||
public setConfig(config: LightCardConfig): void {
|
public setConfig(config: LightCardConfig): void {
|
||||||
config = cardConfigStruct(config);
|
this._config = cardConfigStruct(config);
|
||||||
this._config = config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get _name(): string {
|
get _name(): string {
|
||||||
@ -86,16 +88,18 @@ export class HuiLightCardEditor extends LitElement
|
|||||||
.configValue="${"name"}"
|
.configValue="${"name"}"
|
||||||
@value-changed="${this._valueChanged}"
|
@value-changed="${this._valueChanged}"
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<ha-icon-input
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.generic.icon"
|
"ui.panel.lovelace.editor.card.generic.icon"
|
||||||
)} (${this.hass.localize(
|
)} (${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.config.optional"
|
"ui.panel.lovelace.editor.card.config.optional"
|
||||||
)})"
|
)})"
|
||||||
.value="${this._icon}"
|
.value="${this._icon}"
|
||||||
|
.placeholder=${this._icon ||
|
||||||
|
stateIcon(this.hass.states[this._entity])}
|
||||||
.configValue="${"icon"}"
|
.configValue="${"icon"}"
|
||||||
@value-changed="${this._valueChanged}"
|
@value-changed="${this._valueChanged}"
|
||||||
></paper-input>
|
></ha-icon-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hui-theme-select-editor
|
<hui-theme-select-editor
|
||||||
|
@ -20,7 +20,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name";
|
|||||||
|
|
||||||
import { HomeAssistant, InputSelectEntity } from "../../../types";
|
import { HomeAssistant, InputSelectEntity } from "../../../types";
|
||||||
import { LovelaceRow } from "./types";
|
import { LovelaceRow } from "./types";
|
||||||
import { setInputSelectOption } from "../../../data/input-select";
|
import { setInputSelectOption } from "../../../data/input_select";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import { forwardHaptic } from "../../../data/haptics";
|
import { forwardHaptic } from "../../../data/haptics";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
|
@ -118,6 +118,9 @@ export const haStyle = css`
|
|||||||
.layout.center-center {
|
.layout.center-center {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.layout.bottom {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
.layout.center-justified,
|
.layout.center-justified,
|
||||||
.layout.center-center {
|
.layout.center-center {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -18,7 +18,7 @@ import "../components/entity/state-badge";
|
|||||||
|
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { HomeAssistant, InputSelectEntity } from "../types";
|
import { HomeAssistant, InputSelectEntity } from "../types";
|
||||||
import { setInputSelectOption } from "../data/input-select";
|
import { setInputSelectOption } from "../data/input_select";
|
||||||
import { PolymerIronSelectEvent } from "../polymer-types";
|
import { PolymerIronSelectEvent } from "../polymer-types";
|
||||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import { fireEvent } from "../common/dom/fire_event";
|
|||||||
import { Constructor, ServiceCallResponse } from "../types";
|
import { Constructor, ServiceCallResponse } from "../types";
|
||||||
import { HassBaseEl } from "./hass-base-mixin";
|
import { HassBaseEl } from "./hass-base-mixin";
|
||||||
import { broadcastConnectionStatus } from "../data/connection-status";
|
import { broadcastConnectionStatus } from "../data/connection-status";
|
||||||
|
import { subscribeFrontendUserData } from "../data/frontend";
|
||||||
|
|
||||||
export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
||||||
superClass: T
|
superClass: T
|
||||||
@ -141,6 +142,9 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
|||||||
subscribeConfig(conn, (config) => this._updateHass({ config }));
|
subscribeConfig(conn, (config) => this._updateHass({ config }));
|
||||||
subscribeServices(conn, (services) => this._updateHass({ services }));
|
subscribeServices(conn, (services) => this._updateHass({ services }));
|
||||||
subscribePanels(conn, (panels) => this._updateHass({ panels }));
|
subscribePanels(conn, (panels) => this._updateHass({ panels }));
|
||||||
|
subscribeFrontendUserData(conn, "core", (userData) =>
|
||||||
|
this._updateHass({ userData })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected hassReconnected() {
|
protected hassReconnected() {
|
||||||
|
@ -644,6 +644,8 @@
|
|||||||
"no_unique_id": "This entity does not have a unique ID, therefore it's settings can not be managed from the UI.",
|
"no_unique_id": "This entity does not have a unique ID, therefore it's settings can not be managed from the UI.",
|
||||||
"editor": {
|
"editor": {
|
||||||
"name": "Name Override",
|
"name": "Name Override",
|
||||||
|
"icon": "Icon Override",
|
||||||
|
"icon_error": "Icons should be in the format 'prefix:iconname', e.g. 'mdi:home'",
|
||||||
"entity_id": "Entity ID",
|
"entity_id": "Entity ID",
|
||||||
"unavailable": "This entity is not currently available.",
|
"unavailable": "This entity is not currently available.",
|
||||||
"enabled_label": "Enable entity",
|
"enabled_label": "Enable entity",
|
||||||
@ -655,6 +657,44 @@
|
|||||||
"note": "Note: this might not work yet with all integrations."
|
"note": "Note: this might not work yet with all integrations."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"helper_settings": {
|
||||||
|
"not_editable": "Not editable",
|
||||||
|
"not_editable_text": "This entity can't be changed from the UI because it is defined in configuration.yaml.",
|
||||||
|
"required_error_msg": "This field is required",
|
||||||
|
"generic": {
|
||||||
|
"name": "Name",
|
||||||
|
"icon": "Icon",
|
||||||
|
"initial_value": "Initial value at start",
|
||||||
|
"initial_value_explain": "The value the element will have when Home Assistant starts. When left empty, the value will be restored to it's previous value."
|
||||||
|
},
|
||||||
|
"input_datetime": {
|
||||||
|
"has_time": "Time",
|
||||||
|
"has_date": "Date"
|
||||||
|
},
|
||||||
|
"input_text": {
|
||||||
|
"min": "Minimum lenght",
|
||||||
|
"max": "Maximum lenght",
|
||||||
|
"mode": "Display mode",
|
||||||
|
"text": "Text",
|
||||||
|
"password": "Password",
|
||||||
|
"pattern": "Regex pattern for client-side validation"
|
||||||
|
},
|
||||||
|
"input_number": {
|
||||||
|
"min": "Minimum value",
|
||||||
|
"max": "Maximum value",
|
||||||
|
"mode": "Display mode",
|
||||||
|
"box": "Input field",
|
||||||
|
"slider": "Slider",
|
||||||
|
"step": "Step size of the slider",
|
||||||
|
"unit_of_measurement": "Unit of measurement"
|
||||||
|
},
|
||||||
|
"input_select": {
|
||||||
|
"options": "Options",
|
||||||
|
"add_option": "Add option",
|
||||||
|
"no_options": "There are no options yet.",
|
||||||
|
"add": "Add"
|
||||||
|
}
|
||||||
|
},
|
||||||
"options_flow": {
|
"options_flow": {
|
||||||
"form": {
|
"form": {
|
||||||
"header": "Options"
|
"header": "Options"
|
||||||
@ -757,6 +797,30 @@
|
|||||||
"create": "CREATE"
|
"create": "CREATE"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"helpers": {
|
||||||
|
"caption": "Helpers",
|
||||||
|
"description": "Elements that can help build automations.",
|
||||||
|
"types": {
|
||||||
|
"input_text": "Text",
|
||||||
|
"input_number": "Number",
|
||||||
|
"input_select": "Dropdown",
|
||||||
|
"input_boolean": "Toggle",
|
||||||
|
"input_datetime": "Date and/or time"
|
||||||
|
},
|
||||||
|
"picker": {
|
||||||
|
"headers": {
|
||||||
|
"name": "Name",
|
||||||
|
"type": "Type",
|
||||||
|
"editable": "Editable"
|
||||||
|
},
|
||||||
|
"add_helper": "Add helper"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"create": "Create",
|
||||||
|
"add_helper": "Add helper",
|
||||||
|
"add_platform": "Add {platform}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"caption": "General",
|
"caption": "General",
|
||||||
"description": "Change your general Home Assistant configuration",
|
"description": "Change your general Home Assistant configuration",
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { LocalizeFunc } from "./common/translations/localize";
|
import { LocalizeFunc } from "./common/translations/localize";
|
||||||
import { ExternalMessaging } from "./external_app/external_messaging";
|
import { ExternalMessaging } from "./external_app/external_messaging";
|
||||||
|
import { CoreFrontendUserData } from "./data/frontend";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
var __DEV__: boolean;
|
var __DEV__: boolean;
|
||||||
@ -155,6 +156,7 @@ export interface HomeAssistant {
|
|||||||
dockedSidebar: "docked" | "always_hidden" | "auto";
|
dockedSidebar: "docked" | "always_hidden" | "auto";
|
||||||
moreInfoEntityId: string | null;
|
moreInfoEntityId: string | null;
|
||||||
user?: CurrentUser;
|
user?: CurrentUser;
|
||||||
|
userData?: CoreFrontendUserData | null;
|
||||||
hassUrl(path?): string;
|
hassUrl(path?): string;
|
||||||
callService(
|
callService(
|
||||||
domain: string,
|
domain: string,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user