diff --git a/src/panels/lovelace/create-element/create-tile-feature-element.ts b/src/panels/lovelace/create-element/create-tile-feature-element.ts index e8af650baad8..c40aa421037b 100644 --- a/src/panels/lovelace/create-element/create-tile-feature-element.ts +++ b/src/panels/lovelace/create-element/create-tile-feature-element.ts @@ -1,5 +1,6 @@ import "../tile-features/hui-alarm-modes-tile-feature"; import "../tile-features/hui-climate-hvac-modes-tile-feature"; +import "../tile-features/hui-climate-presets-tile-feature"; import "../tile-features/hui-cover-open-close-tile-feature"; import "../tile-features/hui-cover-position-tile-feature"; import "../tile-features/hui-cover-tilt-position-tile-feature"; @@ -21,6 +22,7 @@ import { const TYPES: Set = new Set([ "alarm-modes", "climate-hvac-modes", + "climate-presets", "cover-open-close", "cover-position", "cover-tilt-position", diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts index ea8d25b91a3b..cdb420d230a0 100644 --- a/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts @@ -27,6 +27,7 @@ import { HomeAssistant } from "../../../../types"; import { getTileFeatureElementClass } from "../../create-element/create-tile-feature-element"; import { supportsAlarmModesTileFeature } from "../../tile-features/hui-alarm-modes-tile-feature"; import { supportsClimateHvacModesTileFeature } from "../../tile-features/hui-climate-hvac-modes-tile-feature"; +import { supportsClimatePresetsTileFeature } from "../../tile-features/hui-climate-presets-tile-feature"; import { supportsCoverOpenCloseTileFeature } from "../../tile-features/hui-cover-open-close-tile-feature"; import { supportsCoverPositionTileFeature } from "../../tile-features/hui-cover-position-tile-feature"; import { supportsCoverTiltPositionTileFeature } from "../../tile-features/hui-cover-tilt-position-tile-feature"; @@ -47,6 +48,7 @@ type SupportsFeature = (stateObj: HassEntity) => boolean; const FEATURE_TYPES: FeatureType[] = [ "alarm-modes", "climate-hvac-modes", + "climate-presets", "cover-open-close", "cover-position", "cover-tilt-position", @@ -73,6 +75,7 @@ const SUPPORTS_FEATURE_TYPES: Record = { "alarm-modes": supportsAlarmModesTileFeature, "climate-hvac-modes": supportsClimateHvacModesTileFeature, + "climate-presets": supportsClimatePresetsTileFeature, "cover-open-close": supportsCoverOpenCloseTileFeature, "cover-position": supportsCoverPositionTileFeature, "cover-tilt-position": supportsCoverTiltPositionTileFeature, diff --git a/src/panels/lovelace/tile-features/hui-climate-presets-tile-feature.ts b/src/panels/lovelace/tile-features/hui-climate-presets-tile-feature.ts new file mode 100644 index 000000000000..4dd81fd00788 --- /dev/null +++ b/src/panels/lovelace/tile-features/hui-climate-presets-tile-feature.ts @@ -0,0 +1,140 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import { stateColorCss } from "../../../common/entity/state_color"; +import { ClimateEntity, computePresetModeIcon } from "../../../data/climate"; +import { UNAVAILABLE } from "../../../data/entity"; +import { HomeAssistant } from "../../../types"; +import { LovelaceTileFeature } from "../types"; +import { ClimatePresetsTileFeatureConfig } from "./types"; +import type { ControlSelectOption } from "../../../components/ha-control-select"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import "../../../components/ha-control-button"; +import "../../../components/ha-control-button-group"; +import "../../../components/ha-control-select"; +import "../../../components/ha-control-slider"; + +export const supportsClimatePresetsTileFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return domain === "climate"; +}; + +@customElement("hui-climate-presets-tile-feature") +class HuiClimatePresetsTileFeature + extends LitElement + implements LovelaceTileFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: ClimateEntity; + + @state() private _config?: ClimatePresetsTileFeatureConfig; + + @state() _currentPreset?: string; + + static getStubConfig(): ClimatePresetsTileFeatureConfig { + return { + type: "climate-presets", + }; + } + + public setConfig(config: ClimatePresetsTileFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + protected willUpdate(changedProp: PropertyValues): void { + super.willUpdate(changedProp); + if (changedProp.has("stateObj") && this.stateObj) { + this._currentPreset = this.stateObj.attributes.preset_mode as string; + } + } + + private async _valueChanged(ev: CustomEvent) { + const preset = (ev.detail as any).value as string; + const oldPreset = this.stateObj!.attributes.preset_mode; + + if (preset === oldPreset) return; + this._currentPreset = preset; + + try { + await this._setPreset(preset); + } catch (err) { + this._currentPreset = oldPreset; + } + } + + private async _setPreset(preset: string) { + await this.hass!.callService("climate", "set_preset_mode", { + entity_id: this.stateObj!.entity_id, + preset_mode: preset, + }); + } + + protected render(): TemplateResult | null { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsClimatePresetsTileFeature(this.stateObj) + ) { + return null; + } + + const color = stateColorCss(this.stateObj); + + const presets = (this.stateObj.attributes.preset_modes as string[]) || []; + + const options = presets.map((preset) => ({ + value: preset, + label: this.hass!.formatEntityAttributeName(this.stateObj!, preset), + path: computePresetModeIcon(preset), + })); + + return html` +
+ + +
+ `; + } + + static get styles() { + return css` + ha-control-select { + --control-select-color: var(--tile-color); + --control-select-padding: 0; + --control-select-thickness: 40px; + --control-select-border-radius: 10px; + --control-select-button-border-radius: 10px; + } + ha-control-button-group { + margin: 0 12px 12px 12px; + --control-button-group-spacing: 12px; + } + .container { + padding: 0 12px 12px 12px; + width: auto; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-climate-preset-tile-feature": HuiClimatePresetsTileFeature; + } +} diff --git a/src/panels/lovelace/tile-features/types.ts b/src/panels/lovelace/tile-features/types.ts index 809cffe1d910..9a6670bf528d 100644 --- a/src/panels/lovelace/tile-features/types.ts +++ b/src/panels/lovelace/tile-features/types.ts @@ -40,6 +40,10 @@ export interface ClimateHvacModesTileFeatureConfig { hvac_modes?: HvacMode[]; } +export interface ClimatePresetsTileFeatureConfig { + type: "climate-presets"; +} + export interface SelectOptionsTileFeatureConfig { type: "select-options"; } @@ -80,6 +84,7 @@ export interface LawnMowerCommandsTileFeatureConfig { export type LovelaceTileFeatureConfig = | AlarmModesTileFeatureConfig | ClimateHvacModesTileFeatureConfig + | ClimatePresetsTileFeatureConfig | CoverOpenCloseTileFeatureConfig | CoverPositionTileFeatureConfig | CoverTiltPositionTileFeatureConfig diff --git a/src/translations/en.json b/src/translations/en.json index d2fac51d1355..a0a3103b988d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -105,7 +105,8 @@ "cooling": "{name} cooling", "high": "high", "low": "low", - "mode": "Mode" + "mode": "Mode", + "preset": "Preset" }, "counter": { "actions": { @@ -5037,6 +5038,9 @@ "label": "Climate HVAC modes", "hvac_modes": "HVAC modes" }, + "climate-presets": { + "label": "Climate Presets" + }, "target-temperature": { "label": "Target temperature" },