-
-
-
-
-
-
- ${this.hass!.localize(
- `ui.panel.lovelace.editor.card.tile.features.types.${featureConf.type}.label`
- )}
-
- ${this.stateObj &&
- !supportsTileFeature(this.stateObj, featureConf.type)
- ? html`
- ${this.hass!.localize(
- "ui.panel.lovelace.editor.card.tile.features.not_compatible"
- )}
- `
- : null}
+ (featureConf, index) => {
+ const type = featureConf.type;
+ const supported = this._supportsFeatureType(type);
+ const editable = this._isFeatureTypeEditable(type);
+ return html`
+
+
+
+
+
+ ${this._getFeatureTypeLabel(type)}
+ ${this.stateObj && !supported
+ ? html`
+
+ ${this.hass!.localize(
+ "ui.panel.lovelace.editor.card.tile.features.not_compatible"
+ )}
+
+ `
+ : null}
+
+
+ ${editable
+ ? html`
+
+ `
+ : null}
+
- ${isTileFeatureEditable(featureConf.type)
- ? html`
`
- : null}
-
-
- `
+ `;
+ }
)}
- ${this._supportedFeatureTypes.length > 0
+ ${supportedFeaturesType.length > 0
? html`
- ${this._supportedFeatureTypes.map(
- (featureType) => html`
-
- ${this.hass!.localize(
- `ui.panel.lovelace.editor.card.tile.features.types.${featureType}.label`
- )}
- `
+ ${types.map(
+ (type) => html`
+
+ ${this._getFeatureTypeLabel(type)}
+
+ `
+ )}
+ ${types.length > 0 && customTypes.length > 0
+ ? html``
+ : null}
+ ${customTypes.map(
+ (type) => html`
+
+ ${this._getFeatureTypeLabel(type)}
+
+ `
)}
`
@@ -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);
+ }
`,
];
}
diff --git a/src/panels/lovelace/editor/get-card-documentation-url.ts b/src/panels/lovelace/editor/get-card-documentation-url.ts
index 7d01eb9419..e312463d4b 100644
--- a/src/panels/lovelace/editor/get-card-documentation-url.ts
+++ b/src/panels/lovelace/editor/get-card-documentation-url.ts
@@ -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}`;
diff --git a/src/panels/lovelace/tile-features/hui-cover-open-close-tile-feature.ts b/src/panels/lovelace/tile-features/hui-cover-open-close-tile-feature.ts
index 9f2813b157..8a4fe2c7cb 100644
--- a/src/panels/lovelace/tile-features/hui-cover-open-close-tile-feature.ts
+++ b/src/panels/lovelace/tile-features/hui-cover-open-close-tile-feature.ts
@@ -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`
diff --git a/src/panels/lovelace/tile-features/hui-cover-tilt-tile-feature.ts b/src/panels/lovelace/tile-features/hui-cover-tilt-tile-feature.ts
index 43f03fbd80..e2545048e7 100644
--- a/src/panels/lovelace/tile-features/hui-cover-tilt-tile-feature.ts
+++ b/src/panels/lovelace/tile-features/hui-cover-tilt-tile-feature.ts
@@ -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`
diff --git a/src/panels/lovelace/tile-features/hui-light-brightness-tile-feature.ts b/src/panels/lovelace/tile-features/hui-light-brightness-tile-feature.ts
index 6848604e21..f73261a41c 100644
--- a/src/panels/lovelace/tile-features/hui-light-brightness-tile-feature.ts
+++ b/src/panels/lovelace/tile-features/hui-light-brightness-tile-feature.ts
@@ -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 =
diff --git a/src/panels/lovelace/tile-features/hui-vacuum-commands-tile-feature.ts b/src/panels/lovelace/tile-features/hui-vacuum-commands-tile-feature.ts
index 8089c55d23..4b1993fa2f 100644
--- a/src/panels/lovelace/tile-features/hui-vacuum-commands-tile-feature.ts
+++ b/src/panels/lovelace/tile-features/hui-vacuum-commands-tile-feature.ts
@@ -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;
diff --git a/src/panels/lovelace/tile-features/tile-features.ts b/src/panels/lovelace/tile-features/tile-features.ts
deleted file mode 100644
index 5dfdb32a7f..0000000000
--- a/src/panels/lovelace/tile-features/tile-features.ts
+++ /dev/null
@@ -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
= {
- "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 = 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);
diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts
index 3e0652437b..c99e6a51c9 100644
--- a/src/panels/lovelace/types.ts
+++ b/src/panels/lovelace/types.ts
@@ -109,6 +109,7 @@ export interface LovelaceTileFeatureConstructor
hass: HomeAssistant,
stateObj?: HassEntity
) => LovelaceTileFeatureConfig;
+ isSupported?: (stateObj?: HassEntity) => boolean;
}
export interface LovelaceTileFeatureEditor