From 3246bc2a98782b90e4a61e672b597ae9123f5472 Mon Sep 17 00:00:00 2001 From: farmio Date: Mon, 27 Jan 2025 15:33:31 +0100 Subject: [PATCH 1/3] Support optional ha-selectors in entity configuration --- src/components/knx-configure-entity.ts | 22 ++-- src/components/knx-selector-row.ts | 146 +++++++++++++++++++++++++ src/utils/schema.ts | 19 ++-- 3 files changed, 168 insertions(+), 19 deletions(-) create mode 100644 src/components/knx-selector-row.ts diff --git a/src/components/knx-configure-entity.ts b/src/components/knx-configure-entity.ts index a92ee44..1a5e4d1 100644 --- a/src/components/knx-configure-entity.ts +++ b/src/components/knx-configure-entity.ts @@ -14,6 +14,7 @@ import { fireEvent } from "@ha/common/dom/fire_event"; import type { HomeAssistant } from "@ha/types"; import "./knx-group-address-selector"; +import "./knx-selector-row"; import "./knx-sync-state-selector-row"; import { renderConfigureEntityCard } from "./knx-configure-entity-options"; import { KNXLogger } from "../tools/knx-logger"; @@ -165,20 +166,14 @@ export class KNXConfigureEntity extends LitElement { > `; case "selector": - // apply default value if available and no value is set - if (selector.default !== undefined && this.config!.knx[selector.name] == null) { - this.config!.knx[selector.name] = selector.default; - } return html` - + > `; case "sync_state": return html` @@ -234,8 +229,13 @@ export class KNXConfigureEntity extends LitElement { if (!this.config[baseKey]) { this.config[baseKey] = {}; } - this.config[baseKey][ev.target.key] = ev.detail.value; - logger.debug(`update ${baseKey} key "${ev.target.key}" with "${ev.detail.value}"`); + if (ev.detail.value === undefined) { + logger.debug(`remove ${baseKey} key "${ev.target.key}"`); + delete this.config[baseKey][ev.target.key]; + } else { + logger.debug(`update ${baseKey} key "${ev.target.key}" with "${ev.detail.value}"`); + this.config[baseKey][ev.target.key] = ev.detail.value; + } fireEvent(this, "knx-entity-configuration-changed", this.config); this.requestUpdate(); }; diff --git a/src/components/knx-selector-row.ts b/src/components/knx-selector-row.ts new file mode 100644 index 0000000..c5831d0 --- /dev/null +++ b/src/components/knx-selector-row.ts @@ -0,0 +1,146 @@ +import { LitElement, html, css, nothing } from "lit"; +import type { TemplateResult, CSSResultGroup } from "lit"; +import { customElement, property, state } from "lit/decorators"; + +import { fireEvent } from "@ha/common/dom/fire_event"; +import "@ha/components/ha-checkbox"; +import "@ha/components/ha-selector/ha-selector"; +import "@ha/components/ha-switch"; +import type { HomeAssistant } from "@ha/types"; +import type { KnxHaSelector } from "../utils/schema"; + +@customElement("knx-selector-row") +export class KnxSelectorRow extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public key!: string; + + @property({ attribute: false }) public selector!: KnxHaSelector; + + @property() public value?: any; + + @state() private _disabled: boolean = false; + + private _haSelectorValue: any = null; + + public connectedCallback() { + super.connectedCallback(); + this._disabled = !!this.selector.optional && this.value === undefined; + // apply default value if available or no value is set yet + this._haSelectorValue = this.value ?? this.selector.default ?? null; + } + + protected render(): TemplateResult { + const haSelector = html``; + + const possibleInlineSelector = + "boolean" in this.selector.selector || "number" in this.selector.selector; + const inlineSelector = !this.selector.optional && possibleInlineSelector; + + return html`
+
+
+

${this.selector.label}

+

${this.selector.helper}

+
+ ${this.selector.optional + ? html`` + : inlineSelector + ? haSelector + : nothing} +
+ ${inlineSelector ? nothing : haSelector} +
`; + } + + private _toggleDisabled(ev: Event) { + ev.stopPropagation(); + this._disabled = !this._disabled; + this._propagateValue(); + } + + private _valueChange(ev: Event) { + ev.stopPropagation(); + this._haSelectorValue = ev.detail.value; + this._propagateValue(); + } + + private _propagateValue() { + fireEvent(this, "value-changed", { value: this._disabled ? undefined : this._haSelectorValue }); + } + + static get styles(): CSSResultGroup { + return css` + :host { + display: block; + padding: 8px 16px 16px 0; + border-top: 1px solid var(--divider-color); + } + .body { + padding-bottom: 8px; + display: flex; + flex-wrap: wrap; + align-items: center; + row-gap: 8px; + } + .body > * { + flex-grow: 1; + } + .text { + flex-basis: 260px; /* min size of text - if inline selector is too big it will be pushed to next row */ + } + .heading { + margin: 0; + } + .description { + margin: 0; + display: block; + padding-top: 4px; + font-family: var( + --mdc-typography-body2-font-family, + var(--mdc-typography-font-family, Roboto, sans-serif) + ); + -webkit-font-smoothing: antialiased; + font-size: var(--mdc-typography-body2-font-size, 0.875rem); + font-weight: var(--mdc-typography-body2-font-weight, 400); + line-height: normal; + color: var(--secondary-text-color); + } + /* .optional-switch { + margin-left: auto; + } */ + /* ha-settings-row { + margin: 0 -16px; + padding: var(--service-control-padding, 0 16px); + } */ + /* ha-settings-row { + padding: 0 8px; + --settings-row-content-width: 100%; + --settings-row-prefix-display: contents; + border-top: 1px solid var(--divider-color); + } */ + /* ha-checkbox { + margin-left: -16px; + margin-inline-start: -16px; + margin-inline-end: initial; + } */ + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "knx-selector-row": KnxSelectorRow; + } +} diff --git a/src/utils/schema.ts b/src/utils/schema.ts index 997d8d9..4f3c9ad 100644 --- a/src/utils/schema.ts +++ b/src/utils/schema.ts @@ -14,14 +14,17 @@ export type SelectorSchema = | GASchema | GroupSelect | { name: "sync_state"; type: "sync_state" } - | { - name: string; - type: "selector"; - default?: any; - selector: Selector; - label: string; - helper?: string; - }; + | KnxHaSelector; + +export type KnxHaSelector = { + name: string; + type: "selector"; + default?: any; + optional?: boolean; + selector: Selector; + label: string; + helper?: string; +}; export type GASchema = { name: string; From 9a67cd44c32a503590c6e1cabf27c19488951e1c Mon Sep 17 00:00:00 2001 From: farmio Date: Mon, 27 Jan 2025 16:09:13 +0100 Subject: [PATCH 2/3] style --- src/components/knx-selector-row.ts | 37 ++++++++++-------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/components/knx-selector-row.ts b/src/components/knx-selector-row.ts index c5831d0..6b9c7b7 100644 --- a/src/components/knx-selector-row.ts +++ b/src/components/knx-selector-row.ts @@ -1,6 +1,7 @@ import { LitElement, html, css, nothing } from "lit"; import type { TemplateResult, CSSResultGroup } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; import { fireEvent } from "@ha/common/dom/fire_event"; import "@ha/components/ha-checkbox"; @@ -31,7 +32,12 @@ export class KnxSelectorRow extends LitElement { } protected render(): TemplateResult { + const possibleInlineSelector = + "boolean" in this.selector.selector || "number" in this.selector.selector; + const inlineSelector = !this.selector.optional && possibleInlineSelector; + const haSelector = html``; - const possibleInlineSelector = - "boolean" in this.selector.selector || "number" in this.selector.selector; - const inlineSelector = !this.selector.optional && possibleInlineSelector; - - return html`
+ return html`

${this.selector.label}

@@ -61,7 +63,7 @@ export class KnxSelectorRow extends LitElement { : nothing}
${inlineSelector ? nothing : haSelector} -
`; + `; } private _toggleDisabled(ev: Event) { @@ -84,9 +86,12 @@ export class KnxSelectorRow extends LitElement { return css` :host { display: block; - padding: 8px 16px 16px 0; + padding: 8px 16px 8px 0; border-top: 1px solid var(--divider-color); } + .newline-selector { + padding-top: 8px; + } .body { padding-bottom: 8px; display: flex; @@ -117,24 +122,6 @@ export class KnxSelectorRow extends LitElement { line-height: normal; color: var(--secondary-text-color); } - /* .optional-switch { - margin-left: auto; - } */ - /* ha-settings-row { - margin: 0 -16px; - padding: var(--service-control-padding, 0 16px); - } */ - /* ha-settings-row { - padding: 0 8px; - --settings-row-content-width: 100%; - --settings-row-prefix-display: contents; - border-top: 1px solid var(--divider-color); - } */ - /* ha-checkbox { - margin-left: -16px; - margin-inline-start: -16px; - margin-inline-end: initial; - } */ `; } } From e327387771235355d40a1dcb29b80b7345a63165 Mon Sep 17 00:00:00 2001 From: farmio Date: Mon, 27 Jan 2025 16:49:59 +0100 Subject: [PATCH 3/3] more uniform style with sync-state-selector --- src/components/knx-configure-entity.ts | 9 +++++---- src/components/knx-selector-row.ts | 2 +- src/components/knx-sync-state-selector-row.ts | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/knx-configure-entity.ts b/src/components/knx-configure-entity.ts index 1a5e4d1..b5ac6f6 100644 --- a/src/components/knx-configure-entity.ts +++ b/src/components/knx-configure-entity.ts @@ -293,15 +293,16 @@ export class KNXConfigureEntity extends LitElement { ha-expansion-panel { margin-bottom: 16px; } - ha-expansion-panel > :first-child { - margin-top: 16px; + ha-expansion-panel > :first-child:not(ha-settings-row) { + margin-top: 16px; /* ha-settings-row has this margin internally */ } - ha-expansion-panel > ha-settings-row:first-child { + ha-expansion-panel > ha-settings-row:first-child, + ha-expansion-panel > knx-selector-row:first-child { border: 0; } ha-settings-row { - margin-bottom: 16px; + margin-bottom: 8px; padding: 0; } ha-control-select { diff --git a/src/components/knx-selector-row.ts b/src/components/knx-selector-row.ts index 6b9c7b7..e93f447 100644 --- a/src/components/knx-selector-row.ts +++ b/src/components/knx-selector-row.ts @@ -90,10 +90,10 @@ export class KnxSelectorRow extends LitElement { border-top: 1px solid var(--divider-color); } .newline-selector { + display: block; padding-top: 8px; } .body { - padding-bottom: 8px; display: flex; flex-wrap: wrap; align-items: center; diff --git a/src/components/knx-sync-state-selector-row.ts b/src/components/knx-sync-state-selector-row.ts index 6370050..e19cf42 100644 --- a/src/components/knx-sync-state-selector-row.ts +++ b/src/components/knx-sync-state-selector-row.ts @@ -107,6 +107,7 @@ export class KnxSyncStateSelectorRow extends LitElement { .inline > * { flex: 1; + width: 100%; /* to not overflow when wrapped */ } `; }