From 0b3e4eab23feefe48a2cc9ac87c64147a22b3ba8 Mon Sep 17 00:00:00 2001 From: Luke Mondy Date: Thu, 7 Aug 2025 23:40:27 +1000 Subject: [PATCH] Add 'Not' to lovelace visibility conditions (#26408) :Add 'Not' to lovelace visibility conditions --- src/panels/lovelace/common/icon-condition.ts | 2 + .../lovelace/common/validate-condition.ts | 21 ++++++- .../conditions/ha-card-conditions-editor.ts | 2 + .../conditions/types/ha-card-condition-not.ts | 62 +++++++++++++++++++ src/translations/en.json | 3 + 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/panels/lovelace/editor/conditions/types/ha-card-condition-not.ts diff --git a/src/panels/lovelace/common/icon-condition.ts b/src/panels/lovelace/common/icon-condition.ts index 12341d69c5..02aa15ac25 100644 --- a/src/panels/lovelace/common/icon-condition.ts +++ b/src/panels/lovelace/common/icon-condition.ts @@ -2,6 +2,7 @@ import { mdiAccount, mdiAmpersand, mdiGateOr, + mdiNotEqualVariant, mdiNumeric, mdiResponsive, mdiStateMachine, @@ -14,5 +15,6 @@ export const ICON_CONDITION: Record = { screen: mdiResponsive, user: mdiAccount, and: mdiAmpersand, + not: mdiNotEqualVariant, or: mdiGateOr, }; diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index c71ca4ea68..3c7ea7cfe9 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -11,7 +11,8 @@ export type Condition = | ScreenCondition | UserCondition | OrCondition - | AndCondition; + | AndCondition + | NotCondition; // Legacy conditional card condition export interface LegacyCondition { @@ -58,6 +59,11 @@ export interface AndCondition extends BaseCondition { conditions?: Condition[]; } +export interface NotCondition extends BaseCondition { + condition: "not"; + conditions?: Condition[]; +} + function getValueFromEntityId( hass: HomeAssistant, value: string @@ -149,6 +155,11 @@ function checkAndCondition(condition: AndCondition, hass: HomeAssistant) { return checkConditionsMet(condition.conditions, hass); } +function checkNotCondition(condition: NotCondition, hass: HomeAssistant) { + if (!condition.conditions) return true; + return !checkConditionsMet(condition.conditions, hass); +} + function checkOrCondition(condition: OrCondition, hass: HomeAssistant) { if (!condition.conditions) return true; return condition.conditions.some((c) => checkConditionsMet([c], hass)); @@ -175,6 +186,8 @@ export function checkConditionsMet( return checkStateNumericCondition(c, hass); case "and": return checkAndCondition(c, hass); + case "not": + return checkNotCondition(c, hass); case "or": return checkOrCondition(c, hass); default: @@ -247,6 +260,10 @@ function validateAndCondition(condition: AndCondition) { return condition.conditions != null; } +function validateNotCondition(condition: NotCondition) { + return condition.conditions != null; +} + function validateOrCondition(condition: OrCondition) { return condition.conditions != null; } @@ -276,6 +293,8 @@ export function validateConditionalConfig( return validateNumericStateCondition(c); case "and": return validateAndCondition(c); + case "not": + return validateNotCondition(c); case "or": return validateOrCondition(c); default: diff --git a/src/panels/lovelace/editor/conditions/ha-card-conditions-editor.ts b/src/panels/lovelace/editor/conditions/ha-card-conditions-editor.ts index e9480ade06..e1dfaa6fb7 100644 --- a/src/panels/lovelace/editor/conditions/ha-card-conditions-editor.ts +++ b/src/panels/lovelace/editor/conditions/ha-card-conditions-editor.ts @@ -18,6 +18,7 @@ import "./ha-card-condition-editor"; import type { HaCardConditionEditor } from "./ha-card-condition-editor"; import type { LovelaceConditionEditorConstructor } from "./types"; import "./types/ha-card-condition-and"; +import "./types/ha-card-condition-not"; import "./types/ha-card-condition-numeric_state"; import "./types/ha-card-condition-or"; import "./types/ha-card-condition-screen"; @@ -30,6 +31,7 @@ const UI_CONDITION = [ "screen", "user", "and", + "not", "or", ] as const satisfies readonly Condition["condition"][]; diff --git a/src/panels/lovelace/editor/conditions/types/ha-card-condition-not.ts b/src/panels/lovelace/editor/conditions/types/ha-card-condition-not.ts new file mode 100644 index 0000000000..78ed7d29de --- /dev/null +++ b/src/panels/lovelace/editor/conditions/types/ha-card-condition-not.ts @@ -0,0 +1,62 @@ +import { html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import { any, array, assert, literal, object, optional } from "superstruct"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import "../../../../../components/ha-form/ha-form"; +import type { HomeAssistant } from "../../../../../types"; +import type { + NotCondition, + Condition, + StateCondition, +} from "../../../common/validate-condition"; +import "../ha-card-conditions-editor"; + +const notConditionStruct = object({ + condition: literal("not"), + conditions: optional(array(any())), +}); + +@customElement("ha-card-condition-not") +export class HaCardConditionNot extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public condition!: NotCondition; + + @property({ type: Boolean }) public disabled = false; + + public static get defaultConfig(): NotCondition { + return { condition: "not", conditions: [] }; + } + + protected static validateUIConfig(condition: StateCondition) { + return assert(condition, notConditionStruct); + } + + protected render() { + return html` + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + ev.stopPropagation(); + const conditions = ev.detail.value as Condition[]; + const condition = { + ...this.condition, + conditions, + }; + fireEvent(this, "value-changed", { value: condition }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-card-condition-not": HaCardConditionNot; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index 15ca72cccf..a120aec39e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -7283,6 +7283,9 @@ "or": { "label": "Or" }, + "not": { + "label": "Not" + }, "and": { "label": "And" }