mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-09 10:59:50 +00:00
Add fan direction feature (#26467)
* Create fan-direction feature * Translate direction buttons tooltip * Update src/translations/en.json fix fan-direction label to sentence-cased Co-authored-by: Norbert Rittel <norbert@rittel.de> * Update src/translations/en.json Remove usued translation Co-authored-by: Paul Bottein <paul.bottein@gmail.com> --------- Co-authored-by: Norbert Rittel <norbert@rittel.de> Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
This commit is contained in:
@@ -34,6 +34,8 @@ export interface FanEntity extends HassEntityBase {
|
|||||||
attributes: FanEntityAttributes;
|
attributes: FanEntityAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FanDirection = "forward" | "reverse";
|
||||||
|
|
||||||
export type FanSpeed = "off" | "low" | "medium" | "high" | "on";
|
export type FanSpeed = "off" | "low" | "medium" | "high" | "on";
|
||||||
|
|
||||||
export const FAN_SPEEDS: Partial<Record<number, FanSpeed[]>> = {
|
export const FAN_SPEEDS: Partial<Record<number, FanSpeed[]>> = {
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import type { PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { html, LitElement } from "lit";
|
||||||
|
import { customElement, property, state } 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";
|
||||||
|
import type { FanEntity, FanDirection } 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 type {
|
||||||
|
FanDirectionCardFeatureConfig,
|
||||||
|
LovelaceCardFeatureContext,
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
|
export const supportsFanDirectionCardFeature = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
context: LovelaceCardFeatureContext
|
||||||
|
) => {
|
||||||
|
const stateObj = context.entity_id
|
||||||
|
? hass.states[context.entity_id]
|
||||||
|
: undefined;
|
||||||
|
if (!stateObj) return false;
|
||||||
|
const domain = computeDomain(stateObj.entity_id);
|
||||||
|
return (
|
||||||
|
domain === "fan" && supportsFeature(stateObj, FanEntityFeature.DIRECTION)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("hui-fan-direction-card-feature")
|
||||||
|
class HuiFanDirectionCardFeature
|
||||||
|
extends LitElement
|
||||||
|
implements LovelaceCardFeature
|
||||||
|
{
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||||
|
|
||||||
|
@state() private _config?: FanDirectionCardFeatureConfig;
|
||||||
|
|
||||||
|
@state() _currentDirection?: FanDirection;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getStubConfig(): FanDirectionCardFeatureConfig {
|
||||||
|
return {
|
||||||
|
type: "fan-direction",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateObj = this._stateObj;
|
||||||
|
const FAN_DIRECTION_MAP: FanDirection[] = ["forward", "reverse"];
|
||||||
|
|
||||||
|
const options = FAN_DIRECTION_MAP.map<ControlSelectOption>((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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-fan-direction-card-feature": HuiFanDirectionCardFeature;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,10 @@ export interface MediaPlayerVolumeSliderCardFeatureConfig {
|
|||||||
type: "media-player-volume-slider";
|
type: "media-player-volume-slider";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FanDirectionCardFeatureConfig {
|
||||||
|
type: "fan-direction";
|
||||||
|
}
|
||||||
|
|
||||||
export interface FanPresetModesCardFeatureConfig {
|
export interface FanPresetModesCardFeatureConfig {
|
||||||
type: "fan-preset-modes";
|
type: "fan-preset-modes";
|
||||||
style?: "dropdown" | "icons";
|
style?: "dropdown" | "icons";
|
||||||
@@ -201,6 +205,7 @@ export type LovelaceCardFeatureConfig =
|
|||||||
| CoverPositionCardFeatureConfig
|
| CoverPositionCardFeatureConfig
|
||||||
| CoverTiltPositionCardFeatureConfig
|
| CoverTiltPositionCardFeatureConfig
|
||||||
| CoverTiltCardFeatureConfig
|
| CoverTiltCardFeatureConfig
|
||||||
|
| FanDirectionCardFeatureConfig
|
||||||
| FanPresetModesCardFeatureConfig
|
| FanPresetModesCardFeatureConfig
|
||||||
| FanSpeedCardFeatureConfig
|
| FanSpeedCardFeatureConfig
|
||||||
| HumidifierToggleCardFeatureConfig
|
| HumidifierToggleCardFeatureConfig
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import "../card-features/hui-cover-open-close-card-feature";
|
|||||||
import "../card-features/hui-cover-position-card-feature";
|
import "../card-features/hui-cover-position-card-feature";
|
||||||
import "../card-features/hui-cover-tilt-card-feature";
|
import "../card-features/hui-cover-tilt-card-feature";
|
||||||
import "../card-features/hui-cover-tilt-position-card-feature";
|
import "../card-features/hui-cover-tilt-position-card-feature";
|
||||||
|
import "../card-features/hui-fan-direction-card-feature";
|
||||||
import "../card-features/hui-fan-preset-modes-card-feature";
|
import "../card-features/hui-fan-preset-modes-card-feature";
|
||||||
import "../card-features/hui-fan-speed-card-feature";
|
import "../card-features/hui-fan-speed-card-feature";
|
||||||
import "../card-features/hui-humidifier-modes-card-feature";
|
import "../card-features/hui-humidifier-modes-card-feature";
|
||||||
@@ -50,6 +51,7 @@ const TYPES = new Set<LovelaceCardFeatureConfig["type"]>([
|
|||||||
"cover-position",
|
"cover-position",
|
||||||
"cover-tilt-position",
|
"cover-tilt-position",
|
||||||
"cover-tilt",
|
"cover-tilt",
|
||||||
|
"fan-direction",
|
||||||
"fan-preset-modes",
|
"fan-preset-modes",
|
||||||
"fan-speed",
|
"fan-speed",
|
||||||
"humidifier-modes",
|
"humidifier-modes",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover
|
|||||||
import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature";
|
import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature";
|
||||||
import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature";
|
import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature";
|
||||||
import { supportsCoverTiltPositionCardFeature } from "../../card-features/hui-cover-tilt-position-card-feature";
|
import { supportsCoverTiltPositionCardFeature } from "../../card-features/hui-cover-tilt-position-card-feature";
|
||||||
|
import { supportsFanDirectionCardFeature } from "../../card-features/hui-fan-direction-card-feature";
|
||||||
import { supportsFanPresetModesCardFeature } from "../../card-features/hui-fan-preset-modes-card-feature";
|
import { supportsFanPresetModesCardFeature } from "../../card-features/hui-fan-preset-modes-card-feature";
|
||||||
import { supportsFanSpeedCardFeature } from "../../card-features/hui-fan-speed-card-feature";
|
import { supportsFanSpeedCardFeature } from "../../card-features/hui-fan-speed-card-feature";
|
||||||
import { supportsHumidifierModesCardFeature } from "../../card-features/hui-humidifier-modes-card-feature";
|
import { supportsHumidifierModesCardFeature } from "../../card-features/hui-humidifier-modes-card-feature";
|
||||||
@@ -75,6 +76,7 @@ const UI_FEATURE_TYPES = [
|
|||||||
"cover-position",
|
"cover-position",
|
||||||
"cover-tilt-position",
|
"cover-tilt-position",
|
||||||
"cover-tilt",
|
"cover-tilt",
|
||||||
|
"fan-direction",
|
||||||
"fan-preset-modes",
|
"fan-preset-modes",
|
||||||
"fan-speed",
|
"fan-speed",
|
||||||
"humidifier-modes",
|
"humidifier-modes",
|
||||||
@@ -135,6 +137,7 @@ const SUPPORTS_FEATURE_TYPES: Record<
|
|||||||
"cover-position": supportsCoverPositionCardFeature,
|
"cover-position": supportsCoverPositionCardFeature,
|
||||||
"cover-tilt-position": supportsCoverTiltPositionCardFeature,
|
"cover-tilt-position": supportsCoverTiltPositionCardFeature,
|
||||||
"cover-tilt": supportsCoverTiltCardFeature,
|
"cover-tilt": supportsCoverTiltCardFeature,
|
||||||
|
"fan-direction": supportsFanDirectionCardFeature,
|
||||||
"fan-preset-modes": supportsFanPresetModesCardFeature,
|
"fan-preset-modes": supportsFanPresetModesCardFeature,
|
||||||
"fan-speed": supportsFanSpeedCardFeature,
|
"fan-speed": supportsFanSpeedCardFeature,
|
||||||
"humidifier-modes": supportsHumidifierModesCardFeature,
|
"humidifier-modes": supportsHumidifierModesCardFeature,
|
||||||
|
|||||||
@@ -7835,6 +7835,9 @@
|
|||||||
"cover-tilt-position": {
|
"cover-tilt-position": {
|
||||||
"label": "Cover tilt position"
|
"label": "Cover tilt position"
|
||||||
},
|
},
|
||||||
|
"fan-direction": {
|
||||||
|
"label": "Fan direction"
|
||||||
|
},
|
||||||
"fan-speed": {
|
"fan-speed": {
|
||||||
"label": "Fan speed"
|
"label": "Fan speed"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user