mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 10:16:46 +00:00
Use context instead of stateObj for card features (#25577)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
3ce639946c
commit
fcf5ed7731
@ -1,4 +1,6 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { LovelaceCardFeatureContext } from "../panels/lovelace/card-features/types";
|
||||
|
||||
export interface CustomCardEntry {
|
||||
type: string;
|
||||
@ -19,7 +21,12 @@ export interface CustomBadgeEntry {
|
||||
export interface CustomCardFeatureEntry {
|
||||
type: string;
|
||||
name?: string;
|
||||
/** @deprecated Use `isSupported` */
|
||||
supported?: (stateObj: HassEntity) => boolean;
|
||||
isSupported?: (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
) => boolean;
|
||||
configurable?: boolean;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { mdiShieldOff } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
@ -26,9 +25,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { AlarmModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
AlarmModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsAlarmModesCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsAlarmModesCardFeature = (
|
||||
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 === "alarm_control_panel";
|
||||
};
|
||||
@ -40,7 +49,7 @@ class HuiAlarmModeCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: AlarmControlPanelEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: AlarmModesCardFeatureConfig;
|
||||
|
||||
@ -66,10 +75,26 @@ class HuiAlarmModeCardFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id] as
|
||||
| AlarmControlPanelEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentMode = this._getCurrentMode(this.stateObj);
|
||||
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._currentMode = this._getCurrentMode(this._stateObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,12 +104,12 @@ class HuiAlarmModeCardFeature
|
||||
});
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
if (!this.stateObj) return;
|
||||
if (!this._stateObj) return;
|
||||
const mode = (ev.detail as any).value as AlarmMode;
|
||||
|
||||
if (mode === this.stateObj.state) return;
|
||||
if (mode === this._stateObj.state) return;
|
||||
|
||||
const oldMode = this._getCurrentMode(this.stateObj);
|
||||
const oldMode = this._getCurrentMode(this._stateObj);
|
||||
this._currentMode = mode;
|
||||
|
||||
try {
|
||||
@ -102,24 +127,25 @@ class HuiAlarmModeCardFeature
|
||||
await setProtectedAlarmControlPanelMode(
|
||||
this,
|
||||
this.hass!,
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
mode
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | null {
|
||||
protected render(): TemplateResult | typeof nothing {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsAlarmModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsAlarmModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const color = stateColorCss(this._stateObj);
|
||||
|
||||
const supportedModes = supportedAlarmModes(this.stateObj).reverse();
|
||||
const supportedModes = supportedAlarmModes(this._stateObj).reverse();
|
||||
|
||||
const options = filterModes(
|
||||
supportedModes,
|
||||
@ -130,7 +156,7 @@ class HuiAlarmModeCardFeature
|
||||
path: ALARM_MODES[mode].path,
|
||||
}));
|
||||
|
||||
if (["triggered", "arming", "pending"].includes(this.stateObj.state)) {
|
||||
if (["triggered", "arming", "pending"].includes(this._stateObj.state)) {
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-button
|
||||
@ -156,7 +182,7 @@ class HuiAlarmModeCardFeature
|
||||
"--control-select-color": color,
|
||||
"--modes-count": options.length.toString(),
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
|
@ -1,17 +1,19 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { HuiErrorCard } from "../cards/hui-error-card";
|
||||
import { createCardFeatureElement } from "../create-element/create-card-feature-element";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import type { LovelaceCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
@customElement("hui-card-feature")
|
||||
export class HuiCardFeature extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||
@property({ attribute: false }) public context!: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public feature?: LovelaceCardFeatureConfig;
|
||||
|
||||
@ -22,9 +24,7 @@ export class HuiCardFeature extends LitElement {
|
||||
private _getFeatureElement(feature: LovelaceCardFeatureConfig) {
|
||||
if (!this._element) {
|
||||
this._element = createCardFeatureElement(feature);
|
||||
return this._element;
|
||||
}
|
||||
|
||||
return this._element;
|
||||
}
|
||||
|
||||
@ -33,12 +33,21 @@ export class HuiCardFeature extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const element = this._getFeatureElement(this.feature);
|
||||
const element = this._getFeatureElement(
|
||||
this.feature
|
||||
) as LovelaceCardFeature;
|
||||
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
(element as LovelaceCardFeature).stateObj = this.stateObj;
|
||||
(element as LovelaceCardFeature).color = this.color;
|
||||
element.context = this.context;
|
||||
element.color = this.color;
|
||||
// Backwards compatibility from custom card features
|
||||
if (this.context.entity_id) {
|
||||
const stateObj = this.hass.states[this.context.entity_id];
|
||||
if (stateObj) {
|
||||
element.stateObj = stateObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
return html`${element}`;
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "./hui-card-feature";
|
||||
import type { LovelaceCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
@customElement("hui-card-features")
|
||||
export class HuiCardFeatures extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||
@property({ attribute: false }) public context!: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public features?: LovelaceCardFeatureConfig[];
|
||||
|
||||
@ -24,7 +26,7 @@ export class HuiCardFeatures extends LitElement {
|
||||
(feature) => html`
|
||||
<hui-card-feature
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
.context=${this.context}
|
||||
.color=${this.color}
|
||||
.feature=${feature}
|
||||
></hui-card-feature>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiFan } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -19,9 +18,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { ClimateFanModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
ClimateFanModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsClimateFanModesCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsClimateFanModesCardFeature = (
|
||||
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 === "climate" &&
|
||||
@ -36,7 +45,7 @@ class HuiClimateFanModesCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: ClimateEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ClimateFanModesCardFeatureConfig;
|
||||
|
||||
@ -45,6 +54,15 @@ class HuiClimateFanModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| ClimateEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): ClimateFanModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-fan-modes",
|
||||
@ -68,8 +86,15 @@ class HuiClimateFanModesCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentFanMode = this.stateObj.attributes.fan_mode;
|
||||
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._currentFanMode = this._stateObj.attributes.fan_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +116,7 @@ class HuiClimateFanModesCardFeature
|
||||
const fanMode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as string);
|
||||
|
||||
const oldFanMode = this.stateObj!.attributes.fan_mode;
|
||||
const oldFanMode = this._stateObj!.attributes.fan_mode;
|
||||
|
||||
if (fanMode === oldFanMode) return;
|
||||
|
||||
@ -106,7 +131,7 @@ class HuiClimateFanModesCardFeature
|
||||
|
||||
private async _setMode(mode: string) {
|
||||
await this.hass!.callService("climate", "set_fan_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
fan_mode: mode,
|
||||
});
|
||||
}
|
||||
@ -115,13 +140,14 @@ class HuiClimateFanModesCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsClimateFanModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsClimateFanModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const options = filterModes(
|
||||
stateObj.attributes.fan_modes,
|
||||
@ -129,7 +155,7 @@ class HuiClimateFanModesCardFeature
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
"fan_mode",
|
||||
mode
|
||||
),
|
||||
@ -153,7 +179,7 @@ class HuiClimateFanModesCardFeature
|
||||
stateObj,
|
||||
"fan_mode"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
@ -165,7 +191,7 @@ class HuiClimateFanModesCardFeature
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "fan_mode")}
|
||||
.value=${this._currentFanMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiThermostat } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -22,9 +21,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { ClimateHvacModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
ClimateHvacModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsClimateHvacModesCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsClimateHvacModesCardFeature = (
|
||||
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 === "climate";
|
||||
};
|
||||
@ -36,7 +45,7 @@ class HuiClimateHvacModesCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: ClimateEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ClimateHvacModesCardFeatureConfig;
|
||||
|
||||
@ -45,6 +54,15 @@ class HuiClimateHvacModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| ClimateEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): ClimateHvacModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-hvac-modes",
|
||||
@ -67,8 +85,15 @@ class HuiClimateHvacModesCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentHvacMode = this.stateObj.state as HvacMode;
|
||||
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._currentHvacMode = this._stateObj.state as HvacMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,9 +115,9 @@ class HuiClimateHvacModesCardFeature
|
||||
const mode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as HvacMode);
|
||||
|
||||
if (mode === this.stateObj!.state) return;
|
||||
if (mode === this._stateObj!.state) return;
|
||||
|
||||
const oldMode = this.stateObj!.state as HvacMode;
|
||||
const oldMode = this._stateObj!.state as HvacMode;
|
||||
this._currentHvacMode = mode;
|
||||
|
||||
try {
|
||||
@ -104,7 +129,7 @@ class HuiClimateHvacModesCardFeature
|
||||
|
||||
private async _setMode(mode: HvacMode) {
|
||||
await this.hass!.callService("climate", "set_hvac_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
hvac_mode: mode,
|
||||
});
|
||||
}
|
||||
@ -113,15 +138,16 @@ class HuiClimateHvacModesCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsClimateHvacModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsClimateHvacModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const color = stateColorCss(this._stateObj);
|
||||
|
||||
const ordererHvacModes = (this.stateObj.attributes.hvac_modes || [])
|
||||
const ordererHvacModes = (this._stateObj.attributes.hvac_modes || [])
|
||||
.concat()
|
||||
.sort(compareClimateHvacModes)
|
||||
.reverse();
|
||||
@ -131,7 +157,7 @@ class HuiClimateHvacModesCardFeature
|
||||
this._config.hvac_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, mode),
|
||||
label: this.hass!.formatEntityState(this._stateObj!, mode),
|
||||
icon: html`
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
@ -147,7 +173,7 @@ class HuiClimateHvacModesCardFeature
|
||||
hide-label
|
||||
.label=${this.hass.localize("ui.card.climate.mode")}
|
||||
.value=${this._currentHvacMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
@ -184,7 +210,7 @@ class HuiClimateHvacModesCardFeature
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiTuneVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -19,9 +18,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { ClimatePresetModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
ClimatePresetModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsClimatePresetModesCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsClimatePresetModesCardFeature = (
|
||||
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 === "climate" &&
|
||||
@ -36,7 +45,7 @@ class HuiClimatePresetModesCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: ClimateEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ClimatePresetModesCardFeatureConfig;
|
||||
|
||||
@ -45,6 +54,15 @@ class HuiClimatePresetModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| ClimateEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): ClimatePresetModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-preset-modes",
|
||||
@ -70,8 +88,15 @@ class HuiClimatePresetModesCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentPresetMode = this.stateObj.attributes.preset_mode;
|
||||
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._currentPresetMode = this._stateObj.attributes.preset_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +118,7 @@ class HuiClimatePresetModesCardFeature
|
||||
const presetMode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as string);
|
||||
|
||||
const oldPresetMode = this.stateObj!.attributes.preset_mode;
|
||||
const oldPresetMode = this._stateObj!.attributes.preset_mode;
|
||||
|
||||
if (presetMode === oldPresetMode) return;
|
||||
|
||||
@ -108,7 +133,7 @@ class HuiClimatePresetModesCardFeature
|
||||
|
||||
private async _setMode(mode: string) {
|
||||
await this.hass!.callService("climate", "set_preset_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
preset_mode: mode,
|
||||
});
|
||||
}
|
||||
@ -117,13 +142,14 @@ class HuiClimatePresetModesCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsClimatePresetModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsClimatePresetModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const options = filterModes(
|
||||
stateObj.attributes.preset_modes,
|
||||
@ -131,7 +157,7 @@ class HuiClimatePresetModesCardFeature
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
@ -155,7 +181,7 @@ class HuiClimatePresetModesCardFeature
|
||||
stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
@ -167,7 +193,7 @@ class HuiClimatePresetModesCardFeature
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "preset_mode")}
|
||||
.value=${this._currentPresetMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiArrowOscillating } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -19,11 +18,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { ClimateSwingHorizontalModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
ClimateSwingHorizontalModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsClimateSwingHorizontalModesCardFeature = (
|
||||
stateObj: HassEntity
|
||||
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 === "climate" &&
|
||||
@ -38,7 +45,7 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: ClimateEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ClimateSwingHorizontalModesCardFeatureConfig;
|
||||
|
||||
@ -47,6 +54,15 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| ClimateEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): ClimateSwingHorizontalModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-swing-horizontal-modes",
|
||||
@ -72,9 +88,16 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentSwingHorizontalMode =
|
||||
this.stateObj.attributes.swing_horizontal_mode;
|
||||
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._currentSwingHorizontalMode =
|
||||
this._stateObj.attributes.swing_horizontal_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +120,7 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as string);
|
||||
|
||||
const oldSwingHorizontalMode =
|
||||
this.stateObj!.attributes.swing_horizontal_mode;
|
||||
this._stateObj!.attributes.swing_horizontal_mode;
|
||||
|
||||
if (swingHorizontalMode === oldSwingHorizontalMode) return;
|
||||
|
||||
@ -112,7 +135,7 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
|
||||
private async _setMode(mode: string) {
|
||||
await this.hass!.callService("climate", "set_swing_horizontal_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
swing_horizontal_mode: mode,
|
||||
});
|
||||
}
|
||||
@ -121,13 +144,14 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsClimateSwingHorizontalModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsClimateSwingHorizontalModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const options = filterModes(
|
||||
stateObj.attributes.swing_horizontal_modes,
|
||||
@ -135,7 +159,7 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
"swing_horizontal_mode",
|
||||
mode
|
||||
),
|
||||
@ -159,7 +183,7 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
stateObj,
|
||||
"swing_horizontal_mode"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
@ -174,7 +198,7 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
"swing_horizontal_mode"
|
||||
)}
|
||||
.value=${this._currentSwingHorizontalMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiArrowOscillating } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -19,9 +18,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { ClimateSwingModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
ClimateSwingModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsClimateSwingModesCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsClimateSwingModesCardFeature = (
|
||||
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 === "climate" &&
|
||||
@ -36,7 +45,7 @@ class HuiClimateSwingModesCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: ClimateEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ClimateSwingModesCardFeatureConfig;
|
||||
|
||||
@ -45,6 +54,15 @@ class HuiClimateSwingModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| ClimateEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): ClimateSwingModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-swing-modes",
|
||||
@ -70,8 +88,15 @@ class HuiClimateSwingModesCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentSwingMode = this.stateObj.attributes.swing_mode;
|
||||
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._currentSwingMode = this._stateObj.attributes.swing_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +118,7 @@ class HuiClimateSwingModesCardFeature
|
||||
const swingMode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as string);
|
||||
|
||||
const oldSwingMode = this.stateObj!.attributes.swing_mode;
|
||||
const oldSwingMode = this._stateObj!.attributes.swing_mode;
|
||||
|
||||
if (swingMode === oldSwingMode) return;
|
||||
|
||||
@ -108,7 +133,7 @@ class HuiClimateSwingModesCardFeature
|
||||
|
||||
private async _setMode(mode: string) {
|
||||
await this.hass!.callService("climate", "set_swing_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
swing_mode: mode,
|
||||
});
|
||||
}
|
||||
@ -117,13 +142,14 @@ class HuiClimateSwingModesCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsClimateSwingModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsClimateSwingModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const options = filterModes(
|
||||
stateObj.attributes.swing_modes,
|
||||
@ -131,7 +157,7 @@ class HuiClimateSwingModesCardFeature
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
"swing_mode",
|
||||
mode
|
||||
),
|
||||
@ -155,7 +181,7 @@ class HuiClimateSwingModesCardFeature
|
||||
stateObj,
|
||||
"swing_mode"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
@ -167,7 +193,7 @@ class HuiClimateSwingModesCardFeature
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "swing_mode")}
|
||||
.value=${this._currentSwingMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
|
@ -1,19 +1,30 @@
|
||||
import { mdiRestore, mdiPlus, mdiMinus } from "@mdi/js";
|
||||
import { mdiMinus, mdiPlus, mdiRestore } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-select";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { COUNTER_ACTIONS, type CounterActionsCardFeatureConfig } from "./types";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-button";
|
||||
import {
|
||||
COUNTER_ACTIONS,
|
||||
type CounterActionsCardFeatureConfig,
|
||||
type LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsCounterActionsCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsCounterActionsCardFeature = (
|
||||
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 === "counter";
|
||||
};
|
||||
@ -56,10 +67,17 @@ class HuiCounterActionsCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CounterActionsCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/hui-counter-actions-card-feature-editor"
|
||||
@ -85,8 +103,9 @@ class HuiCounterActionsCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCounterActionsCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCounterActionsCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@ -96,7 +115,7 @@ class HuiCounterActionsCardFeature
|
||||
${this._config?.actions
|
||||
?.filter((action) => COUNTER_ACTIONS.includes(action))
|
||||
.map((action) => {
|
||||
const button = COUNTER_ACTIONS_BUTTON[action](this.stateObj!);
|
||||
const button = COUNTER_ACTIONS_BUTTON[action](this._stateObj!);
|
||||
return html`
|
||||
<ha-control-button
|
||||
.entry=${button}
|
||||
@ -106,7 +125,7 @@ class HuiCounterActionsCardFeature
|
||||
)}
|
||||
@click=${this._onActionTap}
|
||||
.disabled=${button.disabled ||
|
||||
this.stateObj?.state === UNAVAILABLE}
|
||||
this._stateObj?.state === UNAVAILABLE}
|
||||
>
|
||||
<ha-svg-icon .path=${button.icon}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
@ -120,7 +139,7 @@ class HuiCounterActionsCardFeature
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.target! as any).entry as CounterButton;
|
||||
this.hass!.callService("counter", entry.serviceName, {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiStop } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
@ -9,20 +8,31 @@ import {
|
||||
} from "../../../common/entity/cover_icon";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
canClose,
|
||||
canOpen,
|
||||
canStop,
|
||||
CoverEntityFeature,
|
||||
type CoverEntity,
|
||||
} from "../../../data/cover";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { CoverOpenCloseCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
CoverOpenCloseCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsCoverOpenCloseCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsCoverOpenCloseCardFeature = (
|
||||
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 === "cover" &&
|
||||
@ -38,10 +48,17 @@ class HuiCoverOpenCloseCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CoverOpenCloseCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as CoverEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): CoverOpenCloseCardFeatureConfig {
|
||||
return {
|
||||
type: "cover-open-close",
|
||||
@ -58,21 +75,21 @@ class HuiCoverOpenCloseCardFeature
|
||||
private _onOpenTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "open_cover", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "close_cover", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStopTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "stop_cover", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -80,47 +97,48 @@ class HuiCoverOpenCloseCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCoverOpenCloseCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverOpenCloseCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.OPEN)
|
||||
${supportsFeature(this._stateObj, CoverEntityFeature.OPEN)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.open_cover")}
|
||||
@click=${this._onOpenTap}
|
||||
.disabled=${!canOpen(this.stateObj)}
|
||||
.disabled=${!canOpen(this._stateObj)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${computeOpenIcon(this.stateObj)}
|
||||
.path=${computeOpenIcon(this._stateObj)}
|
||||
></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
`
|
||||
: nothing}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.STOP)
|
||||
${supportsFeature(this._stateObj, CoverEntityFeature.STOP)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.stop_cover")}
|
||||
@click=${this._onStopTap}
|
||||
.disabled=${!canStop(this.stateObj)}
|
||||
.disabled=${!canStop(this._stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiStop}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
`
|
||||
: nothing}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.CLOSE)
|
||||
${supportsFeature(this._stateObj, CoverEntityFeature.CLOSE)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.close_cover")}
|
||||
@click=${this._onCloseTap}
|
||||
.disabled=${!canClose(this.stateObj)}
|
||||
.disabled=${!canClose(this._stateObj)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${computeCloseIcon(this.stateObj)}
|
||||
.path=${computeCloseIcon(this._stateObj)}
|
||||
></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
`
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
@ -8,16 +7,26 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { CoverEntityFeature } from "../../../data/cover";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { CoverEntityFeature, type CoverEntity } from "../../../data/cover";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { CoverPositionCardFeatureConfig } from "./types";
|
||||
import "../../../components/ha-control-slider";
|
||||
import type {
|
||||
CoverPositionCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsCoverPositionCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsCoverPositionCardFeature = (
|
||||
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 === "cover" &&
|
||||
@ -32,12 +41,19 @@ class HuiCoverPositionCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state() private _config?: CoverPositionCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as CoverEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): CoverPositionCardFeatureConfig {
|
||||
return {
|
||||
type: "cover-position",
|
||||
@ -55,23 +71,24 @@ class HuiCoverPositionCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCoverPositionCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverPositionCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const percentage = stateActive(this.stateObj)
|
||||
? (this.stateObj.attributes.current_position ?? 0)
|
||||
const percentage = stateActive(this._stateObj)
|
||||
? (this._stateObj.attributes.current_position ?? 0)
|
||||
: 0;
|
||||
|
||||
const value = Math.max(Math.round(percentage), 0);
|
||||
|
||||
const openColor = stateColorCss(this.stateObj, "open");
|
||||
const openColor = stateColorCss(this._stateObj, "open");
|
||||
|
||||
const color = this.color
|
||||
? computeCssColor(this.color)
|
||||
: stateColorCss(this.stateObj);
|
||||
: stateColorCss(this._stateObj);
|
||||
|
||||
const style = {
|
||||
"--feature-color": color,
|
||||
@ -91,11 +108,11 @@ class HuiCoverPositionCardFeature
|
||||
@value-changed=${this._valueChanged}
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
"current_position"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_position}
|
||||
.locale=${this.hass.locale}
|
||||
></ha-control-slider>
|
||||
@ -107,7 +124,7 @@ class HuiCoverPositionCardFeature
|
||||
if (isNaN(value)) return;
|
||||
|
||||
this.hass!.callService("cover", "set_cover_position", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
position: value,
|
||||
});
|
||||
}
|
||||
|
@ -1,24 +1,34 @@
|
||||
import { mdiArrowBottomLeft, mdiArrowTopRight, mdiStop } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html, 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 "../../../components/ha-control-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
CoverEntityFeature,
|
||||
canCloseTilt,
|
||||
canOpenTilt,
|
||||
canStopTilt,
|
||||
type CoverEntity,
|
||||
} from "../../../data/cover";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { CoverTiltCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
CoverTiltCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsCoverTiltCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsCoverTiltCardFeature = (
|
||||
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 === "cover" &&
|
||||
@ -34,10 +44,17 @@ class HuiCoverTiltCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CoverTiltCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as CoverEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): CoverTiltCardFeatureConfig {
|
||||
return {
|
||||
type: "cover-tilt",
|
||||
@ -54,21 +71,21 @@ class HuiCoverTiltCardFeature
|
||||
private _onOpenTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "open_cover_tilt", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onCloseTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "close_cover_tilt", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStopTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
this.hass!.callService("cover", "stop_cover_tilt", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -76,42 +93,43 @@ class HuiCoverTiltCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCoverTiltCardFeature
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverTiltCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.OPEN_TILT)
|
||||
${supportsFeature(this._stateObj, CoverEntityFeature.OPEN_TILT)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.open_tilt_cover")}
|
||||
@click=${this._onOpenTap}
|
||||
.disabled=${!canOpenTilt(this.stateObj)}
|
||||
.disabled=${!canOpenTilt(this._stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiArrowTopRight}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
`
|
||||
: nothing}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.STOP_TILT)
|
||||
${supportsFeature(this._stateObj, CoverEntityFeature.STOP_TILT)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.stop_cover")}
|
||||
@click=${this._onStopTap}
|
||||
.disabled=${!canStopTilt(this.stateObj)}
|
||||
.disabled=${!canStopTilt(this._stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiStop}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
`
|
||||
: nothing}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.CLOSE_TILT)
|
||||
${supportsFeature(this._stateObj, CoverEntityFeature.CLOSE_TILT)
|
||||
? html`
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.cover.close_tilt_cover")}
|
||||
@click=${this._onCloseTap}
|
||||
.disabled=${!canCloseTilt(this.stateObj)}
|
||||
.disabled=${!canCloseTilt(this._stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiArrowBottomLeft}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
@ -15,11 +14,21 @@ import { generateTiltSliderTrackBackgroundGradient } from "../../../state-contro
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { CoverTiltPositionCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
CoverTiltPositionCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
const GRADIENT = generateTiltSliderTrackBackgroundGradient();
|
||||
|
||||
export const supportsCoverTiltPositionCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsCoverTiltPositionCardFeature = (
|
||||
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 === "cover" &&
|
||||
@ -34,12 +43,19 @@ class HuiCoverTiltPositionCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: CoverEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state() private _config?: CoverTiltPositionCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as CoverEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): CoverTiltPositionCardFeatureConfig {
|
||||
return {
|
||||
type: "cover-tilt-position",
|
||||
@ -57,21 +73,22 @@ class HuiCoverTiltPositionCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCoverTiltPositionCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsCoverTiltPositionCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const percentage = this.stateObj.attributes.current_tilt_position ?? 0;
|
||||
const percentage = this._stateObj.attributes.current_tilt_position ?? 0;
|
||||
|
||||
const value = Math.max(Math.round(percentage), 0);
|
||||
|
||||
const openColor = stateColorCss(this.stateObj, "open");
|
||||
const openColor = stateColorCss(this._stateObj, "open");
|
||||
|
||||
const color = this.color
|
||||
? computeCssColor(this.color)
|
||||
: stateColorCss(this.stateObj);
|
||||
: stateColorCss(this._stateObj);
|
||||
|
||||
const style = {
|
||||
"--feature-color": color,
|
||||
@ -90,11 +107,11 @@ class HuiCoverTiltPositionCardFeature
|
||||
@value-changed=${this._valueChanged}
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
"current_tilt_position"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_tilt_position}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
@ -108,7 +125,7 @@ class HuiCoverTiltPositionCardFeature
|
||||
if (isNaN(value)) return;
|
||||
|
||||
this.hass!.callService("cover", "set_cover_tilt_position", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
tilt_position: value,
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiTuneVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -19,9 +18,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { FanPresetModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
FanPresetModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsFanPresetModesCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsFanPresetModesCardFeature = (
|
||||
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.PRESET_MODE)
|
||||
@ -35,7 +44,7 @@ class HuiFanPresetModesCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: FanEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: FanPresetModesCardFeatureConfig;
|
||||
|
||||
@ -44,6 +53,13 @@ class HuiFanPresetModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
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(): FanPresetModesCardFeatureConfig {
|
||||
return {
|
||||
type: "fan-preset-modes",
|
||||
@ -66,9 +82,15 @@ class HuiFanPresetModesCardFeature
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentPresetMode = this.stateObj.attributes.preset_mode;
|
||||
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._currentPresetMode = this._stateObj.attributes.preset_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +112,7 @@ class HuiFanPresetModesCardFeature
|
||||
const presetMode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as string);
|
||||
|
||||
const oldPresetMode = this.stateObj!.attributes.preset_mode;
|
||||
const oldPresetMode = this._stateObj!.attributes.preset_mode;
|
||||
|
||||
if (presetMode === oldPresetMode) return;
|
||||
|
||||
@ -105,7 +127,7 @@ class HuiFanPresetModesCardFeature
|
||||
|
||||
private async _setMode(mode: string) {
|
||||
await this.hass!.callService("fan", "set_preset_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
preset_mode: mode,
|
||||
});
|
||||
}
|
||||
@ -114,13 +136,14 @@ class HuiFanPresetModesCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsFanPresetModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsFanPresetModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const options = filterModes(
|
||||
stateObj.attributes.preset_modes,
|
||||
@ -128,7 +151,7 @@ class HuiFanPresetModesCardFeature
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
@ -152,7 +175,7 @@ class HuiFanPresetModesCardFeature
|
||||
stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
@ -164,7 +187,7 @@ class HuiFanPresetModesCardFeature
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "preset_mode")}
|
||||
.value=${this._currentPresetMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
@ -9,6 +8,7 @@ import "../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
|
||||
import type { FanEntity, FanSpeed } from "../../../data/fan";
|
||||
import {
|
||||
computeFanSpeedCount,
|
||||
@ -21,11 +21,20 @@ import {
|
||||
} from "../../../data/fan";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import type { FanSpeedCardFeatureConfig } from "./types";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
FanSpeedCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsFanSpeedCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsFanSpeedCardFeature = (
|
||||
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.SET_SPEED)
|
||||
@ -36,10 +45,17 @@ export const supportsFanSpeedCardFeature = (stateObj: HassEntity) => {
|
||||
class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: FanEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: FanSpeedCardFeatureConfig;
|
||||
|
||||
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(): FanSpeedCardFeatureConfig {
|
||||
return {
|
||||
type: "fan-speed",
|
||||
@ -55,7 +71,7 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
|
||||
private _localizeSpeed(speed: FanSpeed) {
|
||||
if (speed === "on" || speed === "off") {
|
||||
return this.hass!.formatEntityState(this.stateObj!, speed);
|
||||
return this.hass!.formatEntityState(this._stateObj!, speed);
|
||||
}
|
||||
return this.hass!.localize(`ui.card.fan.speed.${speed}`) || speed;
|
||||
}
|
||||
@ -64,16 +80,17 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsFanSpeedCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsFanSpeedCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const speedCount = computeFanSpeedCount(this.stateObj);
|
||||
const speedCount = computeFanSpeedCount(this._stateObj);
|
||||
|
||||
const percentage = stateActive(this.stateObj)
|
||||
? (this.stateObj.attributes.percentage ?? 0)
|
||||
const percentage = stateActive(this._stateObj)
|
||||
? (this._stateObj.attributes.percentage ?? 0)
|
||||
: 0;
|
||||
|
||||
if (speedCount <= FAN_SPEED_COUNT_MAX_FOR_BUTTONS) {
|
||||
@ -81,11 +98,11 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
(speed) => ({
|
||||
value: speed,
|
||||
label: this._localizeSpeed(speed),
|
||||
path: computeFanSpeedIcon(this.stateObj!, speed),
|
||||
path: computeFanSpeedIcon(this._stateObj!, speed),
|
||||
})
|
||||
);
|
||||
|
||||
const speed = fanPercentageToSpeed(this.stateObj, percentage);
|
||||
const speed = fanPercentageToSpeed(this._stateObj, percentage);
|
||||
|
||||
return html`
|
||||
<ha-control-select
|
||||
@ -95,11 +112,11 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
hide-label
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
"percentage"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
@ -112,15 +129,15 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
.value=${value}
|
||||
min="0"
|
||||
max="100"
|
||||
.step=${this.stateObj.attributes.percentage_step ?? 1}
|
||||
.step=${this._stateObj.attributes.percentage_step ?? 1}
|
||||
@value-changed=${this._valueChanged}
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
this.hass.entities,
|
||||
"percentage"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.fan.percentage}
|
||||
.locale=${this.hass.locale}
|
||||
></ha-control-slider>
|
||||
@ -130,10 +147,10 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
private _speedValueChanged(ev: CustomEvent) {
|
||||
const speed = (ev.detail as any).value as FanSpeed;
|
||||
|
||||
const percentage = fanSpeedToPercentage(this.stateObj!, speed);
|
||||
const percentage = fanSpeedToPercentage(this._stateObj!, speed);
|
||||
|
||||
this.hass!.callService("fan", "set_percentage", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
percentage: percentage,
|
||||
});
|
||||
}
|
||||
@ -143,7 +160,7 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
if (isNaN(value)) return;
|
||||
|
||||
this.hass!.callService("fan", "set_percentage", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
percentage: value,
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiTuneVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -19,9 +18,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { HumidifierModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
HumidifierModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsHumidifierModesCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsHumidifierModesCardFeature = (
|
||||
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 === "humidifier" &&
|
||||
@ -36,12 +45,21 @@ class HuiHumidifierModesCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HumidifierEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: HumidifierModesCardFeatureConfig;
|
||||
|
||||
@state() _currentMode?: string;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| HumidifierEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
@ -68,8 +86,15 @@ class HuiHumidifierModesCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentMode = this.stateObj.attributes.mode;
|
||||
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._currentMode = this._stateObj.attributes.mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +116,7 @@ class HuiHumidifierModesCardFeature
|
||||
const mode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as string);
|
||||
|
||||
const oldMode = this.stateObj!.attributes.mode;
|
||||
const oldMode = this._stateObj!.attributes.mode;
|
||||
|
||||
if (mode === oldMode) return;
|
||||
|
||||
@ -106,7 +131,7 @@ class HuiHumidifierModesCardFeature
|
||||
|
||||
private async _setMode(mode: string) {
|
||||
await this.hass!.callService("humidifier", "set_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
mode: mode,
|
||||
});
|
||||
}
|
||||
@ -115,13 +140,14 @@ class HuiHumidifierModesCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsHumidifierModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsHumidifierModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const options = filterModes(
|
||||
stateObj.attributes.available_modes,
|
||||
@ -129,7 +155,7 @@ class HuiHumidifierModesCardFeature
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
"mode",
|
||||
mode
|
||||
),
|
||||
@ -150,7 +176,7 @@ class HuiHumidifierModesCardFeature
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-label
|
||||
.ariaLabel=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
@ -162,7 +188,7 @@ class HuiHumidifierModesCardFeature
|
||||
hide-label
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
|
||||
.value=${this._currentMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiPower, mdiWaterPercent } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@ -16,9 +15,19 @@ import type {
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { HumidifierToggleCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
HumidifierToggleCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsHumidifierToggleCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsHumidifierToggleCardFeature = (
|
||||
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 === "humidifier";
|
||||
};
|
||||
@ -30,12 +39,21 @@ class HuiHumidifierToggleCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HumidifierEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: HumidifierToggleCardFeatureConfig;
|
||||
|
||||
@state() _currentState?: HumidifierState;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| HumidifierEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): HumidifierToggleCardFeatureConfig {
|
||||
return {
|
||||
type: "humidifier-toggle",
|
||||
@ -51,17 +69,24 @@ class HuiHumidifierToggleCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentState = this.stateObj.state as HumidifierState;
|
||||
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._currentState = this._stateObj.state as HumidifierState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
const newState = (ev.detail as any).value as HumidifierState;
|
||||
|
||||
if (newState === this.stateObj!.state) return;
|
||||
if (newState === this._stateObj!.state) return;
|
||||
|
||||
const oldState = this.stateObj!.state as HumidifierState;
|
||||
const oldState = this._stateObj!.state as HumidifierState;
|
||||
this._currentState = newState;
|
||||
|
||||
try {
|
||||
@ -76,7 +101,7 @@ class HuiHumidifierToggleCardFeature
|
||||
"humidifier",
|
||||
newState === "on" ? "turn_on" : "turn_off",
|
||||
{
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -85,17 +110,18 @@ class HuiHumidifierToggleCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsHumidifierToggleCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsHumidifierToggleCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const color = stateColorCss(this._stateObj);
|
||||
|
||||
const options = ["off", "on"].map<ControlSelectOption>((entityState) => ({
|
||||
value: entityState,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, entityState),
|
||||
label: this.hass!.formatEntityState(this._stateObj!, entityState),
|
||||
path: entityState === "on" ? mdiWaterPercent : mdiPower,
|
||||
}));
|
||||
|
||||
@ -109,7 +135,7 @@ class HuiHumidifierToggleCardFeature
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
|
@ -5,8 +5,8 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import type { LawnMowerEntity } from "../../../data/lawn_mower";
|
||||
import { LawnMowerEntityFeature, canDock } from "../../../data/lawn_mower";
|
||||
@ -16,6 +16,7 @@ import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
LawnMowerCommand,
|
||||
LawnMowerCommandsCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
import { LAWN_MOWER_COMMANDS } from "./types";
|
||||
|
||||
@ -74,7 +75,14 @@ export const LAWN_MOWER_COMMANDS_BUTTONS: Record<
|
||||
}),
|
||||
};
|
||||
|
||||
export const supportsLawnMowerCommandCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsLawnMowerCommandCardFeature = (
|
||||
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 === "lawn_mower" &&
|
||||
@ -89,14 +97,26 @@ class HuiLawnMowerCommandCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LawnMowerCommandsCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| LawnMowerEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
): LawnMowerCommandsCardFeatureConfig {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
return {
|
||||
type: "lawn-mower-commands",
|
||||
commands: stateObj
|
||||
@ -127,7 +147,7 @@ class HuiLawnMowerCommandCardFeature
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.target! as any).entry as LawnMowerButton;
|
||||
this.hass!.callService("lawn_mower", entry.serviceName, {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -135,13 +155,14 @@ class HuiLawnMowerCommandCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsLawnMowerCommandCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLawnMowerCommandCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj as LawnMowerEntity;
|
||||
const stateObj = this._stateObj as LawnMowerEntity;
|
||||
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
|
@ -1,17 +1,26 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { lightSupportsBrightness } from "../../../data/light";
|
||||
import { lightSupportsBrightness, type LightEntity } from "../../../data/light";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { LightBrightnessCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LightBrightnessCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsLightBrightnessCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsLightBrightnessCardFeature = (
|
||||
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 === "light" && lightSupportsBrightness(stateObj);
|
||||
};
|
||||
@ -23,10 +32,17 @@ class HuiLightBrightnessCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LightBrightnessCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id] as LightEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): LightBrightnessCardFeatureConfig {
|
||||
return {
|
||||
type: "light-brightness",
|
||||
@ -44,16 +60,17 @@ class HuiLightBrightnessCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsLightBrightnessCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLightBrightnessCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const position =
|
||||
this.stateObj.attributes.brightness != null
|
||||
this._stateObj.attributes.brightness != null
|
||||
? Math.max(
|
||||
Math.round((this.stateObj.attributes.brightness * 100) / 255),
|
||||
Math.round((this._stateObj.attributes.brightness * 100) / 255),
|
||||
1
|
||||
)
|
||||
: undefined;
|
||||
@ -63,8 +80,8 @@ class HuiLightBrightnessCardFeature
|
||||
.value=${position}
|
||||
min="1"
|
||||
max="100"
|
||||
.showHandle=${stateActive(this.stateObj)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.showHandle=${stateActive(this._stateObj)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.localize("ui.card.light.brightness")}
|
||||
unit="%"
|
||||
@ -78,7 +95,7 @@ class HuiLightBrightnessCardFeature
|
||||
const value = ev.detail.value;
|
||||
|
||||
this.hass!.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
brightness_pct: value,
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
@ -12,14 +11,28 @@ import { stateActive } from "../../../common/entity/state_active";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
|
||||
import { LightColorMode, lightSupportsColorMode } from "../../../data/light";
|
||||
import {
|
||||
LightColorMode,
|
||||
lightSupportsColorMode,
|
||||
type LightEntity,
|
||||
} from "../../../data/light";
|
||||
import { generateColorTemperatureGradient } from "../../../dialogs/more-info/components/lights/light-color-temp-picker";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { LightColorTempCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LightColorTempCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsLightColorTempCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsLightColorTempCardFeature = (
|
||||
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 === "light" &&
|
||||
@ -34,10 +47,17 @@ class HuiLightColorTempCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LightColorTempCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as LightEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): LightColorTempCardFeatureConfig {
|
||||
return {
|
||||
type: "light-color-temp",
|
||||
@ -55,21 +75,22 @@ class HuiLightColorTempCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsLightColorTempCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLightColorTempCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const position =
|
||||
this.stateObj.attributes.color_temp_kelvin != null
|
||||
? this.stateObj.attributes.color_temp_kelvin
|
||||
this._stateObj.attributes.color_temp_kelvin != null
|
||||
? this._stateObj.attributes.color_temp_kelvin
|
||||
: undefined;
|
||||
|
||||
const minKelvin =
|
||||
this.stateObj.attributes.min_color_temp_kelvin ?? DEFAULT_MIN_KELVIN;
|
||||
this._stateObj.attributes.min_color_temp_kelvin ?? DEFAULT_MIN_KELVIN;
|
||||
const maxKelvin =
|
||||
this.stateObj.attributes.max_color_temp_kelvin ?? DEFAULT_MAX_KELVIN;
|
||||
this._stateObj.attributes.max_color_temp_kelvin ?? DEFAULT_MAX_KELVIN;
|
||||
|
||||
const gradient = this._generateTemperatureGradient(minKelvin!, maxKelvin);
|
||||
|
||||
@ -77,8 +98,8 @@ class HuiLightColorTempCardFeature
|
||||
<ha-control-slider
|
||||
.value=${position}
|
||||
mode="cursor"
|
||||
.showHandle=${stateActive(this.stateObj)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.showHandle=${stateActive(this._stateObj)}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.localize("ui.card.light.color_temperature")}
|
||||
.min=${minKelvin}
|
||||
@ -101,7 +122,7 @@ class HuiLightColorTempCardFeature
|
||||
const value = ev.detail.value;
|
||||
|
||||
this.hass!.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
color_temp_kelvin: value,
|
||||
});
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { mdiLock, mdiLockOpenVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
@ -12,13 +10,24 @@ import {
|
||||
callProtectedLockService,
|
||||
canLock,
|
||||
canUnlock,
|
||||
type LockEntity,
|
||||
} from "../../../data/lock";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { LockCommandsCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LockCommandsCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsLockCommandsCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsLockCommandsCardFeature = (
|
||||
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 === "lock";
|
||||
};
|
||||
@ -30,10 +39,17 @@ class HuiLockCommandsCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: LockCommandsCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as LockEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): LockCommandsCardFeatureConfig {
|
||||
return {
|
||||
type: "lock-commands",
|
||||
@ -50,19 +66,20 @@ class HuiLockCommandsCardFeature
|
||||
private _onTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
const service = ev.target.dataset.service;
|
||||
if (!this.hass || !this.stateObj || !service) {
|
||||
if (!this.hass || !this._stateObj || !service) {
|
||||
return;
|
||||
}
|
||||
forwardHaptic("light");
|
||||
callProtectedLockService(this, this.hass, this.stateObj, service);
|
||||
callProtectedLockService(this, this.hass, this._stateObj, service);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsLockCommandsCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLockCommandsCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@ -71,7 +88,7 @@ class HuiLockCommandsCardFeature
|
||||
<ha-control-button-group>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.lock.lock")}
|
||||
.disabled=${!canLock(this.stateObj)}
|
||||
.disabled=${!canLock(this._stateObj)}
|
||||
@click=${this._onTap}
|
||||
data-service="lock"
|
||||
>
|
||||
@ -79,7 +96,7 @@ class HuiLockCommandsCardFeature
|
||||
</ha-control-button>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.lock.unlock")}
|
||||
.disabled=${!canUnlock(this.stateObj)}
|
||||
.disabled=${!canUnlock(this._stateObj)}
|
||||
@click=${this._onTap}
|
||||
data-service="unlock"
|
||||
>
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { mdiCheck } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
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 "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
@ -12,13 +10,24 @@ import {
|
||||
callProtectedLockService,
|
||||
canOpen,
|
||||
LockEntityFeature,
|
||||
type LockEntity,
|
||||
} from "../../../data/lock";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import type { LockOpenDoorCardFeatureConfig } from "./types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
LockOpenDoorCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
|
||||
export const supportsLockOpenDoorCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsLockOpenDoorCardFeature = (
|
||||
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 === "lock" && supportsFeature(stateObj, LockEntityFeature.OPEN);
|
||||
};
|
||||
@ -35,7 +44,7 @@ class HuiLockOpenDoorCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() public _buttonState: ButtonState = "normal";
|
||||
|
||||
@ -43,6 +52,13 @@ class HuiLockOpenDoorCardFeature
|
||||
|
||||
private _buttonTimeout?: number;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as LockEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): LockOpenDoorCardFeatureConfig {
|
||||
return {
|
||||
type: "lock-open-door",
|
||||
@ -71,10 +87,10 @@ class HuiLockOpenDoorCardFeature
|
||||
this._setButtonState("confirm", CONFIRM_TIMEOUT_SECOND);
|
||||
return;
|
||||
}
|
||||
if (!this.hass || !this.stateObj) {
|
||||
if (!this.hass || !this._stateObj) {
|
||||
return;
|
||||
}
|
||||
callProtectedLockService(this, this.hass, this.stateObj!, "open");
|
||||
callProtectedLockService(this, this.hass, this._stateObj!, "open");
|
||||
|
||||
this._setButtonState("done", DONE_TIMEOUT_SECOND);
|
||||
}
|
||||
@ -83,8 +99,9 @@ class HuiLockOpenDoorCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsLockOpenDoorCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsLockOpenDoorCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
@ -100,7 +117,7 @@ class HuiLockOpenDoorCardFeature
|
||||
: html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-button
|
||||
.disabled=${!canOpen(this.stateObj)}
|
||||
.disabled=${!canOpen(this._stateObj)}
|
||||
class="open-button ${this._buttonState}"
|
||||
@click=${this._open}
|
||||
>
|
||||
|
@ -1,20 +1,30 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { isUnavailableState } from "../../../data/entity";
|
||||
import {
|
||||
MediaPlayerEntityFeature,
|
||||
type MediaPlayerEntity,
|
||||
} from "../../../data/media-player";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { MediaPlayerVolumeSliderCardFeatureConfig } from "./types";
|
||||
import { MediaPlayerEntityFeature } from "../../../data/media-player";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
MediaPlayerVolumeSliderCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsMediaPlayerVolumeSliderCardFeature = (
|
||||
stateObj: HassEntity
|
||||
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 === "media_player" &&
|
||||
@ -29,10 +39,19 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: MediaPlayerVolumeSliderCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| MediaPlayerEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): MediaPlayerVolumeSliderCardFeatureConfig {
|
||||
return {
|
||||
type: "media-player-volume-slider",
|
||||
@ -50,15 +69,16 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsMediaPlayerVolumeSliderCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsMediaPlayerVolumeSliderCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const position =
|
||||
this.stateObj.attributes.volume_level != null
|
||||
? Math.round(this.stateObj.attributes.volume_level * 100)
|
||||
this._stateObj.attributes.volume_level != null
|
||||
? Math.round(this._stateObj.attributes.volume_level * 100)
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
@ -66,8 +86,8 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
.value=${position}
|
||||
min="0"
|
||||
max="100"
|
||||
.showHandle=${stateActive(this.stateObj)}
|
||||
.disabled=${!this.stateObj || isUnavailableState(this.stateObj.state)}
|
||||
.showHandle=${stateActive(this._stateObj)}
|
||||
.disabled=${!this._stateObj || isUnavailableState(this._stateObj.state)}
|
||||
@value-changed=${this._valueChanged}
|
||||
unit="%"
|
||||
.locale=${this.hass.locale}
|
||||
@ -80,7 +100,7 @@ class HuiMediaPlayerVolumeSliderCardFeature
|
||||
const value = ev.detail.value;
|
||||
|
||||
this.hass!.callService("media_player", "volume_set", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
volume_level: value / 100,
|
||||
});
|
||||
}
|
||||
|
@ -12,9 +12,19 @@ import { isUnavailableState } from "../../../data/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { NumericInputCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
NumericInputCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsNumericInputCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsNumericInputCardFeature = (
|
||||
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 === "input_number" || domain === "number";
|
||||
};
|
||||
@ -26,7 +36,7 @@ class HuiNumericInputCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: NumericInputCardFeatureConfig;
|
||||
|
||||
@ -39,6 +49,13 @@ class HuiNumericInputCardFeature
|
||||
};
|
||||
}
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/hui-numeric-input-card-feature-editor"
|
||||
@ -55,13 +72,20 @@ class HuiNumericInputCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentState = this.stateObj.state;
|
||||
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._currentState = this._stateObj.state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _setValue(ev: CustomEvent) {
|
||||
const stateObj = this.stateObj!;
|
||||
const stateObj = this._stateObj!;
|
||||
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
|
||||
@ -75,13 +99,14 @@ class HuiNumericInputCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsNumericInputCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsNumericInputCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const parsedState = Number(stateObj.state);
|
||||
const value = !isNaN(parsedState) ? parsedState : undefined;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -15,9 +14,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { SelectOptionsCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
SelectOptionsCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsSelectOptionsCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsSelectOptionsCardFeature = (
|
||||
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 === "select" || domain === "input_select";
|
||||
};
|
||||
@ -29,9 +38,7 @@ class HuiSelectOptionsCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?:
|
||||
| SelectEntity
|
||||
| InputSelectEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: SelectOptionsCardFeatureConfig;
|
||||
|
||||
@ -40,6 +47,16 @@ class HuiSelectOptionsCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect!: HaControlSelectMenu;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| SelectEntity
|
||||
| InputSelectEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): SelectOptionsCardFeatureConfig {
|
||||
return {
|
||||
type: "select-options",
|
||||
@ -62,8 +79,15 @@ class HuiSelectOptionsCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentOption = this.stateObj.state;
|
||||
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._currentOption = this._stateObj.state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,11 +108,11 @@ class HuiSelectOptionsCardFeature
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
const option = (ev.target as any).value as string;
|
||||
|
||||
const oldOption = this.stateObj!.state;
|
||||
const oldOption = this._stateObj!.state;
|
||||
|
||||
if (
|
||||
option === oldOption ||
|
||||
!this.stateObj!.attributes.options.includes(option)
|
||||
!this._stateObj!.attributes.options.includes(option)
|
||||
)
|
||||
return;
|
||||
|
||||
@ -102,9 +126,9 @@ class HuiSelectOptionsCardFeature
|
||||
}
|
||||
|
||||
private async _setOption(option: string) {
|
||||
const domain = computeDomain(this.stateObj!.entity_id);
|
||||
const domain = computeDomain(this._stateObj!.entity_id);
|
||||
await this.hass!.callService(domain, "select_option", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
option: option,
|
||||
});
|
||||
}
|
||||
@ -113,16 +137,17 @@ class HuiSelectOptionsCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsSelectOptionsCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsSelectOptionsCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
const options = this._getOptions(
|
||||
this.stateObj.attributes.options,
|
||||
this._stateObj.attributes.options,
|
||||
this._config.options
|
||||
);
|
||||
|
||||
@ -133,7 +158,7 @@ class HuiSelectOptionsCardFeature
|
||||
.label=${this.hass.localize("ui.card.select.option")}
|
||||
.value=${stateObj.state}
|
||||
.options=${options}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@ -9,9 +8,19 @@ import type { HumidifierEntity } from "../../../data/humidifier";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { TargetHumidityCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
TargetHumidityCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsTargetHumidityCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsTargetHumidityCardFeature = (
|
||||
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 === "humidifier";
|
||||
};
|
||||
@ -23,12 +32,21 @@ class HuiTargetHumidityCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HumidifierEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: TargetHumidityCardFeatureConfig;
|
||||
|
||||
@state() private _targetHumidity?: number;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| HumidifierEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): TargetHumidityCardFeatureConfig {
|
||||
return {
|
||||
type: "target-humidity",
|
||||
@ -44,19 +62,26 @@ class HuiTargetHumidityCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj")) {
|
||||
this._targetHumidity = this.stateObj!.attributes.humidity;
|
||||
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._targetHumidity = this._stateObj!.attributes.humidity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _step = 1;
|
||||
|
||||
private get _min() {
|
||||
return this.stateObj!.attributes.min_humidity ?? 0;
|
||||
return this._stateObj!.attributes.min_humidity ?? 0;
|
||||
}
|
||||
|
||||
private get _max() {
|
||||
return this.stateObj!.attributes.max_humidity ?? 100;
|
||||
return this._stateObj!.attributes.max_humidity ?? 100;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
@ -68,7 +93,7 @@ class HuiTargetHumidityCardFeature
|
||||
|
||||
private _callService() {
|
||||
this.hass!.callService("humidifier", "set_humidity", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
humidity: this._targetHumidity,
|
||||
});
|
||||
}
|
||||
@ -77,21 +102,25 @@ class HuiTargetHumidityCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsTargetHumidityCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsTargetHumidityCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-slider
|
||||
.value=${this.stateObj.attributes.humidity}
|
||||
.value=${this._stateObj.attributes.humidity}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
.step=${this._step}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(this.stateObj, "humidity")}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this._stateObj,
|
||||
"humidity"
|
||||
)}
|
||||
unit="%"
|
||||
.locale=${this.hass.locale}
|
||||
></ha-control-slider>
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@ -19,11 +18,21 @@ import { WaterHeaterEntityFeature } from "../../../data/water_heater";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { TargetTemperatureCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
TargetTemperatureCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
type Target = "value" | "low" | "high";
|
||||
|
||||
export const supportsTargetTemperatureCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsTargetTemperatureCardFeature = (
|
||||
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 === "climate" &&
|
||||
@ -44,14 +53,22 @@ class HuiTargetTemperatureCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?:
|
||||
| ClimateEntity
|
||||
| WaterHeaterEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: TargetTemperatureCardFeatureConfig;
|
||||
|
||||
@state() private _targetTemperature: Partial<Record<Target, number>> = {};
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| WaterHeaterEntity
|
||||
| ClimateEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): TargetTemperatureCardFeatureConfig {
|
||||
return {
|
||||
type: "target-temperature",
|
||||
@ -67,34 +84,41 @@ class HuiTargetTemperatureCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj")) {
|
||||
this._targetTemperature = {
|
||||
value: this.stateObj!.attributes.temperature,
|
||||
low:
|
||||
"target_temp_low" in this.stateObj!.attributes
|
||||
? this.stateObj!.attributes.target_temp_low
|
||||
: undefined,
|
||||
high:
|
||||
"target_temp_high" in this.stateObj!.attributes
|
||||
? this.stateObj!.attributes.target_temp_high
|
||||
: undefined,
|
||||
};
|
||||
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._targetTemperature = {
|
||||
value: this._stateObj!.attributes.temperature,
|
||||
low:
|
||||
"target_temp_low" in this._stateObj!.attributes
|
||||
? this._stateObj!.attributes.target_temp_low
|
||||
: undefined,
|
||||
high:
|
||||
"target_temp_high" in this._stateObj!.attributes
|
||||
? this._stateObj!.attributes.target_temp_high
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private get _step() {
|
||||
return (
|
||||
this.stateObj!.attributes.target_temp_step ||
|
||||
this._stateObj!.attributes.target_temp_step ||
|
||||
(this.hass!.config.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
||||
);
|
||||
}
|
||||
|
||||
private get _min() {
|
||||
return this.stateObj!.attributes.min_temp;
|
||||
return this._stateObj!.attributes.min_temp;
|
||||
}
|
||||
|
||||
private get _max() {
|
||||
return this.stateObj!.attributes.max_temp;
|
||||
return this._stateObj!.attributes.max_temp;
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
@ -115,43 +139,43 @@ class HuiTargetTemperatureCardFeature
|
||||
);
|
||||
|
||||
private _callService(type: string) {
|
||||
const domain = computeStateDomain(this.stateObj!);
|
||||
const domain = computeStateDomain(this._stateObj!);
|
||||
if (type === "high" || type === "low") {
|
||||
this.hass!.callService(domain, "set_temperature", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
target_temp_low: this._targetTemperature.low,
|
||||
target_temp_high: this._targetTemperature.high,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.hass!.callService(domain, "set_temperature", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
temperature: this._targetTemperature.value,
|
||||
});
|
||||
}
|
||||
|
||||
private _supportsTarget() {
|
||||
const domain = computeStateDomain(this.stateObj!);
|
||||
const domain = computeStateDomain(this._stateObj!);
|
||||
return (
|
||||
(domain === "climate" &&
|
||||
supportsFeature(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
)) ||
|
||||
(domain === "water_heater" &&
|
||||
supportsFeature(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
private _supportsTargetRange() {
|
||||
const domain = computeStateDomain(this.stateObj!);
|
||||
const domain = computeStateDomain(this._stateObj!);
|
||||
return (
|
||||
domain === "climate" &&
|
||||
supportsFeature(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||
)
|
||||
);
|
||||
@ -161,13 +185,14 @@ class HuiTargetTemperatureCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsTargetTemperatureCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsTargetTemperatureCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateColor = stateColorCss(this.stateObj);
|
||||
const stateColor = stateColorCss(this._stateObj);
|
||||
const digits = this._step.toString().split(".")?.[1]?.length ?? 0;
|
||||
|
||||
const options = {
|
||||
@ -178,27 +203,27 @@ class HuiTargetTemperatureCardFeature
|
||||
if (
|
||||
this._supportsTarget() &&
|
||||
this._targetTemperature.value != null &&
|
||||
this.stateObj.state !== UNAVAILABLE
|
||||
this._stateObj.state !== UNAVAILABLE
|
||||
) {
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-number-buttons
|
||||
.formatOptions=${options}
|
||||
.target=${"value"}
|
||||
.value=${this.stateObj.attributes.temperature}
|
||||
.value=${this._stateObj.attributes.temperature}
|
||||
.unit=${this.hass.config.unit_system.temperature}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
"temperature"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
@ -210,7 +235,7 @@ class HuiTargetTemperatureCardFeature
|
||||
this._supportsTargetRange() &&
|
||||
this._targetTemperature.low != null &&
|
||||
this._targetTemperature.high != null &&
|
||||
this.stateObj.state !== UNAVAILABLE
|
||||
this._stateObj.state !== UNAVAILABLE
|
||||
) {
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
@ -227,13 +252,13 @@ class HuiTargetTemperatureCardFeature
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
"target_temp_low"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
@ -250,13 +275,13 @@ class HuiTargetTemperatureCardFeature
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
"target_temp_high"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
@ -267,10 +292,10 @@ class HuiTargetTemperatureCardFeature
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-number-buttons
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
.unit=${this.hass.config.unit_system.temperature}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
this._stateObj,
|
||||
"temperature"
|
||||
)}
|
||||
style=${styleMap({
|
||||
|
@ -23,9 +23,19 @@ import { forwardHaptic } from "../../../data/haptics";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { ToggleCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
ToggleCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsToggleCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsToggleCardFeature = (
|
||||
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 [
|
||||
"switch",
|
||||
@ -56,10 +66,17 @@ const DOMAIN_ICONS: Record<string, { on: string; off: string }> = {
|
||||
class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ToggleCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): ToggleCardFeatureConfig {
|
||||
return {
|
||||
type: "toggle",
|
||||
@ -92,16 +109,16 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
}
|
||||
|
||||
private async _callService(turnOn): Promise<void> {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
if (!this.hass || !this._stateObj) {
|
||||
return;
|
||||
}
|
||||
forwardHaptic("light");
|
||||
const stateDomain = computeDomain(this.stateObj.entity_id);
|
||||
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,
|
||||
entity_id: this._stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -109,32 +126,33 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsToggleCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsToggleCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const onColor = "var(--feature-color)";
|
||||
const offColor = stateColorCss(this.stateObj, "off");
|
||||
const offColor = stateColorCss(this._stateObj, "off");
|
||||
|
||||
const isOn = this.stateObj.state === "on";
|
||||
const isOff = this.stateObj.state === "off";
|
||||
const isOn = this._stateObj.state === "on";
|
||||
const isOff = this._stateObj.state === "off";
|
||||
|
||||
const domain = computeDomain(this.stateObj.entity_id);
|
||||
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
|
||||
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}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
class=${classMap({
|
||||
active: isOff,
|
||||
})}
|
||||
@ -147,7 +165,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.common.turn_on")}
|
||||
@click=${this._turnOn}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
class=${classMap({
|
||||
active: isOn,
|
||||
})}
|
||||
@ -168,7 +186,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
|
||||
.checked=${isOn}
|
||||
@change=${this._valueChanged}
|
||||
.ariaLabel=${this.hass.localize("ui.card.common.toggle")}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-switch>
|
||||
`;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiCancel, mdiCellphoneArrowDown } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
@ -14,11 +13,21 @@ import { showUpdateBackupDialogParams } from "../../../dialogs/update_backup/sho
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { UpdateActionsCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
UpdateActionsCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const DEFAULT_UPDATE_BACKUP_OPTION = "no";
|
||||
|
||||
export const supportsUpdateActionsCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsUpdateActionsCardFeature = (
|
||||
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 === "update" &&
|
||||
@ -33,10 +42,19 @@ class HuiUpdateActionsCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: UpdateActionsCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| UpdateEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/hui-update-actions-card-feature-editor"
|
||||
@ -59,7 +77,7 @@ class HuiUpdateActionsCardFeature
|
||||
}
|
||||
|
||||
private get _installDisabled(): boolean {
|
||||
const stateObj = this.stateObj as UpdateEntity;
|
||||
const stateObj = this._stateObj as UpdateEntity;
|
||||
|
||||
if (stateObj.state === UNAVAILABLE) return true;
|
||||
|
||||
@ -74,7 +92,7 @@ class HuiUpdateActionsCardFeature
|
||||
}
|
||||
|
||||
private get _skipDisabled(): boolean {
|
||||
const stateObj = this.stateObj as UpdateEntity;
|
||||
const stateObj = this._stateObj as UpdateEntity;
|
||||
|
||||
if (stateObj.state === UNAVAILABLE) return true;
|
||||
|
||||
@ -89,7 +107,7 @@ class HuiUpdateActionsCardFeature
|
||||
|
||||
private async _install(): Promise<void> {
|
||||
const supportsBackup = supportsFeature(
|
||||
this.stateObj!,
|
||||
this._stateObj!,
|
||||
UpdateEntityFeature.BACKUP
|
||||
);
|
||||
let backup = supportsBackup && this._config?.backup === "yes";
|
||||
@ -101,14 +119,14 @@ class HuiUpdateActionsCardFeature
|
||||
}
|
||||
|
||||
this.hass!.callService("update", "install", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
backup: backup,
|
||||
});
|
||||
}
|
||||
|
||||
private async _skip(): Promise<void> {
|
||||
this.hass!.callService("update", "skip", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -116,8 +134,9 @@ class HuiUpdateActionsCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsUpdateActionsCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsUpdateActionsCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import type { VacuumEntity } from "../../../data/vacuum";
|
||||
import {
|
||||
@ -27,7 +27,11 @@ import {
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type { VacuumCommand, VacuumCommandsCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
VacuumCommand,
|
||||
VacuumCommandsCardFeatureConfig,
|
||||
} from "./types";
|
||||
import { VACUUM_COMMANDS } from "./types";
|
||||
|
||||
interface VacuumButton {
|
||||
@ -115,7 +119,14 @@ export const VACUUM_COMMANDS_BUTTONS: Record<
|
||||
}),
|
||||
};
|
||||
|
||||
export const supportsVacuumCommandsCardFeature = (stateObj: HassEntity) => {
|
||||
export const supportsVacuumCommandsCardFeature = (
|
||||
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 === "vacuum" &&
|
||||
@ -130,14 +141,26 @@ class HuiVacuumCommandCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: VacuumCommandsCardFeatureConfig;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| VacuumEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
): VacuumCommandsCardFeatureConfig {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
return {
|
||||
type: "vacuum-commands",
|
||||
commands: stateObj
|
||||
@ -166,7 +189,7 @@ class HuiVacuumCommandCardFeature
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.target! as any).entry as VacuumButton;
|
||||
this.hass!.callService("vacuum", entry.serviceName, {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -174,13 +197,14 @@ class HuiVacuumCommandCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsVacuumCommandsCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsVacuumCommandsCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj as VacuumEntity;
|
||||
const stateObj = this._stateObj as VacuumEntity;
|
||||
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@ -23,11 +22,19 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import type { WaterHeaterOperationModesCardFeatureConfig } from "./types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
WaterHeaterOperationModesCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsWaterHeaterOperationModesCardFeature = (
|
||||
stateObj: HassEntity
|
||||
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 === "water_heater";
|
||||
};
|
||||
@ -39,12 +46,21 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: WaterHeaterEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: WaterHeaterOperationModesCardFeatureConfig;
|
||||
|
||||
@state() _currentOperationMode?: OperationMode;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| WaterHeaterEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): WaterHeaterOperationModesCardFeatureConfig {
|
||||
return {
|
||||
type: "water-heater-operation-modes",
|
||||
@ -69,17 +85,24 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentOperationMode = this.stateObj.state as OperationMode;
|
||||
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._currentOperationMode = this._stateObj.state as OperationMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
const mode = (ev.detail as any).value as OperationMode;
|
||||
|
||||
if (mode === this.stateObj!.state) return;
|
||||
if (mode === this._stateObj!.state) return;
|
||||
|
||||
const oldMode = this.stateObj!.state as OperationMode;
|
||||
const oldMode = this._stateObj!.state as OperationMode;
|
||||
this._currentOperationMode = mode;
|
||||
|
||||
try {
|
||||
@ -91,7 +114,7 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
|
||||
private async _setMode(mode: OperationMode) {
|
||||
await this.hass!.callService("water_heater", "set_operation_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
operation_mode: mode,
|
||||
});
|
||||
}
|
||||
@ -100,15 +123,16 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsWaterHeaterOperationModesCardFeature(this.stateObj)
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsWaterHeaterOperationModesCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const color = stateColorCss(this._stateObj);
|
||||
|
||||
const orderedModes = (this.stateObj.attributes.operation_list || [])
|
||||
const orderedModes = (this._stateObj.attributes.operation_list || [])
|
||||
.concat()
|
||||
.sort(compareWaterHeaterOperationMode)
|
||||
.reverse();
|
||||
@ -118,7 +142,7 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
this._config.operation_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, mode),
|
||||
label: this.hass!.formatEntityState(this._stateObj!, mode),
|
||||
path: computeOperationModeIcon(mode as OperationMode),
|
||||
}));
|
||||
|
||||
@ -132,7 +156,7 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.disabled=${this._stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
`;
|
||||
|
@ -14,6 +14,7 @@ import type { HumidifierEntity } from "../../../data/humidifier";
|
||||
import "../../../state-control/humidifier/ha-state-control-humidifier-humidity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../card-features/hui-card-features";
|
||||
import type { LovelaceCardFeatureContext } from "../card-features/types";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import type {
|
||||
@ -69,6 +70,8 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@state() private _config?: HumidifierCardConfig;
|
||||
|
||||
@state() private _featureContext: LovelaceCardFeatureContext = {};
|
||||
|
||||
public getCardSize(): number {
|
||||
return 7;
|
||||
}
|
||||
@ -79,6 +82,9 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
this._featureContext = {
|
||||
entity_id: config.entity,
|
||||
};
|
||||
}
|
||||
|
||||
private _handleMoreInfo() {
|
||||
@ -165,7 +171,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
|
||||
"--feature-color": color,
|
||||
})}
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.context=${this._featureContext}
|
||||
.features=${this._config.features}
|
||||
></hui-card-features>`
|
||||
: nothing}
|
||||
|
@ -14,6 +14,7 @@ import type { ClimateEntity } from "../../../data/climate";
|
||||
import "../../../state-control/climate/ha-state-control-climate-temperature";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../card-features/hui-card-features";
|
||||
import type { LovelaceCardFeatureContext } from "../card-features/types";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import type {
|
||||
@ -61,6 +62,8 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@state() private _config?: ThermostatCardConfig;
|
||||
|
||||
@state() private _featureContext: LovelaceCardFeatureContext = {};
|
||||
|
||||
public getCardSize(): number {
|
||||
return 7;
|
||||
}
|
||||
@ -71,6 +74,9 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
this._featureContext = {
|
||||
entity_id: config.entity,
|
||||
};
|
||||
}
|
||||
|
||||
private _handleMoreInfo() {
|
||||
@ -157,7 +163,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
"--feature-color": color,
|
||||
})}
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.context=${this._featureContext}
|
||||
.features=${this._config.features}
|
||||
></hui-card-features>`
|
||||
: nothing}
|
||||
|
@ -36,6 +36,7 @@ import type {
|
||||
} from "../types";
|
||||
import { renderTileBadge } from "./tile/badges/tile-badge";
|
||||
import type { TileCardConfig } from "./types";
|
||||
import type { LovelaceCardFeatureContext } from "../card-features/types";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
|
||||
export const getEntityDefaultTileIconAction = (entityId: string) => {
|
||||
@ -84,6 +85,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@state() private _config?: TileCardConfig;
|
||||
|
||||
@state() private _featureContext: LovelaceCardFeatureContext = {};
|
||||
|
||||
public setConfig(config: TileCardConfig): void {
|
||||
if (!config.entity) {
|
||||
throw new Error("Specify an entity");
|
||||
@ -98,6 +101,9 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
},
|
||||
...config,
|
||||
};
|
||||
this._featureContext = {
|
||||
entity_id: config.entity,
|
||||
};
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
@ -335,7 +341,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
? html`
|
||||
<hui-card-features
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.context=${this._featureContext}
|
||||
.color=${this._config.color}
|
||||
.features=${features}
|
||||
></hui-card-features>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiDelete, mdiDrag, mdiPencil, mdiPlus } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
@ -22,8 +21,8 @@ import { supportsAlarmModesCardFeature } from "../../card-features/hui-alarm-mod
|
||||
import { supportsClimateFanModesCardFeature } from "../../card-features/hui-climate-fan-modes-card-feature";
|
||||
import { supportsClimateHvacModesCardFeature } from "../../card-features/hui-climate-hvac-modes-card-feature";
|
||||
import { supportsClimatePresetModesCardFeature } from "../../card-features/hui-climate-preset-modes-card-feature";
|
||||
import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature";
|
||||
import { supportsClimateSwingHorizontalModesCardFeature } from "../../card-features/hui-climate-swing-horizontal-modes-card-feature";
|
||||
import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature";
|
||||
import { supportsCounterActionsCardFeature } from "../../card-features/hui-counter-actions-card-feature";
|
||||
import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover-open-close-card-feature";
|
||||
import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature";
|
||||
@ -47,11 +46,18 @@ import { supportsToggleCardFeature } from "../../card-features/hui-toggle-card-f
|
||||
import { supportsUpdateActionsCardFeature } from "../../card-features/hui-update-actions-card-feature";
|
||||
import { supportsVacuumCommandsCardFeature } from "../../card-features/hui-vacuum-commands-card-feature";
|
||||
import { supportsWaterHeaterOperationModesCardFeature } from "../../card-features/hui-water-heater-operation-modes-card-feature";
|
||||
import type { LovelaceCardFeatureConfig } from "../../card-features/types";
|
||||
import type {
|
||||
LovelaceCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "../../card-features/types";
|
||||
import { getCardFeatureElementClass } from "../../create-element/create-card-feature-element";
|
||||
|
||||
export type FeatureType = LovelaceCardFeatureConfig["type"];
|
||||
type SupportsFeature = (stateObj: HassEntity) => boolean;
|
||||
|
||||
type SupportsFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
) => boolean;
|
||||
|
||||
const UI_FEATURE_TYPES = [
|
||||
"alarm-modes",
|
||||
@ -152,7 +158,8 @@ customCardFeatures.forEach((feature) => {
|
||||
});
|
||||
|
||||
export const getSupportedFeaturesType = (
|
||||
stateObj: HassEntity,
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext,
|
||||
featuresTypes?: string[]
|
||||
) => {
|
||||
const filteredFeaturesTypes = UI_FEATURE_TYPES.filter(
|
||||
@ -164,23 +171,41 @@ export const getSupportedFeaturesType = (
|
||||
);
|
||||
return filteredFeaturesTypes
|
||||
.concat(customFeaturesTypes)
|
||||
.filter((type) => supportsFeaturesType(stateObj, type));
|
||||
.filter((type) => supportsFeaturesType(hass, context, type));
|
||||
};
|
||||
|
||||
export const supportsFeaturesType = (stateObj: HassEntity, type: string) => {
|
||||
export const supportsFeaturesType = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext,
|
||||
type: string
|
||||
) => {
|
||||
if (isCustomType(type)) {
|
||||
const customType = stripCustomPrefix(type);
|
||||
const customFeatureEntry = CUSTOM_FEATURE_ENTRIES[customType];
|
||||
if (!customFeatureEntry?.supported) return true;
|
||||
|
||||
if (!customFeatureEntry) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return customFeatureEntry.supported(stateObj);
|
||||
if (customFeatureEntry.isSupported) {
|
||||
return customFeatureEntry.isSupported(hass, context);
|
||||
}
|
||||
// Fallback to the old supported method
|
||||
if (customFeatureEntry.supported) {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
return customFeatureEntry.supported(stateObj);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const supportsFeature = SUPPORTS_FEATURE_TYPES[type];
|
||||
return !supportsFeature || supportsFeature(stateObj);
|
||||
return !supportsFeature || supportsFeature(hass, context);
|
||||
};
|
||||
|
||||
declare global {
|
||||
@ -195,7 +220,7 @@ declare global {
|
||||
export class HuiCardFeaturesEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@property({ attribute: false })
|
||||
public features?: LovelaceCardFeatureConfig[];
|
||||
@ -209,13 +234,17 @@ export class HuiCardFeaturesEditor extends LitElement {
|
||||
private _featuresKeys = new WeakMap<LovelaceCardFeatureConfig, string>();
|
||||
|
||||
private _supportsFeatureType(type: string): boolean {
|
||||
if (!this.stateObj) return false;
|
||||
return supportsFeaturesType(this.stateObj, type);
|
||||
if (!this.hass || !this.context) return false;
|
||||
return supportsFeaturesType(this.hass, this.context, type);
|
||||
}
|
||||
|
||||
private _getSupportedFeaturesType() {
|
||||
if (!this.stateObj) return [];
|
||||
return getSupportedFeaturesType(this.stateObj, this.featuresTypes);
|
||||
if (!this.hass || !this.context) return [];
|
||||
return getSupportedFeaturesType(
|
||||
this.hass,
|
||||
this.context,
|
||||
this.featuresTypes
|
||||
);
|
||||
}
|
||||
|
||||
private _isFeatureTypeEditable(type: string) {
|
||||
@ -288,7 +317,7 @@ export class HuiCardFeaturesEditor extends LitElement {
|
||||
<div class="feature-content">
|
||||
<div>
|
||||
<span> ${this._getFeatureTypeLabel(type)} </span>
|
||||
${this.stateObj && !supported
|
||||
${this.context && !supported
|
||||
? html`
|
||||
<span class="secondary">
|
||||
${this.hass!.localize(
|
||||
@ -379,7 +408,14 @@ export class HuiCardFeaturesEditor extends LitElement {
|
||||
|
||||
let newFeature: LovelaceCardFeatureConfig;
|
||||
if (elClass && elClass.getStubConfig) {
|
||||
newFeature = await elClass.getStubConfig(this.hass!, this.stateObj);
|
||||
try {
|
||||
newFeature = await elClass.getStubConfig(this.hass!, this.context!);
|
||||
} catch (_err) {
|
||||
const stateObj = this.context!.entity_id
|
||||
? this.hass!.states[this.context!.entity_id]
|
||||
: undefined;
|
||||
newFeature = await elClass.getStubConfig(this.hass!, stateObj);
|
||||
}
|
||||
} else {
|
||||
newFeature = { type: value } as LovelaceCardFeatureConfig;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { mdiListBox } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
any,
|
||||
array,
|
||||
@ -85,13 +86,19 @@ export class HuiHumidifierCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _featureContext = memoizeOne(
|
||||
(entityId?: string): LovelaceCardFeatureContext => ({
|
||||
entity_id: entityId,
|
||||
})
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const entityId = this._config!.entity;
|
||||
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
||||
const entityId = this._config.entity;
|
||||
const featureContext = this._featureContext(entityId);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
@ -111,7 +118,7 @@ export class HuiHumidifierCardEditor
|
||||
<div class="content">
|
||||
<hui-card-features-editor
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.context=${featureContext}
|
||||
.featuresTypes=${COMPATIBLE_FEATURES_TYPES}
|
||||
.features=${this._config!.features ?? []}
|
||||
@features-changed=${this._featuresChanged}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { mdiListBox } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
any,
|
||||
array,
|
||||
@ -84,13 +85,19 @@ export class HuiThermostatCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _featureContext = memoizeOne(
|
||||
(entityId?: string): LovelaceCardFeatureContext => ({
|
||||
entity_id: entityId,
|
||||
})
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const entityId = this._config!.entity;
|
||||
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
||||
const entityId = this._config.entity;
|
||||
const featureContext = this._featureContext(entityId);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
@ -110,7 +117,7 @@ export class HuiThermostatCardEditor
|
||||
<div class="content">
|
||||
<hui-card-features-editor
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.context=${featureContext}
|
||||
.featuresTypes=${COMPATIBLE_FEATURES_TYPES}
|
||||
.features=${this._config!.features ?? []}
|
||||
@features-changed=${this._featuresChanged}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { mdiGestureTap, mdiListBox, mdiTextShort } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
@ -75,6 +74,12 @@ export class HuiTileCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _featureContext = memoizeOne(
|
||||
(entityId?: string): LovelaceCardFeatureContext => ({
|
||||
entity_id: entityId,
|
||||
})
|
||||
);
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
@ -239,7 +244,8 @@ export class HuiTileCardEditor
|
||||
);
|
||||
|
||||
private _hasCompatibleFeatures = memoizeOne(
|
||||
(stateObj: HassEntity) => getSupportedFeaturesType(stateObj).length > 0
|
||||
(context: LovelaceCardFeatureContext) =>
|
||||
getSupportedFeaturesType(this.hass!, context).length > 0
|
||||
);
|
||||
|
||||
protected render() {
|
||||
@ -248,7 +254,6 @@ export class HuiTileCardEditor
|
||||
}
|
||||
|
||||
const entityId = this._config!.entity;
|
||||
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
@ -271,8 +276,8 @@ export class HuiTileCardEditor
|
||||
data.features_position = "bottom";
|
||||
}
|
||||
|
||||
const hasCompatibleFeatures =
|
||||
(stateObj && this._hasCompatibleFeatures(stateObj)) || false;
|
||||
const featureContext = this._featureContext(entityId);
|
||||
const hasCompatibleFeatures = this._hasCompatibleFeatures(featureContext);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
@ -306,7 +311,7 @@ export class HuiTileCardEditor
|
||||
: nothing}
|
||||
<hui-card-features-editor
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.context=${featureContext}
|
||||
.features=${this._config!.features ?? []}
|
||||
@features-changed=${this._featuresChanged}
|
||||
@edit-detail-element=${this._editDetailElement}
|
||||
@ -368,13 +373,12 @@ export class HuiTileCardEditor
|
||||
private _editDetailElement(ev: HASSDomEvent<EditDetailElementEvent>): void {
|
||||
const index = ev.detail.subElementConfig.index;
|
||||
const config = this._config!.features![index!];
|
||||
const featureContext = this._featureContext(this._config!.entity);
|
||||
|
||||
fireEvent(this, "edit-sub-element", {
|
||||
config: config,
|
||||
saveConfig: (newConfig) => this._updateFeature(index!, newConfig),
|
||||
context: {
|
||||
entity_id: this._config!.entity,
|
||||
},
|
||||
context: featureContext,
|
||||
type: "feature",
|
||||
} as EditSubElementEvent<
|
||||
LovelaceCardFeatureConfig,
|
||||
|
@ -13,7 +13,10 @@ import { supportsCoverOpenCloseCardFeature } from "../../../card-features/hui-co
|
||||
import { supportsLightBrightnessCardFeature } from "../../../card-features/hui-light-brightness-card-feature";
|
||||
import { supportsLockCommandsCardFeature } from "../../../card-features/hui-lock-commands-card-feature";
|
||||
import { supportsTargetTemperatureCardFeature } from "../../../card-features/hui-target-temperature-card-feature";
|
||||
import type { LovelaceCardFeatureConfig } from "../../../card-features/types";
|
||||
import type {
|
||||
LovelaceCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "../../../card-features/types";
|
||||
import type { TileCardConfig } from "../../../cards/types";
|
||||
|
||||
export const AREA_STRATEGY_GROUPS = [
|
||||
@ -206,6 +209,10 @@ export const computeAreaTileCardConfig =
|
||||
(entity: string): LovelaceCardConfig => {
|
||||
const stateObj = hass.states[entity];
|
||||
|
||||
const context: LovelaceCardFeatureContext = {
|
||||
entity_id: entity,
|
||||
};
|
||||
|
||||
const additionalCardConfig: Partial<TileCardConfig> = {};
|
||||
|
||||
const domain = computeDomain(entity);
|
||||
@ -225,23 +232,23 @@ export const computeAreaTileCardConfig =
|
||||
|
||||
let feature: LovelaceCardFeatureConfig | undefined;
|
||||
if (includeFeature) {
|
||||
if (supportsLightBrightnessCardFeature(stateObj)) {
|
||||
if (supportsLightBrightnessCardFeature(hass, context)) {
|
||||
feature = {
|
||||
type: "light-brightness",
|
||||
};
|
||||
} else if (supportsCoverOpenCloseCardFeature(stateObj)) {
|
||||
} else if (supportsCoverOpenCloseCardFeature(hass, context)) {
|
||||
feature = {
|
||||
type: "cover-open-close",
|
||||
};
|
||||
} else if (supportsTargetTemperatureCardFeature(stateObj)) {
|
||||
} else if (supportsTargetTemperatureCardFeature(hass, context)) {
|
||||
feature = {
|
||||
type: "target-temperature",
|
||||
};
|
||||
} else if (supportsAlarmModesCardFeature(stateObj)) {
|
||||
} else if (supportsAlarmModesCardFeature(hass, context)) {
|
||||
feature = {
|
||||
type: "alarm-modes",
|
||||
};
|
||||
} else if (supportsLockCommandsCardFeature(stateObj)) {
|
||||
} else if (supportsLockCommandsCardFeature(hass, context)) {
|
||||
feature = {
|
||||
type: "lock-commands",
|
||||
};
|
||||
|
@ -8,13 +8,16 @@ import type {
|
||||
LovelaceRawConfig,
|
||||
} from "../../data/lovelace/config/types";
|
||||
import type { FrontendLocaleData } from "../../data/translation";
|
||||
import type { ShowToastParams } from "../../managers/notification-manager";
|
||||
import type { Constructor, HomeAssistant } from "../../types";
|
||||
import type {
|
||||
LovelaceCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./card-features/types";
|
||||
import type { LovelaceElement, LovelaceElementConfig } from "./elements/types";
|
||||
import type { LovelaceRow, LovelaceRowConfig } from "./entity-rows/types";
|
||||
import type { LovelaceHeaderFooterConfig } from "./header-footer/types";
|
||||
import type { LovelaceCardFeatureConfig } from "./card-features/types";
|
||||
import type { LovelaceElement, LovelaceElementConfig } from "./elements/types";
|
||||
import type { LovelaceHeadingBadgeConfig } from "./heading-badges/types";
|
||||
import type { ShowToastParams } from "../../managers/notification-manager";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@ -169,7 +172,9 @@ export interface LovelaceGenericElementEditor<C = any> extends HTMLElement {
|
||||
|
||||
export interface LovelaceCardFeature extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
/** @deprecated Use `context` instead */
|
||||
stateObj?: HassEntity;
|
||||
context?: LovelaceCardFeatureContext;
|
||||
setConfig(config: LovelaceCardFeatureConfig);
|
||||
color?: string;
|
||||
}
|
||||
@ -178,7 +183,7 @@ export interface LovelaceCardFeatureConstructor
|
||||
extends Constructor<LovelaceCardFeature> {
|
||||
getStubConfig?: (
|
||||
hass: HomeAssistant,
|
||||
stateObj?: HassEntity
|
||||
context?: LovelaceCardFeatureContext
|
||||
) => LovelaceCardFeatureConfig;
|
||||
getConfigElement?: () => LovelaceCardFeatureEditor;
|
||||
getConfigForm?: () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user