mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 02:36:37 +00:00
Merge branch 'dev'
This commit is contained in:
commit
735560c552
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20240805.0"
|
version = "20240805.1"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -81,6 +81,9 @@ class HaEntityStatePicker extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public required = false;
|
@property({ type: Boolean }) public required = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "allow-name" }) public allowName =
|
||||||
|
false;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@property() public value?: string[] | string;
|
@property() public value?: string[] | string;
|
||||||
@ -95,13 +98,24 @@ class HaEntityStatePicker extends LitElement {
|
|||||||
return !(!changedProps.has("_opened") && this._opened);
|
return !(!changedProps.has("_opened") && this._opened);
|
||||||
}
|
}
|
||||||
|
|
||||||
private options = memoizeOne((entityId?: string, stateObj?: HassEntity) => {
|
private options = memoizeOne(
|
||||||
|
(entityId?: string, stateObj?: HassEntity, allowName?: boolean) => {
|
||||||
const domain = entityId ? computeDomain(entityId) : undefined;
|
const domain = entityId ? computeDomain(entityId) : undefined;
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: this.hass.localize("ui.components.state-content-picker.state"),
|
label: this.hass.localize("ui.components.state-content-picker.state"),
|
||||||
value: "state",
|
value: "state",
|
||||||
},
|
},
|
||||||
|
...(allowName
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
label: this.hass.localize(
|
||||||
|
"ui.components.state-content-picker.name"
|
||||||
|
),
|
||||||
|
value: "name",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
label: this.hass.localize(
|
label: this.hass.localize(
|
||||||
"ui.components.state-content-picker.last_changed"
|
"ui.components.state-content-picker.last_changed"
|
||||||
@ -131,7 +145,8 @@ class HaEntityStatePicker extends LitElement {
|
|||||||
label: this.hass.formatEntityAttributeName(stateObj!, attribute),
|
label: this.hass.formatEntityAttributeName(stateObj!, attribute),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
private _filter = "";
|
private _filter = "";
|
||||||
|
|
||||||
@ -146,7 +161,7 @@ class HaEntityStatePicker extends LitElement {
|
|||||||
? this.hass.states[this.entityId]
|
? this.hass.states[this.entityId]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const options = this.options(this.entityId, stateObj);
|
const options = this.options(this.entityId, stateObj, this.allowName);
|
||||||
const optionItems = options.filter(
|
const optionItems = options.filter(
|
||||||
(option) => !this._value.includes(option.value)
|
(option) => !this._value.includes(option.value)
|
||||||
);
|
);
|
||||||
|
@ -36,6 +36,7 @@ export class HaSelectorUiStateContent extends SubscribeMixin(LitElement) {
|
|||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
|
.allowName=${this.selector.ui_state_content?.allow_name}
|
||||||
></ha-entity-state-content-picker>
|
></ha-entity-state-content-picker>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ export const ensureBadgeConfig = (
|
|||||||
return {
|
return {
|
||||||
type: "entity",
|
type: "entity",
|
||||||
entity: config,
|
entity: config,
|
||||||
|
display_type: "complete",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if ("type" in config && config.type) {
|
if ("type" in config && config.type) {
|
||||||
|
@ -461,6 +461,7 @@ export interface UiStateContentSelector {
|
|||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
ui_state_content: {
|
ui_state_content: {
|
||||||
entity_id?: string;
|
entity_id?: string;
|
||||||
|
allow_name?: boolean;
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ export class HuiBadge extends ReactiveElement {
|
|||||||
|
|
||||||
private _updateElement(config: LovelaceBadgeConfig) {
|
private _updateElement(config: LovelaceBadgeConfig) {
|
||||||
if (config.type === "state-label") {
|
if (config.type === "state-label") {
|
||||||
config = { ...config, type: "entity" };
|
config = { display_type: "complete", ...config, type: "entity" };
|
||||||
}
|
}
|
||||||
if (!this._element) {
|
if (!this._element) {
|
||||||
return;
|
return;
|
||||||
@ -70,7 +70,7 @@ export class HuiBadge extends ReactiveElement {
|
|||||||
|
|
||||||
private _loadElement(config: LovelaceBadgeConfig) {
|
private _loadElement(config: LovelaceBadgeConfig) {
|
||||||
if (config.type === "state-label") {
|
if (config.type === "state-label") {
|
||||||
config = { ...config, type: "entity" };
|
config = { display_type: "complete", ...config, type: "entity" };
|
||||||
}
|
}
|
||||||
this._element = createBadgeElement(config);
|
this._element = createBadgeElement(config);
|
||||||
this._elementConfig = config;
|
this._elementConfig = config;
|
||||||
|
@ -144,6 +144,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
|||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.content=${this._config.state_content}
|
.content=${this._config.state_content}
|
||||||
|
.name=${this._config.name}
|
||||||
>
|
>
|
||||||
</state-display>
|
</state-display>
|
||||||
`;
|
`;
|
||||||
|
@ -23,7 +23,7 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { findEntities } from "../common/find-entities";
|
import { findEntities } from "../common/find-entities";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction, hasAnyAction } from "../common/has-action";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
@ -128,24 +128,20 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
const name = this._config.name ?? computeStateName(stateObj);
|
const name = this._config.name ?? computeStateName(stateObj);
|
||||||
|
|
||||||
const hasAnyAction =
|
|
||||||
!this._config.tap_action ||
|
|
||||||
hasAction(this._config.tap_action) ||
|
|
||||||
hasAction(this._config.hold_action) ||
|
|
||||||
hasAction(this._config.double_tap_action);
|
|
||||||
|
|
||||||
// Use `stateObj.state` as value to keep formatting (e.g trailing zeros)
|
// Use `stateObj.state` as value to keep formatting (e.g trailing zeros)
|
||||||
// for consistent value display across gauge, entity, entity-row, etc.
|
// for consistent value display across gauge, entity, entity-row, etc.
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
class=${classMap({ action: hasAnyAction })}
|
class=${classMap({ action: hasAnyAction(this._config) })}
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({
|
||||||
hasHold: hasAction(this._config.hold_action),
|
hasHold: hasAction(this._config.hold_action),
|
||||||
hasDoubleClick: hasAction(this._config.double_tap_action),
|
hasDoubleClick: hasAction(this._config.double_tap_action),
|
||||||
})}
|
})}
|
||||||
tabindex=${ifDefined(
|
tabindex=${ifDefined(
|
||||||
hasAction(this._config.tap_action) ? "0" : undefined
|
!this._config.tap_action || hasAction(this._config.tap_action)
|
||||||
|
? "0"
|
||||||
|
: undefined
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ha-gauge
|
<ha-gauge
|
||||||
|
@ -28,7 +28,7 @@ import { HomeAssistant } from "../../../types";
|
|||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { findEntities } from "../common/find-entities";
|
import { findEntities } from "../common/find-entities";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction, hasAnyAction } from "../common/has-action";
|
||||||
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
|
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
|
||||||
import { processConfigEntities } from "../common/process-config-entities";
|
import { processConfigEntities } from "../common/process-config-entities";
|
||||||
import "../components/hui-timestamp-display";
|
import "../components/hui-timestamp-display";
|
||||||
@ -263,15 +263,9 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
const name = entityConf.name ?? computeStateName(stateObj);
|
const name = entityConf.name ?? computeStateName(stateObj);
|
||||||
|
|
||||||
const hasAnyAction =
|
|
||||||
!entityConf.tap_action ||
|
|
||||||
hasAction(entityConf.tap_action) ||
|
|
||||||
hasAction(entityConf.hold_action) ||
|
|
||||||
hasAction(entityConf.double_tap_action);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
class=${classMap({ entity: true, action: hasAnyAction })}
|
class=${classMap({ entity: true, action: hasAnyAction(entityConf) })}
|
||||||
.config=${entityConf}
|
.config=${entityConf}
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({
|
||||||
|
@ -251,6 +251,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.content=${this._config.state_content}
|
.content=${this._config.state_content}
|
||||||
|
.name=${this._config.name}
|
||||||
>
|
>
|
||||||
</state-display>
|
</state-display>
|
||||||
`;
|
`;
|
||||||
|
@ -12,7 +12,7 @@ export function createStyledHuiElement(
|
|||||||
|
|
||||||
if (elementConfig.style) {
|
if (elementConfig.style) {
|
||||||
Object.keys(elementConfig.style).forEach((prop) => {
|
Object.keys(elementConfig.style).forEach((prop) => {
|
||||||
element.style.setProperty(prop, elementConfig.style[prop]);
|
element.style.setProperty(prop, elementConfig.style![prop]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
import { ActionConfig } from "../../../data/lovelace/config/action";
|
import { ActionConfig } from "../../../data/lovelace/config/action";
|
||||||
|
import { ConfigEntity } from "../cards/types";
|
||||||
|
|
||||||
export function hasAction(config?: ActionConfig): boolean {
|
export function hasAction(config?: ActionConfig): boolean {
|
||||||
return config !== undefined && config.action !== "none";
|
return config !== undefined && config.action !== "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasAnyAction(config: ConfigEntity): boolean {
|
||||||
|
return (
|
||||||
|
!config.tap_action ||
|
||||||
|
hasAction(config.tap_action) ||
|
||||||
|
hasAction(config.hold_action) ||
|
||||||
|
hasAction(config.double_tap_action)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -20,7 +20,7 @@ import { HomeAssistant } from "../../../types";
|
|||||||
import { EntitiesCardEntityConfig } from "../cards/types";
|
import { EntitiesCardEntityConfig } from "../cards/types";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction, hasAnyAction } from "../common/has-action";
|
||||||
import { createEntityNotFoundWarning } from "./hui-warning";
|
import { createEntityNotFoundWarning } from "./hui-warning";
|
||||||
|
|
||||||
@customElement("hui-generic-entity-row")
|
@customElement("hui-generic-entity-row")
|
||||||
@ -60,9 +60,7 @@ export class HuiGenericEntityRow extends LitElement {
|
|||||||
// By default, we always show a pointer, since if there is no explicit configuration provided,
|
// By default, we always show a pointer, since if there is no explicit configuration provided,
|
||||||
// the frontend always assumes "more-info" in the action handler. We only need to hide the pointer
|
// the frontend always assumes "more-info" in the action handler. We only need to hide the pointer
|
||||||
// if the tap action is explicitly set to "none".
|
// if the tap action is explicitly set to "none".
|
||||||
const pointer = !(
|
const pointer = hasAnyAction(this.config);
|
||||||
this.config.tap_action && this.config.tap_action.action === "none"
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasSecondary = this.secondaryText || this.config.secondary_info;
|
const hasSecondary = this.secondaryText || this.config.secondary_info;
|
||||||
const name = this.config.name ?? computeStateName(stateObj);
|
const name = this.config.name ?? computeStateName(stateObj);
|
||||||
@ -82,7 +80,11 @@ export class HuiGenericEntityRow extends LitElement {
|
|||||||
hasHold: hasAction(this.config!.hold_action),
|
hasHold: hasAction(this.config!.hold_action),
|
||||||
hasDoubleClick: hasAction(this.config!.double_tap_action),
|
hasDoubleClick: hasAction(this.config!.double_tap_action),
|
||||||
})}
|
})}
|
||||||
tabindex=${ifDefined(pointer ? "0" : undefined)}
|
tabindex=${ifDefined(
|
||||||
|
!this.config.tap_action || hasAction(this.config.tap_action)
|
||||||
|
? "0"
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
></state-badge>
|
></state-badge>
|
||||||
${!this.hideName
|
${!this.hideName
|
||||||
? html`<div
|
? html`<div
|
||||||
|
@ -35,7 +35,7 @@ export const createPictureElementElement = (config: LovelaceElementConfig) =>
|
|||||||
|
|
||||||
export const getPictureElementClass = (type: string) =>
|
export const getPictureElementClass = (type: string) =>
|
||||||
getLovelaceElementClass(
|
getLovelaceElementClass(
|
||||||
type,
|
type === "action-button" ? "service-button" : type,
|
||||||
"element",
|
"element",
|
||||||
ALWAYS_LOADED_TYPES,
|
ALWAYS_LOADED_TYPES,
|
||||||
LAZY_LOAD_TYPES
|
LAZY_LOAD_TYPES
|
||||||
|
@ -106,12 +106,23 @@ export class HuiConditionalElementEditor
|
|||||||
private _elementsChanged(ev: CustomEvent): void {
|
private _elementsChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
const oldLength = this._config?.elements?.length || 0;
|
||||||
const config = {
|
const config = {
|
||||||
...this._config,
|
...this._config,
|
||||||
elements: ev.detail.elements as LovelaceElementConfig[],
|
elements: ev.detail.elements as LovelaceElementConfig[],
|
||||||
} as LovelaceCardConfig;
|
} as LovelaceCardConfig;
|
||||||
|
|
||||||
fireEvent(this, "config-changed", { config });
|
fireEvent(this, "config-changed", { config });
|
||||||
|
|
||||||
|
const newLength = ev.detail.elements?.length || 0;
|
||||||
|
if (newLength === oldLength + 1) {
|
||||||
|
const index = newLength - 1;
|
||||||
|
this._subElementEditorConfig = {
|
||||||
|
index,
|
||||||
|
type: "element",
|
||||||
|
elementConfig: { ...ev.detail.elements[index] },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleSubElementChanged(ev: CustomEvent): void {
|
private _handleSubElementChanged(ev: CustomEvent): void {
|
||||||
|
@ -122,7 +122,9 @@ export class HuiEntityBadgeEditor
|
|||||||
{
|
{
|
||||||
name: "state_content",
|
name: "state_content",
|
||||||
selector: {
|
selector: {
|
||||||
ui_state_content: {},
|
ui_state_content: {
|
||||||
|
allow_name: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
filter_entity: "entity",
|
filter_entity: "entity",
|
||||||
|
@ -23,7 +23,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|||||||
import { DEFAULT_MIN, DEFAULT_MAX } from "../../cards/hui-gauge-card";
|
import { DEFAULT_MIN, DEFAULT_MAX } from "../../cards/hui-gauge-card";
|
||||||
import { UiAction } from "../../components/hui-action-editor";
|
import { UiAction } from "../../components/hui-action-editor";
|
||||||
|
|
||||||
const TAP_ACTIONS: UiAction[] = ["navigate", "url", "call-service", "none"];
|
const TAP_ACTIONS: UiAction[] = ["navigate", "url", "perform-action", "none"];
|
||||||
|
|
||||||
const gaugeSegmentStruct = object({
|
const gaugeSegmentStruct = object({
|
||||||
from: number(),
|
from: number(),
|
||||||
|
@ -138,12 +138,23 @@ export class HuiPictureElementsCardEditor
|
|||||||
private _elementsChanged(ev: CustomEvent): void {
|
private _elementsChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
const oldLength = this._config?.elements?.length || 0;
|
||||||
const config = {
|
const config = {
|
||||||
...this._config,
|
...this._config,
|
||||||
elements: ev.detail.elements as LovelaceElementConfig[],
|
elements: ev.detail.elements as LovelaceElementConfig[],
|
||||||
} as LovelaceCardConfig;
|
} as LovelaceCardConfig;
|
||||||
|
|
||||||
fireEvent(this, "config-changed", { config });
|
fireEvent(this, "config-changed", { config });
|
||||||
|
|
||||||
|
const newLength = ev.detail.elements?.length || 0;
|
||||||
|
if (newLength === oldLength + 1) {
|
||||||
|
const index = newLength - 1;
|
||||||
|
this._subElementEditorConfig = {
|
||||||
|
index,
|
||||||
|
type: "element",
|
||||||
|
elementConfig: { ...ev.detail.elements[index] },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleSubElementChanged(ev: CustomEvent): void {
|
private _handleSubElementChanged(ev: CustomEvent): void {
|
||||||
|
30
src/panels/lovelace/editor/get-element-stub-config.ts
Normal file
30
src/panels/lovelace/editor/get-element-stub-config.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { LovelaceElementConfig } from "../elements/types";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { getPictureElementClass } from "../create-element/create-picture-element";
|
||||||
|
|
||||||
|
export const getElementStubConfig = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
type: string,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
): Promise<LovelaceElementConfig> => {
|
||||||
|
let elementConfig: LovelaceElementConfig = { type };
|
||||||
|
|
||||||
|
if (type !== "conditional") {
|
||||||
|
elementConfig.style = { left: "50%", top: "50%" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const elClass = await getPictureElementClass(type);
|
||||||
|
|
||||||
|
if (elClass && elClass.getStubConfig) {
|
||||||
|
const classStubConfig = await elClass.getStubConfig(
|
||||||
|
hass,
|
||||||
|
entities,
|
||||||
|
entitiesFallback
|
||||||
|
);
|
||||||
|
|
||||||
|
elementConfig = { ...elementConfig, ...classStubConfig };
|
||||||
|
}
|
||||||
|
|
||||||
|
return elementConfig;
|
||||||
|
};
|
@ -1,3 +1,4 @@
|
|||||||
|
import deepClone from "deep-clone-simple";
|
||||||
import { mdiClose, mdiPencil, mdiContentDuplicate } from "@mdi/js";
|
import { mdiClose, mdiPencil, mdiContentDuplicate } from "@mdi/js";
|
||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
@ -9,6 +10,7 @@ import { HomeAssistant } from "../../../types";
|
|||||||
import "../../../components/ha-select";
|
import "../../../components/ha-select";
|
||||||
import type { HaSelect } from "../../../components/ha-select";
|
import type { HaSelect } from "../../../components/ha-select";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
|
import { getElementStubConfig } from "./get-element-stub-config";
|
||||||
import {
|
import {
|
||||||
ConditionalElementConfig,
|
ConditionalElementConfig,
|
||||||
IconElementConfig,
|
IconElementConfig,
|
||||||
@ -171,17 +173,14 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
|||||||
if (value === "") {
|
if (value === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newElements = this.elements!.concat({
|
const newElements = this.elements!.concat(
|
||||||
type: value! as string,
|
await getElementStubConfig(
|
||||||
...(value !== "conditional"
|
this.hass!,
|
||||||
? {
|
value,
|
||||||
style: {
|
Object.keys(this.hass!.entities),
|
||||||
top: "50%",
|
[]
|
||||||
left: "50%",
|
)
|
||||||
},
|
);
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
} as LovelaceElementConfig);
|
|
||||||
fireEvent(this, "elements-changed", { elements: newElements });
|
fireEvent(this, "elements-changed", { elements: newElements });
|
||||||
this._select.select(-1);
|
this._select.select(-1);
|
||||||
}
|
}
|
||||||
@ -225,7 +224,7 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
|||||||
|
|
||||||
private _duplicateRow(ev: CustomEvent): void {
|
private _duplicateRow(ev: CustomEvent): void {
|
||||||
const index = (ev.currentTarget as any).index;
|
const index = (ev.currentTarget as any).index;
|
||||||
const newElements = [...this.elements!, this.elements![index]];
|
const newElements = [...this.elements!, deepClone(this.elements![index])];
|
||||||
|
|
||||||
fireEvent(this, "elements-changed", { elements: newElements });
|
fireEvent(this, "elements-changed", { elements: newElements });
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,17 @@ export class HuiSubElementEditor extends LitElement {
|
|||||||
@click=${this._goBack}
|
@click=${this._goBack}
|
||||||
></ha-icon-button-prev>
|
></ha-icon-button-prev>
|
||||||
<span slot="title"
|
<span slot="title"
|
||||||
>${this.hass.localize(
|
>${this.config?.type === "element"
|
||||||
|
? this.hass.localize(
|
||||||
|
`ui.panel.lovelace.editor.sub-element-editor.types.element_type`,
|
||||||
|
{
|
||||||
|
type:
|
||||||
|
this.hass.localize(
|
||||||
|
`ui.panel.lovelace.editor.card.picture-elements.element_types.${this.config?.elementConfig?.type}`
|
||||||
|
) || this.config?.elementConfig?.type,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
`ui.panel.lovelace.editor.sub-element-editor.types.${this.config?.type}`
|
`ui.panel.lovelace.editor.sub-element-editor.types.${this.config?.type}`
|
||||||
)}</span
|
)}</span
|
||||||
>
|
>
|
||||||
|
@ -18,6 +18,10 @@ export class HuiIconElement extends LitElement implements LovelaceElement {
|
|||||||
return document.createElement("hui-icon-element-editor");
|
return document.createElement("hui-icon-element-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(): IconElementConfig {
|
||||||
|
return { type: "icon", icon: "mdi:alert-circle" };
|
||||||
|
}
|
||||||
|
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _config?: IconElementConfig;
|
@state() private _config?: IconElementConfig;
|
||||||
|
@ -17,6 +17,14 @@ export class HuiServiceButtonElement
|
|||||||
return document.createElement("hui-service-button-element-editor");
|
return document.createElement("hui-service-button-element-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(hass: HomeAssistant): ServiceButtonElementConfig {
|
||||||
|
return {
|
||||||
|
type: "action-button",
|
||||||
|
action: "homeassistant.turn_on",
|
||||||
|
title: hass.localize("ui.card.common.turn_on"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _config?: ServiceButtonElementConfig;
|
@state() private _config?: ServiceButtonElementConfig;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { LitElement, PropertyValues, html, nothing } from "lit";
|
import { LitElement, PropertyValues, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import { findEntities } from "../common/find-entities";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import "../../../components/entity/ha-state-label-badge";
|
import "../../../components/entity/ha-state-label-badge";
|
||||||
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
@ -8,6 +10,7 @@ import { HomeAssistant } from "../../../types";
|
|||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction } from "../common/has-action";
|
||||||
|
import { isUnavailableState } from "../../../data/entity";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import "../components/hui-warning-element";
|
import "../components/hui-warning-element";
|
||||||
@ -26,6 +29,27 @@ export class HuiStateBadgeElement
|
|||||||
return document.createElement("hui-state-badge-element-editor");
|
return document.createElement("hui-state-badge-element-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
): StateBadgeElementConfig {
|
||||||
|
const includeDomains = ["light", "switch", "sensor"];
|
||||||
|
const maxEntities = 1;
|
||||||
|
const entityFilter = (stateObj: HassEntity): boolean =>
|
||||||
|
!isUnavailableState(stateObj.state);
|
||||||
|
const foundEntities = findEntities(
|
||||||
|
hass,
|
||||||
|
maxEntities,
|
||||||
|
entities,
|
||||||
|
entitiesFallback,
|
||||||
|
includeDomains,
|
||||||
|
entityFilter
|
||||||
|
);
|
||||||
|
|
||||||
|
return { type: "state-badge", entity: foundEntities[0] || "" };
|
||||||
|
}
|
||||||
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _config?: StateBadgeElementConfig;
|
@state() private _config?: StateBadgeElementConfig;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -8,12 +9,14 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import { findEntities } from "../common/find-entities";
|
||||||
import "../../../components/entity/state-badge";
|
import "../../../components/entity/state-badge";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { computeTooltip } from "../common/compute-tooltip";
|
import { computeTooltip } from "../common/compute-tooltip";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction } from "../common/has-action";
|
||||||
|
import { isUnavailableState } from "../../../data/entity";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import "../components/hui-warning-element";
|
import "../components/hui-warning-element";
|
||||||
@ -30,6 +33,27 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
|
|||||||
return document.createElement("hui-state-icon-element-editor");
|
return document.createElement("hui-state-icon-element-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
): StateIconElementConfig {
|
||||||
|
const includeDomains = ["light", "switch", "sensor"];
|
||||||
|
const maxEntities = 1;
|
||||||
|
const entityFilter = (stateObj: HassEntity): boolean =>
|
||||||
|
!isUnavailableState(stateObj.state);
|
||||||
|
const foundEntities = findEntities(
|
||||||
|
hass,
|
||||||
|
maxEntities,
|
||||||
|
entities,
|
||||||
|
entitiesFallback,
|
||||||
|
includeDomains,
|
||||||
|
entityFilter
|
||||||
|
);
|
||||||
|
|
||||||
|
return { type: "state-icon", entity: foundEntities[0] || "" };
|
||||||
|
}
|
||||||
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _config?: StateIconElementConfig;
|
@state() private _config?: StateIconElementConfig;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
LitElement,
|
LitElement,
|
||||||
@ -8,12 +9,14 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import { findEntities } from "../common/find-entities";
|
||||||
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { computeTooltip } from "../common/compute-tooltip";
|
import { computeTooltip } from "../common/compute-tooltip";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction } from "../common/has-action";
|
||||||
|
import { isUnavailableState } from "../../../data/entity";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import "../components/hui-warning-element";
|
import "../components/hui-warning-element";
|
||||||
@ -29,6 +32,27 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
|||||||
return document.createElement("hui-state-label-element-editor");
|
return document.createElement("hui-state-label-element-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
): StateLabelElementConfig {
|
||||||
|
const includeDomains = ["light", "switch", "sensor"];
|
||||||
|
const maxEntities = 1;
|
||||||
|
const entityFilter = (stateObj: HassEntity): boolean =>
|
||||||
|
!isUnavailableState(stateObj.state);
|
||||||
|
const foundEntities = findEntities(
|
||||||
|
hass,
|
||||||
|
maxEntities,
|
||||||
|
entities,
|
||||||
|
entitiesFallback,
|
||||||
|
includeDomains,
|
||||||
|
entityFilter
|
||||||
|
);
|
||||||
|
|
||||||
|
return { type: "state-label", entity: foundEntities[0] || "" };
|
||||||
|
}
|
||||||
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _config?: StateLabelElementConfig;
|
@state() private _config?: StateLabelElementConfig;
|
||||||
|
@ -6,7 +6,7 @@ import { HuiImage } from "../components/hui-image";
|
|||||||
|
|
||||||
interface LovelaceElementConfigBase {
|
interface LovelaceElementConfigBase {
|
||||||
type: string;
|
type: string;
|
||||||
style: Record<string, string>;
|
style?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LovelaceElementConfig =
|
export type LovelaceElementConfig =
|
||||||
|
@ -123,7 +123,6 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow {
|
|||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
cursor: pointer;
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.flex {
|
.flex {
|
||||||
|
@ -70,9 +70,6 @@ class HuiSceneEntityRow extends LitElement implements LovelaceRow {
|
|||||||
margin-inline-end: -0.57em;
|
margin-inline-end: -0.57em;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
}
|
}
|
||||||
:host {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +58,6 @@ class HuiSimpleEntityRow extends LitElement implements LovelaceRow {
|
|||||||
div {
|
div {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,9 +61,6 @@ class HuiUpdateEntityRow extends LitElement implements LovelaceRow {
|
|||||||
div {
|
div {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import type { EntitiesCardEntityConfig } from "../cards/types";
|
import type { EntitiesCardEntityConfig } from "../cards/types";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction, hasAnyAction } from "../common/has-action";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
@ -118,9 +118,7 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pointer = !(
|
const pointer = hasAnyAction(this._config);
|
||||||
this._config.tap_action && this._config.tap_action.action !== "none"
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasSecondary = this._config.secondary_info;
|
const hasSecondary = this._config.secondary_info;
|
||||||
const weatherStateIcon = getWeatherStateIcon(stateObj.state, this);
|
const weatherStateIcon = getWeatherStateIcon(stateObj.state, this);
|
||||||
@ -138,7 +136,11 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
|
|||||||
hasHold: hasAction(this._config!.hold_action),
|
hasHold: hasAction(this._config!.hold_action),
|
||||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||||
})}
|
})}
|
||||||
tabindex=${ifDefined(pointer ? "0" : undefined)}
|
tabindex=${ifDefined(
|
||||||
|
!this._config.tap_action || hasAction(this._config.tap_action)
|
||||||
|
? "0"
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
${weatherStateIcon ||
|
${weatherStateIcon ||
|
||||||
html`
|
html`
|
||||||
|
@ -109,6 +109,11 @@ export interface LovelaceRowConstructor extends Constructor<LovelaceRow> {
|
|||||||
export interface LovelaceElementConstructor
|
export interface LovelaceElementConstructor
|
||||||
extends Constructor<LovelaceElement> {
|
extends Constructor<LovelaceElement> {
|
||||||
getConfigElement?: () => LovelacePictureElementEditor;
|
getConfigElement?: () => LovelacePictureElementEditor;
|
||||||
|
getStubConfig?: (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entities: string[],
|
||||||
|
entitiesFallback: string[]
|
||||||
|
) => LovelaceElementConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceHeaderFooter extends HTMLElement {
|
export interface LovelaceHeaderFooter extends HTMLElement {
|
||||||
|
@ -55,6 +55,8 @@ class StateDisplay extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public content?: StateContent;
|
@property({ attribute: false }) public content?: StateContent;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public name?: string;
|
||||||
|
|
||||||
protected createRenderRoot() {
|
protected createRenderRoot() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -88,6 +90,9 @@ class StateDisplay extends LitElement {
|
|||||||
|
|
||||||
return this.hass!.formatEntityState(stateObj);
|
return this.hass!.formatEntityState(stateObj);
|
||||||
}
|
}
|
||||||
|
if (content === "name") {
|
||||||
|
return html`${this.name || stateObj.attributes.friendly_name}`;
|
||||||
|
}
|
||||||
// Check last-changed for backwards compatibility
|
// Check last-changed for backwards compatibility
|
||||||
if (content === "last_changed" || content === "last-changed") {
|
if (content === "last_changed" || content === "last-changed") {
|
||||||
return html`
|
return html`
|
||||||
|
@ -1026,6 +1026,7 @@
|
|||||||
},
|
},
|
||||||
"state-content-picker": {
|
"state-content-picker": {
|
||||||
"state": "State",
|
"state": "State",
|
||||||
|
"name": "Name",
|
||||||
"last_changed": "Last changed",
|
"last_changed": "Last changed",
|
||||||
"last_updated": "Last updated",
|
"last_updated": "Last updated",
|
||||||
"remaining_time": "Remaining time",
|
"remaining_time": "Remaining time",
|
||||||
@ -6323,7 +6324,8 @@
|
|||||||
"footer": "Footer editor",
|
"footer": "Footer editor",
|
||||||
"row": "Entity row editor",
|
"row": "Entity row editor",
|
||||||
"feature": "Feature editor",
|
"feature": "Feature editor",
|
||||||
"element": "Element editor"
|
"element": "Element editor",
|
||||||
|
"element_type": "{type} element editor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user