Skip to content

Commit

Permalink
Support optional ha-selectors in entity configuration (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
farmio authored Jan 27, 2025
1 parent a8cd8b1 commit bf5141b
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 23 deletions.
31 changes: 16 additions & 15 deletions src/components/knx-configure-entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -165,20 +166,14 @@ export class KNXConfigureEntity extends LitElement {
></knx-group-address-selector>
`;
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`
<ha-selector
<knx-selector-row
.hass=${this.hass}
.selector=${selector.selector}
.label=${selector.label}
.helper=${selector.helper}
.key=${selector.name}
.selector=${selector}
.value=${this.config!.knx[selector.name]}
@value-changed=${this._updateConfig("knx")}
></ha-selector>
></knx-selector-row>
`;
case "sync_state":
return html`
Expand Down Expand Up @@ -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();
};
Expand Down Expand Up @@ -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 {
Expand Down
133 changes: 133 additions & 0 deletions src/components/knx-selector-row.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
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";
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 possibleInlineSelector =
"boolean" in this.selector.selector || "number" in this.selector.selector;
const inlineSelector = !this.selector.optional && possibleInlineSelector;

const haSelector = html`<ha-selector
class=${classMap({ "newline-selector": !inlineSelector })}
.hass=${this.hass}
.selector=${this.selector.selector}
.disabled=${this._disabled}
.value=${this._haSelectorValue}
@value-changed=${this._valueChange}
></ha-selector>`;

return html`
<div class="body">
<div class="text">
<p class="heading">${this.selector.label}</p>
<p class="description">${this.selector.helper}</p>
</div>
${this.selector.optional
? html`<ha-selector
class="optional-switch"
.selector=${{ boolean: {} }}
.value=${!this._disabled}
@value-changed=${this._toggleDisabled}
></ha-selector>`
: inlineSelector
? haSelector
: nothing}
</div>
${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 8px 0;
border-top: 1px solid var(--divider-color);
}
.newline-selector {
display: block;
padding-top: 8px;
}
.body {
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);
}
`;
}
}

declare global {
interface HTMLElementTagNameMap {
"knx-selector-row": KnxSelectorRow;
}
}
1 change: 1 addition & 0 deletions src/components/knx-sync-state-selector-row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export class KnxSyncStateSelectorRow extends LitElement {
.inline > * {
flex: 1;
width: 100%; /* to not overflow when wrapped */
}
`;
}
Expand Down
19 changes: 11 additions & 8 deletions src/utils/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit bf5141b

Please sign in to comment.