From 9e597d22a55daf4e6e715ca6bddf0ff573dd335b Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Sat, 19 Jul 2025 09:18:27 +0300 Subject: [PATCH] Handle predefined options in Z-Wave config panel (#26097) * Handle predefined options in Z-Wave config panel * use ha-combo-box * lint * display invalid status on the input * show number and label * compute items outside of render --- src/components/ha-combo-box.ts | 10 ++- .../zwave_js/zwave_js-node-config.ts | 71 +++++++++++++++++-- src/translations/en.json | 1 + 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/components/ha-combo-box.ts b/src/components/ha-combo-box.ts index 44e55c55d9..4d07dcba6c 100644 --- a/src/components/ha-combo-box.ts +++ b/src/components/ha-combo-box.ts @@ -205,7 +205,7 @@ export class HaComboBox extends LitElement { role="button" tabindex="-1" aria-label=${ifDefined(this.hass?.localize("ui.common.clear"))} - class="clear-button" + class=${`clear-button ${this.label ? "" : "no-label"}`} .path=${mdiClose} @click=${this._clearValue} >` @@ -215,7 +215,7 @@ export class HaComboBox extends LitElement { tabindex="-1" aria-label=${ifDefined(this.label)} aria-expanded=${this.opened ? "true" : "false"} - class="toggle-button" + class=${`toggle-button ${this.label ? "" : "no-label"}`} .path=${this.opened ? mdiMenuUp : mdiMenuDown} ?disabled=${this.disabled} @click=${this._toggleOpen} @@ -397,6 +397,9 @@ export class HaComboBox extends LitElement { color: var(--disabled-text-color); pointer-events: none; } + .toggle-button.no-label { + top: -3px; + } .clear-button { --mdc-icon-size: 20px; top: -7px; @@ -405,6 +408,9 @@ export class HaComboBox extends LitElement { inset-inline-end: 36px; direction: var(--direction); } + .clear-button.no-label { + top: 0; + } ha-input-helper-text { margin-top: 4px; } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts index 1d98a00eb7..f7afb04bba 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts @@ -10,6 +10,7 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { computeDeviceNameDisplay } from "../../../../../common/entity/compute_device_name"; import { groupBy } from "../../../../../common/util/group-by"; @@ -23,6 +24,7 @@ import "../../../../../components/ha-selector/ha-selector-boolean"; import "../../../../../components/ha-settings-row"; import "../../../../../components/ha-svg-icon"; import "../../../../../components/ha-textfield"; +import "../../../../../components/ha-combo-box"; import type { ZWaveJSNodeCapabilities, ZWaveJSNodeConfigParam, @@ -55,7 +57,7 @@ const icons = { @customElement("zwave_js-node-config") class ZWaveJSNodeConfig extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; + public hass!: HomeAssistant; @property({ attribute: false }) public route!: Route; @@ -294,9 +296,10 @@ class ZWaveJSNodeConfig extends LitElement { ? this.hass.localize( item.metadata.default === 1 ? "ui.common.yes" : "ui.common.no" ) - : item.configuration_value_type === "enumerated" - ? item.metadata.states[item.metadata.default] || - item.metadata.default + : item.metadata.states?.[item.metadata.default] + ? item.configuration_value_type === "manual_entry" + ? `${item.metadata.default} - ${item.metadata.states[item.metadata.default]}` + : item.metadata.states[item.metadata.default] : item.metadata.default }` : ""; @@ -319,8 +322,30 @@ class ZWaveJSNodeConfig extends LitElement { `; } - if (item.configuration_value_type === "manual_entry") { + if ( + item.metadata.states && + item.metadata.min != null && + item.metadata.max != null && + item.metadata.max - item.metadata.min <= 100 + ) { + return html` + ${labelAndDescription} + + + `; + } return html`${labelAndDescription} ev.target.max) @@ -441,6 +475,33 @@ class ZWaveJSNodeConfig extends LitElement { this._updateConfigParameter(ev.target, value); } + private _getComboBoxOptions = memoizeOne((states: Record) => + Object.entries(states).map(([value, label]) => ({ + value, + label: `${value} - ${label}`, + })) + ); + + private _getComboBoxValueChangedCallback( + id: string, + item: ZWaveJSNodeConfigParam + ) { + return (ev: CustomEvent<{ value: number }>) => + this._numericInputChanged({ + ...ev, + target: { + ...ev.target, + key: id, + min: item.metadata.min, + max: item.metadata.max, + value: ev.detail.value, + property: item.property, + endpoint: item.endpoint, + propertyKey: item.property_key, + }, + }); + } + private async _updateConfigParameter(target, value) { try { const result = await setZwaveNodeConfigParameter( diff --git a/src/translations/en.json b/src/translations/en.json index 93be6ec206..08f70946b4 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6072,6 +6072,7 @@ "parameter_is_read_only": "This parameter is read-only.", "between_min_max": "Between {min} and {max}", "error_not_in_range": "Value must be between {min} and {max}", + "error_not_numeric": "Value must be a number", "error_required": "{entity} is required", "error_device_not_found": "Device not found", "set_param_accepted": "The parameter has been updated.",