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 b0cc259cb2ac..0f608494c9c5 100644 --- a/src/panels/lovelace/create-element/create-tile-feature-element.ts +++ b/src/panels/lovelace/create-element/create-tile-feature-element.ts @@ -13,6 +13,7 @@ import "../tile-features/hui-select-options-tile-feature"; import "../tile-features/hui-target-temperature-tile-feature"; import "../tile-features/hui-vacuum-commands-tile-feature"; import "../tile-features/hui-water-heater-operation-modes-tile-feature"; +import "../tile-features/hui-number-tile-feature"; import { LovelaceTileFeatureConfig } from "../tile-features/types"; import { createLovelaceElement, @@ -35,6 +36,7 @@ const TYPES: Set = new Set([ "target-temperature", "vacuum-commands", "water-heater-operation-modes", + "number", ]); export const createTileFeatureElement = (config: LovelaceTileFeatureConfig) => diff --git a/src/panels/lovelace/editor/config-elements/hui-number-tile-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-number-tile-feature-editor.ts new file mode 100644 index 000000000000..ac0b5d78cec1 --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-number-tile-feature-editor.ts @@ -0,0 +1,90 @@ +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-form/ha-form"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; +import type { HomeAssistant } from "../../../../types"; +import { + NumberTileFeatureConfig, + LovelaceTileFeatureContext, +} from "../../tile-features/types"; +import type { LovelaceTileFeatureEditor } from "../../types"; +import { LocalizeFunc } from "../../../../common/translations/localize"; + +@customElement("hui-number-tile-feature-editor") +export class HuiNumberTileFeatureEditor + extends LitElement + implements LovelaceTileFeatureEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public context?: LovelaceTileFeatureContext; + + @state() private _config?: NumberTileFeatureConfig; + + public setConfig(config: NumberTileFeatureConfig): void { + this._config = config; + } + + private _schema = memoizeOne( + (localize: LocalizeFunc) => + [ + { + name: "style", + selector: { + select: { + multiple: false, + mode: "list", + options: ["slider", "buttons"].map((mode) => ({ + value: mode, + label: localize( + `ui.panel.lovelace.editor.card.tile.features.types.number.style_list.${mode}` + ), + })), + }, + }, + }, + ] as const + ); + + protected render() { + if (!this.hass || !this._config) { + return nothing; + } + + const data: NumberTileFeatureConfig = { + style: "buttons", + ...this._config, + }; + + const schema = this._schema(this.hass.localize); + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + fireEvent(this, "config-changed", { config: ev.detail.value }); + } + + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => + this.hass!.localize( + `ui.panel.lovelace.editor.card.tile.features.types.number.${schema.name}` + ); +} + +declare global { + interface HTMLElementTagNameMap { + "hui-number-tile-feature-editor": HuiNumberTileFeatureEditor; + } +} 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 a4b3ea2ad26a..2c4a60b8b52c 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 @@ -38,6 +38,7 @@ import { supportsVacuumCommandTileFeature } from "../../tile-features/hui-vacuum import { supportsWaterHeaterOperationModesTileFeature } from "../../tile-features/hui-water-heater-operation-modes-tile-feature"; import { LovelaceTileFeatureConfig } from "../../tile-features/types"; import { supportsClimatePresetModesTileFeature } from "../../tile-features/hui-climate-preset-modes-tile-feature"; +import { supportsNumberTileFeature } from "../../tile-features/hui-number-tile-feature"; type FeatureType = LovelaceTileFeatureConfig["type"]; type SupportsFeature = (stateObj: HassEntity) => boolean; @@ -58,6 +59,7 @@ const UI_FEATURE_TYPES = [ "target-temperature", "vacuum-commands", "water-heater-operation-modes", + "number", ] as const satisfies readonly FeatureType[]; type UiFeatureTypes = (typeof UI_FEATURE_TYPES)[number]; @@ -69,6 +71,7 @@ const EDITABLES_FEATURE_TYPES = new Set([ "water-heater-operation-modes", "lawn-mower-commands", "climate-preset-modes", + "number", ]); const SUPPORTS_FEATURE_TYPES: Record< @@ -90,6 +93,7 @@ const SUPPORTS_FEATURE_TYPES: Record< "vacuum-commands": supportsVacuumCommandTileFeature, "water-heater-operation-modes": supportsWaterHeaterOperationModesTileFeature, "select-options": supportsSelectOptionTileFeature, + number: supportsNumberTileFeature, }; const CUSTOM_FEATURE_ENTRIES: Record< diff --git a/src/panels/lovelace/tile-features/hui-number-tile-feature.ts b/src/panels/lovelace/tile-features/hui-number-tile-feature.ts new file mode 100644 index 000000000000..768d7a45b490 --- /dev/null +++ b/src/panels/lovelace/tile-features/hui-number-tile-feature.ts @@ -0,0 +1,122 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { css, html, LitElement, nothing, PropertyValues } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { isUnavailableState } from "../../../data/entity"; +import { HomeAssistant } from "../../../types"; +import { LovelaceTileFeature, LovelaceTileFeatureEditor } from "../types"; +import { NumberTileFeatureConfig } from "./types"; +import "../../../components/ha-control-button"; +import "../../../components/ha-control-button-group"; +import "../../../components/ha-control-number-buttons"; +import "../../../components/ha-control-slider"; +import "../../../components/ha-icon"; + +export const supportsNumberTileFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return domain === "input_number" || domain === "number"; +}; + +@customElement("hui-number-tile-feature") +class HuiNumberTileFeature extends LitElement implements LovelaceTileFeature { + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: NumberTileFeatureConfig; + + @state() _currentState?: string; + + static getStubConfig(): NumberTileFeatureConfig { + return { + type: "number", + style: "buttons", + }; + } + + public static async getConfigElement(): Promise { + await import("../editor/config-elements/hui-number-tile-feature-editor"); + return document.createElement("hui-number-tile-feature-editor"); + } + + public setConfig(config: NumberTileFeatureConfig): 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._currentState = this.stateObj.state; + } + } + + private async _setValue(ev: CustomEvent) { + const stateObj = this.stateObj!; + + const domain = computeDomain(stateObj.entity_id); + + await this.hass!.callService(domain, "set_value", { + entity_id: stateObj.entity_id, + value: ev.detail.value, + }); + } + + protected render() { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsNumberTileFeature(this.stateObj) + ) { + return nothing; + } + + const stateObj = this.stateObj; + + return html` +
+ ${this._config.style === "buttons" + ? html`` + : html``} +
+ `; + } + + static get styles() { + return css` + ha-control-number-buttons { + width: auto; + } + ha-control-slider { + --control-slider-color: var(--tile-color); + } + .container { + padding: 0 12px 12px 12px; + width: auto; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-number-tile-feature": HuiNumberTileFeature; + } +} diff --git a/src/panels/lovelace/tile-features/types.ts b/src/panels/lovelace/tile-features/types.ts index 893791f235d9..8eb4fa556ef9 100644 --- a/src/panels/lovelace/tile-features/types.ts +++ b/src/panels/lovelace/tile-features/types.ts @@ -50,6 +50,11 @@ export interface SelectOptionsTileFeatureConfig { type: "select-options"; } +export interface NumberTileFeatureConfig { + type: "number"; + style?: "buttons" | "slider"; +} + export interface TargetTemperatureTileFeatureConfig { type: "target-temperature"; } @@ -98,7 +103,8 @@ export type LovelaceTileFeatureConfig = | VacuumCommandsTileFeatureConfig | TargetTemperatureTileFeatureConfig | WaterHeaterOperationModesTileFeatureConfig - | SelectOptionsTileFeatureConfig; + | SelectOptionsTileFeatureConfig + | NumberTileFeatureConfig; export type LovelaceTileFeatureContext = { entity_id?: string; diff --git a/src/translations/en.json b/src/translations/en.json index 907445f5b42a..566fedce9640 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5207,6 +5207,14 @@ "select-options": { "label": "Select options" }, + "number": { + "label": "Number", + "style": "Style", + "style_list": { + "buttons": "Buttons", + "slider": "Slider" + } + }, "target-temperature": { "label": "Target temperature" },