mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Add fan new more info (#15843)
* Add more info fan * Change icon * Use backend translations * Fix computeAttributeValueDisplay * Clean code * Clean code * Fix some styles * Improve ha-select rounded style * Use button instead of select * Show fan speed percentage
This commit is contained in:
parent
cd2996734c
commit
45c153d374
@ -11,7 +11,7 @@ export const enum FanEntityFeature {
|
||||
}
|
||||
|
||||
interface FanEntityAttributes extends HassEntityAttributeBase {
|
||||
direction?: number;
|
||||
direction?: string;
|
||||
oscillating?: boolean;
|
||||
percentage?: number;
|
||||
percentage_step?: number;
|
||||
|
217
src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts
Normal file
217
src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts
Normal file
@ -0,0 +1,217 @@
|
||||
import {
|
||||
mdiFan,
|
||||
mdiFanOff,
|
||||
mdiFanSpeed1,
|
||||
mdiFanSpeed2,
|
||||
mdiFanSpeed3,
|
||||
} from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeAttributeNameDisplay } from "../../../../common/entity/compute_attribute_display";
|
||||
import { computeStateDisplay } from "../../../../common/entity/compute_state_display";
|
||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||
import "../../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../../components/ha-control-select";
|
||||
import "../../../../components/ha-control-slider";
|
||||
import { UNAVAILABLE } from "../../../../data/entity";
|
||||
import { FanEntity } from "../../../../data/fan";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
||||
type Speed = "off" | "low" | "medium" | "high" | "on";
|
||||
|
||||
const SPEEDS: Partial<Record<number, Speed[]>> = {
|
||||
2: ["off", "on"],
|
||||
3: ["off", "low", "high"],
|
||||
4: ["off", "low", "medium", "high"],
|
||||
};
|
||||
|
||||
function percentageToSpeed(stateObj: HassEntity, value: number): string {
|
||||
const step = stateObj.attributes.percentage_step ?? 1;
|
||||
const speedValue = Math.round(value / step);
|
||||
const speedCount = Math.round(100 / step) + 1;
|
||||
|
||||
const speeds = SPEEDS[speedCount];
|
||||
return speeds?.[speedValue] ?? "off";
|
||||
}
|
||||
|
||||
function speedToPercentage(stateObj: HassEntity, speed: Speed): number {
|
||||
const step = stateObj.attributes.percentage_step ?? 1;
|
||||
const speedCount = Math.round(100 / step) + 1;
|
||||
|
||||
const speeds = SPEEDS[speedCount];
|
||||
|
||||
if (!speeds) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const speedValue = speeds.indexOf(speed);
|
||||
if (speedValue === -1) {
|
||||
return 0;
|
||||
}
|
||||
return Math.round(speedValue * step);
|
||||
}
|
||||
|
||||
const SPEED_ICON_NUMBER: string[] = [mdiFanSpeed1, mdiFanSpeed2, mdiFanSpeed3];
|
||||
|
||||
export function getFanSpeedCount(stateObj: HassEntity) {
|
||||
const step = stateObj.attributes.percentage_step ?? 1;
|
||||
const speedCount = Math.round(100 / step) + 1;
|
||||
return speedCount;
|
||||
}
|
||||
|
||||
export const FAN_SPEED_COUNT_MAX_FOR_BUTTONS = 4;
|
||||
|
||||
@customElement("ha-more-info-fan-speed")
|
||||
export class HaMoreInfoFanSpeed extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: FanEntity;
|
||||
|
||||
@state() value?: number;
|
||||
|
||||
protected updated(changedProp: Map<string | number | symbol, unknown>): void {
|
||||
if (changedProp.has("stateObj")) {
|
||||
this.value =
|
||||
this.stateObj.attributes.percentage != null
|
||||
? Math.max(Math.round(this.stateObj.attributes.percentage), 1)
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _speedValueChanged(ev: CustomEvent) {
|
||||
const speed = (ev.detail as any).value as Speed;
|
||||
|
||||
const percentage = speedToPercentage(this.stateObj, speed);
|
||||
|
||||
this.hass.callService("fan", "set_percentage", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
percentage: percentage,
|
||||
});
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
|
||||
this.hass.callService("fan", "set_percentage", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
percentage: value,
|
||||
});
|
||||
}
|
||||
|
||||
private _localizeSpeed(speed: Speed) {
|
||||
if (speed === "on" || speed === "off") {
|
||||
return computeStateDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.locale,
|
||||
this.hass.entities,
|
||||
speed
|
||||
);
|
||||
}
|
||||
return (
|
||||
this.hass.localize(`ui.dialogs.more_info_control.fan.speed.${speed}`) ||
|
||||
speed
|
||||
);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const color = stateColorCss(this.stateObj);
|
||||
|
||||
const speedCount = getFanSpeedCount(this.stateObj);
|
||||
|
||||
if (speedCount <= FAN_SPEED_COUNT_MAX_FOR_BUTTONS) {
|
||||
const options = SPEEDS[speedCount]!.map<ControlSelectOption>(
|
||||
(speed, index) => ({
|
||||
value: speed,
|
||||
label: this._localizeSpeed(speed),
|
||||
path:
|
||||
speed === "on"
|
||||
? mdiFan
|
||||
: speed === "off"
|
||||
? mdiFanOff
|
||||
: SPEED_ICON_NUMBER[index - 1],
|
||||
})
|
||||
).reverse();
|
||||
|
||||
const speed = percentageToSpeed(
|
||||
this.stateObj,
|
||||
this.stateObj.attributes.percentage ?? 0
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-control-select
|
||||
vertical
|
||||
.options=${options}
|
||||
.value=${speed}
|
||||
@value-changed=${this._speedValueChanged}
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.entities,
|
||||
"percentage"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-slider
|
||||
vertical
|
||||
.value=${this.value}
|
||||
min="0"
|
||||
max="100"
|
||||
.step=${this.stateObj.attributes.percentage_step ?? 1}
|
||||
@value-changed=${this._valueChanged}
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.entities,
|
||||
"percentage"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-slider-color": color,
|
||||
})}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-control-slider {
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
--control-slider-thickness: 100px;
|
||||
--control-slider-border-radius: 24px;
|
||||
--control-slider-color: var(--primary-color);
|
||||
--control-slider-background: var(--disabled-color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
}
|
||||
ha-control-select {
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
--control-select-thickness: 100px;
|
||||
--control-select-border-radius: 24px;
|
||||
--control-select-color: var(--primary-color);
|
||||
--control-select-background: var(--disabled-color);
|
||||
--control-select-background-opacity: 0.2;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-more-info-fan-speed": HaMoreInfoFanSpeed;
|
||||
}
|
||||
}
|
@ -18,6 +18,17 @@ export const moreInfoControlStyle = css`
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.buttons > * {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
ha-attributes {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ export const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"];
|
||||
export const EDITABLE_DOMAINS_WITH_UNIQUE_ID = ["script"];
|
||||
/** Domains with with new more info design. */
|
||||
export const DOMAINS_WITH_NEW_MORE_INFO = [
|
||||
"fan",
|
||||
"input_boolean",
|
||||
"light",
|
||||
"siren",
|
||||
|
@ -1,238 +0,0 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { attributeClassNames } from "../../../common/entity/attribute_class_names";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-attributes";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-labeled-slider";
|
||||
import "../../../components/ha-select";
|
||||
import "../../../components/ha-switch";
|
||||
import { FanEntityFeature } from "../../../data/fan";
|
||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
*/
|
||||
class MoreInfoFan extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="iron-flex"></style>
|
||||
<style>
|
||||
.container-preset_modes,
|
||||
.container-direction,
|
||||
.container-percentage,
|
||||
.container-oscillating {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.has-percentage .container-percentage,
|
||||
.has-preset_modes .container-preset_modes,
|
||||
.has-direction .container-direction,
|
||||
.has-oscillating .container-oscillating {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
ha-select {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class$="[[computeClassNames(stateObj)]]">
|
||||
<div class="container-percentage">
|
||||
<ha-labeled-slider
|
||||
caption="[[localize('ui.card.fan.speed')]]"
|
||||
min="0"
|
||||
max="100"
|
||||
step="[[computePercentageStepSize(stateObj)]]"
|
||||
value="{{percentageSliderValue}}"
|
||||
on-change="percentageChanged"
|
||||
pin=""
|
||||
extra=""
|
||||
></ha-labeled-slider>
|
||||
</div>
|
||||
|
||||
<div class="container-preset_modes">
|
||||
<ha-select
|
||||
label="[[localize('ui.card.fan.preset_mode')]]"
|
||||
value="[[stateObj.attributes.preset_mode]]"
|
||||
on-selected="presetModeChanged"
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
on-closed="stopPropagation"
|
||||
>
|
||||
<template
|
||||
is="dom-repeat"
|
||||
items="[[stateObj.attributes.preset_modes]]"
|
||||
>
|
||||
<mwc-list-item value="[[item]]">[[item]]</mwc-list-item>
|
||||
</template>
|
||||
</ha-select>
|
||||
</div>
|
||||
|
||||
<div class="container-oscillating">
|
||||
<div class="center horizontal layout single-row">
|
||||
<div class="flex">[[localize('ui.card.fan.oscillate')]]</div>
|
||||
<ha-switch
|
||||
checked="[[oscillationToggleChecked]]"
|
||||
on-change="oscillationToggleChanged"
|
||||
>
|
||||
</ha-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-direction">
|
||||
<div class="direction">
|
||||
<div>[[localize('ui.card.fan.direction')]]</div>
|
||||
<ha-icon-button
|
||||
on-click="onDirectionReverse"
|
||||
title="[[localize('ui.card.fan.reverse')]]"
|
||||
disabled="[[computeIsRotatingReverse(stateObj)]]"
|
||||
>
|
||||
<ha-icon icon="hass:rotate-left"></ha-icon>
|
||||
</ha-icon-button>
|
||||
<ha-icon-button
|
||||
on-click="onDirectionForward"
|
||||
title="[[localize('ui.card.fan.forward')]]"
|
||||
disabled="[[computeIsRotatingForward(stateObj)]]"
|
||||
>
|
||||
<ha-icon icon="hass:rotate-right"></ha-icon>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ha-attributes
|
||||
hass="[[hass]]"
|
||||
state-obj="[[stateObj]]"
|
||||
extra-filters="percentage_step,speed,preset_mode,preset_modes,speed_list,percentage,oscillating,direction"
|
||||
></ha-attributes>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
observer: "stateObjChanged",
|
||||
},
|
||||
|
||||
oscillationToggleChecked: {
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
percentageSliderValue: {
|
||||
type: Number,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
stateObjChanged(newVal, oldVal) {
|
||||
if (newVal) {
|
||||
this.setProperties({
|
||||
oscillationToggleChecked: newVal.attributes.oscillating,
|
||||
percentageSliderValue: newVal.attributes.percentage,
|
||||
});
|
||||
}
|
||||
|
||||
if (oldVal) {
|
||||
setTimeout(() => {
|
||||
this.fire("iron-resize");
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
computePercentageStepSize(stateObj) {
|
||||
if (stateObj.attributes.percentage_step) {
|
||||
return stateObj.attributes.percentage_step;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
computeClassNames(stateObj) {
|
||||
return (
|
||||
"more-info-fan " +
|
||||
(supportsFeature(stateObj, FanEntityFeature.SET_SPEED)
|
||||
? "has-percentage "
|
||||
: "") +
|
||||
(stateObj.attributes.preset_modes &&
|
||||
stateObj.attributes.preset_modes.length
|
||||
? "has-preset_modes "
|
||||
: "") +
|
||||
attributeClassNames(stateObj, ["oscillating", "direction"])
|
||||
);
|
||||
}
|
||||
|
||||
presetModeChanged(ev) {
|
||||
const oldVal = this.stateObj.attributes.preset_mode;
|
||||
const newVal = ev.target.value;
|
||||
|
||||
if (!newVal || oldVal === newVal) return;
|
||||
|
||||
this.hass.callService("fan", "set_preset_mode", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
preset_mode: newVal,
|
||||
});
|
||||
}
|
||||
|
||||
stopPropagation(ev) {
|
||||
ev.stopPropagation();
|
||||
}
|
||||
|
||||
percentageChanged(ev) {
|
||||
const oldVal = parseInt(this.stateObj.attributes.percentage, 10);
|
||||
const newVal = ev.target.value;
|
||||
|
||||
if (isNaN(newVal) || oldVal === newVal) return;
|
||||
|
||||
this.hass.callService("fan", "set_percentage", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
percentage: newVal,
|
||||
});
|
||||
}
|
||||
|
||||
oscillationToggleChanged(ev) {
|
||||
const oldVal = this.stateObj.attributes.oscillating;
|
||||
const newVal = ev.target.checked;
|
||||
|
||||
if (oldVal === newVal) return;
|
||||
|
||||
this.hass.callService("fan", "oscillate", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
oscillating: newVal,
|
||||
});
|
||||
}
|
||||
|
||||
onDirectionReverse() {
|
||||
this.hass.callService("fan", "set_direction", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
direction: "reverse",
|
||||
});
|
||||
}
|
||||
|
||||
onDirectionForward() {
|
||||
this.hass.callService("fan", "set_direction", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
direction: "forward",
|
||||
});
|
||||
}
|
||||
|
||||
computeIsRotatingReverse(stateObj) {
|
||||
return stateObj.attributes.direction === "reverse";
|
||||
}
|
||||
|
||||
computeIsRotatingForward(stateObj) {
|
||||
return stateObj.attributes.direction === "forward";
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("more-info-fan", MoreInfoFan);
|
323
src/dialogs/more-info/controls/more-info-fan.ts
Normal file
323
src/dialogs/more-info/controls/more-info-fan.ts
Normal file
@ -0,0 +1,323 @@
|
||||
import "@material/web/button/outlined-button";
|
||||
import "@material/web/iconbutton/outlined-icon-button";
|
||||
import {
|
||||
mdiAutorenew,
|
||||
mdiAutorenewOff,
|
||||
mdiCreation,
|
||||
mdiFan,
|
||||
mdiFanOff,
|
||||
mdiPower,
|
||||
mdiRotateLeft,
|
||||
mdiRotateRight,
|
||||
} from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import {
|
||||
computeAttributeNameDisplay,
|
||||
computeAttributeValueDisplay,
|
||||
} from "../../../common/entity/compute_attribute_display";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
|
||||
import "../../../components/ha-attributes";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { FanEntity, FanEntityFeature } from "../../../data/fan";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
FAN_SPEED_COUNT_MAX_FOR_BUTTONS,
|
||||
getFanSpeedCount,
|
||||
} from "../components/fan/ha-more-info-fan-speed";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/ha-more-info-state-header";
|
||||
import "../components/ha-more-info-toggle";
|
||||
|
||||
@customElement("more-info-fan")
|
||||
class MoreInfoFan extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: FanEntity;
|
||||
|
||||
@state() public _presetMode?: string;
|
||||
|
||||
@state() private _selectedPercentage?: number;
|
||||
|
||||
private _percentageChanged(ev) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
this._selectedPercentage = value;
|
||||
}
|
||||
|
||||
private _toggle = () => {
|
||||
const service = this.stateObj?.state === "on" ? "turn_off" : "turn_on";
|
||||
forwardHaptic("light");
|
||||
this.hass.callService("fan", service, {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
});
|
||||
};
|
||||
|
||||
_setReverseDirection() {
|
||||
this.hass.callService("fan", "set_direction", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
direction: "reverse",
|
||||
});
|
||||
}
|
||||
|
||||
_setForwardDirection() {
|
||||
this.hass.callService("fan", "set_direction", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
direction: "forward",
|
||||
});
|
||||
}
|
||||
|
||||
_toggleOscillate() {
|
||||
const oscillating = this.stateObj!.attributes.oscillating;
|
||||
this.hass.callService("fan", "oscillate", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
oscillating: !oscillating,
|
||||
});
|
||||
}
|
||||
|
||||
_handlePresetMode(ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
|
||||
const index = ev.detail.index;
|
||||
const newVal = this.stateObj!.attributes.preset_modes![index];
|
||||
const oldVal = this._presetMode;
|
||||
|
||||
if (!newVal || oldVal === newVal) return;
|
||||
|
||||
this._presetMode = newVal;
|
||||
this.hass.callService("fan", "set_preset_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
preset_mode: newVal,
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (changedProps.has("stateObj")) {
|
||||
this._presetMode = this.stateObj?.attributes.preset_mode;
|
||||
this._selectedPercentage = this.stateObj?.attributes.percentage
|
||||
? Math.round(this.stateObj.attributes.percentage)
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | null {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const supportsSpeed = supportsFeature(
|
||||
this.stateObj,
|
||||
FanEntityFeature.SET_SPEED
|
||||
);
|
||||
|
||||
const supportsDirection = supportsFeature(
|
||||
this.stateObj,
|
||||
FanEntityFeature.DIRECTION
|
||||
);
|
||||
const supportsOscillate = supportsFeature(
|
||||
this.stateObj,
|
||||
FanEntityFeature.OSCILLATE
|
||||
);
|
||||
const supportsPresetMode = supportsFeature(
|
||||
this.stateObj,
|
||||
FanEntityFeature.PRESET_MODE
|
||||
);
|
||||
|
||||
const supportSpeedPercentage =
|
||||
supportsSpeed &&
|
||||
getFanSpeedCount(this.stateObj) > FAN_SPEED_COUNT_MAX_FOR_BUTTONS;
|
||||
|
||||
const stateOverride = this._selectedPercentage
|
||||
? `${Math.round(this._selectedPercentage)}${blankBeforePercent(
|
||||
this.hass!.locale
|
||||
)}%`
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<ha-more-info-state-header
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
.stateOverride=${stateOverride}
|
||||
></ha-more-info-state-header>
|
||||
<div class="controls">
|
||||
${
|
||||
supportsSpeed
|
||||
? html`
|
||||
<ha-more-info-fan-speed
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
@slider-moved=${this._percentageChanged}
|
||||
>
|
||||
</ha-more-info-fan-speed>
|
||||
`
|
||||
: html`
|
||||
<ha-more-info-toggle
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
.iconPathOn=${mdiFan}
|
||||
.iconPathOff=${mdiFanOff}
|
||||
></ha-more-info-toggle>
|
||||
`
|
||||
}
|
||||
${
|
||||
supportSpeedPercentage || supportsDirection || supportsOscillate
|
||||
? html`<div class="buttons">
|
||||
${supportSpeedPercentage
|
||||
? html`
|
||||
<md-outlined-icon-button
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
@click=${this._toggle}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPower}></ha-svg-icon>
|
||||
</md-outlined-icon-button>
|
||||
`
|
||||
: null}
|
||||
${supportsDirection
|
||||
? html`
|
||||
<md-outlined-icon-button
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE ||
|
||||
this.stateObj.attributes.direction === "reverse"}
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.fan.set_reverse_direction"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.fan.set_reverse_direction"
|
||||
)}
|
||||
@click=${this._setReverseDirection}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiRotateLeft}></ha-svg-icon>
|
||||
</md-outlined-icon-button>
|
||||
<md-outlined-icon-button
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE ||
|
||||
this.stateObj.attributes.direction === "forward"}
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.fan.set_forward_direction"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.fan.set_forward_direction"
|
||||
)}
|
||||
@click=${this._setForwardDirection}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiRotateRight}></ha-svg-icon>
|
||||
</md-outlined-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
${supportsOscillate
|
||||
? html`
|
||||
<md-outlined-icon-button
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.title=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.fan.${
|
||||
this.stateObj.attributes.oscillating
|
||||
? "turn_off_oscillating"
|
||||
: "turn_on_oscillating"
|
||||
}`
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.fan.${
|
||||
this.stateObj.attributes.oscillating
|
||||
? "turn_off_oscillating"
|
||||
: "turn_on_oscillating"
|
||||
}`
|
||||
)}
|
||||
@click=${this._toggleOscillate}
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${this.stateObj.attributes.oscillating
|
||||
? mdiAutorenew
|
||||
: mdiAutorenewOff}
|
||||
></ha-svg-icon>
|
||||
</md-outlined-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
</div> `
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
supportsPresetMode && this.stateObj.attributes.preset_modes
|
||||
? html`
|
||||
<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
@action=${this._handlePresetMode}
|
||||
@closed=${stopPropagation}
|
||||
fixed
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
<md-outlined-button
|
||||
slot="trigger"
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.label=${this._presetMode ||
|
||||
computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.entities,
|
||||
"preset_mode"
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
path=${mdiCreation}
|
||||
></ha-svg-icon>
|
||||
</md-outlined-button>
|
||||
${this.stateObj.attributes.preset_modes?.map(
|
||||
(mode) =>
|
||||
html`
|
||||
<ha-list-item
|
||||
.value=${mode}
|
||||
.activated=${this._presetMode === mode}
|
||||
>
|
||||
${computeAttributeValueDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj!,
|
||||
this.hass.locale,
|
||||
this.hass.entities,
|
||||
"preset_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-button-menu>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
</div>
|
||||
<ha-attributes
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
extra-filters="percentage_step,speed,preset_mode,preset_modes,speed_list,percentage,oscillating,direction"
|
||||
></ha-attributes>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
moreInfoControlStyle,
|
||||
css`
|
||||
md-outlined-button {
|
||||
--ha-icon-display: block;
|
||||
--md-sys-color-primary: var(--primary-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"more-info-fan": MoreInfoFan;
|
||||
}
|
||||
}
|
@ -266,16 +266,6 @@ class MoreInfoLight extends LitElement {
|
||||
return [
|
||||
moreInfoControlStyle,
|
||||
css`
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.buttons > * {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
md-outlined-icon-button-toggle,
|
||||
md-outlined-icon-button {
|
||||
--ha-icon-display: block;
|
||||
|
@ -909,6 +909,17 @@
|
||||
"color_temp": "Temperature"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fan": {
|
||||
"set_forward_direction": "Set forward direction",
|
||||
"set_reverse_direction": "Set reverse direction",
|
||||
"turn_on_oscillating": "Turn on oscillating",
|
||||
"turn_off_oscillating": "Turn off oscillating",
|
||||
"speed": {
|
||||
"low": "Low",
|
||||
"medium": "Medium",
|
||||
"high": "High"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity_registry": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user