diff --git a/src/panels/lovelace/card-features/hui-climate-fan-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-climate-fan-modes-card-feature.ts
index fc03ed3fbd..d347363fe2 100644
--- a/src/panels/lovelace/card-features/hui-climate-fan-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-climate-fan-modes-card-feature.ts
@@ -1,21 +1,12 @@
import { mdiFan } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import type { ControlSelectOption } from "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
import type { ClimateEntity } from "../../../data/climate";
import { ClimateEntityFeature } from "../../../data/climate";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
-import { filterModes } from "./common/filter-modes";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
ClimateFanModesCardFeatureConfig,
LovelaceCardFeatureContext,
@@ -38,34 +29,26 @@ export const supportsClimateFanModesCardFeature = (
@customElement("hui-climate-fan-modes-card-feature")
class HuiClimateFanModesCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ ClimateEntity,
+ ClimateFanModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "fan_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "fan_modes";
- @state() private _config?: ClimateFanModesCardFeatureConfig;
-
- @state() _currentFanMode?: string;
-
- private _renderFanModeIcon = (value: string) =>
- html``;
-
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | ClimateEntity
- | undefined;
+ protected get _configuredModes() {
+ return this._config?.fan_modes;
}
+ protected readonly _dropdownIconPath = mdiFan;
+
+ protected readonly _serviceDomain = "climate";
+
+ protected readonly _serviceAction = "set_fan_mode";
+
static getStubConfig(): ClimateFanModesCardFeatureConfig {
return {
type: "climate-fan-modes",
@@ -78,120 +61,12 @@ class HuiClimateFanModesCardFeature
return document.createElement("hui-climate-fan-modes-card-feature-editor");
}
- public setConfig(config: ClimateFanModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentFanMode = this._stateObj.attributes.fan_mode;
- }
- }
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const fanMode = ev.detail.value ?? ev.detail.item?.value;
-
- const oldFanMode = this._stateObj!.attributes.fan_mode;
-
- if (fanMode === oldFanMode || !fanMode) {
- return;
- }
-
- this._currentFanMode = fanMode;
-
- try {
- await this._setMode(fanMode);
- } catch (_err) {
- this._currentFanMode = oldFanMode;
- }
- }
-
- private async _setMode(mode: string) {
- await this.hass!.callService("climate", "set_fan_mode", {
- entity_id: this._stateObj!.entity_id,
- fan_mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsClimateFanModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const stateObj = this._stateObj;
-
- const options = filterModes(
- stateObj.attributes.fan_modes,
- this._config!.fan_modes
- ).map((mode) => ({
- value: mode,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "fan_mode",
- mode
- ),
- }));
-
- if (this._config.style === "icons") {
- return html`
- ({
- ...option,
- icon: html``,
- }))}
- .value=${this._currentFanMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .label=${this.hass!.formatEntityAttributeName(stateObj, "fan_mode")}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
- }
-
- return html`
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsClimateFanModesCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts
index 202f4d5aa5..bfc00a441d 100644
--- a/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts
@@ -1,28 +1,30 @@
import { mdiThermostat } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
-import { styleMap } from "lit/directives/style-map";
+import type { TemplateResult } from "lit";
+import { html } from "lit";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateColorCss } from "../../../common/entity/state_color";
-import "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
-import type { ClimateEntity, HvacMode } from "../../../data/climate";
+import type { ClimateEntity } from "../../../data/climate";
import {
climateHvacModeIcon,
compareClimateHvacModes,
} from "../../../data/climate";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
import { filterModes } from "./common/filter-modes";
+import {
+ HuiModeSelectCardFeatureBase,
+ type HuiModeSelectOption,
+} from "./hui-mode-select-card-feature-base";
import type {
ClimateHvacModesCardFeatureConfig,
LovelaceCardFeatureContext,
} from "./types";
+interface HvacModeOption extends HuiModeSelectOption {
+ iconPath: string;
+}
+
export const supportsClimateHvacModesCardFeature = (
hass: HomeAssistant,
context: LovelaceCardFeatureContext
@@ -37,24 +39,44 @@ export const supportsClimateHvacModesCardFeature = (
@customElement("hui-climate-hvac-modes-card-feature")
class HuiClimateHvacModesCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ ClimateEntity,
+ ClimateHvacModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "hvac_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "hvac_modes";
- @state() private _config?: ClimateHvacModesCardFeatureConfig;
+ protected get _configuredModes() {
+ return this._config?.hvac_modes;
+ }
- @state() _currentHvacMode?: HvacMode;
+ protected readonly _dropdownIconPath = mdiThermostat;
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
+ protected readonly _serviceDomain = "climate";
+
+ protected readonly _serviceAction = "set_hvac_mode";
+
+ protected get _label(): string {
+ return this.hass!.localize("ui.card.climate.mode");
+ }
+
+ protected readonly _showDropdownOptionIcons = false;
+
+ protected readonly _defaultStyle = "icons";
+
+ protected get _controlSelectStyle():
+ | Record
+ | undefined {
+ if (!this._stateObj) {
return undefined;
}
- return this.hass.states[this.context.entity_id!] as
- | ClimateEntity
- | undefined;
+
+ return {
+ "--control-select-color": stateColorCss(this._stateObj),
+ };
}
static getStubConfig(): ClimateHvacModesCardFeatureConfig {
@@ -68,119 +90,42 @@ class HuiClimateHvacModesCardFeature
return document.createElement("hui-climate-hvac-modes-card-feature-editor");
}
- public setConfig(config: ClimateHvacModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
+ protected _getValue(stateObj: ClimateEntity): string | undefined {
+ return stateObj.state;
}
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentHvacMode = this._stateObj.state as HvacMode;
- }
- }
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const mode = ev.detail.value ?? ev.detail.item?.value;
-
- if (mode === this._stateObj!.state || !mode) {
- return;
+ protected _getOptions(): HvacModeOption[] {
+ if (!this._stateObj || !this.hass) {
+ return [];
}
- const oldMode = this._stateObj!.state as HvacMode;
- this._currentHvacMode = mode as HvacMode;
-
- try {
- await this._setMode(this._currentHvacMode);
- } catch (_err) {
- this._currentHvacMode = oldMode;
- }
- }
-
- private async _setMode(mode: HvacMode) {
- await this.hass!.callService("climate", "set_hvac_mode", {
- entity_id: this._stateObj!.entity_id,
- hvac_mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsClimateHvacModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const color = stateColorCss(this._stateObj);
-
- const ordererHvacModes = (this._stateObj.attributes.hvac_modes || [])
+ const orderedHvacModes = (this._stateObj.attributes.hvac_modes || [])
.concat()
.sort(compareClimateHvacModes)
.reverse();
- const options = filterModes(ordererHvacModes, this._config.hvac_modes).map(
+ return filterModes(orderedHvacModes, this._config?.hvac_modes).map(
(mode) => ({
- value: mode as string,
+ value: mode,
label: this.hass!.formatEntityState(this._stateObj!, mode),
iconPath: climateHvacModeIcon(mode),
})
);
-
- if (this._config.style === "dropdown") {
- return html`
-
-
-
- `;
- }
-
- return html`
- ({
- ...option,
- icon: html``,
- }))}
- .value=${this._currentHvacMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .label=${this.hass.localize("ui.card.climate.mode")}
- style=${styleMap({
- "--control-select-color": color,
- })}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
}
- static get styles() {
- return cardFeatureStyles;
+ protected _renderOptionIcon(option: HvacModeOption): TemplateResult<1> {
+ return html``;
+ }
+
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsClimateHvacModesCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-climate-preset-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-climate-preset-modes-card-feature.ts
index df92172317..69294c900f 100644
--- a/src/panels/lovelace/card-features/hui-climate-preset-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-climate-preset-modes-card-feature.ts
@@ -1,20 +1,12 @@
import { mdiTuneVariant } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
import type { ClimateEntity } from "../../../data/climate";
import { ClimateEntityFeature } from "../../../data/climate";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
-import { filterModes } from "./common/filter-modes";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
ClimatePresetModesCardFeatureConfig,
LovelaceCardFeatureContext,
@@ -37,34 +29,26 @@ export const supportsClimatePresetModesCardFeature = (
@customElement("hui-climate-preset-modes-card-feature")
class HuiClimatePresetModesCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ ClimateEntity,
+ ClimatePresetModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "preset_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "preset_modes";
- @state() private _config?: ClimatePresetModesCardFeatureConfig;
-
- @state() _currentPresetMode?: string;
-
- private _renderPresetModeIcon = (value: string) =>
- html``;
-
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | ClimateEntity
- | undefined;
+ protected get _configuredModes() {
+ return this._config?.preset_modes;
}
+ protected readonly _dropdownIconPath = mdiTuneVariant;
+
+ protected readonly _serviceDomain = "climate";
+
+ protected readonly _serviceAction = "set_preset_mode";
+
static getStubConfig(): ClimatePresetModesCardFeatureConfig {
return {
type: "climate-preset-modes",
@@ -79,124 +63,12 @@ class HuiClimatePresetModesCardFeature
);
}
- public setConfig(config: ClimatePresetModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentPresetMode = this._stateObj.attributes.preset_mode;
- }
- }
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const presetMode = ev.detail.value ?? ev.detail.item?.value;
-
- const oldPresetMode = this._stateObj!.attributes.preset_mode;
-
- if (presetMode === oldPresetMode || !presetMode) {
- return;
- }
-
- this._currentPresetMode = presetMode;
-
- try {
- await this._setMode(presetMode);
- } catch (_err) {
- this._currentPresetMode = oldPresetMode;
- }
- }
-
- private async _setMode(mode: string) {
- await this.hass!.callService("climate", "set_preset_mode", {
- entity_id: this._stateObj!.entity_id,
- preset_mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsClimatePresetModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const stateObj = this._stateObj;
-
- const options = filterModes(
- stateObj.attributes.preset_modes,
- this._config!.preset_modes
- ).map((mode) => ({
- value: mode,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "preset_mode",
- mode
- ),
- }));
-
- if (this._config.style === "icons") {
- return html`
- ({
- ...option,
- icon: html``,
- }))}
- .value=${this._currentPresetMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .label=${this.hass!.formatEntityAttributeName(
- stateObj,
- "preset_mode"
- )}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
- }
-
- return html`
-
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsClimatePresetModesCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-climate-swing-horizontal-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-climate-swing-horizontal-modes-card-feature.ts
index 946dd32f95..f14f11ae5c 100644
--- a/src/panels/lovelace/card-features/hui-climate-swing-horizontal-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-climate-swing-horizontal-modes-card-feature.ts
@@ -1,20 +1,12 @@
import { mdiArrowOscillating } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
import type { ClimateEntity } from "../../../data/climate";
import { ClimateEntityFeature } from "../../../data/climate";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
-import { filterModes } from "./common/filter-modes";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
ClimateSwingHorizontalModesCardFeatureConfig,
LovelaceCardFeatureContext,
@@ -37,34 +29,26 @@ export const supportsClimateSwingHorizontalModesCardFeature = (
@customElement("hui-climate-swing-horizontal-modes-card-feature")
class HuiClimateSwingHorizontalModesCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ ClimateEntity,
+ ClimateSwingHorizontalModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "swing_horizontal_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "swing_horizontal_modes";
- @state() private _config?: ClimateSwingHorizontalModesCardFeatureConfig;
-
- @state() _currentSwingHorizontalMode?: string;
-
- private _renderSwingHorizontalModeIcon = (value: string) =>
- html``;
-
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | ClimateEntity
- | undefined;
+ protected get _configuredModes() {
+ return this._config?.swing_horizontal_modes;
}
+ protected readonly _dropdownIconPath = mdiArrowOscillating;
+
+ protected readonly _serviceDomain = "climate";
+
+ protected readonly _serviceAction = "set_swing_horizontal_mode";
+
static getStubConfig(): ClimateSwingHorizontalModesCardFeatureConfig {
return {
type: "climate-swing-horizontal-modes",
@@ -79,132 +63,12 @@ class HuiClimateSwingHorizontalModesCardFeature
);
}
- public setConfig(config: ClimateSwingHorizontalModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentSwingHorizontalMode =
- this._stateObj.attributes.swing_horizontal_mode;
- }
- }
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const swingHorizontalMode = ev.detail.value ?? ev.detail.item?.value;
-
- const oldSwingHorizontalMode =
- this._stateObj!.attributes.swing_horizontal_mode;
-
- if (
- swingHorizontalMode === oldSwingHorizontalMode ||
- !swingHorizontalMode
- ) {
- return;
- }
-
- this._currentSwingHorizontalMode = swingHorizontalMode;
-
- try {
- await this._setMode(swingHorizontalMode);
- } catch (_err) {
- this._currentSwingHorizontalMode = oldSwingHorizontalMode;
- }
- }
-
- private async _setMode(mode: string) {
- await this.hass!.callService("climate", "set_swing_horizontal_mode", {
- entity_id: this._stateObj!.entity_id,
- swing_horizontal_mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsClimateSwingHorizontalModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const stateObj = this._stateObj;
-
- const options = filterModes(
- stateObj.attributes.swing_horizontal_modes,
- this._config!.swing_horizontal_modes
- ).map((mode) => ({
- value: mode,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "swing_horizontal_mode",
- mode
- ),
- }));
-
- if (this._config.style === "icons") {
- return html`
- ({
- ...option,
- icon: html``,
- }))}
- .value=${this._currentSwingHorizontalMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .label=${this.hass!.formatEntityAttributeName(
- stateObj,
- "swing_horizontal_mode"
- )}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
- }
-
- return html`
-
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsClimateSwingHorizontalModesCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-climate-swing-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-climate-swing-modes-card-feature.ts
index 4b2c9d5ded..183ea75665 100644
--- a/src/panels/lovelace/card-features/hui-climate-swing-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-climate-swing-modes-card-feature.ts
@@ -1,20 +1,12 @@
import { mdiArrowOscillating } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
import type { ClimateEntity } from "../../../data/climate";
import { ClimateEntityFeature } from "../../../data/climate";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
-import { filterModes } from "./common/filter-modes";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
ClimateSwingModesCardFeatureConfig,
LovelaceCardFeatureContext,
@@ -37,34 +29,26 @@ export const supportsClimateSwingModesCardFeature = (
@customElement("hui-climate-swing-modes-card-feature")
class HuiClimateSwingModesCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ ClimateEntity,
+ ClimateSwingModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "swing_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "swing_modes";
- @state() private _config?: ClimateSwingModesCardFeatureConfig;
-
- @state() _currentSwingMode?: string;
-
- private _renderSwingModeIcon = (value: string) =>
- html``;
-
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | ClimateEntity
- | undefined;
+ protected get _configuredModes() {
+ return this._config?.swing_modes;
}
+ protected readonly _dropdownIconPath = mdiArrowOscillating;
+
+ protected readonly _serviceDomain = "climate";
+
+ protected readonly _serviceAction = "set_swing_mode";
+
static getStubConfig(): ClimateSwingModesCardFeatureConfig {
return {
type: "climate-swing-modes",
@@ -79,123 +63,12 @@ class HuiClimateSwingModesCardFeature
);
}
- public setConfig(config: ClimateSwingModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentSwingMode = this._stateObj.attributes.swing_mode;
- }
- }
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const swingMode = ev.detail.value ?? ev.detail.item?.value;
-
- const oldSwingMode = this._stateObj!.attributes.swing_mode;
-
- if (swingMode === oldSwingMode || !swingMode) {
- return;
- }
-
- this._currentSwingMode = swingMode;
-
- try {
- await this._setMode(swingMode);
- } catch (_err) {
- this._currentSwingMode = oldSwingMode;
- }
- }
-
- private async _setMode(mode: string) {
- await this.hass!.callService("climate", "set_swing_mode", {
- entity_id: this._stateObj!.entity_id,
- swing_mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsClimateSwingModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const stateObj = this._stateObj;
-
- const options = filterModes(
- stateObj.attributes.swing_modes,
- this._config!.swing_modes
- ).map((mode) => ({
- value: mode,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "swing_mode",
- mode
- ),
- }));
-
- if (this._config.style === "icons") {
- return html`
- ({
- ...option,
- icon: html``,
- }))}
- .value=${this._currentSwingMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .ariaLabel=${this.hass!.formatEntityAttributeName(
- stateObj,
- "swing_mode"
- )}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
- }
-
- return html`
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsClimateSwingModesCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-fan-direction-card-feature.ts b/src/panels/lovelace/card-features/hui-fan-direction-card-feature.ts
index 6a714bbdd0..90ee61bd90 100644
--- a/src/panels/lovelace/card-features/hui-fan-direction-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-fan-direction-card-feature.ts
@@ -1,22 +1,21 @@
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import type { ControlSelectOption } from "../../../components/ha-control-select";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { FanDirection, FanEntity } from "../../../data/fan";
import { FanEntityFeature } from "../../../data/fan";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
+import {
+ HuiModeSelectCardFeatureBase,
+ type HuiModeSelectOption,
+} from "./hui-mode-select-card-feature-base";
import type {
FanDirectionCardFeatureConfig,
LovelaceCardFeatureContext,
} from "./types";
+const FAN_DIRECTIONS: FanDirection[] = ["forward", "reverse"];
+
export const supportsFanDirectionCardFeature = (
hass: HomeAssistant,
context: LovelaceCardFeatureContext
@@ -33,23 +32,18 @@ export const supportsFanDirectionCardFeature = (
@customElement("hui-fan-direction-card-feature")
class HuiFanDirectionCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "direction";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "direction";
- @state() private _config?: FanDirectionCardFeatureConfig;
+ protected readonly _serviceDomain = "fan";
- @state() _currentDirection?: FanDirection;
+ protected readonly _serviceAction = "set_direction";
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as FanEntity | undefined;
- }
+ protected readonly _defaultStyle = "icons";
static getStubConfig(): FanDirectionCardFeatureConfig {
return {
@@ -57,90 +51,23 @@ class HuiFanDirectionCardFeature
};
}
- public setConfig(config: FanDirectionCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProp: PropertyValues): void {
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentDirection = this._stateObj.attributes
- .direction as FanDirection;
- }
- }
- }
-
- private async _valueChanged(ev: CustomEvent) {
- const newDirection = (ev.detail as any).value as FanDirection;
-
- if (newDirection === this._stateObj!.attributes.direction) return;
-
- const oldDirection = this._stateObj!.attributes.direction as FanDirection;
- this._currentDirection = newDirection;
-
- try {
- await this._setDirection(newDirection);
- } catch (_err) {
- this._currentDirection = oldDirection;
- }
- }
-
- private async _setDirection(direction: string) {
- await this.hass!.callService("fan", "set_direction", {
- entity_id: this._stateObj!.entity_id,
- direction: direction,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsFanDirectionCardFeature(this.hass, this.context)
- ) {
- return null;
+ protected _getOptions(): HuiModeSelectOption[] {
+ if (!this.hass) {
+ return [];
}
- const stateObj = this._stateObj;
- const FAN_DIRECTION_MAP: FanDirection[] = ["forward", "reverse"];
-
- const options = FAN_DIRECTION_MAP.map((direction) => ({
+ return FAN_DIRECTIONS.map((direction) => ({
value: direction,
label: this.hass!.localize(`ui.card.fan.${direction}`),
- icon: html``,
}));
-
- return html`
-
-
- `;
}
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsFanDirectionCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts
index b63acb8aae..c804427117 100644
--- a/src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts
@@ -1,20 +1,12 @@
import { mdiTuneVariant } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { FanEntity } from "../../../data/fan";
import { FanEntityFeature } from "../../../data/fan";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
-import { filterModes } from "./common/filter-modes";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
FanPresetModesCardFeatureConfig,
LovelaceCardFeatureContext,
@@ -36,32 +28,26 @@ export const supportsFanPresetModesCardFeature = (
@customElement("hui-fan-preset-modes-card-feature")
class HuiFanPresetModesCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ FanEntity,
+ FanPresetModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "preset_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "preset_modes";
- @state() private _config?: FanPresetModesCardFeatureConfig;
-
- @state() _currentPresetMode?: string;
-
- private _renderPresetModeIcon = (value: string) =>
- html``;
-
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as FanEntity | undefined;
+ protected get _configuredModes() {
+ return this._config?.preset_modes;
}
+ protected readonly _dropdownIconPath = mdiTuneVariant;
+
+ protected readonly _serviceDomain = "fan";
+
+ protected readonly _serviceAction = "set_preset_mode";
+
static getStubConfig(): FanPresetModesCardFeatureConfig {
return {
type: "fan-preset-modes",
@@ -74,123 +60,12 @@ class HuiFanPresetModesCardFeature
return document.createElement("hui-fan-preset-modes-card-feature-editor");
}
- public setConfig(config: FanPresetModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProp: PropertyValues): void {
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentPresetMode = this._stateObj.attributes.preset_mode;
- }
- }
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const presetMode = ev.detail.value ?? ev.detail.item?.value;
-
- const oldPresetMode = this._stateObj!.attributes.preset_mode;
-
- if (presetMode === oldPresetMode || !presetMode) {
- return;
- }
-
- this._currentPresetMode = presetMode;
-
- try {
- await this._setMode(presetMode);
- } catch (_err) {
- this._currentPresetMode = oldPresetMode;
- }
- }
-
- private async _setMode(mode: string) {
- await this.hass!.callService("fan", "set_preset_mode", {
- entity_id: this._stateObj!.entity_id,
- preset_mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsFanPresetModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const stateObj = this._stateObj;
-
- const options = filterModes(
- stateObj.attributes.preset_modes,
- this._config!.preset_modes
- ).map((mode) => ({
- value: mode,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "preset_mode",
- mode
- ),
- }));
-
- if (this._config.style === "icons") {
- return html`
- ({
- ...option,
- icon: html``,
- }))}
- .value=${this._currentPresetMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .label=${this.hass!.formatEntityAttributeName(
- stateObj,
- "preset_mode"
- )}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
- }
-
- return html`
-
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsFanPresetModesCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-humidifier-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-humidifier-modes-card-feature.ts
index f65b7582b9..e437eb73ee 100644
--- a/src/panels/lovelace/card-features/hui-humidifier-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-humidifier-modes-card-feature.ts
@@ -1,20 +1,12 @@
import { mdiTuneVariant } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { HumidifierEntity } from "../../../data/humidifier";
import { HumidifierEntityFeature } from "../../../data/humidifier";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
-import { filterModes } from "./common/filter-modes";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
HumidifierModesCardFeatureConfig,
LovelaceCardFeatureContext,
@@ -37,34 +29,26 @@ export const supportsHumidifierModesCardFeature = (
@customElement("hui-humidifier-modes-card-feature")
class HuiHumidifierModesCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ HumidifierEntity,
+ HumidifierModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "available_modes";
- @state() private _config?: HumidifierModesCardFeatureConfig;
-
- @state() _currentMode?: string;
-
- private _renderModeIcon = (value: string) =>
- html``;
-
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | HumidifierEntity
- | undefined;
+ protected get _configuredModes() {
+ return this._config?.modes;
}
+ protected readonly _dropdownIconPath = mdiTuneVariant;
+
+ protected readonly _serviceDomain = "humidifier";
+
+ protected readonly _serviceAction = "set_mode";
+
static getStubConfig(): HumidifierModesCardFeatureConfig {
return {
type: "humidifier-modes",
@@ -77,121 +61,12 @@ class HuiHumidifierModesCardFeature
return document.createElement("hui-humidifier-modes-card-feature-editor");
}
- public setConfig(config: HumidifierModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentMode = this._stateObj.attributes.mode;
- }
- }
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const mode = ev.detail.value ?? ev.detail.item?.value;
-
- const oldMode = this._stateObj!.attributes.mode;
-
- if (mode === oldMode || !mode) {
- return;
- }
-
- this._currentMode = mode;
-
- try {
- await this._setMode(mode);
- } catch (_err) {
- this._currentMode = oldMode;
- }
- }
-
- private async _setMode(mode: string) {
- await this.hass!.callService("humidifier", "set_mode", {
- entity_id: this._stateObj!.entity_id,
- mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsHumidifierModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const stateObj = this._stateObj;
-
- const options = filterModes(
- stateObj.attributes.available_modes,
- this._config!.modes
- ).map((mode) => ({
- value: mode,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "mode",
- mode
- ),
- }));
-
- if (this._config.style === "icons") {
- return html`
- ({
- ...option,
- icon: html``,
- }))}
- .value=${this._currentMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .label=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
- }
-
- return html`
-
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsHumidifierModesCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-media-player-sound-mode-card-feature.ts b/src/panels/lovelace/card-features/hui-media-player-sound-mode-card-feature.ts
index 1020658796..091324ff61 100644
--- a/src/panels/lovelace/card-features/hui-media-player-sound-mode-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-media-player-sound-mode-card-feature.ts
@@ -1,12 +1,7 @@
import type { PropertyValues } from "lit";
-import { html, LitElement, nothing } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import {
MediaPlayerEntityFeature,
type MediaPlayerEntity,
@@ -14,7 +9,7 @@ import {
import type { HomeAssistant } from "../../../types";
import { hasConfigChanged } from "../common/has-changed";
import type { LovelaceCardFeature } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
LovelaceCardFeatureContext,
MediaPlayerSoundModeCardFeatureConfig,
@@ -38,59 +33,42 @@ export const supportsMediaPlayerSoundModeCardFeature = (
@customElement("hui-media-player-sound-mode-card-feature")
class HuiMediaPlayerSoundModeCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ MediaPlayerEntity,
+ MediaPlayerSoundModeCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "sound_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "sound_mode_list";
- @state() private _config?: MediaPlayerSoundModeCardFeatureConfig;
+ protected readonly _serviceDomain = "media_player";
- @state() private _currentSoundMode?: string;
+ protected readonly _serviceAction = "select_sound_mode";
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | MediaPlayerEntity
- | undefined;
+ protected get _label(): string {
+ return this.hass!.localize("ui.card.media_player.sound_mode");
}
+ protected readonly _hideLabel = false;
+
+ protected readonly _showDropdownOptionIcons = false;
+
+ protected readonly _allowIconsStyle = false;
+
static getStubConfig(): MediaPlayerSoundModeCardFeatureConfig {
return {
type: "media-player-sound-mode",
};
}
- public setConfig(config: MediaPlayerSoundModeCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProps: PropertyValues): void {
- super.willUpdate(changedProps);
- if (
- (changedProps.has("hass") || changedProps.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentSoundMode = this._stateObj.attributes.sound_mode;
- }
- }
- }
-
protected shouldUpdate(changedProps: PropertyValues): boolean {
const entityId = this.context?.entity_id;
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
return (
- changedProps.has("_currentSoundMode") ||
+ changedProps.has("_currentValue") ||
changedProps.has("context") ||
hasConfigChanged(this, changedProps) ||
(changedProps.has("hass") &&
@@ -100,64 +78,12 @@ class HuiMediaPlayerSoundModeCardFeature
);
}
- private async _valueChanged(ev: HaDropdownSelectEvent) {
- const soundMode = ev.detail.item?.value;
- const oldSoundMode = this._stateObj!.attributes.sound_mode;
-
- if (soundMode === oldSoundMode || !soundMode) {
- return;
- }
-
- this._currentSoundMode = soundMode;
-
- try {
- await this.hass!.callService("media_player", "select_sound_mode", {
- entity_id: this._stateObj!.entity_id,
- sound_mode: soundMode,
- });
- } catch (_err) {
- this._currentSoundMode = oldSoundMode;
- }
- }
-
- protected render() {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsMediaPlayerSoundModeCardFeature(this.hass, this.context)
- ) {
- return nothing;
- }
-
- const options = this._stateObj.attributes.sound_mode_list!.map(
- (soundMode) => ({
- value: soundMode,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "sound_mode",
- soundMode
- ),
- })
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsMediaPlayerSoundModeCardFeature(this.hass, this.context)
);
-
- return html`
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
}
}
diff --git a/src/panels/lovelace/card-features/hui-media-player-source-card-feature.ts b/src/panels/lovelace/card-features/hui-media-player-source-card-feature.ts
index ab657d4654..8dddd64d44 100644
--- a/src/panels/lovelace/card-features/hui-media-player-source-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-media-player-source-card-feature.ts
@@ -1,11 +1,7 @@
import type { PropertyValues } from "lit";
-import { html, LitElement, nothing } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { supportsFeature } from "../../../common/entity/supports-feature";
-import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
-import "../../../components/ha-control-select-menu";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import {
MediaPlayerEntityFeature,
type MediaPlayerEntity,
@@ -13,7 +9,7 @@ import {
import type { HomeAssistant } from "../../../types";
import { hasConfigChanged } from "../common/has-changed";
import type { LovelaceCardFeature } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
LovelaceCardFeatureContext,
MediaPlayerSourceCardFeatureConfig,
@@ -37,59 +33,42 @@ export const supportsMediaPlayerSourceCardFeature = (
@customElement("hui-media-player-source-card-feature")
class HuiMediaPlayerSourceCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ MediaPlayerEntity,
+ MediaPlayerSourceCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "source";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "source_list";
- @state() private _config?: MediaPlayerSourceCardFeatureConfig;
+ protected readonly _serviceDomain = "media_player";
- @state() private _currentSource?: string;
+ protected readonly _serviceAction = "select_source";
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | MediaPlayerEntity
- | undefined;
+ protected get _label(): string {
+ return this.hass!.localize("ui.card.media_player.source");
}
+ protected readonly _hideLabel = false;
+
+ protected readonly _showDropdownOptionIcons = false;
+
+ protected readonly _allowIconsStyle = false;
+
static getStubConfig(): MediaPlayerSourceCardFeatureConfig {
return {
type: "media-player-source",
};
}
- public setConfig(config: MediaPlayerSourceCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
- }
-
- protected willUpdate(changedProps: PropertyValues): void {
- super.willUpdate(changedProps);
- if (
- (changedProps.has("hass") || changedProps.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentSource = this._stateObj.attributes.source;
- }
- }
- }
-
protected shouldUpdate(changedProps: PropertyValues): boolean {
const entityId = this.context?.entity_id;
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
return (
- changedProps.has("_currentSource") ||
+ changedProps.has("_currentValue") ||
changedProps.has("context") ||
hasConfigChanged(this, changedProps) ||
(changedProps.has("hass") &&
@@ -99,61 +78,12 @@ class HuiMediaPlayerSourceCardFeature
);
}
- private async _valueChanged(ev: HaDropdownSelectEvent) {
- const source = ev.detail.item?.value;
- const oldSource = this._stateObj!.attributes.source;
-
- if (source === oldSource || !source) {
- return;
- }
-
- this._currentSource = source;
-
- try {
- await this.hass!.callService("media_player", "select_source", {
- entity_id: this._stateObj!.entity_id,
- source: source,
- });
- } catch (_err) {
- this._currentSource = oldSource;
- }
- }
-
- protected render() {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsMediaPlayerSourceCardFeature(this.hass, this.context)
- ) {
- return nothing;
- }
-
- const options = this._stateObj.attributes.source_list!.map((source) => ({
- value: source,
- label: this.hass!.formatEntityAttributeValue(
- this._stateObj!,
- "source",
- source
- ),
- }));
-
- return html`
-
-
- `;
- }
-
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsMediaPlayerSourceCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-mode-select-card-feature-base.ts b/src/panels/lovelace/card-features/hui-mode-select-card-feature-base.ts
new file mode 100644
index 0000000000..ad0ab5cb2f
--- /dev/null
+++ b/src/panels/lovelace/card-features/hui-mode-select-card-feature-base.ts
@@ -0,0 +1,263 @@
+import type { HassEntity } from "home-assistant-js-websocket";
+import type { PropertyValues, TemplateResult } from "lit";
+import { html, LitElement, nothing } from "lit";
+import { property, state } from "lit/decorators";
+import { styleMap } from "lit/directives/style-map";
+import "../../../components/ha-attribute-icon";
+import "../../../components/ha-control-select";
+import "../../../components/ha-control-select-menu";
+import "../../../components/ha-svg-icon";
+import { UNAVAILABLE } from "../../../data/entity/entity";
+import type { HomeAssistant } from "../../../types";
+import type { LovelaceCardFeature } from "../types";
+import { cardFeatureStyles } from "./common/card-feature-styles";
+import { filterModes } from "./common/filter-modes";
+import type {
+ LovelaceCardFeatureConfig,
+ LovelaceCardFeatureContext,
+} from "./types";
+
+type AttributeModeChangeEvent = CustomEvent<{
+ value?: string;
+ item?: { value: string };
+}>;
+
+type AttributeModeCardFeatureConfig = LovelaceCardFeatureConfig & {
+ style?: "dropdown" | "icons";
+};
+
+export interface HuiModeSelectOption {
+ value: string;
+ label: string;
+}
+
+export abstract class HuiModeSelectCardFeatureBase<
+ TEntity extends HassEntity,
+ TConfig extends AttributeModeCardFeatureConfig,
+>
+ extends LitElement
+ implements LovelaceCardFeature
+{
+ @property({ attribute: false }) public hass?: HomeAssistant;
+
+ @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+
+ @state() protected _config?: TConfig;
+
+ @state() protected _currentValue?: string;
+
+ protected abstract readonly _attribute: string;
+
+ protected abstract readonly _modesAttribute: string;
+
+ protected get _configuredModes(): string[] | undefined {
+ return undefined;
+ }
+
+ protected readonly _dropdownIconPath?: string;
+
+ protected abstract readonly _serviceDomain: string;
+
+ protected abstract readonly _serviceAction: string;
+
+ protected abstract _isSupported(): boolean;
+
+ protected get _label(): string {
+ return this.hass!.formatEntityAttributeName(
+ this._stateObj!,
+ this._attribute
+ );
+ }
+
+ protected readonly _hideLabel: boolean = true;
+
+ protected readonly _showDropdownOptionIcons: boolean = true;
+
+ protected readonly _allowIconsStyle: boolean = true;
+
+ protected readonly _defaultStyle: "dropdown" | "icons" = "dropdown";
+
+ protected get _controlSelectStyle():
+ | Record
+ | undefined {
+ return undefined;
+ }
+
+ protected _getServiceDomain(_stateObj: TEntity): string {
+ return this._serviceDomain;
+ }
+
+ protected _isValueValid(_value: string, _stateObj: TEntity): boolean {
+ return true;
+ }
+
+ protected get _stateObj(): TEntity | undefined {
+ if (!this.hass || !this.context?.entity_id) {
+ return undefined;
+ }
+
+ return this.hass.states[this.context.entity_id] as TEntity | undefined;
+ }
+
+ public setConfig(config: TConfig): void {
+ if (!config) {
+ throw new Error("Invalid configuration");
+ }
+
+ this._config = config;
+ }
+
+ protected willUpdate(changedProps: PropertyValues): void {
+ super.willUpdate(changedProps);
+
+ if (
+ (changedProps.has("hass") || changedProps.has("context")) &&
+ this._stateObj
+ ) {
+ const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
+ const oldStateObj = this.context?.entity_id
+ ? (oldHass?.states[this.context.entity_id] as TEntity | undefined)
+ : undefined;
+
+ if (oldStateObj !== this._stateObj) {
+ this._currentValue = this._getValue(this._stateObj);
+ }
+ }
+ }
+
+ protected render(): TemplateResult | null {
+ if (
+ !this._config ||
+ !this.hass ||
+ !this.context ||
+ !this._stateObj ||
+ !this._isSupported()
+ ) {
+ return null;
+ }
+
+ const stateObj = this._stateObj;
+ const options = this._getOptions();
+ const label = this._label;
+ const renderIcons =
+ this._allowIconsStyle &&
+ (this._config.style === "icons" ||
+ (this._config.style === undefined && this._defaultStyle === "icons"));
+
+ if (renderIcons) {
+ return html`
+ ({
+ ...option,
+ icon: this._renderOptionIcon(option),
+ }))}
+ .value=${this._currentValue}
+ @value-changed=${this._valueChanged}
+ hide-option-label
+ .label=${label}
+ style=${styleMap(this._controlSelectStyle ?? {})}
+ .disabled=${stateObj.state === UNAVAILABLE}
+ >
+
+ `;
+ }
+
+ return html`
+
+ ${this._dropdownIconPath
+ ? html``
+ : nothing}
+
+ `;
+ }
+
+ protected _getValue(stateObj: TEntity): string | undefined {
+ return stateObj.attributes[this._attribute] as string | undefined;
+ }
+
+ protected _getOptions(): HuiModeSelectOption[] {
+ if (!this._stateObj || !this.hass) {
+ return [];
+ }
+
+ return filterModes(
+ this._stateObj.attributes[this._modesAttribute] as string[] | undefined,
+ this._configuredModes
+ ).map((mode) => ({
+ value: mode,
+ label: this.hass!.formatEntityAttributeValue(
+ this._stateObj!,
+ this._attribute,
+ mode
+ ),
+ }));
+ }
+
+ protected _renderOptionIcon(option: HuiModeSelectOption): TemplateResult<1> {
+ return html``;
+ }
+
+ private _renderMenuIcon = (value: string): TemplateResult<1> =>
+ html``;
+
+ private async _valueChanged(ev: AttributeModeChangeEvent) {
+ if (!this.hass || !this._stateObj) {
+ return;
+ }
+
+ const value = ev.detail.value ?? ev.detail.item?.value;
+ const oldValue = this._getValue(this._stateObj);
+
+ if (
+ value === oldValue ||
+ !value ||
+ !this._isValueValid(value, this._stateObj)
+ ) {
+ return;
+ }
+
+ this._currentValue = value;
+
+ try {
+ await this.hass.callService(
+ this._getServiceDomain(this._stateObj),
+ this._serviceAction,
+ {
+ entity_id: this._stateObj.entity_id,
+ [this._attribute]: value,
+ }
+ );
+ } catch (_err) {
+ this._currentValue = oldValue;
+ }
+ }
+
+ static get styles() {
+ return cardFeatureStyles;
+ }
+}
diff --git a/src/panels/lovelace/card-features/hui-select-options-card-feature.ts b/src/panels/lovelace/card-features/hui-select-options-card-feature.ts
index 1180dd9a8c..51d2b792ef 100644
--- a/src/panels/lovelace/card-features/hui-select-options-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-select-options-card-feature.ts
@@ -1,22 +1,20 @@
-import type { PropertyValues } from "lit";
-import { html, LitElement, nothing } from "lit";
-import { customElement, property, state } from "lit/decorators";
-import memoizeOne from "memoize-one";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
-import { UNAVAILABLE } from "../../../data/entity/entity";
import type { InputSelectEntity } from "../../../data/input_select";
import type { SelectEntity } from "../../../data/select";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
import { filterModes } from "./common/filter-modes";
+import {
+ HuiModeSelectCardFeatureBase,
+ type HuiModeSelectOption,
+} from "./hui-mode-select-card-feature-base";
import type {
LovelaceCardFeatureContext,
SelectOptionsCardFeatureConfig,
} from "./types";
-import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
+
+type SelectOptionEntity = SelectEntity | InputSelectEntity;
export const supportsSelectOptionsCardFeature = (
hass: HomeAssistant,
@@ -32,27 +30,32 @@ export const supportsSelectOptionsCardFeature = (
@customElement("hui-select-options-card-feature")
class HuiSelectOptionsCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ SelectOptionEntity,
+ SelectOptionsCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "option";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "options";
- @state() private _config?: SelectOptionsCardFeatureConfig;
-
- @state() _currentOption?: string;
-
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
- return undefined;
- }
- return this.hass.states[this.context.entity_id!] as
- | SelectEntity
- | InputSelectEntity
- | undefined;
+ protected get _configuredModes() {
+ return this._config?.options;
}
+ protected readonly _serviceDomain = "select";
+
+ protected readonly _serviceAction = "select_option";
+
+ protected get _label(): string {
+ return this.hass!.localize("ui.card.select.option");
+ }
+
+ protected readonly _allowIconsStyle = false;
+
+ protected readonly _showDropdownOptionIcons = false;
+
static getStubConfig(): SelectOptionsCardFeatureConfig {
return {
type: "select-options",
@@ -64,98 +67,41 @@ class HuiSelectOptionsCardFeature
return document.createElement("hui-select-options-card-feature-editor");
}
- public setConfig(config: SelectOptionsCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
+ protected _getValue(stateObj: SelectOptionEntity): string | undefined {
+ return stateObj.state;
}
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentOption = this._stateObj.state;
- }
- }
- }
-
- private async _valueChanged(ev: HaDropdownSelectEvent) {
- const option = ev.detail.item?.value;
-
- const oldOption = this._stateObj!.state;
-
- if (
- option === oldOption ||
- !this._stateObj!.attributes.options.includes(option)
- ) {
- return;
+ protected _getOptions(): HuiModeSelectOption[] {
+ if (!this._stateObj || !this.hass) {
+ return [];
}
- this._currentOption = option;
-
- try {
- await this._setOption(option);
- } catch (_err) {
- this._currentOption = oldOption;
- }
- }
-
- private async _setOption(option: string) {
- const domain = computeDomain(this._stateObj!.entity_id);
- await this.hass!.callService(domain, "select_option", {
- entity_id: this._stateObj!.entity_id,
- option: option,
- });
- }
-
- protected render() {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsSelectOptionsCardFeature(this.hass, this.context)
- ) {
- return nothing;
- }
-
- const stateObj = this._stateObj;
-
- const options = this._getOptions(
+ return filterModes(
this._stateObj.attributes.options,
- this._config.options
- );
-
- return html`
-
-
- `;
+ this._config?.options
+ ).map((option) => ({
+ value: option,
+ label: this.hass!.formatEntityState(this._stateObj!, option),
+ }));
}
- private _getOptions = memoizeOne(
- (attributeOptions: string[], configOptions: string[] | undefined) =>
- filterModes(attributeOptions, configOptions).map((option) => ({
- value: option,
- label: this.hass!.formatEntityState(this._stateObj!, option),
- }))
- );
+ protected _getServiceDomain(stateObj: SelectOptionEntity): string {
+ return computeDomain(stateObj.entity_id);
+ }
- static get styles() {
- return cardFeatureStyles;
+ protected _isValueValid(
+ value: string,
+ stateObj: SelectOptionEntity
+ ): boolean {
+ return stateObj.attributes.options.includes(value);
+ }
+
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsSelectOptionsCardFeature(this.hass, this.context)
+ );
}
}
diff --git a/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts
index 7b007ab28e..1c9d1d6929 100644
--- a/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts
+++ b/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts
@@ -1,24 +1,13 @@
import { mdiWaterBoiler } from "@mdi/js";
-import type { PropertyValues, TemplateResult } from "lit";
-import { html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
-import { styleMap } from "lit/directives/style-map";
+import { customElement } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateColorCss } from "../../../common/entity/state_color";
-import "../../../components/ha-attribute-icon";
-import "../../../components/ha-control-select";
-import "../../../components/ha-control-select-menu";
-import "../../../components/ha-list-item";
-import { UNAVAILABLE } from "../../../data/entity/entity";
-import type {
- OperationMode,
- WaterHeaterEntity,
-} from "../../../data/water_heater";
+import type { WaterHeaterEntity } from "../../../data/water_heater";
import { compareWaterHeaterOperationMode } from "../../../data/water_heater";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
-import { cardFeatureStyles } from "./common/card-feature-styles";
import { filterModes } from "./common/filter-modes";
+import { HuiModeSelectCardFeatureBase } from "./hui-mode-select-card-feature-base";
import type {
LovelaceCardFeatureContext,
WaterHeaterOperationModesCardFeatureConfig,
@@ -38,32 +27,42 @@ export const supportsWaterHeaterOperationModesCardFeature = (
@customElement("hui-water-heater-operation-modes-card-feature")
class HuiWaterHeaterOperationModeCardFeature
- extends LitElement
+ extends HuiModeSelectCardFeatureBase<
+ WaterHeaterEntity,
+ WaterHeaterOperationModesCardFeatureConfig
+ >
implements LovelaceCardFeature
{
- @property({ attribute: false }) public hass?: HomeAssistant;
+ protected readonly _attribute = "operation_mode";
- @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
+ protected readonly _modesAttribute = "operation_list";
- @state() private _config?: WaterHeaterOperationModesCardFeatureConfig;
+ protected get _configuredModes() {
+ return this._config?.operation_modes;
+ }
- @state() _currentOperationMode?: OperationMode;
+ protected readonly _dropdownIconPath = mdiWaterBoiler;
- private _renderOperationModeIcon = (value: string) =>
- html``;
+ protected readonly _serviceDomain = "water_heater";
- private get _stateObj() {
- if (!this.hass || !this.context || !this.context.entity_id) {
+ protected readonly _serviceAction = "set_operation_mode";
+
+ protected get _label(): string {
+ return this.hass!.localize("ui.card.water_heater.mode");
+ }
+
+ protected readonly _defaultStyle = "icons";
+
+ protected get _controlSelectStyle():
+ | Record
+ | undefined {
+ if (!this._stateObj) {
return undefined;
}
- return this.hass.states[this.context.entity_id!] as
- | WaterHeaterEntity
- | undefined;
+
+ return {
+ "--control-select-color": stateColorCss(this._stateObj),
+ };
}
static getStubConfig(): WaterHeaterOperationModesCardFeatureConfig {
@@ -79,125 +78,34 @@ class HuiWaterHeaterOperationModeCardFeature
);
}
- public setConfig(config: WaterHeaterOperationModesCardFeatureConfig): void {
- if (!config) {
- throw new Error("Invalid configuration");
- }
- this._config = config;
+ protected _getValue(stateObj: WaterHeaterEntity): string | undefined {
+ return stateObj.state;
}
- protected willUpdate(changedProp: PropertyValues): void {
- super.willUpdate(changedProp);
- if (
- (changedProp.has("hass") || changedProp.has("context")) &&
- this._stateObj
- ) {
- const oldHass = changedProp.get("hass") as HomeAssistant | undefined;
- const oldStateObj = oldHass?.states[this.context!.entity_id!];
- if (oldStateObj !== this._stateObj) {
- this._currentOperationMode = this._stateObj.state as OperationMode;
- }
+ protected _getOptions() {
+ if (!this._stateObj || !this.hass) {
+ return [];
}
- }
-
- private async _valueChanged(
- ev: CustomEvent<{ value?: string; item?: { value: string } }>
- ) {
- const mode = ev.detail.value ?? ev.detail.item?.value;
-
- if (mode === this._stateObj!.state || !mode) {
- return;
- }
-
- const oldMode = this._stateObj!.state as OperationMode;
- this._currentOperationMode = mode as OperationMode;
-
- try {
- await this._setMode(this._currentOperationMode);
- } catch (_err) {
- this._currentOperationMode = oldMode;
- }
- }
-
- private async _setMode(mode: OperationMode) {
- await this.hass!.callService("water_heater", "set_operation_mode", {
- entity_id: this._stateObj!.entity_id,
- operation_mode: mode,
- });
- }
-
- protected render(): TemplateResult | null {
- if (
- !this._config ||
- !this.hass ||
- !this.context ||
- !this._stateObj ||
- !supportsWaterHeaterOperationModesCardFeature(this.hass, this.context)
- ) {
- return null;
- }
-
- const color = stateColorCss(this._stateObj);
const orderedModes = (this._stateObj.attributes.operation_list || [])
.concat()
.sort(compareWaterHeaterOperationMode)
.reverse();
- const options = filterModes(orderedModes, this._config.operation_modes).map(
+ return filterModes(orderedModes, this._config?.operation_modes).map(
(mode) => ({
value: mode,
label: this.hass!.formatEntityState(this._stateObj!, mode),
})
);
-
- if (this._config.style === "dropdown") {
- return html`
-
-
-
- `;
- }
-
- return html`
- ({
- ...option,
- icon: html`
-
- `,
- }))}
- .value=${this._currentOperationMode}
- @value-changed=${this._valueChanged}
- hide-option-label
- .label=${this.hass.localize("ui.card.water_heater.mode")}
- style=${styleMap({
- "--control-select-color": color,
- })}
- .disabled=${this._stateObj!.state === UNAVAILABLE}
- >
-
- `;
}
- static get styles() {
- return cardFeatureStyles;
+ protected _isSupported(): boolean {
+ return !!(
+ this.hass &&
+ this.context &&
+ supportsWaterHeaterOperationModesCardFeature(this.hass, this.context)
+ );
}
}