mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-01 16:17:21 +01:00
Create shared select card feature base class (#51333)
* Create shared select card feature base class * Add sound mode and source features * Remove serviceValueKey as its the same as attribute * Migrate more * Migrate select options * Add fan direction * Remove default usages
This commit is contained in:
@@ -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`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="fan_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
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<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this._stateObj!,
|
||||
"fan_mode",
|
||||
mode
|
||||
),
|
||||
}));
|
||||
|
||||
if (this._config.style === "icons") {
|
||||
return html`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="fan_mode"
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>`,
|
||||
}))}
|
||||
.value=${this._currentFanMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "fan_mode")}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "fan_mode")}
|
||||
.value=${this._currentFanMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderFanModeIcon}
|
||||
><ha-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateFanModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string, string | undefined>
|
||||
| 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`
|
||||
<ha-control-select-menu
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass.localize("ui.card.climate.mode")}
|
||||
.value=${this._currentHvacMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiThermostat}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${option.iconPath}
|
||||
></ha-svg-icon>`,
|
||||
}))}
|
||||
.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}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _renderOptionIcon(option: HvacModeOption): TemplateResult<1> {
|
||||
return html`<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${option.iconPath}
|
||||
></ha-svg-icon>`;
|
||||
}
|
||||
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateHvacModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
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`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>`,
|
||||
}))}
|
||||
.value=${this._currentPresetMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass!.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "preset_mode")}
|
||||
.value=${this._currentPresetMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderPresetModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimatePresetModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="swing_horizontal_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
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`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="swing_horizontal_mode"
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>`,
|
||||
}))}
|
||||
.value=${this._currentSwingHorizontalMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass!.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"swing_horizontal_mode"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"swing_horizontal_mode"
|
||||
)}
|
||||
.value=${this._currentSwingHorizontalMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderSwingHorizontalModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiArrowOscillating}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateSwingHorizontalModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="swing_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
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`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="swing_mode"
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>`,
|
||||
}))}
|
||||
.value=${this._currentSwingMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.ariaLabel=${this.hass!.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"swing_mode"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "swing_mode")}
|
||||
.value=${this._currentSwingMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderSwingModeIcon}
|
||||
><ha-svg-icon slot="icon" .path=${mdiArrowOscillating}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsClimateSwingModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FanEntity, FanDirectionCardFeatureConfig>
|
||||
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<ControlSelectOption>((direction) => ({
|
||||
return FAN_DIRECTIONS.map((direction) => ({
|
||||
value: direction,
|
||||
label: this.hass!.localize(`ui.card.fan.${direction}`),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="direction"
|
||||
.attributeValue=${direction}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
|
||||
return html`
|
||||
<ha-control-select
|
||||
.options=${options}
|
||||
.value=${this._currentDirection}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "direction")}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsFanDirectionCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
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`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>`,
|
||||
}))}
|
||||
.value=${this._currentPresetMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass!.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "preset_mode")}
|
||||
.value=${this._currentPresetMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderPresetModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsFanPresetModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
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`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="mode"
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>`,
|
||||
}))}
|
||||
.value=${this._currentMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-option-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
|
||||
.value=${this._currentMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsHumidifierModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
.label=${this.hass.localize("ui.card.media_player.sound_mode")}
|
||||
.value=${this._currentSoundMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
.options=${options}
|
||||
@wa-select=${this._valueChanged}
|
||||
>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`
|
||||
<ha-control-select-menu
|
||||
show-arrow
|
||||
.label=${this.hass.localize("ui.card.media_player.source")}
|
||||
.value=${this._currentSource}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
.options=${options}
|
||||
@wa-select=${this._valueChanged}
|
||||
>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsMediaPlayerSourceCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string, string | undefined>
|
||||
| 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`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...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}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
show-arrow
|
||||
?hide-label=${this._hideLabel}
|
||||
.label=${label}
|
||||
.value=${this._currentValue}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._showDropdownOptionIcons
|
||||
? this._renderMenuIcon
|
||||
: undefined}
|
||||
>
|
||||
${this._dropdownIconPath
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${this._dropdownIconPath}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
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`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass!}
|
||||
.stateObj=${this._stateObj}
|
||||
.attribute=${this._attribute}
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>`;
|
||||
}
|
||||
|
||||
private _renderMenuIcon = (value: string): TemplateResult<1> =>
|
||||
html`<ha-attribute-icon
|
||||
.hass=${this.hass!}
|
||||
.stateObj=${this._stateObj}
|
||||
.attribute=${this._attribute}
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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`
|
||||
<ha-control-select-menu
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass.localize("ui.card.select.option")}
|
||||
.value=${stateObj.state}
|
||||
.options=${options}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`<ha-attribute-icon
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="operation_mode"
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
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<string, string | undefined>
|
||||
| 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`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass.localize("ui.card.water_heater.mode")}
|
||||
.value=${this._currentOperationMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
@wa-select=${this._valueChanged}
|
||||
.options=${options}
|
||||
.renderIcon=${this._renderOperationModeIcon}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiWaterBoiler}></ha-svg-icon>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-select
|
||||
.options=${options.map((option) => ({
|
||||
...option,
|
||||
icon: html`
|
||||
<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._stateObj}
|
||||
attribute="operation_mode"
|
||||
.attributeValue=${option.value}
|
||||
></ha-attribute-icon>
|
||||
`,
|
||||
}))}
|
||||
.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}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
protected _isSupported(): boolean {
|
||||
return !!(
|
||||
this.hass &&
|
||||
this.context &&
|
||||
supportsWaterHeaterOperationModesCardFeature(this.hass, this.context)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user