mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-25 20:12:48 +00:00
Compare commits
4 Commits
dashboard_
...
favorites-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca9561bdf1 | ||
|
|
8504faae93 | ||
|
|
481d7181b9 | ||
|
|
0c92f60c2a |
@@ -3,6 +3,7 @@ import type {
|
||||
HassEntityBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { temperature2rgb } from "../common/color/convert-light-color";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
export const enum LightEntityFeature {
|
||||
EFFECT = 4,
|
||||
@@ -160,4 +161,19 @@ export const computeDefaultFavoriteColors = (
|
||||
return colors;
|
||||
};
|
||||
|
||||
export const resolveLightFavoriteColors = (
|
||||
stateObj: LightEntity,
|
||||
favoriteColors?: LightColor[] | null
|
||||
): LightColor[] => favoriteColors ?? computeDefaultFavoriteColors(stateObj);
|
||||
|
||||
export const applyLightFavoriteColor = (
|
||||
hass: HomeAssistant,
|
||||
stateObj: LightEntity,
|
||||
favorite: LightColor
|
||||
) =>
|
||||
hass.callService("light", "turn_on", {
|
||||
entity_id: stateObj.entity_id,
|
||||
...favorite,
|
||||
});
|
||||
|
||||
export const formatTempColor = (value: number) => `${value} K`;
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-control-button";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { CoverEntity } from "../../../../data/cover";
|
||||
import {
|
||||
DEFAULT_COVER_FAVORITE_POSITIONS,
|
||||
@@ -15,35 +10,16 @@ import {
|
||||
} from "../../../../data/cover";
|
||||
import { UNAVAILABLE } from "../../../../data/entity/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity/entity_attributes";
|
||||
import type {
|
||||
CoverEntityOptions,
|
||||
ExtEntityRegistryEntry,
|
||||
} from "../../../../data/entity/entity_registry";
|
||||
import { updateEntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type { ExtEntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
showConfirmationDialog,
|
||||
showPromptDialog,
|
||||
} from "../../../generic/show-dialog-box";
|
||||
import "../ha-more-info-favorites";
|
||||
import type { HaMoreInfoFavorites } from "../ha-more-info-favorites";
|
||||
import {
|
||||
NumericMoreInfoFavoritesController,
|
||||
type NumericFavoriteLocalizeKey,
|
||||
} from "../numeric-more-info-favorites-controller";
|
||||
|
||||
type FavoriteKind = "position" | "tilt";
|
||||
|
||||
type FavoriteLocalizeKey =
|
||||
| "set"
|
||||
| "edit"
|
||||
| "delete"
|
||||
| "delete_confirm_title"
|
||||
| "delete_confirm_text"
|
||||
| "delete_confirm_action"
|
||||
| "add"
|
||||
| "edit_title"
|
||||
| "add_title";
|
||||
|
||||
const favoriteKindFromEvent = (ev: Event): FavoriteKind =>
|
||||
(ev.currentTarget as HTMLElement).dataset.kind as FavoriteKind;
|
||||
|
||||
@customElement("ha-more-info-cover-favorite-positions")
|
||||
export class HaMoreInfoCoverFavoritePositions extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -54,35 +30,57 @@ export class HaMoreInfoCoverFavoritePositions extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public editMode?: boolean;
|
||||
|
||||
@state() private _favoritePositions: number[] = [];
|
||||
private readonly _positionFavorites =
|
||||
new NumericMoreInfoFavoritesController<CoverEntity>(this, {
|
||||
getHass: () => this.hass,
|
||||
getStateObj: () => this.stateObj,
|
||||
getEntry: () => this.entry,
|
||||
getEditMode: () => this.editMode ?? false,
|
||||
domain: "cover",
|
||||
option: "favorite_positions",
|
||||
defaultFavorites: DEFAULT_COVER_FAVORITE_POSITIONS,
|
||||
getStoredFavorites: (entry) => entry.options?.cover?.favorite_positions,
|
||||
normalizeFavorites: normalizeCoverFavoritePositions,
|
||||
getCurrentValue: (stateObj) => {
|
||||
const current = stateObj.attributes.current_position;
|
||||
|
||||
@state() private _favoriteTiltPositions: number[] = [];
|
||||
return current == null ? undefined : Math.round(current);
|
||||
},
|
||||
setPositionService: "set_cover_position",
|
||||
serviceDataKey: "position",
|
||||
localize: (key, values) =>
|
||||
this._localizeFavorite("position", key, values),
|
||||
getInputLabel: () => this.hass.localize("ui.card.cover.position"),
|
||||
inputSuffix: DOMAIN_ATTRIBUTES_UNITS.cover.current_position,
|
||||
});
|
||||
|
||||
protected updated(changedProps: PropertyValues<this>): void {
|
||||
if (
|
||||
(changedProps.has("entry") || changedProps.has("stateObj")) &&
|
||||
this.entry &&
|
||||
this.stateObj
|
||||
) {
|
||||
const options = this.entry.options?.cover;
|
||||
private readonly _tiltFavorites =
|
||||
new NumericMoreInfoFavoritesController<CoverEntity>(this, {
|
||||
getHass: () => this.hass,
|
||||
getStateObj: () => this.stateObj,
|
||||
getEntry: () => this.entry,
|
||||
getEditMode: () => this.editMode ?? false,
|
||||
domain: "cover",
|
||||
option: "favorite_tilt_positions",
|
||||
defaultFavorites: DEFAULT_COVER_FAVORITE_POSITIONS,
|
||||
getStoredFavorites: (entry) =>
|
||||
entry.options?.cover?.favorite_tilt_positions,
|
||||
normalizeFavorites: normalizeCoverFavoritePositions,
|
||||
getCurrentValue: (stateObj) => {
|
||||
const current = stateObj.attributes.current_tilt_position;
|
||||
|
||||
this._favoritePositions = coverSupportsPosition(this.stateObj)
|
||||
? normalizeCoverFavoritePositions(
|
||||
options?.favorite_positions ?? DEFAULT_COVER_FAVORITE_POSITIONS
|
||||
)
|
||||
: [];
|
||||
|
||||
this._favoriteTiltPositions = coverSupportsTiltPosition(this.stateObj)
|
||||
? normalizeCoverFavoritePositions(
|
||||
options?.favorite_tilt_positions ?? DEFAULT_COVER_FAVORITE_POSITIONS
|
||||
)
|
||||
: [];
|
||||
}
|
||||
}
|
||||
return current == null ? undefined : Math.round(current);
|
||||
},
|
||||
setPositionService: "set_cover_tilt_position",
|
||||
serviceDataKey: "tilt_position",
|
||||
localize: (key, values) => this._localizeFavorite("tilt", key, values),
|
||||
getInputLabel: () => this.hass.localize("ui.card.cover.tilt_position"),
|
||||
inputSuffix: DOMAIN_ATTRIBUTES_UNITS.cover.current_position,
|
||||
});
|
||||
|
||||
private _localizeFavorite(
|
||||
kind: FavoriteKind,
|
||||
key: FavoriteLocalizeKey,
|
||||
key: NumericFavoriteLocalizeKey,
|
||||
values?: Record<string, string | number>
|
||||
): string {
|
||||
return this.hass.localize(
|
||||
@@ -91,277 +89,15 @@ export class HaMoreInfoCoverFavoritePositions extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _getFavorites(kind: FavoriteKind): number[] {
|
||||
return kind === "position"
|
||||
? this._favoritePositions
|
||||
: this._favoriteTiltPositions;
|
||||
}
|
||||
|
||||
private _getCurrentValue(kind: FavoriteKind): number | undefined {
|
||||
const current =
|
||||
kind === "position"
|
||||
? this.stateObj.attributes.current_position
|
||||
: this.stateObj.attributes.current_tilt_position;
|
||||
|
||||
return current == null ? undefined : Math.round(current);
|
||||
}
|
||||
|
||||
private async _save(options: CoverEntityOptions): Promise<void> {
|
||||
if (!this.entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOptions: CoverEntityOptions = {
|
||||
...(this.entry.options?.cover ?? {}),
|
||||
};
|
||||
|
||||
if (coverSupportsPosition(this.stateObj)) {
|
||||
currentOptions.favorite_positions = this._favoritePositions;
|
||||
}
|
||||
|
||||
if (coverSupportsTiltPosition(this.stateObj)) {
|
||||
currentOptions.favorite_tilt_positions = this._favoriteTiltPositions;
|
||||
}
|
||||
|
||||
const result = await updateEntityRegistryEntry(
|
||||
this.hass,
|
||||
this.entry.entity_id,
|
||||
{
|
||||
options_domain: "cover",
|
||||
options: {
|
||||
...currentOptions,
|
||||
...options,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
fireEvent(this, "entity-entry-updated", result.entity_entry);
|
||||
}
|
||||
|
||||
private async _setFavorites(
|
||||
kind: FavoriteKind,
|
||||
favorites: number[]
|
||||
): Promise<void> {
|
||||
const normalized = normalizeCoverFavoritePositions(favorites);
|
||||
|
||||
if (kind === "position") {
|
||||
this._favoritePositions = normalized;
|
||||
await this._save({ favorite_positions: normalized });
|
||||
return;
|
||||
}
|
||||
|
||||
this._favoriteTiltPositions = normalized;
|
||||
await this._save({ favorite_tilt_positions: normalized });
|
||||
}
|
||||
|
||||
private _move(kind: FavoriteKind, index: number, newIndex: number): void {
|
||||
const favorites = this._getFavorites(kind).concat();
|
||||
const moved = favorites.splice(index, 1)[0];
|
||||
favorites.splice(newIndex, 0, moved);
|
||||
this._setFavorites(kind, favorites);
|
||||
}
|
||||
|
||||
private _applyFavorite(kind: FavoriteKind, index: number): void {
|
||||
const favorite = this._getFavorites(kind)[index];
|
||||
|
||||
if (favorite === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind === "position") {
|
||||
this.hass.callService("cover", "set_cover_position", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
position: favorite,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass.callService("cover", "set_cover_tilt_position", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
tilt_position: favorite,
|
||||
});
|
||||
}
|
||||
|
||||
private async _promptFavoriteValue(
|
||||
kind: FavoriteKind,
|
||||
value?: number
|
||||
): Promise<number | undefined> {
|
||||
const response = await showPromptDialog(this, {
|
||||
title: this._localizeFavorite(
|
||||
kind,
|
||||
value === undefined ? "add_title" : "edit_title"
|
||||
),
|
||||
inputLabel: this.hass.localize(
|
||||
kind === "position"
|
||||
? "ui.card.cover.position"
|
||||
: "ui.card.cover.tilt_position"
|
||||
),
|
||||
inputType: "number",
|
||||
inputMin: "0",
|
||||
inputMax: "100",
|
||||
inputSuffix: DOMAIN_ATTRIBUTES_UNITS.cover.current_position,
|
||||
defaultValue: value === undefined ? undefined : String(value),
|
||||
});
|
||||
|
||||
if (response === null || response.trim() === "") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const number = Number(response);
|
||||
|
||||
if (isNaN(number)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Math.max(0, Math.min(100, Math.round(number)));
|
||||
}
|
||||
|
||||
private async _addFavorite(kind: FavoriteKind): Promise<void> {
|
||||
const value = await this._promptFavoriteValue(kind);
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._setFavorites(kind, [...this._getFavorites(kind), value]);
|
||||
}
|
||||
|
||||
private async _editFavorite(
|
||||
kind: FavoriteKind,
|
||||
index: number
|
||||
): Promise<void> {
|
||||
const favorites = this._getFavorites(kind);
|
||||
const current = favorites[index];
|
||||
|
||||
if (current === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = await this._promptFavoriteValue(kind, current);
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updated = [...favorites];
|
||||
updated[index] = value;
|
||||
await this._setFavorites(kind, updated);
|
||||
}
|
||||
|
||||
private async _deleteFavorite(
|
||||
kind: FavoriteKind,
|
||||
index: number
|
||||
): Promise<void> {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
destructive: true,
|
||||
title: this._localizeFavorite(kind, "delete_confirm_title"),
|
||||
text: this._localizeFavorite(kind, "delete_confirm_text"),
|
||||
confirmText: this._localizeFavorite(kind, "delete_confirm_action"),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._setFavorites(
|
||||
kind,
|
||||
this._getFavorites(kind).filter((_, itemIndex) => itemIndex !== index)
|
||||
);
|
||||
}
|
||||
|
||||
private _renderFavoriteButton =
|
||||
(kind: FavoriteKind): HaMoreInfoFavorites["renderItem"] =>
|
||||
(favorite, _index, editMode) => {
|
||||
const currentValue = this._getCurrentValue(kind);
|
||||
const active = currentValue === favorite;
|
||||
const label = this._localizeFavorite(kind, editMode ? "edit" : "set", {
|
||||
value: `${favorite as number}%`,
|
||||
});
|
||||
|
||||
return html`
|
||||
<ha-control-button
|
||||
class=${classMap({
|
||||
active,
|
||||
})}
|
||||
style=${styleMap({
|
||||
"--control-button-border-radius": "var(--ha-border-radius-pill)",
|
||||
width: "72px",
|
||||
height: "36px",
|
||||
})}
|
||||
.label=${label}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
${favorite as number}%
|
||||
</ha-control-button>
|
||||
`;
|
||||
};
|
||||
|
||||
private _deleteLabel =
|
||||
(kind: FavoriteKind): HaMoreInfoFavorites["deleteLabel"] =>
|
||||
(index) =>
|
||||
this._localizeFavorite(kind, "delete", {
|
||||
number: index + 1,
|
||||
});
|
||||
|
||||
private _handleFavoriteAction = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-action"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
const kind = favoriteKindFromEvent(ev);
|
||||
|
||||
const { action, index } = ev.detail;
|
||||
|
||||
if (action === "hold" && this.hass.user?.is_admin) {
|
||||
fireEvent(this, "toggle-edit-mode", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.editMode) {
|
||||
this._editFavorite(kind, index);
|
||||
return;
|
||||
}
|
||||
|
||||
this._applyFavorite(kind, index);
|
||||
};
|
||||
|
||||
private _handleFavoriteMoved = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-moved"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
const kind = favoriteKindFromEvent(ev);
|
||||
this._move(kind, ev.detail.oldIndex, ev.detail.newIndex);
|
||||
};
|
||||
|
||||
private _handleFavoriteDelete = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-delete"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
const kind = favoriteKindFromEvent(ev);
|
||||
this._deleteFavorite(kind, ev.detail.index);
|
||||
};
|
||||
|
||||
private _handleFavoriteAdd = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-add"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
const kind = favoriteKindFromEvent(ev);
|
||||
this._addFavorite(kind);
|
||||
};
|
||||
|
||||
private _handleFavoriteDone = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-done"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "toggle-edit-mode", false);
|
||||
};
|
||||
|
||||
private _renderKindSection(
|
||||
kind: FavoriteKind,
|
||||
label: string,
|
||||
favorites: number[],
|
||||
controller: NumericMoreInfoFavoritesController<CoverEntity>,
|
||||
addLabel: string,
|
||||
showDone: boolean,
|
||||
showLabel: boolean
|
||||
): TemplateResult | typeof nothing {
|
||||
const favorites = controller.favorites;
|
||||
|
||||
if (!this.editMode && favorites.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -370,23 +106,22 @@ export class HaMoreInfoCoverFavoritePositions extends LitElement {
|
||||
<section class="group">
|
||||
${showLabel ? html`<h4>${label}</h4>` : nothing}
|
||||
<ha-more-info-favorites
|
||||
data-kind=${kind}
|
||||
.items=${favorites}
|
||||
.renderItem=${this._renderFavoriteButton(kind)}
|
||||
.deleteLabel=${this._deleteLabel(kind)}
|
||||
.renderItem=${controller.renderItem}
|
||||
.deleteLabel=${controller.deleteLabel}
|
||||
.editMode=${this.editMode ?? false}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.isAdmin=${Boolean(this.hass.user?.is_admin)}
|
||||
.showDone=${showDone}
|
||||
.addLabel=${this._localizeFavorite(kind, "add")}
|
||||
.addLabel=${addLabel}
|
||||
.doneLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.exit_edit_mode"
|
||||
)}
|
||||
@favorite-item-action=${this._handleFavoriteAction}
|
||||
@favorite-item-moved=${this._handleFavoriteMoved}
|
||||
@favorite-item-delete=${this._handleFavoriteDelete}
|
||||
@favorite-item-add=${this._handleFavoriteAdd}
|
||||
@favorite-item-done=${this._handleFavoriteDone}
|
||||
@favorite-item-action=${controller.handleAction}
|
||||
@favorite-item-moved=${controller.handleMoved}
|
||||
@favorite-item-delete=${controller.handleDelete}
|
||||
@favorite-item-add=${controller.handleAdd}
|
||||
@favorite-item-done=${controller.handleDone}
|
||||
></ha-more-info-favorites>
|
||||
</section>
|
||||
`;
|
||||
@@ -400,10 +135,10 @@ export class HaMoreInfoCoverFavoritePositions extends LitElement {
|
||||
const supportsPosition = coverSupportsPosition(this.stateObj);
|
||||
const supportsTiltPosition = coverSupportsTiltPosition(this.stateObj);
|
||||
const showPositionSection = supportsPosition
|
||||
? this.editMode || this._favoritePositions.length > 0
|
||||
? this.editMode || this._positionFavorites.favorites.length > 0
|
||||
: false;
|
||||
const showTiltSection = supportsTiltPosition
|
||||
? this.editMode || this._favoriteTiltPositions.length > 0
|
||||
? this.editMode || this._tiltFavorites.favorites.length > 0
|
||||
: false;
|
||||
const showLabels =
|
||||
[showPositionSection, showTiltSection].filter(Boolean).length > 1;
|
||||
@@ -414,18 +149,18 @@ export class HaMoreInfoCoverFavoritePositions extends LitElement {
|
||||
<div class="groups">
|
||||
${supportsPosition
|
||||
? this._renderKindSection(
|
||||
"position",
|
||||
this.hass.localize("ui.card.cover.position"),
|
||||
this._favoritePositions,
|
||||
this._positionFavorites,
|
||||
this._localizeFavorite("position", "add"),
|
||||
showDoneOnPosition,
|
||||
showLabels
|
||||
)
|
||||
: nothing}
|
||||
${supportsTiltPosition
|
||||
? this._renderKindSection(
|
||||
"tilt",
|
||||
this.hass.localize("ui.card.cover.tilt_position"),
|
||||
this._favoriteTiltPositions,
|
||||
this._tiltFavorites,
|
||||
this._localizeFavorite("tilt", "add"),
|
||||
true,
|
||||
showLabels
|
||||
)
|
||||
|
||||
@@ -7,7 +7,10 @@ import { UNAVAILABLE } from "../../../../data/entity/entity";
|
||||
import type { ExtEntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import { updateEntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type { LightColor, LightEntity } from "../../../../data/light";
|
||||
import { computeDefaultFavoriteColors } from "../../../../data/light";
|
||||
import {
|
||||
applyLightFavoriteColor,
|
||||
resolveLightFavoriteColors,
|
||||
} from "../../../../data/light";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { showConfirmationDialog } from "../../../generic/show-dialog-box";
|
||||
import "../ha-more-info-favorites";
|
||||
@@ -35,11 +38,10 @@ export class HaMoreInfoLightFavoriteColors extends LitElement {
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (changedProps.has("entry") && this.entry) {
|
||||
if (this.entry.options?.light?.favorite_colors) {
|
||||
this._favoriteColors = this.entry.options.light.favorite_colors;
|
||||
} else if (this.stateObj) {
|
||||
this._favoriteColors = computeDefaultFavoriteColors(this.stateObj);
|
||||
}
|
||||
this._favoriteColors = resolveLightFavoriteColors(
|
||||
this.stateObj,
|
||||
this.entry.options?.light?.favorite_colors
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +55,8 @@ export class HaMoreInfoLightFavoriteColors extends LitElement {
|
||||
|
||||
private _apply(index: number): void {
|
||||
const favorite = this._favoriteColors[index];
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
...favorite,
|
||||
});
|
||||
|
||||
applyLightFavoriteColor(this.hass, this.stateObj, favorite);
|
||||
}
|
||||
|
||||
private async _save(newFavoriteColors: LightColor[]): Promise<void> {
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
import type {
|
||||
ReactiveController,
|
||||
ReactiveControllerHost,
|
||||
} from "@lit/reactive-element/reactive-controller";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html } from "lit";
|
||||
import type { LitElement } from "lit";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-control-button";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type {
|
||||
ExtEntityRegistryEntry,
|
||||
FavoriteOption,
|
||||
} from "../../../data/entity/entity_registry";
|
||||
import { updateEntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
showConfirmationDialog,
|
||||
showPromptDialog,
|
||||
} from "../../generic/show-dialog-box";
|
||||
import type { HaMoreInfoFavorites } from "./ha-more-info-favorites";
|
||||
|
||||
export type NumericFavoriteLocalizeKey =
|
||||
| "set"
|
||||
| "edit"
|
||||
| "delete"
|
||||
| "delete_confirm_title"
|
||||
| "delete_confirm_text"
|
||||
| "delete_confirm_action"
|
||||
| "add"
|
||||
| "edit_title"
|
||||
| "add_title";
|
||||
|
||||
interface NumericMoreInfoFavoritesControllerConfig<TEntity extends HassEntity> {
|
||||
getHass: () => HomeAssistant | undefined;
|
||||
getStateObj: () => TEntity | undefined;
|
||||
getEntry: () => ExtEntityRegistryEntry | null | undefined;
|
||||
getEditMode: () => boolean;
|
||||
domain: "cover" | "valve";
|
||||
option: Extract<
|
||||
FavoriteOption,
|
||||
"favorite_positions" | "favorite_tilt_positions"
|
||||
>;
|
||||
defaultFavorites: number[];
|
||||
getStoredFavorites: (entry: ExtEntityRegistryEntry) => number[] | undefined;
|
||||
normalizeFavorites: (favorites?: number[]) => number[];
|
||||
getCurrentValue: (stateObj: TEntity) => number | undefined;
|
||||
setPositionService: string;
|
||||
serviceDataKey: string;
|
||||
localize: (
|
||||
key: NumericFavoriteLocalizeKey,
|
||||
values?: Record<string, string | number>
|
||||
) => string;
|
||||
getInputLabel: () => string;
|
||||
inputSuffix: string;
|
||||
}
|
||||
|
||||
export class NumericMoreInfoFavoritesController<
|
||||
TEntity extends HassEntity,
|
||||
> implements ReactiveController {
|
||||
public favorites: number[] = [];
|
||||
|
||||
public readonly renderItem: HaMoreInfoFavorites["renderItem"] = (
|
||||
favorite,
|
||||
_index,
|
||||
editMode
|
||||
) => {
|
||||
const value = favorite as number;
|
||||
const stateObj = this._config.getStateObj();
|
||||
const active =
|
||||
stateObj !== undefined &&
|
||||
this._config.getCurrentValue(stateObj) === value;
|
||||
const label = this._config.localize(editMode ? "edit" : "set", {
|
||||
value: `${value}%`,
|
||||
});
|
||||
|
||||
return html`
|
||||
<ha-control-button
|
||||
class=${classMap({ active })}
|
||||
style=${styleMap({
|
||||
"--control-button-border-radius": "var(--ha-border-radius-pill)",
|
||||
width: "72px",
|
||||
height: "36px",
|
||||
})}
|
||||
.label=${label}
|
||||
.disabled=${stateObj?.state === UNAVAILABLE}
|
||||
>
|
||||
${value}%
|
||||
</ha-control-button>
|
||||
`;
|
||||
};
|
||||
|
||||
public readonly deleteLabel: HaMoreInfoFavorites["deleteLabel"] = (index) =>
|
||||
this._config.localize("delete", { number: index + 1 });
|
||||
|
||||
public readonly handleAction = async (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-action"]>
|
||||
): Promise<void> => {
|
||||
ev.stopPropagation();
|
||||
|
||||
const hass = this._config.getHass();
|
||||
|
||||
if (!hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { action, index } = ev.detail;
|
||||
|
||||
if (action === "hold" && hass.user?.is_admin) {
|
||||
fireEvent(this._host, "toggle-edit-mode", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._config.getEditMode()) {
|
||||
await this._editFavorite(index);
|
||||
return;
|
||||
}
|
||||
|
||||
await this._applyFavorite(index);
|
||||
};
|
||||
|
||||
public readonly handleMoved = async (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-moved"]>
|
||||
): Promise<void> => {
|
||||
ev.stopPropagation();
|
||||
await this._move(ev.detail.oldIndex, ev.detail.newIndex);
|
||||
};
|
||||
|
||||
public readonly handleDelete = async (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-delete"]>
|
||||
): Promise<void> => {
|
||||
ev.stopPropagation();
|
||||
await this._deleteFavorite(ev.detail.index);
|
||||
};
|
||||
|
||||
public readonly handleAdd = async (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-add"]>
|
||||
): Promise<void> => {
|
||||
ev.stopPropagation();
|
||||
await this._addFavorite();
|
||||
};
|
||||
|
||||
public readonly handleDone = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-done"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this._host, "toggle-edit-mode", false);
|
||||
};
|
||||
|
||||
private _lastEntry?: ExtEntityRegistryEntry | null;
|
||||
|
||||
private _lastStateObj?: TEntity;
|
||||
|
||||
constructor(
|
||||
private readonly _host: ReactiveControllerHost & LitElement,
|
||||
private readonly _config: NumericMoreInfoFavoritesControllerConfig<TEntity>
|
||||
) {
|
||||
this._host.addController(this);
|
||||
}
|
||||
|
||||
public hostUpdated(): void {
|
||||
const entry = this._config.getEntry();
|
||||
const stateObj = this._config.getStateObj();
|
||||
|
||||
if (entry === this._lastEntry && stateObj === this._lastStateObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._lastEntry = entry;
|
||||
this._lastStateObj = stateObj;
|
||||
this.favorites =
|
||||
entry && stateObj
|
||||
? this._config.normalizeFavorites(
|
||||
this._config.getStoredFavorites(entry) ??
|
||||
this._config.defaultFavorites
|
||||
)
|
||||
: [];
|
||||
this._host.requestUpdate();
|
||||
}
|
||||
|
||||
private async _saveFavorites(favorites: number[]): Promise<void> {
|
||||
const hass = this._config.getHass();
|
||||
const entry = this._config.getEntry();
|
||||
|
||||
if (!hass || !entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await updateEntityRegistryEntry(hass, entry.entity_id, {
|
||||
options_domain: this._config.domain,
|
||||
options: {
|
||||
...(entry.options?.[this._config.domain] ?? {}),
|
||||
[this._config.option]: favorites,
|
||||
},
|
||||
});
|
||||
|
||||
fireEvent(this._host, "entity-entry-updated", result.entity_entry);
|
||||
}
|
||||
|
||||
private async _setFavorites(favorites: number[]): Promise<void> {
|
||||
this.favorites = this._config.normalizeFavorites(favorites);
|
||||
this._host.requestUpdate();
|
||||
await this._saveFavorites(this.favorites);
|
||||
}
|
||||
|
||||
private async _move(index: number, newIndex: number): Promise<void> {
|
||||
const favorites = this.favorites.concat();
|
||||
const moved = favorites.splice(index, 1)[0];
|
||||
|
||||
favorites.splice(newIndex, 0, moved);
|
||||
await this._setFavorites(favorites);
|
||||
}
|
||||
|
||||
private async _applyFavorite(index: number): Promise<void> {
|
||||
const hass = this._config.getHass();
|
||||
const stateObj = this._config.getStateObj();
|
||||
const favorite = this.favorites[index];
|
||||
|
||||
if (!hass || !stateObj || favorite === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
await hass.callService(
|
||||
this._config.domain,
|
||||
this._config.setPositionService,
|
||||
{
|
||||
entity_id: stateObj.entity_id,
|
||||
[this._config.serviceDataKey]: favorite,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async _promptFavoriteValue(
|
||||
value?: number
|
||||
): Promise<number | undefined> {
|
||||
const hass = this._config.getHass();
|
||||
|
||||
if (!hass) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const response = await showPromptDialog(this._host, {
|
||||
title: this._config.localize(
|
||||
value === undefined ? "add_title" : "edit_title"
|
||||
),
|
||||
inputLabel: this._config.getInputLabel(),
|
||||
inputType: "number",
|
||||
inputMin: "0",
|
||||
inputMax: "100",
|
||||
inputSuffix: this._config.inputSuffix,
|
||||
defaultValue: value === undefined ? undefined : String(value),
|
||||
});
|
||||
|
||||
if (response === null || response.trim() === "") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const number = Number(response);
|
||||
|
||||
if (isNaN(number)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Math.max(0, Math.min(100, Math.round(number)));
|
||||
}
|
||||
|
||||
private async _addFavorite(): Promise<void> {
|
||||
const value = await this._promptFavoriteValue();
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._setFavorites([...this.favorites, value]);
|
||||
}
|
||||
|
||||
private async _editFavorite(index: number): Promise<void> {
|
||||
const current = this.favorites[index];
|
||||
|
||||
if (current === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = await this._promptFavoriteValue(current);
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const favorites = [...this.favorites];
|
||||
|
||||
favorites[index] = value;
|
||||
await this._setFavorites(favorites);
|
||||
}
|
||||
|
||||
private async _deleteFavorite(index: number): Promise<void> {
|
||||
const confirmed = await showConfirmationDialog(this._host, {
|
||||
destructive: true,
|
||||
title: this._config.localize("delete_confirm_title"),
|
||||
text: this._config.localize("delete_confirm_text"),
|
||||
confirmText: this._config.localize("delete_confirm_action"),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._setFavorites(
|
||||
this.favorites.filter((_, itemIndex) => itemIndex !== index)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,20 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-control-button";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { UNAVAILABLE } from "../../../../data/entity/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity/entity_attributes";
|
||||
import type {
|
||||
ExtEntityRegistryEntry,
|
||||
ValveEntityOptions,
|
||||
} from "../../../../data/entity/entity_registry";
|
||||
import { updateEntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type { ExtEntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { ValveEntity } from "../../../../data/valve";
|
||||
import {
|
||||
DEFAULT_VALVE_FAVORITE_POSITIONS,
|
||||
normalizeValveFavoritePositions,
|
||||
} from "../../../../data/valve";
|
||||
import {
|
||||
showConfirmationDialog,
|
||||
showPromptDialog,
|
||||
} from "../../../generic/show-dialog-box";
|
||||
import "../ha-more-info-favorites";
|
||||
import type { HaMoreInfoFavorites } from "../ha-more-info-favorites";
|
||||
|
||||
type FavoriteLocalizeKey =
|
||||
| "set"
|
||||
| "edit"
|
||||
| "delete"
|
||||
| "delete_confirm_title"
|
||||
| "delete_confirm_text"
|
||||
| "delete_confirm_action"
|
||||
| "add"
|
||||
| "edit_title"
|
||||
| "add_title";
|
||||
import {
|
||||
NumericMoreInfoFavoritesController,
|
||||
type NumericFavoriteLocalizeKey,
|
||||
} from "../numeric-more-info-favorites-controller";
|
||||
|
||||
@customElement("ha-more-info-valve-favorite-positions")
|
||||
export class HaMoreInfoValveFavoritePositions extends LitElement {
|
||||
@@ -47,23 +26,31 @@ export class HaMoreInfoValveFavoritePositions extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public editMode?: boolean;
|
||||
|
||||
@state() private _favoritePositions: number[] = [];
|
||||
private readonly _favorites =
|
||||
new NumericMoreInfoFavoritesController<ValveEntity>(this, {
|
||||
getHass: () => this.hass,
|
||||
getStateObj: () => this.stateObj,
|
||||
getEntry: () => this.entry,
|
||||
getEditMode: () => this.editMode ?? false,
|
||||
domain: "valve",
|
||||
option: "favorite_positions",
|
||||
defaultFavorites: DEFAULT_VALVE_FAVORITE_POSITIONS,
|
||||
getStoredFavorites: (entry) => entry.options?.valve?.favorite_positions,
|
||||
normalizeFavorites: normalizeValveFavoritePositions,
|
||||
getCurrentValue: (stateObj) => {
|
||||
const current = stateObj.attributes.current_position;
|
||||
|
||||
protected updated(changedProps: PropertyValues<this>): void {
|
||||
if (
|
||||
(changedProps.has("entry") || changedProps.has("stateObj")) &&
|
||||
this.entry &&
|
||||
this.stateObj
|
||||
) {
|
||||
this._favoritePositions = normalizeValveFavoritePositions(
|
||||
this.entry.options?.valve?.favorite_positions ??
|
||||
DEFAULT_VALVE_FAVORITE_POSITIONS
|
||||
);
|
||||
}
|
||||
}
|
||||
return current == null ? undefined : Math.round(current);
|
||||
},
|
||||
setPositionService: "set_valve_position",
|
||||
serviceDataKey: "position",
|
||||
localize: (key, values) => this._localizeFavorite(key, values),
|
||||
getInputLabel: () => this.hass.localize("ui.card.cover.position"),
|
||||
inputSuffix: DOMAIN_ATTRIBUTES_UNITS.valve.current_position,
|
||||
});
|
||||
|
||||
private _localizeFavorite(
|
||||
key: FavoriteLocalizeKey,
|
||||
key: NumericFavoriteLocalizeKey,
|
||||
values?: Record<string, string | number>
|
||||
): string {
|
||||
return this.hass.localize(
|
||||
@@ -72,230 +59,17 @@ export class HaMoreInfoValveFavoritePositions extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _currentValue(): number | undefined {
|
||||
const current = this.stateObj.attributes.current_position;
|
||||
|
||||
return current == null ? undefined : Math.round(current);
|
||||
}
|
||||
|
||||
private async _save(favorite_positions: number[]): Promise<void> {
|
||||
if (!this.entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOptions: ValveEntityOptions = {
|
||||
...(this.entry.options?.valve ?? {}),
|
||||
};
|
||||
|
||||
currentOptions.favorite_positions = this._favoritePositions;
|
||||
|
||||
const result = await updateEntityRegistryEntry(
|
||||
this.hass,
|
||||
this.entry.entity_id,
|
||||
{
|
||||
options_domain: "valve",
|
||||
options: {
|
||||
...currentOptions,
|
||||
favorite_positions,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
fireEvent(this, "entity-entry-updated", result.entity_entry);
|
||||
}
|
||||
|
||||
private async _setFavorites(favorites: number[]): Promise<void> {
|
||||
const normalized = normalizeValveFavoritePositions(favorites);
|
||||
this._favoritePositions = normalized;
|
||||
await this._save(normalized);
|
||||
}
|
||||
|
||||
private _move(index: number, newIndex: number): void {
|
||||
const favorites = this._favoritePositions.concat();
|
||||
const moved = favorites.splice(index, 1)[0];
|
||||
favorites.splice(newIndex, 0, moved);
|
||||
this._setFavorites(favorites);
|
||||
}
|
||||
|
||||
private _applyFavorite(index: number): void {
|
||||
const favorite = this._favoritePositions[index];
|
||||
|
||||
if (favorite === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass.callService("valve", "set_valve_position", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
position: favorite,
|
||||
});
|
||||
}
|
||||
|
||||
private async _promptFavoriteValue(
|
||||
value?: number
|
||||
): Promise<number | undefined> {
|
||||
const response = await showPromptDialog(this, {
|
||||
title: this._localizeFavorite(
|
||||
value === undefined ? "add_title" : "edit_title"
|
||||
),
|
||||
inputLabel: this.hass.localize("ui.card.valve.position"),
|
||||
inputType: "number",
|
||||
inputMin: "0",
|
||||
inputMax: "100",
|
||||
inputSuffix: DOMAIN_ATTRIBUTES_UNITS.valve.current_position,
|
||||
defaultValue: value === undefined ? undefined : String(value),
|
||||
});
|
||||
|
||||
if (response === null || response.trim() === "") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const number = Number(response);
|
||||
|
||||
if (isNaN(number)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Math.max(0, Math.min(100, Math.round(number)));
|
||||
}
|
||||
|
||||
private async _addFavorite(): Promise<void> {
|
||||
const value = await this._promptFavoriteValue();
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._setFavorites([...this._favoritePositions, value]);
|
||||
}
|
||||
|
||||
private async _editFavorite(index: number): Promise<void> {
|
||||
const current = this._favoritePositions[index];
|
||||
|
||||
if (current === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = await this._promptFavoriteValue(current);
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updated = [...this._favoritePositions];
|
||||
updated[index] = value;
|
||||
await this._setFavorites(updated);
|
||||
}
|
||||
|
||||
private async _deleteFavorite(index: number): Promise<void> {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
destructive: true,
|
||||
title: this._localizeFavorite("delete_confirm_title"),
|
||||
text: this._localizeFavorite("delete_confirm_text"),
|
||||
confirmText: this._localizeFavorite("delete_confirm_action"),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._setFavorites(
|
||||
this._favoritePositions.filter((_, itemIndex) => itemIndex !== index)
|
||||
);
|
||||
}
|
||||
|
||||
private _renderFavorite: HaMoreInfoFavorites["renderItem"] = (
|
||||
favorite,
|
||||
_index,
|
||||
editMode
|
||||
) => {
|
||||
const value = favorite as number;
|
||||
const active = this._currentValue() === value;
|
||||
const label = this._localizeFavorite(editMode ? "edit" : "set", {
|
||||
value: `${value}%`,
|
||||
});
|
||||
|
||||
return html`
|
||||
<ha-control-button
|
||||
class=${classMap({
|
||||
active,
|
||||
})}
|
||||
style=${styleMap({
|
||||
"--control-button-border-radius": "var(--ha-border-radius-pill)",
|
||||
width: "72px",
|
||||
height: "36px",
|
||||
})}
|
||||
.label=${label}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
${value}%
|
||||
</ha-control-button>
|
||||
`;
|
||||
};
|
||||
|
||||
private _deleteLabel = (index: number): string =>
|
||||
this._localizeFavorite("delete", {
|
||||
number: index + 1,
|
||||
});
|
||||
|
||||
private _handleFavoriteAction = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-action"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
|
||||
const { action, index } = ev.detail;
|
||||
|
||||
if (action === "hold" && this.hass.user?.is_admin) {
|
||||
fireEvent(this, "toggle-edit-mode", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.editMode) {
|
||||
this._editFavorite(index);
|
||||
return;
|
||||
}
|
||||
|
||||
this._applyFavorite(index);
|
||||
};
|
||||
|
||||
private _handleFavoriteMoved = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-moved"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
this._move(ev.detail.oldIndex, ev.detail.newIndex);
|
||||
};
|
||||
|
||||
private _handleFavoriteDelete = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-delete"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
this._deleteFavorite(ev.detail.index);
|
||||
};
|
||||
|
||||
private _handleFavoriteAdd = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-add"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
this._addFavorite();
|
||||
};
|
||||
|
||||
private _handleFavoriteDone = (
|
||||
ev: HASSDomEvent<HASSDomEvents["favorite-item-done"]>
|
||||
): void => {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "toggle-edit-mode", false);
|
||||
};
|
||||
|
||||
private _renderSection(): TemplateResult | typeof nothing {
|
||||
if (!this.editMode && this._favoritePositions.length === 0) {
|
||||
if (!this.editMode && this._favorites.favorites.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<section class="group">
|
||||
<ha-more-info-favorites
|
||||
.items=${this._favoritePositions}
|
||||
.renderItem=${this._renderFavorite}
|
||||
.deleteLabel=${this._deleteLabel}
|
||||
.items=${this._favorites.favorites}
|
||||
.renderItem=${this._favorites.renderItem}
|
||||
.deleteLabel=${this._favorites.deleteLabel}
|
||||
.editMode=${this.editMode ?? false}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.isAdmin=${Boolean(this.hass.user?.is_admin)}
|
||||
@@ -304,11 +78,11 @@ export class HaMoreInfoValveFavoritePositions extends LitElement {
|
||||
.doneLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.exit_edit_mode"
|
||||
)}
|
||||
@favorite-item-action=${this._handleFavoriteAction}
|
||||
@favorite-item-moved=${this._handleFavoriteMoved}
|
||||
@favorite-item-delete=${this._handleFavoriteDelete}
|
||||
@favorite-item-add=${this._handleFavoriteAdd}
|
||||
@favorite-item-done=${this._handleFavoriteDone}
|
||||
@favorite-item-action=${this._favorites.handleAction}
|
||||
@favorite-item-moved=${this._favorites.handleMoved}
|
||||
@favorite-item-delete=${this._favorites.handleDelete}
|
||||
@favorite-item-add=${this._favorites.handleAdd}
|
||||
@favorite-item-done=${this._favorites.handleDone}
|
||||
></ha-more-info-favorites>
|
||||
</section>
|
||||
`;
|
||||
|
||||
@@ -26,10 +26,10 @@ import {
|
||||
import type { LightColor, LightEntity } from "../../data/light";
|
||||
import {
|
||||
LightColorMode,
|
||||
computeDefaultFavoriteColors,
|
||||
lightSupportsFavoriteColors,
|
||||
lightSupportsColor,
|
||||
lightSupportsColorMode,
|
||||
lightSupportsFavoriteColors,
|
||||
resolveLightFavoriteColors,
|
||||
} from "../../data/light";
|
||||
import type { ValveEntity } from "../../data/valve";
|
||||
import {
|
||||
@@ -256,9 +256,10 @@ const lightFavoritesHandler: FavoritesDialogHandler = {
|
||||
getLabels: (hass) => getFavoritesDialogLabels(hass, "light"),
|
||||
copy: async ({ entry, hass, host, stateObj }) => {
|
||||
const lightStateObj = stateObj as LightEntity;
|
||||
const favorites: LightColor[] =
|
||||
entry.options?.light?.favorite_colors ??
|
||||
computeDefaultFavoriteColors(lightStateObj);
|
||||
const favorites: LightColor[] = resolveLightFavoriteColors(
|
||||
lightStateObj,
|
||||
entry.options?.light?.favorite_colors
|
||||
);
|
||||
|
||||
const favoriteTypes = [
|
||||
...new Set(favorites.map((item) => Object.keys(item)[0])),
|
||||
@@ -268,6 +269,7 @@ const lightFavoritesHandler: FavoritesDialogHandler = {
|
||||
(candidate) =>
|
||||
candidate.entity_id !== lightStateObj.entity_id &&
|
||||
computeStateDomain(candidate) === "light" &&
|
||||
favoriteTypes.length > 0 &&
|
||||
favoriteTypes.every((type) =>
|
||||
type === "color_temp_kelvin"
|
||||
? lightSupportsColorMode(
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type {
|
||||
ReactiveController,
|
||||
ReactiveControllerHost,
|
||||
} from "@lit/reactive-element/reactive-controller";
|
||||
import type { LitElement } from "lit";
|
||||
import type { ExtEntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import {
|
||||
getExtendedEntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
||||
interface FavoritesEntityRegistryEntryControllerConfig {
|
||||
getHass: () => HomeAssistant | undefined;
|
||||
getEntityId: () => string | undefined;
|
||||
getEntry: () => ExtEntityRegistryEntry | null | undefined;
|
||||
setEntry: (entry: ExtEntityRegistryEntry | null | undefined) => void;
|
||||
}
|
||||
|
||||
export const createFavoritesEntityRegistryEntryController = (
|
||||
host: ReactiveControllerHost & LitElement,
|
||||
config: FavoritesEntityRegistryEntryControllerConfig
|
||||
): FavoritesEntityRegistryEntryController =>
|
||||
new FavoritesEntityRegistryEntryController(host, config);
|
||||
|
||||
export class FavoritesEntityRegistryEntryController implements ReactiveController {
|
||||
private _unsubEntityRegistry?: UnsubscribeFunc;
|
||||
|
||||
private _subscribedEntityId?: string;
|
||||
|
||||
private _subscribedConnection?: HomeAssistant["connection"];
|
||||
|
||||
constructor(
|
||||
private readonly _host: ReactiveControllerHost & LitElement,
|
||||
private readonly _config: FavoritesEntityRegistryEntryControllerConfig
|
||||
) {
|
||||
this._host.addController(this);
|
||||
}
|
||||
|
||||
public hostConnected(): void {
|
||||
this._refreshEntitySubscription();
|
||||
}
|
||||
|
||||
public hostUpdated(): void {
|
||||
this._refreshEntitySubscription();
|
||||
}
|
||||
|
||||
public hostDisconnected(): void {
|
||||
this._unsubscribeEntityRegistry();
|
||||
}
|
||||
|
||||
private _refreshEntitySubscription(): void {
|
||||
this._ensureEntitySubscription().catch(() => undefined);
|
||||
}
|
||||
|
||||
private _setEntry(entry: ExtEntityRegistryEntry | null | undefined): void {
|
||||
if (this._config.getEntry() === entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._config.setEntry(entry);
|
||||
}
|
||||
|
||||
private _unsubscribeEntityRegistry(): void {
|
||||
if (this._unsubEntityRegistry) {
|
||||
this._unsubEntityRegistry();
|
||||
this._unsubEntityRegistry = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadEntityEntry(entityId: string): Promise<void> {
|
||||
const hass = this._config.getHass();
|
||||
|
||||
if (!hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = await getExtendedEntityRegistryEntry(hass, entityId);
|
||||
|
||||
if (this._config.getEntityId() === entityId) {
|
||||
this._setEntry(entry);
|
||||
}
|
||||
} catch (_err) {
|
||||
if (this._config.getEntityId() === entityId) {
|
||||
this._setEntry(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _subscribeEntityEntry(entityId: string): Promise<void> {
|
||||
this._unsubscribeEntityRegistry();
|
||||
|
||||
await this._loadEntityEntry(entityId);
|
||||
|
||||
try {
|
||||
this._unsubEntityRegistry = subscribeEntityRegistry(
|
||||
this._config.getHass()!.connection,
|
||||
async (entries) => {
|
||||
if (this._config.getEntityId() !== entityId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entries.some((entry) => entry.entity_id === entityId)) {
|
||||
await this._loadEntityEntry(entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
this._setEntry(null);
|
||||
}
|
||||
);
|
||||
} catch (_err) {
|
||||
this._unsubEntityRegistry = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async _ensureEntitySubscription(): Promise<void> {
|
||||
const hass = this._config.getHass();
|
||||
const entityId = this._config.getEntityId();
|
||||
const connection = hass?.connection;
|
||||
|
||||
if (!hass || !entityId || !connection) {
|
||||
this._unsubscribeEntityRegistry();
|
||||
this._subscribedEntityId = undefined;
|
||||
this._subscribedConnection = undefined;
|
||||
this._setEntry(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this._subscribedEntityId === entityId &&
|
||||
this._subscribedConnection === connection &&
|
||||
this._unsubEntityRegistry
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._subscribedEntityId = entityId;
|
||||
this._subscribedConnection = connection;
|
||||
|
||||
await this._subscribeEntityEntry(entityId);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,24 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import {
|
||||
computeDefaultFavoriteColors,
|
||||
applyLightFavoriteColor,
|
||||
type LightEntity,
|
||||
type LightColor,
|
||||
lightSupportsFavoriteColors,
|
||||
resolveLightFavoriteColors,
|
||||
} from "../../../data/light";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { createFavoritesEntityRegistryEntryController } from "./common/favorites-entity-registry-entry-controller";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
LightColorFavoritesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "./types";
|
||||
import {
|
||||
type EntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity/entity_registry";
|
||||
import type { ExtEntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import "../../../dialogs/more-info/components/lights/ha-favorite-color-button";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
@@ -48,7 +46,7 @@ class HuiLightColorFavoritesCardFeature
|
||||
|
||||
@state() private _config?: LightColorFavoritesCardFeatureConfig;
|
||||
|
||||
@state() private _entry?: EntityRegistryEntry | null;
|
||||
@state() private _entry?: ExtEntityRegistryEntry | null;
|
||||
|
||||
@state() private _favoriteColors: LightColor[] = [];
|
||||
|
||||
@@ -58,46 +56,29 @@ class HuiLightColorFavoritesCardFeature
|
||||
|
||||
private _resizeObserver?: ResizeObserver;
|
||||
|
||||
private _unsubEntityRegistry?: UnsubscribeFunc;
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
createFavoritesEntityRegistryEntryController(this, {
|
||||
getHass: () => this.hass,
|
||||
getEntityId: () => this.context?.entity_id,
|
||||
getEntry: () => this._entry,
|
||||
setEntry: (entry) => {
|
||||
this._entry = entry;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._subscribeEntityEntry();
|
||||
this.updateComplete.then(() => this._attachObserver());
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unsubscribeEntityRegistry();
|
||||
this._resizeObserver?.disconnect();
|
||||
}
|
||||
|
||||
private _unsubscribeEntityRegistry() {
|
||||
if (this._unsubEntityRegistry) {
|
||||
this._unsubEntityRegistry();
|
||||
this._unsubEntityRegistry = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _subscribeEntityEntry() {
|
||||
if (this.hass && this.context?.entity_id) {
|
||||
const id = this.context.entity_id;
|
||||
try {
|
||||
this._unsubEntityRegistry = subscribeEntityRegistry(
|
||||
this.hass!.connection,
|
||||
(entries) => {
|
||||
const entry = entries.find((e) => e.entity_id === id);
|
||||
if (entry) {
|
||||
this._entry = entry;
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (_e) {
|
||||
this._entry = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _measure() {
|
||||
const w = this._container.clientWidth;
|
||||
const pillMin = 32 + 8;
|
||||
@@ -121,24 +102,14 @@ class HuiLightColorFavoritesCardFeature
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (changedProps.has("context")) {
|
||||
this._unsubscribeEntityRegistry();
|
||||
this._subscribeEntityEntry();
|
||||
}
|
||||
|
||||
if (changedProps.has("_entry") || changedProps.has("_maxVisible")) {
|
||||
if (this._entry) {
|
||||
if (this._entry.options?.light?.favorite_colors) {
|
||||
this._favoriteColors =
|
||||
this._entry.options.light.favorite_colors.slice(
|
||||
0,
|
||||
this._maxVisible
|
||||
);
|
||||
} else if (this._stateObj) {
|
||||
this._favoriteColors = computeDefaultFavoriteColors(
|
||||
this._stateObj
|
||||
).slice(0, this._maxVisible);
|
||||
}
|
||||
this._favoriteColors = this._stateObj
|
||||
? resolveLightFavoriteColors(
|
||||
this._stateObj,
|
||||
this._entry.options?.light?.favorite_colors
|
||||
).slice(0, this._maxVisible)
|
||||
: [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,10 +170,7 @@ class HuiLightColorFavoritesCardFeature
|
||||
const index = (ev.target! as any).index!;
|
||||
|
||||
const favorite = this._favoriteColors[index];
|
||||
this.hass!.callService("light", "turn_on", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
...favorite,
|
||||
});
|
||||
applyLightFavoriteColor(this.hass!, this._stateObj!, favorite);
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
@@ -11,12 +11,9 @@ import type { LocalizeKeys } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-control-select";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { ExtEntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import {
|
||||
getExtendedEntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { createFavoritesEntityRegistryEntryController } from "./common/favorites-entity-registry-entry-controller";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
LovelaceCardFeatureConfig,
|
||||
@@ -87,14 +84,21 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
|
||||
@state() protected _currentPosition?: number;
|
||||
|
||||
private _unsubEntityRegistry?: UnsubscribeFunc;
|
||||
|
||||
private _subscribedEntityId?: string;
|
||||
|
||||
private _subscribedConnection?: HomeAssistant["connection"];
|
||||
|
||||
protected abstract get _definition(): NumericFavoriteCardFeatureDefinition<TEntity>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
createFavoritesEntityRegistryEntryController(this, {
|
||||
getHass: () => this.hass,
|
||||
getEntityId: () => this.context?.entity_id,
|
||||
getEntry: () => this._entry,
|
||||
setEntry: (entry) => {
|
||||
this._entry = entry;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
protected get _stateObj(): TEntity | undefined {
|
||||
if (!this.hass || !this.context?.entity_id) {
|
||||
return undefined;
|
||||
@@ -103,16 +107,6 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
return this.hass.states[this.context.entity_id] as TEntity | undefined;
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._refreshEntitySubscription();
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unsubscribeEntityRegistry();
|
||||
}
|
||||
|
||||
public setConfig(config: LovelaceCardFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
@@ -139,103 +133,6 @@ export abstract class HuiNumericFavoriteCardFeatureBase<
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
changedProp.has("context") &&
|
||||
(changedProp.get("context") as LovelaceCardFeatureContext | undefined)
|
||||
?.entity_id !== this.context?.entity_id
|
||||
) {
|
||||
this._refreshEntitySubscription();
|
||||
}
|
||||
|
||||
if (
|
||||
changedProp.has("hass") &&
|
||||
(changedProp.get("hass") as HomeAssistant | undefined)?.connection !==
|
||||
this.hass?.connection
|
||||
) {
|
||||
this._refreshEntitySubscription();
|
||||
}
|
||||
}
|
||||
|
||||
private _refreshEntitySubscription(): void {
|
||||
this._ensureEntitySubscription().catch(() => undefined);
|
||||
}
|
||||
|
||||
private _unsubscribeEntityRegistry(): void {
|
||||
if (this._unsubEntityRegistry) {
|
||||
this._unsubEntityRegistry();
|
||||
this._unsubEntityRegistry = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadEntityEntry(entityId: string): Promise<void> {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = await getExtendedEntityRegistryEntry(this.hass, entityId);
|
||||
|
||||
if (this.context?.entity_id === entityId) {
|
||||
this._entry = entry;
|
||||
}
|
||||
} catch (_err) {
|
||||
if (this.context?.entity_id === entityId) {
|
||||
this._entry = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _subscribeEntityEntry(entityId: string): Promise<void> {
|
||||
this._unsubscribeEntityRegistry();
|
||||
|
||||
await this._loadEntityEntry(entityId);
|
||||
|
||||
try {
|
||||
this._unsubEntityRegistry = subscribeEntityRegistry(
|
||||
this.hass!.connection,
|
||||
async (entries) => {
|
||||
if (this.context?.entity_id !== entityId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entries.some((entry) => entry.entity_id === entityId)) {
|
||||
await this._loadEntityEntry(entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
this._entry = null;
|
||||
}
|
||||
);
|
||||
} catch (_err) {
|
||||
this._unsubEntityRegistry = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async _ensureEntitySubscription(): Promise<void> {
|
||||
const entityId = this.context?.entity_id;
|
||||
const connection = this.hass?.connection;
|
||||
|
||||
if (!this.hass || !entityId || !connection) {
|
||||
this._unsubscribeEntityRegistry();
|
||||
this._subscribedEntityId = undefined;
|
||||
this._subscribedConnection = undefined;
|
||||
this._entry = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this._subscribedEntityId === entityId &&
|
||||
this._subscribedConnection === connection &&
|
||||
this._unsubEntityRegistry
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._subscribedEntityId = entityId;
|
||||
this._subscribedConnection = connection;
|
||||
|
||||
await this._subscribeEntityEntry(entityId);
|
||||
}
|
||||
|
||||
private async _valueChanged(
|
||||
|
||||
@@ -1,41 +1,12 @@
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../components/ha-alert";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type {
|
||||
CoverPositionFavoriteCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
import { customElement } from "lit/decorators";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
import type { CoverPositionFavoriteCardFeatureConfig } from "../../card-features/types";
|
||||
import { HuiFavoriteCardFeatureEditorBase } from "./hui-favorite-card-feature-editor-base";
|
||||
|
||||
@customElement("hui-cover-position-favorite-card-feature-editor")
|
||||
export class HuiCoverPositionFavoriteCardFeatureEditor
|
||||
extends LitElement
|
||||
implements LovelaceCardFeatureEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CoverPositionFavoriteCardFeatureConfig;
|
||||
|
||||
public setConfig(config: CoverPositionFavoriteCardFeatureConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.features.types.cover-position-favorite.description"
|
||||
)}
|
||||
</ha-alert>
|
||||
`;
|
||||
}
|
||||
export class HuiCoverPositionFavoriteCardFeatureEditor extends HuiFavoriteCardFeatureEditorBase<CoverPositionFavoriteCardFeatureConfig> {
|
||||
protected readonly _descriptionKey =
|
||||
"ui.panel.lovelace.editor.features.types.cover-position-favorite.description" satisfies LocalizeKeys;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,41 +1,12 @@
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../components/ha-alert";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type {
|
||||
CoverTiltFavoriteCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
import { customElement } from "lit/decorators";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
import type { CoverTiltFavoriteCardFeatureConfig } from "../../card-features/types";
|
||||
import { HuiFavoriteCardFeatureEditorBase } from "./hui-favorite-card-feature-editor-base";
|
||||
|
||||
@customElement("hui-cover-tilt-favorite-card-feature-editor")
|
||||
export class HuiCoverTiltFavoriteCardFeatureEditor
|
||||
extends LitElement
|
||||
implements LovelaceCardFeatureEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CoverTiltFavoriteCardFeatureConfig;
|
||||
|
||||
public setConfig(config: CoverTiltFavoriteCardFeatureConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.features.types.cover-tilt-favorite.description"
|
||||
)}
|
||||
</ha-alert>
|
||||
`;
|
||||
}
|
||||
export class HuiCoverTiltFavoriteCardFeatureEditor extends HuiFavoriteCardFeatureEditorBase<CoverTiltFavoriteCardFeatureConfig> {
|
||||
protected readonly _descriptionKey =
|
||||
"ui.panel.lovelace.editor.features.types.cover-tilt-favorite.description" satisfies LocalizeKeys;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-alert";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type {
|
||||
LovelaceCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
export abstract class HuiFavoriteCardFeatureEditorBase<
|
||||
TConfig extends LovelaceCardFeatureConfig,
|
||||
>
|
||||
extends LitElement
|
||||
implements LovelaceCardFeatureEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: TConfig;
|
||||
|
||||
protected abstract readonly _descriptionKey: LocalizeKeys;
|
||||
|
||||
public setConfig(config: TConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(this._descriptionKey)}
|
||||
</ha-alert>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,12 @@
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../components/ha-alert";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
ValvePositionFavoriteCardFeatureConfig,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
import { customElement } from "lit/decorators";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
import type { ValvePositionFavoriteCardFeatureConfig } from "../../card-features/types";
|
||||
import { HuiFavoriteCardFeatureEditorBase } from "./hui-favorite-card-feature-editor-base";
|
||||
|
||||
@customElement("hui-valve-position-favorite-card-feature-editor")
|
||||
export class HuiValvePositionFavoriteCardFeatureEditor
|
||||
extends LitElement
|
||||
implements LovelaceCardFeatureEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: ValvePositionFavoriteCardFeatureConfig;
|
||||
|
||||
public setConfig(config: ValvePositionFavoriteCardFeatureConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.features.types.valve-position-favorite.description"
|
||||
)}
|
||||
</ha-alert>
|
||||
`;
|
||||
}
|
||||
export class HuiValvePositionFavoriteCardFeatureEditor extends HuiFavoriteCardFeatureEditorBase<ValvePositionFavoriteCardFeatureConfig> {
|
||||
protected readonly _descriptionKey =
|
||||
"ui.panel.lovelace.editor.features.types.valve-position-favorite.description" satisfies LocalizeKeys;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
Reference in New Issue
Block a user