mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-13 20:36:35 +00:00
Add support for custom tile features (#15411
* Move isSupported to class * Use getConfigElement to display edit button * Remove custom element for test * Don't use class static functions * Refactor render * Add support for custom tile features in editor * Add missing relative position * Rename custom tile features options * PR feedbacks + split offical and custom types * Merge is custom type function
This commit is contained in:
parent
88205a94d6
commit
8ed4914232
@ -1,6 +1,5 @@
|
||||
import { refine, string } from "superstruct";
|
||||
|
||||
export const isCustomType = (value: string) => value.startsWith("custom:");
|
||||
import { isCustomType } from "../../data/lovelace_custom_cards";
|
||||
|
||||
export const customType = () =>
|
||||
refine(string(), "custom element type", isCustomType);
|
||||
|
@ -1,3 +1,5 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
|
||||
export interface CustomCardEntry {
|
||||
type: string;
|
||||
name?: string;
|
||||
@ -6,8 +8,16 @@ export interface CustomCardEntry {
|
||||
documentationURL?: string;
|
||||
}
|
||||
|
||||
export interface CustomTileFeatureEntry {
|
||||
type: string;
|
||||
name?: string;
|
||||
supported?: (stateObj: HassEntity) => boolean;
|
||||
configurable?: boolean;
|
||||
}
|
||||
|
||||
export interface CustomCardsWindow {
|
||||
customCards?: CustomCardEntry[];
|
||||
customTileFeatures?: CustomTileFeatureEntry[];
|
||||
}
|
||||
|
||||
export const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
@ -17,8 +27,18 @@ const customCardsWindow = window as CustomCardsWindow;
|
||||
if (!("customCards" in customCardsWindow)) {
|
||||
customCardsWindow.customCards = [];
|
||||
}
|
||||
if (!("customTileFeatures" in customCardsWindow)) {
|
||||
customCardsWindow.customTileFeatures = [];
|
||||
}
|
||||
|
||||
export const customCards = customCardsWindow.customCards!;
|
||||
export const customTileFeatures = customCardsWindow.customTileFeatures!;
|
||||
|
||||
export const getCustomCardEntry = (type: string) =>
|
||||
customCards.find((card) => card.type === type);
|
||||
|
||||
export const isCustomType = (type: string) =>
|
||||
type.startsWith(CUSTOM_TYPE_PREFIX);
|
||||
|
||||
export const stripCustomPrefix = (type: string) =>
|
||||
type.slice(CUSTOM_TYPE_PREFIX.length);
|
||||
|
@ -40,7 +40,6 @@ import { findEntities } from "../common/find-entities";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import "../components/hui-timestamp-display";
|
||||
import { createTileFeatureElement } from "../create-element/create-tile-feature-element";
|
||||
import { supportsTileFeature } from "../tile-features/tile-features";
|
||||
import { LovelaceTileFeatureConfig } from "../tile-features/types";
|
||||
import {
|
||||
LovelaceCard,
|
||||
@ -309,10 +308,6 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
: undefined;
|
||||
const badge = computeTileBadge(stateObj, this.hass);
|
||||
|
||||
const supportedFeatures = this._config.features?.filter((feature) =>
|
||||
supportsTileFeature(stateObj, feature.type)
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-card style=${styleMap(style)} class=${classMap({ active })}>
|
||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : null}
|
||||
@ -373,15 +368,11 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
></ha-tile-info>
|
||||
</div>
|
||||
</div>
|
||||
${supportedFeatures?.length
|
||||
? html`
|
||||
<div class="features">
|
||||
${supportedFeatures.map((featureConf) =>
|
||||
this.renderFeature(featureConf, stateObj)
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: null}
|
||||
<div class="features">
|
||||
${this._config.features?.map((featureConf) =>
|
||||
this.renderFeature(featureConf, stateObj)
|
||||
)}
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
@ -412,7 +403,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
(element as LovelaceTileFeature).stateObj = stateObj;
|
||||
}
|
||||
|
||||
return html`<div class="feature">${element}</div>`;
|
||||
return html`${element}`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
@ -502,6 +493,9 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
}
|
||||
.features {
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,10 @@ import {
|
||||
LovelaceViewConfig,
|
||||
LovelaceViewElement,
|
||||
} from "../../../data/lovelace";
|
||||
import { CUSTOM_TYPE_PREFIX } from "../../../data/lovelace_custom_cards";
|
||||
import {
|
||||
isCustomType,
|
||||
stripCustomPrefix,
|
||||
} from "../../../data/lovelace_custom_cards";
|
||||
import type { HuiErrorCard } from "../cards/hui-error-card";
|
||||
import type { ErrorCardConfig } from "../cards/types";
|
||||
import { LovelaceElement, LovelaceElementConfig } from "../elements/types";
|
||||
@ -153,9 +156,7 @@ const _lazyCreate = <T extends keyof CreateElementConfigTypes>(
|
||||
};
|
||||
|
||||
const _getCustomTag = (type: string) =>
|
||||
type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||
? type.slice(CUSTOM_TYPE_PREFIX.length)
|
||||
: undefined;
|
||||
isCustomType(type) ? stripCustomPrefix(type) : undefined;
|
||||
|
||||
export const createLovelaceElement = <T extends keyof CreateElementConfigTypes>(
|
||||
tagSuffix: T,
|
||||
|
@ -17,10 +17,7 @@ import {
|
||||
union,
|
||||
} from "superstruct";
|
||||
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import {
|
||||
customType,
|
||||
isCustomType,
|
||||
} from "../../../../common/structs/is-custom-type";
|
||||
import { customType } from "../../../../common/structs/is-custom-type";
|
||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||
import "../../../../components/entity/state-badge";
|
||||
@ -50,6 +47,7 @@ import {
|
||||
} from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { buttonEntityConfigStruct } from "../structs/button-entity-struct";
|
||||
import { isCustomType } from "../../../../data/lovelace_custom_cards";
|
||||
|
||||
const buttonEntitiesRowConfigStruct = object({
|
||||
type: literal("button"),
|
||||
|
@ -1,11 +1,4 @@
|
||||
import {
|
||||
mdiDelete,
|
||||
mdiDrag,
|
||||
mdiListBox,
|
||||
mdiPencil,
|
||||
mdiPlus,
|
||||
mdiWindowShutter,
|
||||
} from "@mdi/js";
|
||||
import { mdiDelete, mdiDrag, mdiListBox, mdiPencil, mdiPlus } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
@ -14,9 +7,17 @@ import type { SortableEvent } from "sortablejs";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-list-item";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
CustomTileFeatureEntry,
|
||||
customTileFeatures,
|
||||
CUSTOM_TYPE_PREFIX,
|
||||
isCustomType,
|
||||
stripCustomPrefix,
|
||||
} from "../../../../data/lovelace_custom_cards";
|
||||
import { sortableStyles } from "../../../../resources/ha-sortable-style";
|
||||
import {
|
||||
loadSortable,
|
||||
@ -24,19 +25,40 @@ import {
|
||||
} from "../../../../resources/sortable.ondemand";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { getTileFeatureElementClass } from "../../create-element/create-tile-feature-element";
|
||||
import {
|
||||
isTileFeatureEditable,
|
||||
supportsTileFeature,
|
||||
} from "../../tile-features/tile-features";
|
||||
import { supportsCoverOpenCloseTileFeature } from "../../tile-features/hui-cover-open-close-tile-feature";
|
||||
import { supportsCoverTiltTileFeature } from "../../tile-features/hui-cover-tilt-tile-feature";
|
||||
import { supportsLightBrightnessTileFeature } from "../../tile-features/hui-light-brightness-tile-feature";
|
||||
import { supportsVacuumCommandTileFeature } from "../../tile-features/hui-vacuum-commands-tile-feature";
|
||||
import { LovelaceTileFeatureConfig } from "../../tile-features/types";
|
||||
|
||||
const FEATURES_TYPE: LovelaceTileFeatureConfig["type"][] = [
|
||||
type FeatureType = LovelaceTileFeatureConfig["type"];
|
||||
type SupportsFeature = (stateObj: HassEntity) => boolean;
|
||||
|
||||
const FEATURE_TYPES: FeatureType[] = [
|
||||
"cover-open-close",
|
||||
"cover-tilt",
|
||||
"light-brightness",
|
||||
"vacuum-commands",
|
||||
];
|
||||
|
||||
const EDITABLES_FEATURE_TYPES = new Set<FeatureType>(["vacuum-commands"]);
|
||||
|
||||
const SUPPORTS_FEATURE_TYPES: Record<FeatureType, SupportsFeature | undefined> =
|
||||
{
|
||||
"cover-open-close": supportsCoverOpenCloseTileFeature,
|
||||
"cover-tilt": supportsCoverTiltTileFeature,
|
||||
"light-brightness": supportsLightBrightnessTileFeature,
|
||||
"vacuum-commands": supportsVacuumCommandTileFeature,
|
||||
};
|
||||
|
||||
const CUSTOM_FEATURE_ENTRIES: Record<
|
||||
string,
|
||||
CustomTileFeatureEntry | undefined
|
||||
> = {};
|
||||
customTileFeatures.forEach((feature) => {
|
||||
CUSTOM_FEATURE_ENTRIES[feature.type] = feature;
|
||||
});
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"features-changed": {
|
||||
@ -64,6 +86,45 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
this._destroySortable();
|
||||
}
|
||||
|
||||
private _supportsFeatureType(type: string): boolean {
|
||||
if (!this.stateObj) return false;
|
||||
|
||||
if (isCustomType(type)) {
|
||||
const customType = stripCustomPrefix(type);
|
||||
const customFeatureEntry = CUSTOM_FEATURE_ENTRIES[customType];
|
||||
if (!customFeatureEntry?.supported) return true;
|
||||
try {
|
||||
return customFeatureEntry.supported(this.stateObj);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const supportsFeature = SUPPORTS_FEATURE_TYPES[type];
|
||||
return !supportsFeature || supportsFeature(this.stateObj);
|
||||
}
|
||||
|
||||
private _isFeatureTypeEditable(type: string) {
|
||||
if (isCustomType(type)) {
|
||||
const customType = stripCustomPrefix(type);
|
||||
const customFeatureEntry = CUSTOM_FEATURE_ENTRIES[customType];
|
||||
return customFeatureEntry?.configurable;
|
||||
}
|
||||
|
||||
return EDITABLES_FEATURE_TYPES.has(type as FeatureType);
|
||||
}
|
||||
|
||||
private _getFeatureTypeLabel(type: string) {
|
||||
if (isCustomType(type)) {
|
||||
const customType = stripCustomPrefix(type);
|
||||
const customFeatureEntry = CUSTOM_FEATURE_ENTRIES[customType];
|
||||
return customFeatureEntry?.name || type;
|
||||
}
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.types.${type}.label`
|
||||
);
|
||||
}
|
||||
|
||||
private _getKey(feature: LovelaceTileFeatureConfig) {
|
||||
if (!this._featuresKeys.has(feature)) {
|
||||
this._featuresKeys.set(feature, Math.random().toString());
|
||||
@ -72,19 +133,32 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
return this._featuresKeys.get(feature)!;
|
||||
}
|
||||
|
||||
private get _supportedFeatureTypes() {
|
||||
if (!this.stateObj) return [];
|
||||
protected firstUpdated() {
|
||||
this._createSortable();
|
||||
}
|
||||
|
||||
return FEATURES_TYPE.filter((type) =>
|
||||
supportsTileFeature(this.stateObj!, type)
|
||||
private _getSupportedFeaturesType() {
|
||||
const featuresTypes = FEATURE_TYPES as string[];
|
||||
const customFeaturesTypes = customTileFeatures.map(
|
||||
(feature) => `${CUSTOM_TYPE_PREFIX}${feature.type}`
|
||||
);
|
||||
return featuresTypes
|
||||
.concat(customFeaturesTypes)
|
||||
.filter((type) => this._supportsFeatureType(type));
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.features || !this.hass) {
|
||||
return html``;
|
||||
return null;
|
||||
}
|
||||
|
||||
const supportedFeaturesType = this._getSupportedFeaturesType();
|
||||
|
||||
const types = supportedFeaturesType.filter((type) => !isCustomType(type));
|
||||
const customTypes = supportedFeaturesType.filter((type) =>
|
||||
isCustomType(type)
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-expansion-panel outlined>
|
||||
<h3 slot="header">
|
||||
@ -94,8 +168,7 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
)}
|
||||
</h3>
|
||||
<div class="content">
|
||||
${this._supportedFeatureTypes.length === 0 &&
|
||||
this.features.length === 0
|
||||
${supportedFeaturesType.length === 0 && this.features.length === 0
|
||||
? html`
|
||||
<ha-alert type="info">
|
||||
${this.hass!.localize(
|
||||
@ -108,53 +181,58 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
${repeat(
|
||||
this.features,
|
||||
(featureConf) => this._getKey(featureConf),
|
||||
(featureConf, index) => html`
|
||||
<div class="feature">
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
</div>
|
||||
<div class="feature-content">
|
||||
<div>
|
||||
<span>
|
||||
${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.types.${featureConf.type}.label`
|
||||
)}
|
||||
</span>
|
||||
${this.stateObj &&
|
||||
!supportsTileFeature(this.stateObj, featureConf.type)
|
||||
? html`<span class="secondary">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.tile.features.not_compatible"
|
||||
)}
|
||||
</span>`
|
||||
: null}
|
||||
(featureConf, index) => {
|
||||
const type = featureConf.type;
|
||||
const supported = this._supportsFeatureType(type);
|
||||
const editable = this._isFeatureTypeEditable(type);
|
||||
return html`
|
||||
<div class="feature">
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
</div>
|
||||
<div class="feature-content">
|
||||
<div>
|
||||
<span> ${this._getFeatureTypeLabel(type)} </span>
|
||||
${this.stateObj && !supported
|
||||
? html`
|
||||
<span class="secondary">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.tile.features.not_compatible"
|
||||
)}
|
||||
</span>
|
||||
`
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
${editable
|
||||
? html`
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.edit`
|
||||
)}
|
||||
.path=${mdiPencil}
|
||||
class="edit-icon"
|
||||
.index=${index}
|
||||
@click=${this._editFeature}
|
||||
.disabled=${!supported}
|
||||
></ha-icon-button>
|
||||
`
|
||||
: null}
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.remove`
|
||||
)}
|
||||
.path=${mdiDelete}
|
||||
class="remove-icon"
|
||||
.index=${index}
|
||||
@click=${this._removeFeature}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
${isTileFeatureEditable(featureConf.type)
|
||||
? html`<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.edit`
|
||||
)}
|
||||
.path=${mdiPencil}
|
||||
class="edit-icon"
|
||||
.index=${index}
|
||||
@click=${this._editFeature}
|
||||
></ha-icon-button>`
|
||||
: null}
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.remove`
|
||||
)}
|
||||
.path=${mdiDelete}
|
||||
class="remove-icon"
|
||||
.index=${index}
|
||||
@click=${this._removeFeature}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
`
|
||||
`;
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
${this._supportedFeatureTypes.length > 0
|
||||
${supportedFeaturesType.length > 0
|
||||
? html`
|
||||
<ha-button-menu
|
||||
fixed
|
||||
@ -170,16 +248,22 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
|
||||
</ha-button>
|
||||
${this._supportedFeatureTypes.map(
|
||||
(featureType) => html`<mwc-list-item .value=${featureType}>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiWindowShutter}
|
||||
></ha-svg-icon>
|
||||
${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.types.${featureType}.label`
|
||||
)}
|
||||
</mwc-list-item>`
|
||||
${types.map(
|
||||
(type) => html`
|
||||
<ha-list-item .value=${type}>
|
||||
${this._getFeatureTypeLabel(type)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
${types.length > 0 && customTypes.length > 0
|
||||
? html`<li divider role="separator"></li>`
|
||||
: null}
|
||||
${customTypes.map(
|
||||
(type) => html`
|
||||
<ha-list-item .value=${type}>
|
||||
${this._getFeatureTypeLabel(type)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-button-menu>
|
||||
`
|
||||
@ -189,10 +273,6 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
this._createSortable();
|
||||
}
|
||||
|
||||
private async _createSortable() {
|
||||
const Sortable = await loadSortable();
|
||||
this._sortable = new Sortable(
|
||||
@ -228,7 +308,9 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
|
||||
if (index == null) return;
|
||||
|
||||
const value = this._supportedFeatureTypes[index];
|
||||
const value = this._getSupportedFeaturesType()[index];
|
||||
if (!value) return;
|
||||
|
||||
const elClass = await getTileFeatureElementClass(value);
|
||||
|
||||
let newFeature: LovelaceTileFeatureConfig;
|
||||
@ -340,6 +422,10 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
li[divider] {
|
||||
border-bottom-color: var(--divider-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
CUSTOM_TYPE_PREFIX,
|
||||
getCustomCardEntry,
|
||||
isCustomType,
|
||||
stripCustomPrefix,
|
||||
} from "../../../data/lovelace_custom_cards";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
@ -9,9 +10,8 @@ export const getCardDocumentationURL = (
|
||||
hass: HomeAssistant,
|
||||
type: string
|
||||
): string | undefined => {
|
||||
if (type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
||||
return getCustomCardEntry(type.slice(CUSTOM_TYPE_PREFIX.length))
|
||||
?.documentationURL;
|
||||
if (isCustomType(type)) {
|
||||
return getCustomCardEntry(stripCustomPrefix(type))?.documentationURL;
|
||||
}
|
||||
|
||||
return `${documentationUrl(hass, "/lovelace/")}${type}`;
|
||||
|
@ -2,6 +2,7 @@ import { mdiStop } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import {
|
||||
computeCloseIcon,
|
||||
computeOpenIcon,
|
||||
@ -19,6 +20,15 @@ import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceTileFeature } from "../types";
|
||||
import { CoverOpenCloseTileFeatureConfig } from "./types";
|
||||
|
||||
export const supportsCoverOpenCloseTileFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE))
|
||||
);
|
||||
};
|
||||
|
||||
@customElement("hui-cover-open-close-tile-feature")
|
||||
class HuiCoverOpenCloseTileFeature
|
||||
extends LitElement
|
||||
@ -64,9 +74,14 @@ class HuiCoverOpenCloseTileFeature
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this.hass || !this.stateObj) {
|
||||
return html``;
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCoverOpenCloseTileFeature(this.stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return html`
|
||||
|
@ -2,6 +2,7 @@ import { mdiArrowBottomLeft, mdiArrowTopRight, mdiStop } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, TemplateResult } 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";
|
||||
@ -15,6 +16,15 @@ import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceTileFeature } from "../types";
|
||||
import { CoverTiltTileFeatureConfig } from "./types";
|
||||
|
||||
export const supportsCoverTiltTileFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN_TILT) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE_TILT))
|
||||
);
|
||||
};
|
||||
|
||||
@customElement("hui-cover-tilt-tile-feature")
|
||||
class HuiCoverTiltTileFeature
|
||||
extends LitElement
|
||||
@ -60,9 +70,14 @@ class HuiCoverTiltTileFeature
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this.hass || !this.stateObj) {
|
||||
return html``;
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCoverTiltTileFeature
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return html`
|
||||
|
@ -1,13 +1,20 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, TemplateResult } 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/tile/ha-tile-slider";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { lightSupportsBrightness } from "../../../data/light";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceTileFeature } from "../types";
|
||||
import { LightBrightnessTileFeatureConfig } from "./types";
|
||||
|
||||
export const supportsLightBrightnessTileFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "light" && lightSupportsBrightness(stateObj);
|
||||
};
|
||||
|
||||
@customElement("hui-light-brightness-tile-feature")
|
||||
class HuiLightBrightnessTileFeature
|
||||
extends LitElement
|
||||
@ -32,9 +39,14 @@ class HuiLightBrightnessTileFeature
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this.hass || !this.stateObj) {
|
||||
return html``;
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsLightBrightnessTileFeature(this.stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const position =
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, TemplateResult } 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";
|
||||
@ -113,6 +114,14 @@ export const VACUUM_COMMANDS_BUTTONS: Record<
|
||||
}),
|
||||
};
|
||||
|
||||
export const supportsVacuumCommandTileFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "vacuum" &&
|
||||
VACUUM_COMMANDS.some((c) => supportsVacuumCommand(stateObj, c))
|
||||
);
|
||||
};
|
||||
|
||||
@customElement("hui-vacuum-commands-tile-feature")
|
||||
class HuiVacuumCommandTileFeature
|
||||
extends LitElement
|
||||
@ -160,9 +169,14 @@ class HuiVacuumCommandTileFeature
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this.hass || !this.stateObj) {
|
||||
return html``;
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsVacuumCommandTileFeature(this.stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stateObj = this.stateObj as VacuumEntity;
|
||||
|
@ -1,44 +0,0 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { CoverEntityFeature } from "../../../data/cover";
|
||||
import { lightSupportsBrightness } from "../../../data/light";
|
||||
import { supportsVacuumCommand } from "./hui-vacuum-commands-tile-feature";
|
||||
import { LovelaceTileFeatureConfig, VACUUM_COMMANDS } from "./types";
|
||||
|
||||
type TileFeatureType = LovelaceTileFeatureConfig["type"];
|
||||
export type SupportsTileFeature = (stateObj: HassEntity) => boolean;
|
||||
|
||||
const TILE_FEATURES_SUPPORT: Record<TileFeatureType, SupportsTileFeature> = {
|
||||
"cover-open-close": (stateObj) =>
|
||||
computeDomain(stateObj.entity_id) === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE)),
|
||||
"cover-tilt": (stateObj) =>
|
||||
computeDomain(stateObj.entity_id) === "cover" &&
|
||||
(supportsFeature(stateObj, CoverEntityFeature.OPEN_TILT) ||
|
||||
supportsFeature(stateObj, CoverEntityFeature.CLOSE_TILT)),
|
||||
"light-brightness": (stateObj) =>
|
||||
computeDomain(stateObj.entity_id) === "light" &&
|
||||
lightSupportsBrightness(stateObj),
|
||||
"vacuum-commands": (stateObj) =>
|
||||
computeDomain(stateObj.entity_id) === "vacuum" &&
|
||||
VACUUM_COMMANDS.some((c) => supportsVacuumCommand(stateObj, c)),
|
||||
};
|
||||
|
||||
const TILE_FEATURE_EDITABLE: Set<TileFeatureType> = new Set([
|
||||
"vacuum-commands",
|
||||
]);
|
||||
|
||||
export const supportsTileFeature = (
|
||||
stateObj: HassEntity,
|
||||
feature: TileFeatureType
|
||||
): boolean => {
|
||||
const supportFunction = TILE_FEATURES_SUPPORT[feature] as
|
||||
| SupportsTileFeature
|
||||
| undefined;
|
||||
return !supportFunction || supportFunction(stateObj);
|
||||
};
|
||||
|
||||
export const isTileFeatureEditable = (feature: TileFeatureType): boolean =>
|
||||
TILE_FEATURE_EDITABLE.has(feature);
|
@ -109,6 +109,7 @@ export interface LovelaceTileFeatureConstructor
|
||||
hass: HomeAssistant,
|
||||
stateObj?: HassEntity
|
||||
) => LovelaceTileFeatureConfig;
|
||||
isSupported?: (stateObj?: HassEntity) => boolean;
|
||||
}
|
||||
|
||||
export interface LovelaceTileFeatureEditor
|
||||
|
Loading…
x
Reference in New Issue
Block a user