mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Use switch and add support for light, fan and valve (#24426)
* Use switch and add support for light and fan * Add icon per domains
This commit is contained in:
parent
51193cf441
commit
8848911b34
@ -3,8 +3,8 @@ import {
|
|||||||
mdiBrightness6,
|
mdiBrightness6,
|
||||||
mdiCreation,
|
mdiCreation,
|
||||||
mdiFileWordBox,
|
mdiFileWordBox,
|
||||||
mdiLightbulb,
|
|
||||||
mdiLightbulbOff,
|
mdiLightbulbOff,
|
||||||
|
mdiLightbulbOn,
|
||||||
mdiPower,
|
mdiPower,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
@ -12,8 +12,8 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attributes";
|
|
||||||
import "../../../components/ha-attribute-icon";
|
import "../../../components/ha-attribute-icon";
|
||||||
|
import "../../../components/ha-attributes";
|
||||||
import "../../../components/ha-control-select-menu";
|
import "../../../components/ha-control-select-menu";
|
||||||
import "../../../components/ha-icon-button-group";
|
import "../../../components/ha-icon-button-group";
|
||||||
import "../../../components/ha-icon-button-toggle";
|
import "../../../components/ha-icon-button-toggle";
|
||||||
@ -121,7 +121,7 @@ class MoreInfoLight extends LitElement {
|
|||||||
<ha-state-control-toggle
|
<ha-state-control-toggle
|
||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.iconPathOn=${mdiLightbulb}
|
.iconPathOn=${mdiLightbulbOn}
|
||||||
.iconPathOff=${mdiLightbulbOff}
|
.iconPathOff=${mdiLightbulbOff}
|
||||||
></ha-state-control-toggle>
|
></ha-state-control-toggle>
|
||||||
`
|
`
|
||||||
|
@ -27,4 +27,12 @@ export const cardFeatureStyles = css`
|
|||||||
--control-slider-thickness: var(--feature-height);
|
--control-slider-thickness: var(--feature-height);
|
||||||
--control-slider-border-radius: var(--feature-border-radius);
|
--control-slider-border-radius: var(--feature-border-radius);
|
||||||
}
|
}
|
||||||
|
ha-control-switch {
|
||||||
|
--control-switch-on-color: var(--feature-color);
|
||||||
|
--control-switch-off-color: var(--feature-color);
|
||||||
|
--control-switch-background-opacity: 0.2;
|
||||||
|
--control-switch-thickness: var(--feature-height);
|
||||||
|
--control-switch-border-radius: var(--feature-border-radius);
|
||||||
|
--control-switch-padding: 0px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,23 +1,50 @@
|
|||||||
import { mdiPowerOff, mdiPower } from "@mdi/js";
|
import {
|
||||||
|
mdiFan,
|
||||||
|
mdiFanOff,
|
||||||
|
mdiLightbulbOff,
|
||||||
|
mdiLightbulbOn,
|
||||||
|
mdiPower,
|
||||||
|
mdiPowerOff,
|
||||||
|
mdiVolumeHigh,
|
||||||
|
mdiVolumeOff,
|
||||||
|
} from "@mdi/js";
|
||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import type { TemplateResult } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { LitElement, html } from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { stateColorCss } from "../../../common/entity/state_color";
|
import { stateColorCss } from "../../../common/entity/state_color";
|
||||||
import "../../../components/ha-control-select";
|
import "../../../components/ha-control-button";
|
||||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
import "../../../components/ha-control-button-group";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import "../../../components/ha-control-switch";
|
||||||
|
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity";
|
||||||
|
import { forwardHaptic } from "../../../data/haptics";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import type { LovelaceCardFeature } from "../types";
|
import type { LovelaceCardFeature } from "../types";
|
||||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||||
import type { ToggleCardFeatureConfig } from "./types";
|
import type { ToggleCardFeatureConfig } from "./types";
|
||||||
import { showToast } from "../../../util/toast";
|
|
||||||
|
|
||||||
export const supportsToggleCardFeature = (stateObj: HassEntity) => {
|
export const supportsToggleCardFeature = (stateObj: HassEntity) => {
|
||||||
const domain = computeDomain(stateObj.entity_id);
|
const domain = computeDomain(stateObj.entity_id);
|
||||||
return ["switch", "input_boolean"].includes(domain);
|
return ["switch", "input_boolean", "light", "fan", "siren", "valve"].includes(
|
||||||
|
domain
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DOMAIN_ICONS: Record<string, { on: string; off: string }> = {
|
||||||
|
siren: {
|
||||||
|
on: mdiVolumeHigh,
|
||||||
|
off: mdiVolumeOff,
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
on: mdiLightbulbOn,
|
||||||
|
off: mdiLightbulbOff,
|
||||||
|
},
|
||||||
|
fan: {
|
||||||
|
on: mdiFan,
|
||||||
|
off: mdiFanOff,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("hui-toggle-card-feature")
|
@customElement("hui-toggle-card-feature")
|
||||||
@ -41,67 +68,120 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
|||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | null {
|
private _valueChanged(ev) {
|
||||||
|
const checked = ev.target.checked as boolean;
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
this._turnOn();
|
||||||
|
} else {
|
||||||
|
this._turnOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _turnOn() {
|
||||||
|
this._callService(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _turnOff() {
|
||||||
|
this._callService(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _callService(turnOn): Promise<void> {
|
||||||
|
if (!this.hass || !this.stateObj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
forwardHaptic("light");
|
||||||
|
const stateDomain = computeDomain(this.stateObj.entity_id);
|
||||||
|
const serviceDomain = stateDomain;
|
||||||
|
const service = turnOn ? "turn_on" : "turn_off";
|
||||||
|
|
||||||
|
await this.hass.callService(serviceDomain, service, {
|
||||||
|
entity_id: this.stateObj.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
if (
|
if (
|
||||||
!this._config ||
|
!this._config ||
|
||||||
!this.hass ||
|
!this.hass ||
|
||||||
!this.stateObj ||
|
!this.stateObj ||
|
||||||
!supportsToggleCardFeature(this.stateObj)
|
!supportsToggleCardFeature(this.stateObj)
|
||||||
) {
|
) {
|
||||||
return null;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const color = stateColorCss(this.stateObj);
|
const onColor = "var(--feature-color)";
|
||||||
|
const offColor = stateColorCss(this.stateObj, "off");
|
||||||
|
|
||||||
const options = ["off", "on"].map<ControlSelectOption>((entityState) => ({
|
const isOn = this.stateObj.state === "on";
|
||||||
value: entityState,
|
const isOff = this.stateObj.state === "off";
|
||||||
label: this.hass!.formatEntityState(this.stateObj!, entityState),
|
|
||||||
path: entityState === "on" ? mdiPower : mdiPowerOff,
|
const domain = computeDomain(this.stateObj.entity_id);
|
||||||
}));
|
const onIcon = DOMAIN_ICONS[domain]?.on || mdiPower;
|
||||||
|
const offIcon = DOMAIN_ICONS[domain]?.off || mdiPowerOff;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.stateObj.attributes.assumed_state ||
|
||||||
|
this.stateObj.state === UNKNOWN
|
||||||
|
) {
|
||||||
|
return html`
|
||||||
|
<ha-control-button-group>
|
||||||
|
<ha-control-button
|
||||||
|
.label=${this.hass.localize("ui.card.common.turn_off")}
|
||||||
|
@click=${this._turnOff}
|
||||||
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
|
class=${classMap({
|
||||||
|
active: isOff,
|
||||||
|
})}
|
||||||
|
style=${styleMap({
|
||||||
|
"--color": offColor,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${offIcon}></ha-svg-icon>
|
||||||
|
</ha-control-button>
|
||||||
|
<ha-control-button
|
||||||
|
.label=${this.hass.localize("ui.card.common.turn_on")}
|
||||||
|
@click=${this._turnOn}
|
||||||
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
|
class=${classMap({
|
||||||
|
active: isOn,
|
||||||
|
})}
|
||||||
|
style=${styleMap({
|
||||||
|
"--color": onColor,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${onIcon}></ha-svg-icon>
|
||||||
|
</ha-control-button>
|
||||||
|
</ha-control-button-group>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-control-select
|
<ha-control-switch
|
||||||
.options=${options}
|
touch-action="none"
|
||||||
.value=${this.stateObj.state}
|
.pathOn=${onIcon}
|
||||||
@value-changed=${this._valueChanged}
|
.pathOff=${offIcon}
|
||||||
hide-label
|
.checked=${isOn}
|
||||||
.ariaLabel=${this.hass.localize("ui.card.humidifier.state")}
|
@change=${this._valueChanged}
|
||||||
style=${styleMap({
|
.ariaLabel=${this.hass.localize("ui.card.common.toggle")}
|
||||||
"--control-select-color": color,
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
})}
|
|
||||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
|
||||||
>
|
>
|
||||||
</ha-control-select>
|
</ha-control-switch>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _valueChanged(ev: CustomEvent) {
|
static get styles() {
|
||||||
const newState = (ev.detail as any).value;
|
return [
|
||||||
|
cardFeatureStyles,
|
||||||
if (
|
css`
|
||||||
newState === this.stateObj!.state &&
|
ha-control-button.active {
|
||||||
!this.stateObj!.attributes.assumed_state
|
--control-button-icon-color: white;
|
||||||
)
|
--control-button-background-color: var(--color);
|
||||||
return;
|
--control-button-background-opacity: 1;
|
||||||
const service = newState === "on" ? "turn_on" : "turn_off";
|
}
|
||||||
const domain = computeDomain(this.stateObj!.entity_id);
|
`,
|
||||||
|
];
|
||||||
try {
|
|
||||||
await this.hass!.callService(domain, service, {
|
|
||||||
entity_id: this.stateObj!.entity_id,
|
|
||||||
});
|
|
||||||
} catch (_err) {
|
|
||||||
showToast(this, {
|
|
||||||
message: this.hass!.localize("ui.notification_toast.action_failed", {
|
|
||||||
service: domain + "." + service,
|
|
||||||
}),
|
|
||||||
duration: 5000,
|
|
||||||
dismissable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = cardFeatureStyles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user