Compare commits

...

2 Commits

Author SHA1 Message Date
Wendelin 6b77315891 Add live card status 2026-05-13 16:53:16 +02:00
Wendelin 1762ee10fe Add lovelace condition live test 2026-05-13 15:33:29 +02:00
10 changed files with 481 additions and 163 deletions
@@ -0,0 +1,59 @@
import type {
ReactiveController,
ReactiveControllerHost,
} from "@lit/reactive-element/reactive-controller";
import type {
Condition,
ConditionContext,
} from "../../panels/lovelace/common/validate-condition";
import type { HomeAssistant } from "../../types";
import { setupConditionListeners } from "../condition/listeners";
/**
* Reactive controller that manages the media-query and time-based listeners
* needed to keep a set of lovelace visibility conditions evaluated live.
*
* The host is responsible for the actual evaluation (e.g. computing visible /
* hidden / invalid state); the controller only triggers it via the supplied
* `onUpdate` callback when something the conditions depend on changes. Call
* `setup()` whenever the conditions change; the controller clears previous
* listeners and re-subscribes. Listeners are automatically released when the
* host disconnects.
*/
export class ConditionListenersController implements ReactiveController {
private _unsubs: (() => void)[] = [];
constructor(host: ReactiveControllerHost) {
host.addController(this);
}
public hostDisconnected(): void {
this.clear();
}
public setup(
conditions: Condition[],
hass: HomeAssistant,
onUpdate: () => void,
getContext?: () => ConditionContext
): void {
this.clear();
if (!conditions.length) {
return;
}
setupConditionListeners(
conditions,
hass,
(unsub) => this._unsubs.push(unsub),
() => onUpdate(),
getContext
);
}
public clear(): void {
for (const unsub of this._unsubs) {
unsub();
}
this._unsubs = [];
}
}
@@ -0,0 +1,91 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import "../ha-tooltip";
export type LiveTestState = "pass" | "fail" | "invalid" | "unknown";
/**
* @element ha-automation-row-live-test
*
* @summary
* Small status indicator dot used in automation/condition rows to surface the
* live evaluation result. Renders an optional tooltip with details on hover.
*
* @attr {"pass"|"fail"|"invalid"|"unknown"} state - The current live-test state. Defaults to `unknown`.
* @attr {string} label - Accessible label announced by assistive technology.
* @attr {string} message - Optional tooltip body shown on hover/focus.
*/
@customElement("ha-automation-row-live-test")
export class HaAutomationRowLiveTest extends LitElement {
@property({ reflect: true }) public state: LiveTestState = "unknown";
@property() public label = "";
@property() public message?: string;
protected render() {
return html`
<div
id="indicator"
role="status"
tabindex="0"
aria-label=${this.label}
></div>
${this.message
? html`<ha-tooltip for="indicator">${this.message}</ha-tooltip>`
: nothing}
`;
}
static styles = css`
:host {
position: absolute;
inset-inline-end: -6px;
display: inline-block;
}
#indicator {
width: 12px;
height: 12px;
border-radius: var(--ha-border-radius-circle);
border: 3px solid;
box-sizing: border-box;
background-color: var(--card-background-color);
transition: all var(--ha-animation-duration-normal) ease-in-out;
}
:host([state="pass"]) #indicator {
background-color: var(--ha-color-fill-success-loud-resting);
border-color: var(--ha-color-fill-success-loud-resting);
}
:host([state="pass"]) #indicator:hover {
background-color: var(--ha-color-fill-success-loud-hover);
border-color: var(--ha-color-fill-success-loud-hover);
}
:host([state="fail"]) #indicator {
border-color: var(--ha-color-fill-warning-loud-resting);
}
:host([state="fail"]) #indicator:hover {
background-color: var(--ha-color-fill-warning-loud-hover);
border-color: var(--ha-color-fill-warning-loud-hover);
}
:host([state="invalid"]) #indicator {
border-color: var(--ha-color-fill-danger-loud-resting);
}
:host([state="invalid"]) #indicator:hover {
background-color: var(--ha-color-fill-danger-loud-hover);
border-color: var(--ha-color-fill-danger-loud-hover);
}
:host([state="unknown"]) #indicator {
border-color: var(--ha-color-fill-neutral-loud-resting);
}
:host([state="unknown"]) #indicator:hover {
background-color: var(--ha-color-fill-neutral-loud-hover);
border-color: var(--ha-color-fill-neutral-loud-hover);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-automation-row-live-test": HaAutomationRowLiveTest;
}
}
@@ -23,7 +23,7 @@ import type {
} from "home-assistant-js-websocket";
import { dump } from "js-yaml";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { LitElement, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
@@ -39,6 +39,7 @@ import { debounce } from "../../../../common/util/debounce";
import "../../../../components/automation/ha-automation-row";
import type { HaAutomationRow } from "../../../../components/automation/ha-automation-row";
import "../../../../components/automation/ha-automation-row-event-chip";
import "../../../../components/automation/ha-automation-row-live-test";
import "../../../../components/ha-card";
import "../../../../components/ha-condition-icon";
import "../../../../components/ha-dropdown";
@@ -46,7 +47,6 @@ import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-tooltip";
import type {
AutomationClipboard,
Condition,
@@ -498,23 +498,15 @@ export default class HaAutomationConditionRow extends LitElement {
@click=${this._toggleSidebar}
@toggle-collapsed=${this._toggleCollapse}
>${this._renderRow()}
<div
<ha-automation-row-live-test
slot="icons"
id="live-test"
class=${this._liveTestResult.state}
role="status"
tabindex="0"
aria-label=${this.hass.localize(
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.config.automation.editor.conditions.live_test_state.${this._liveTestResult.state}`
)}
>
${this._liveTestResult.message
? html`<ha-tooltip for="live-test">
${this._liveTestResult.message}
</ha-tooltip>`
: nothing}
</div></ha-automation-row
>`
.message=${this._liveTestResult.message}
></ha-automation-row-live-test
></ha-automation-row>`
: html`
<ha-expansion-panel
left-chevron
@@ -1064,52 +1056,7 @@ export default class HaAutomationConditionRow extends LitElement {
}
static get styles(): CSSResultGroup {
return [
rowStyles,
overflowStyles,
css`
#live-test {
position: absolute;
inset-inline-end: -6px;
width: 12px;
height: 12px;
border-radius: var(--ha-border-radius-circle);
border: 3px solid;
box-sizing: border-box;
background-color: var(--card-background-color);
transition: all var(--ha-animation-duration-normal) ease-in-out;
}
#live-test.pass {
background-color: var(--ha-color-fill-success-loud-resting);
border-color: var(--ha-color-fill-success-loud-resting);
}
#live-test.pass:hover {
background-color: var(--ha-color-fill-success-loud-hover);
border-color: var(--ha-color-fill-success-loud-hover);
}
#live-test.fail {
border-color: var(--ha-color-fill-warning-loud-resting);
}
#live-test.fail:hover {
background-color: var(--ha-color-fill-warning-loud-hover);
border-color: var(--ha-color-fill-warning-loud-hover);
}
#live-test.invalid {
border-color: var(--ha-color-fill-danger-loud-resting);
}
#live-test.invalid:hover {
background-color: var(--ha-color-fill-danger-loud-hover);
border-color: var(--ha-color-fill-danger-loud-hover);
}
#live-test.unknown {
border-color: var(--ha-color-fill-neutral-loud-resting);
}
#live-test.unknown:hover {
background-color: var(--ha-color-fill-neutral-loud-hover);
border-color: var(--ha-color-fill-neutral-loud-hover);
}
`,
];
return [rowStyles, overflowStyles];
}
}
@@ -3,12 +3,12 @@ import type { PropertyValues } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import { conditionsEntityContext } from "../conditions/context";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
@customElement("hui-badge-visibility-editor")
export class HuiBadgeVisibilityEditor extends LitElement {
@@ -34,11 +34,10 @@ export class HuiBadgeVisibilityEditor extends LitElement {
render() {
const conditions = this.config.visibility ?? [];
return html`
<p class="intro">
${this.hass.localize(
`ui.panel.lovelace.editor.edit_badge.visibility.explanation`
)}
</p>
<ha-visibility-status
.hass=${this.hass}
.conditions=${conditions}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@@ -62,10 +61,8 @@ export class HuiBadgeVisibilityEditor extends LitElement {
}
static styles = css`
.intro {
margin: 0;
color: var(--secondary-text-color);
margin-bottom: 8px;
ha-visibility-status {
margin-bottom: var(--ha-space-3);
}
`;
}
@@ -3,12 +3,12 @@ import type { PropertyValues } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import { conditionsEntityContext } from "../conditions/context";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
@customElement("hui-card-visibility-editor")
export class HuiCardVisibilityEditor extends LitElement {
@@ -34,11 +34,10 @@ export class HuiCardVisibilityEditor extends LitElement {
render() {
const conditions = this.config.visibility ?? [];
return html`
<p class="intro">
${this.hass.localize(
`ui.panel.lovelace.editor.edit_card.visibility.explanation`
)}
</p>
<ha-visibility-status
.hass=${this.hass}
.conditions=${conditions}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@@ -62,10 +61,8 @@ export class HuiCardVisibilityEditor extends LitElement {
}
static styles = css`
.intro {
margin: 0;
color: var(--secondary-text-color);
margin-bottom: 8px;
ha-visibility-status {
margin-bottom: var(--ha-space-3);
}
`;
}
@@ -13,12 +13,14 @@ import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ConditionListenersController } from "../../../../common/controllers/condition-listeners-controller";
import { storage } from "../../../../common/decorators/storage";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { handleStructError } from "../../../../common/structs/handle-errors";
import "../../../../components/automation/ha-automation-row-event-chip";
import "../../../../components/automation/ha-automation-row-live-test";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
@@ -33,11 +35,11 @@ import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import { ICON_CONDITION } from "../../common/icon-condition";
import type {
AndCondition,
Condition,
LegacyCondition,
OrCondition,
AndCondition,
NotCondition,
OrCondition,
} from "../../common/validate-condition";
import {
checkConditionsMet,
@@ -103,6 +105,13 @@ export class HaCardConditionEditor extends LitElement {
@state() private _testingResult?: boolean;
@state() private _liveTestResult: {
state: "pass" | "fail" | "invalid" | "unknown";
message?: string;
} = { state: "unknown" };
private _listeners = new ConditionListenersController(this);
private get _editor() {
if (!this._condition) return undefined;
return customElements.get(
@@ -116,6 +125,14 @@ export class HaCardConditionEditor extends LitElement {
});
}
private _setupConditionListeners() {
this._listeners.setup(
this.condition ? [this.condition as Condition] : [],
this.hass,
() => this._evaluateLiveTest()
);
}
protected willUpdate(changedProperties: PropertyValues<this>): void {
if (changedProperties.has("condition")) {
this._condition = {
@@ -143,7 +160,61 @@ export class HaCardConditionEditor extends LitElement {
if (!this._uiAvailable && !this._yamlMode) {
this._yamlMode = true;
}
this._setupConditionListeners();
}
if (changedProperties.has("condition") || changedProperties.has("hass")) {
this._evaluateLiveTest();
}
}
protected updated(changedProperties: PropertyValues<this>): void {
if ((changedProperties as Map<string, unknown>).has("_entityContext")) {
this._evaluateLiveTest();
}
}
private _evaluateLiveTest() {
if (!this.condition || !this._condition) {
this._liveTestResult = { state: "unknown" };
return;
}
if (
isNoEntityCondition(this._condition.condition, this._noEntity) ||
containsNoEntityCondition(this._condition, this._noEntity)
) {
this._liveTestResult = {
state: "unknown",
message: this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.live_test_state.unknown"
),
};
return;
}
if (!validateConditionalConfig([this.condition])) {
this._liveTestResult = {
state: "invalid",
message: this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.live_test_state.invalid"
),
};
return;
}
const testContext =
this._entityContext?.mode === "current"
? { entity_id: this._entityContext.entityId }
: {};
const pass = checkConditionsMet([this.condition], this.hass, testContext);
this._liveTestResult = {
state: pass ? "pass" : "fail",
message: this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.live_test_state.${pass ? "pass" : "fail"}`
),
};
}
protected render() {
@@ -151,6 +222,10 @@ export class HaCardConditionEditor extends LitElement {
if (!condition) return nothing;
const hideLiveTest =
isNoEntityCondition(condition.condition, this._noEntity) ||
containsNoEntityCondition(condition, this._noEntity);
return html`
<div class="container">
<ha-expansion-panel left-chevron>
@@ -164,6 +239,33 @@ export class HaCardConditionEditor extends LitElement {
`ui.panel.lovelace.editor.condition-editor.condition.${condition.condition}.label`
) || condition.condition}
</h3>
<ha-automation-row-event-chip
.show=${this._testingResult !== undefined}
.variant=${this._testingResult ? "success" : "warning"}
slot="event"
class="event-chip"
aria-live="polite"
>
${this._testingResult
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_pass"
)
: this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_error"
)}
</ha-automation-row-event-chip>
${hideLiveTest
? nothing
: html`
<ha-automation-row-live-test
slot="icons"
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.live_test_state.${this._liveTestResult.state}`
)}
.message=${this._liveTestResult.message}
></ha-automation-row-live-test>
`}
<ha-dropdown
slot="icons"
@wa-select=${this._handleAction}
@@ -268,23 +370,6 @@ export class HaCardConditionEditor extends LitElement {
`}
</div>
</ha-expansion-panel>
<div
class="testing ${classMap({
active: this._testingResult !== undefined,
pass: this._testingResult === true,
error: this._testingResult === false,
})}"
>
${this._testingResult
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_pass"
)
: this._testingResult === false
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_error"
)
: nothing}
</div>
</div>
`;
}
@@ -419,41 +504,9 @@ export class HaCardConditionEditor extends LitElement {
opacity: 0.5;
pointer-events: none;
}
.testing {
.event-chip {
position: absolute;
top: 0px;
right: 0px;
left: 0px;
text-transform: uppercase;
font-size: var(--ha-font-size-m);
font-weight: var(--ha-font-weight-bold);
background-color: var(--divider-color, #e0e0e0);
color: var(--text-primary-color);
max-height: 0px;
overflow: hidden;
transition: max-height 0.3s;
text-align: center;
border-top-right-radius: calc(
var(--ha-card-border-radius, var(--ha-border-radius-lg)) - var(
--ha-card-border-width,
1px
)
);
border-top-left-radius: calc(
var(--ha-card-border-radius, var(--ha-border-radius-lg)) - var(
--ha-card-border-width,
1px
)
);
}
.testing.active {
max-height: 100px;
}
.testing.error {
background-color: var(--accent-color);
}
.testing.pass {
background-color: var(--success-color);
inset-inline-end: 40px;
}
.container {
position: relative;
@@ -0,0 +1,160 @@
import { consume } from "@lit/context";
import { mdiAlertCircle, mdiEye, mdiEyeOff } from "@mdi/js";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ConditionListenersController } from "../../../../common/controllers/condition-listeners-controller";
import "../../../../components/ha-svg-icon";
import { HaRowItem } from "../../../../components/item/ha-row-item";
import type { HomeAssistant } from "../../../../types";
import type {
Condition,
LegacyCondition,
} from "../../common/validate-condition";
import {
checkConditionsMet,
validateConditionalConfig,
} from "../../common/validate-condition";
import type { ConditionsEntityContext } from "./context";
import { conditionsEntityContext } from "./context";
type VisibilityState = "visible" | "hidden" | "invalid";
const STATE_ICONS: Record<VisibilityState, string> = {
visible: mdiEye,
hidden: mdiEyeOff,
invalid: mdiAlertCircle,
};
/**
* @element ha-visibility-status
* @extends {HaRowItem}
*
* @summary
* Row-style banner that surfaces the live visibility result for a set of
* lovelace conditions. Replaces the static explanation alert at the top of
* card / section / badge / conditional-card visibility editors.
*
* @attr {"visible"|"hidden"|"invalid"} state - Computed visibility state (reflected for styling).
*/
@customElement("ha-visibility-status")
export class HaVisibilityStatus extends HaRowItem {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false })
public conditions: (Condition | LegacyCondition)[] = [];
@state()
@consume({ context: conditionsEntityContext, subscribe: true })
private _entityContext?: ConditionsEntityContext;
@property({ reflect: true })
public state: VisibilityState = "visible";
private _listeners = new ConditionListenersController(this);
protected willUpdate(changedProperties: PropertyValues<this>): void {
super.willUpdate(changedProperties);
if (changedProperties.has("conditions") || changedProperties.has("hass")) {
this._listeners.setup(
(this.conditions ?? []) as Condition[],
this.hass,
() => this._evaluate()
);
}
if (
changedProperties.has("hass") ||
changedProperties.has("conditions") ||
(changedProperties as Map<string, unknown>).has("_entityContext")
) {
this._evaluate();
}
}
protected override _renderInner(): TemplateResult {
return html`
<div part="start" class="start">
<ha-svg-icon .path=${STATE_ICONS[this.state]}></ha-svg-icon>
</div>
<div part="content" class="content">
<div part="headline" class="headline">
${this.hass?.localize(
`ui.panel.lovelace.editor.condition-editor.visibility_status.${this.state}.headline`
)}
</div>
<div part="supporting-text" class="supporting">
${this.hass?.localize(
`ui.panel.lovelace.editor.condition-editor.visibility_status.${this.state}.supporting`
)}
</div>
</div>
`;
}
private _evaluate() {
const conditions = this.conditions ?? [];
let newState: VisibilityState;
if (conditions.length === 0) {
newState = "visible";
} else if (!validateConditionalConfig(conditions)) {
newState = "invalid";
} else {
const context =
this._entityContext?.mode === "current"
? { entity_id: this._entityContext.entityId }
: {};
newState = checkConditionsMet(conditions, this.hass, context)
? "visible"
: "hidden";
}
if (newState === this.state) {
return;
}
this.state = newState;
}
static styles: CSSResultGroup = [
HaRowItem.styles,
css`
:host {
display: block;
border-radius: var(--ha-border-radius-xl);
transition: background-color var(--ha-animation-duration-normal)
ease-in-out;
}
.base {
padding: var(--ha-space-4);
}
:host([state="visible"]) {
background-color: var(--ha-color-fill-success-quiet-resting);
--visibility-status-color: var(--ha-color-on-success-normal);
}
:host([state="hidden"]) {
background-color: var(--ha-color-fill-warning-quiet-resting);
--visibility-status-color: var(--ha-color-on-warning-normal);
}
:host([state="invalid"]) {
background-color: var(--ha-color-fill-danger-quiet-resting);
--visibility-status-color: var(--ha-color-on-danger-normal);
}
.start {
align-self: start;
}
.start ha-svg-icon {
color: var(--visibility-status-color);
--mdc-icon-size: 24px;
}
.headline {
font-weight: var(--ha-font-weight-medium);
white-space: normal;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-visibility-status": HaVisibilityStatus;
}
}
@@ -7,7 +7,6 @@ import { any, array, assert, assign, object, optional } from "superstruct";
import { storage } from "../../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-tab-group";
@@ -21,6 +20,7 @@ import "../card-editor/hui-card-element-editor";
import type { HuiCardElementEditor } from "../card-editor/hui-card-element-editor";
import "../card-editor/hui-card-picker";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
import "../hui-element-editor";
import type { ConfigChangedEvent } from "../hui-element-editor";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
@@ -147,11 +147,10 @@ export class HuiConditionalCardEditor
</div>
`
: html`
<ha-alert alert-type="info">
${this.hass!.localize(
"ui.panel.lovelace.editor.condition-editor.explanation"
)}
</ha-alert>
<ha-visibility-status
.hass=${this.hass}
.conditions=${this._config.conditions ?? []}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${this._config.conditions}
@@ -246,9 +245,9 @@ export class HuiConditionalCardEditor
width: 100%;
justify-content: center;
}
ha-alert {
display: block;
margin-top: 12px;
ha-visibility-status {
margin-top: var(--ha-space-3);
margin-bottom: var(--ha-space-3);
}
.card {
margin-top: 8px;
@@ -1,11 +1,11 @@
import { LitElement, html } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import type { LovelaceSectionRawConfig } from "../../../../data/lovelace/config/section";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
@customElement("hui-section-visibility-editor")
export class HuiDialogEditSection extends LitElement {
@@ -16,11 +16,10 @@ export class HuiDialogEditSection extends LitElement {
render() {
const conditions = this.config.visibility ?? [];
return html`
<ha-alert alert-type="info">
${this.hass.localize(
`ui.panel.lovelace.editor.edit_section.visibility.explanation`
)}
</ha-alert>
<ha-visibility-status
.hass=${this.hass}
.conditions=${conditions}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@@ -30,6 +29,12 @@ export class HuiDialogEditSection extends LitElement {
`;
}
static styles = css`
ha-visibility-status {
margin-bottom: var(--ha-space-3);
}
`;
private _valueChanged(ev: CustomEvent): void {
ev.stopPropagation();
const conditions = ev.detail.value as Condition[];
+21 -11
View File
@@ -8917,9 +8917,6 @@
"tab_visibility": "Visibility",
"tab_layout": "Layout",
"paste_condition": "Paste condition",
"visibility": {
"explanation": "The card will be shown when ALL conditions below are fulfilled. If no conditions are set, the card will always be shown."
},
"layout": {
"full_width": "Full width",
"full_width_helper": "Take up the full width of the section whatever its size",
@@ -8950,10 +8947,7 @@
"cut": "[%key:ui::panel::lovelace::editor::edit_card::cut%]",
"duplicate": "[%key:ui::panel::lovelace::editor::edit_card::duplicate%]",
"tab_config": "[%key:ui::panel::lovelace::editor::edit_card::tab_config%]",
"tab_visibility": "[%key:ui::panel::lovelace::editor::edit_card::tab_visibility%]",
"visibility": {
"explanation": "The badge will be shown when ALL conditions below are fulfilled. If no conditions are set, the badge will always be shown."
}
"tab_visibility": "[%key:ui::panel::lovelace::editor::edit_card::tab_visibility%]"
},
"suggest_badge": {
"header": "[%key:ui::panel::lovelace::editor::suggest_card::header%]",
@@ -9025,9 +9019,6 @@
"background_opacity": "Opacity",
"theme": "Theme",
"theme_helper": "Apply a specific theme to this section, overriding the view theme"
},
"visibility": {
"explanation": "The section will be shown when ALL conditions below are fulfilled. If no conditions are set, the section will always be shown."
}
},
"suggest_card": {
@@ -9069,11 +9060,30 @@
}
},
"condition-editor": {
"explanation": "The card will be shown when ALL conditions below are fulfilled.",
"add": "Add condition",
"test": "[%key:ui::panel::config::automation::editor::conditions::test%]",
"testing_pass": "[%key:ui::panel::config::automation::editor::conditions::testing_pass%]",
"testing_error": "[%key:ui::panel::config::automation::editor::conditions::testing_error%]",
"live_test_state": {
"pass": "[%key:ui::panel::config::automation::editor::conditions::testing_pass%]",
"fail": "[%key:ui::panel::config::automation::editor::conditions::testing_error%]",
"invalid": "[%key:ui::panel::config::automation::editor::conditions::live_test_state::invalid%]",
"unknown": "[%key:ui::panel::config::automation::editor::conditions::live_test_state::unknown%]"
},
"visibility_status": {
"visible": {
"headline": "Current visibility: Visible",
"supporting": "All visibility conditions are met"
},
"hidden": {
"headline": "Current visibility: Hidden",
"supporting": "Not all visibility conditions are met"
},
"invalid": {
"headline": "Visibility status unknown",
"supporting": "One or more conditions have an invalid configuration"
}
},
"invalid_config_title": "Invalid configuration",
"invalid_config_text": "The condition cannot be tested because the configuration is not valid.",
"condition": {