Add 'Not' to lovelace visibility conditions (#26408)

:Add 'Not' to lovelace visibility conditions
This commit is contained in:
Luke Mondy 2025-08-07 23:40:27 +10:00 committed by GitHub
parent 39d14c943c
commit 0b3e4eab23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 89 additions and 1 deletions

View File

@ -2,6 +2,7 @@ import {
mdiAccount, mdiAccount,
mdiAmpersand, mdiAmpersand,
mdiGateOr, mdiGateOr,
mdiNotEqualVariant,
mdiNumeric, mdiNumeric,
mdiResponsive, mdiResponsive,
mdiStateMachine, mdiStateMachine,
@ -14,5 +15,6 @@ export const ICON_CONDITION: Record<Condition["condition"], string> = {
screen: mdiResponsive, screen: mdiResponsive,
user: mdiAccount, user: mdiAccount,
and: mdiAmpersand, and: mdiAmpersand,
not: mdiNotEqualVariant,
or: mdiGateOr, or: mdiGateOr,
}; };

View File

@ -11,7 +11,8 @@ export type Condition =
| ScreenCondition | ScreenCondition
| UserCondition | UserCondition
| OrCondition | OrCondition
| AndCondition; | AndCondition
| NotCondition;
// Legacy conditional card condition // Legacy conditional card condition
export interface LegacyCondition { export interface LegacyCondition {
@ -58,6 +59,11 @@ export interface AndCondition extends BaseCondition {
conditions?: Condition[]; conditions?: Condition[];
} }
export interface NotCondition extends BaseCondition {
condition: "not";
conditions?: Condition[];
}
function getValueFromEntityId( function getValueFromEntityId(
hass: HomeAssistant, hass: HomeAssistant,
value: string value: string
@ -149,6 +155,11 @@ function checkAndCondition(condition: AndCondition, hass: HomeAssistant) {
return checkConditionsMet(condition.conditions, hass); 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) { function checkOrCondition(condition: OrCondition, hass: HomeAssistant) {
if (!condition.conditions) return true; if (!condition.conditions) return true;
return condition.conditions.some((c) => checkConditionsMet([c], hass)); return condition.conditions.some((c) => checkConditionsMet([c], hass));
@ -175,6 +186,8 @@ export function checkConditionsMet(
return checkStateNumericCondition(c, hass); return checkStateNumericCondition(c, hass);
case "and": case "and":
return checkAndCondition(c, hass); return checkAndCondition(c, hass);
case "not":
return checkNotCondition(c, hass);
case "or": case "or":
return checkOrCondition(c, hass); return checkOrCondition(c, hass);
default: default:
@ -247,6 +260,10 @@ function validateAndCondition(condition: AndCondition) {
return condition.conditions != null; return condition.conditions != null;
} }
function validateNotCondition(condition: NotCondition) {
return condition.conditions != null;
}
function validateOrCondition(condition: OrCondition) { function validateOrCondition(condition: OrCondition) {
return condition.conditions != null; return condition.conditions != null;
} }
@ -276,6 +293,8 @@ export function validateConditionalConfig(
return validateNumericStateCondition(c); return validateNumericStateCondition(c);
case "and": case "and":
return validateAndCondition(c); return validateAndCondition(c);
case "not":
return validateNotCondition(c);
case "or": case "or":
return validateOrCondition(c); return validateOrCondition(c);
default: default:

View File

@ -18,6 +18,7 @@ import "./ha-card-condition-editor";
import type { HaCardConditionEditor } from "./ha-card-condition-editor"; import type { HaCardConditionEditor } from "./ha-card-condition-editor";
import type { LovelaceConditionEditorConstructor } from "./types"; import type { LovelaceConditionEditorConstructor } from "./types";
import "./types/ha-card-condition-and"; import "./types/ha-card-condition-and";
import "./types/ha-card-condition-not";
import "./types/ha-card-condition-numeric_state"; import "./types/ha-card-condition-numeric_state";
import "./types/ha-card-condition-or"; import "./types/ha-card-condition-or";
import "./types/ha-card-condition-screen"; import "./types/ha-card-condition-screen";
@ -30,6 +31,7 @@ const UI_CONDITION = [
"screen", "screen",
"user", "user",
"and", "and",
"not",
"or", "or",
] as const satisfies readonly Condition["condition"][]; ] as const satisfies readonly Condition["condition"][];

View File

@ -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`
<ha-card-conditions-editor
nested
.hass=${this.hass}
.conditions=${this.condition.conditions}
@value-changed=${this._valueChanged}
>
</ha-card-conditions-editor>
`;
}
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;
}
}

View File

@ -7283,6 +7283,9 @@
"or": { "or": {
"label": "Or" "label": "Or"
}, },
"not": {
"label": "Not"
},
"and": { "and": {
"label": "And" "label": "And"
} }