diff --git a/src/components/ha-control-slider.ts b/src/components/ha-control-slider.ts index c3450c6014..d323bf0a55 100644 --- a/src/components/ha-control-slider.ts +++ b/src/components/ha-control-slider.ts @@ -43,6 +43,9 @@ export class HaControlSlider extends LitElement { @property({ type: Boolean, attribute: "show-handle" }) public showHandle = false; + @property({ type: Boolean, attribute: "inverted" }) + public inverted = false; + @property({ type: Number }) public value?: number; @@ -61,11 +64,16 @@ export class HaControlSlider extends LitElement { public pressed = false; valueToPercentage(value: number) { - return (this.boundedValue(value) - this.min) / (this.max - this.min); + const percentage = + (this.boundedValue(value) - this.min) / (this.max - this.min); + return this.inverted ? 1 - percentage : percentage; } - percentageToValue(value: number) { - return (this.max - this.min) * value + this.min; + percentageToValue(percentage: number) { + return ( + (this.max - this.min) * (this.inverted ? 1 - percentage : percentage) + + this.min + ); } steppedValue(value: number) { diff --git a/src/dialogs/more-info/components/cover/ha-more-info-cover-position.ts b/src/dialogs/more-info/components/cover/ha-more-info-cover-position.ts index ea30c823c6..bd3a365e8e 100644 --- a/src/dialogs/more-info/components/cover/ha-more-info-cover-position.ts +++ b/src/dialogs/more-info/components/cover/ha-more-info-cover-position.ts @@ -50,7 +50,7 @@ export class HaMoreInfoCoverPosition extends LitElement { this.hass.localize, this.stateObj, this.hass.entities, - "position" + "current_position" )} style=${styleMap({ "--control-slider-color": color, 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 1f33678be3..22aafff590 100644 --- a/src/panels/lovelace/create-element/create-tile-feature-element.ts +++ b/src/panels/lovelace/create-element/create-tile-feature-element.ts @@ -1,6 +1,7 @@ import "../tile-features/hui-alarm-modes-tile-feature"; import "../tile-features/hui-climate-hvac-modes-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-tile-feature"; import "../tile-features/hui-fan-speed-tile-feature"; import "../tile-features/hui-light-brightness-tile-feature"; @@ -15,6 +16,7 @@ import { const TYPES: Set = new Set([ "cover-open-close", + "cover-position", "cover-tilt", "light-brightness", "light-color-temp", 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 18091a89b3..de176cc217 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 @@ -28,6 +28,7 @@ import { getTileFeatureElementClass } from "../../create-element/create-tile-fea import { supportsAlarmModesTileFeature } from "../../tile-features/hui-alarm-modes-tile-feature"; import { supportsClimateHvacModesTileFeature } from "../../tile-features/hui-climate-hvac-modes-tile-feature"; import { supportsCoverOpenCloseTileFeature } from "../../tile-features/hui-cover-open-close-tile-feature"; +import { supportsCoverPositionTileFeature } from "../../tile-features/hui-cover-position-tile-feature"; import { supportsCoverTiltTileFeature } from "../../tile-features/hui-cover-tilt-tile-feature"; import { supportsFanSpeedTileFeature } from "../../tile-features/hui-fan-speed-tile-feature"; import { supportsLightBrightnessTileFeature } from "../../tile-features/hui-light-brightness-tile-feature"; @@ -41,6 +42,7 @@ type SupportsFeature = (stateObj: HassEntity) => boolean; const FEATURE_TYPES: FeatureType[] = [ "cover-open-close", + "cover-position", "cover-tilt", "light-brightness", "light-color-temp", @@ -61,6 +63,7 @@ const EDITABLES_FEATURE_TYPES = new Set([ const SUPPORTS_FEATURE_TYPES: Record = { "cover-open-close": supportsCoverOpenCloseTileFeature, + "cover-position": supportsCoverPositionTileFeature, "cover-tilt": supportsCoverTiltTileFeature, "light-brightness": supportsLightBrightnessTileFeature, "light-color-temp": supportsLightColorTempTileFeature, diff --git a/src/panels/lovelace/tile-features/hui-cover-position-tile-feature.ts b/src/panels/lovelace/tile-features/hui-cover-position-tile-feature.ts new file mode 100644 index 0000000000..0a478866e0 --- /dev/null +++ b/src/panels/lovelace/tile-features/hui-cover-position-tile-feature.ts @@ -0,0 +1,114 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { supportsFeature } from "../../../common/entity/supports-feature"; +import { CoverEntityFeature } from "../../../data/cover"; +import { HomeAssistant } from "../../../types"; +import { LovelaceTileFeature } from "../types"; +import { CoverPositionTileFeatureConfig } from "./types"; +import { stateActive } from "../../../common/entity/state_active"; +import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display"; +import { UNAVAILABLE } from "../../../data/entity"; + +export const supportsCoverPositionTileFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return ( + domain === "cover" && + supportsFeature(stateObj, CoverEntityFeature.SET_POSITION) + ); +}; + +@customElement("hui-cover-position-tile-feature") +class HuiCoverPositionTileFeature + extends LitElement + implements LovelaceTileFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: CoverPositionTileFeatureConfig; + + static getStubConfig(): CoverPositionTileFeatureConfig { + return { + type: "cover-position", + }; + } + + public setConfig(config: CoverPositionTileFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + protected render() { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsCoverPositionTileFeature(this.stateObj) + ) { + return nothing; + } + + const percentage = stateActive(this.stateObj) + ? this.stateObj.attributes.current_position ?? 0 + : 0; + + const value = Math.max(Math.round(percentage), 0); + + return html`
+ +
`; + } + + private _valueChanged(ev: CustomEvent) { + const value = (ev.detail as any).value; + if (isNaN(value)) return; + + this.hass!.callService("cover", "set_cover_position", { + entity_id: this.stateObj!.entity_id, + position: value, + }); + } + + static get styles() { + return css` + ha-control-slider { + /* Force inactive state to be colored for the slider */ + --control-slider-color: var(--tile-color); + --control-slider-background: var(--tile-color); + --control-slider-background-opacity: 0.2; + --control-slider-thickness: 40px; + --control-slider-border-radius: 10px; + } + .container { + padding: 0 12px 12px 12px; + width: auto; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-cover-position-tile-feature": HuiCoverPositionTileFeature; + } +} diff --git a/src/panels/lovelace/tile-features/types.ts b/src/panels/lovelace/tile-features/types.ts index 478d4e98e2..79b202ad8f 100644 --- a/src/panels/lovelace/tile-features/types.ts +++ b/src/panels/lovelace/tile-features/types.ts @@ -6,6 +6,10 @@ export interface CoverOpenCloseTileFeatureConfig { type: "cover-open-close"; } +export interface CoverPositionTileFeatureConfig { + type: "cover-position"; +} + export interface CoverTiltTileFeatureConfig { type: "cover-tilt"; } @@ -54,6 +58,7 @@ export interface VacuumCommandsTileFeatureConfig { export type LovelaceTileFeatureConfig = | CoverOpenCloseTileFeatureConfig + | CoverPositionTileFeatureConfig | CoverTiltTileFeatureConfig | LightBrightnessTileFeatureConfig | LightColorTempTileFeatureConfig diff --git a/src/translations/en.json b/src/translations/en.json index 90727c1ad3..448831c0a4 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4967,6 +4967,9 @@ "cover-open-close": { "label": "Cover open/close" }, + "cover-position": { + "label": "Cover position" + }, "cover-tilt": { "label": "Cover tilt" },