From ecc8ecfd7ee5356feb3cb97ba2781b28d12e912f Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sun, 1 Mar 2026 20:39:03 -0800 Subject: [PATCH] fix: Change the `not` condition to exactly match the HA `not` condition (#2387) BREAKING CHANGE: Changes the behavior of the `not` condition when there is more than one condition present. Users who desire the previous behavior should wrap their conditions in an `and` first. - Closes: #2383 --- docs/configuration/conditions.md | 19 +++++++++++-------- src/conditions/conditions-manager.ts | 4 ++-- tests/conditions/conditions-manager.test.ts | 8 ++++++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/docs/configuration/conditions.md b/docs/configuration/conditions.md index 43732541..668140e9 100644 --- a/docs/configuration/conditions.md +++ b/docs/configuration/conditions.md @@ -202,7 +202,10 @@ match. ## `not` -Evaluates to `true` if _all_ embedded conditions evaluate to `false`. +Evaluates to `true` if every embedded condition is `false`. At least one +condition is required. + +> [!IMPORTANT] > `not` is a **NOR** operation, not a **NAND**. If _any_ sub-condition is `true`, the `not` condition evaluates to `false` — even if other sub-conditions are `false`. To pass, _all_ sub-conditions must be `false`. This behavior matches the [Home Assistant equivalent](https://www.home-assistant.io/docs/scripts/conditions/#not-condition). ```yaml conditions: @@ -213,7 +216,7 @@ conditions: | Parameter | Description | | ------------ | --------------------------------------------------------------------------------------------------------------- | | `condition` | Must be `not`. | -| `conditions` | A list of other conditions _all_ of which must evaluate `false` in order for this condition to evaluate `true`. | +| `conditions` | A list of other conditions _none_ of which must evaluate `true` in order for this condition to evaluate `true`. | ## `numeric_state` @@ -244,7 +247,7 @@ conditions: ## `screen` -Matches based on [media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries). +Matches based on [media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Media_queries/Using). ```yaml conditions: @@ -252,10 +255,10 @@ conditions: # [...] ``` -| Parameter | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `condition` | Must be `screen`. | -| `media_query` | Any valid [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries) string. Media queries must start and end with parentheses. This may be used to alter card configuration based on device/media properties (e.g. viewport width, orientation). Please note that `width` and `height` refer to the entire viewport not just the card. | +| Parameter | Description | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `condition` | Must be `screen`. | +| `media_query` | Any valid [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Media_queries/Using) string. Media queries must start and end with parentheses. This may be used to alter card configuration based on device/media properties (e.g. viewport width, orientation). Please note that `width` and `height` refer to the entire viewport not just the card. | See the [screen conditions examples](../examples.md?id=screen-conditions). @@ -334,7 +337,7 @@ conditions: ## `user_agent` -Matches based on the [User-Agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent). +Matches based on the [User-Agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/User-Agent). ```yaml conditions: diff --git a/src/conditions/conditions-manager.ts b/src/conditions/conditions-manager.ts index 85d94d23..088ea624 100644 --- a/src/conditions/conditions-manager.ts +++ b/src/conditions/conditions-manager.ts @@ -331,13 +331,13 @@ export class ConditionsManager implements ConditionsManagerReadonlyInterface { return { result: true, triggerData }; } case 'not': { - // "Not" is just an inversed `and`. There is no trigger data for "not + // "Not" is an inverted `or` (NOR). There is no trigger data for "not // triggering". return { result: !this._evaluateCondition( { ...condition, - condition: 'and', + condition: 'or', }, newState, oldState, diff --git a/tests/conditions/conditions-manager.test.ts b/tests/conditions/conditions-manager.test.ts index 265f9e90..e5cd6d27 100644 --- a/tests/conditions/conditions-manager.test.ts +++ b/tests/conditions/conditions-manager.test.ts @@ -1317,17 +1317,21 @@ describe('ConditionsManager', () => { stateManager, ); + // Neither sub-condition is true initially, so `not` passes. expect(manager.getEvaluation().result).toBeTruthy(); + // fullscreen becomes true — any sub-condition being true means `not` fails. stateManager.setState({ fullscreen: true }); - expect(manager.getEvaluation().result).toBeTruthy(); + expect(manager.getEvaluation().result).toBeFalsy(); stateManager.setState({ expand: true }); expect(manager.getEvaluation().result).toBeFalsy(); + // fullscreen reverts, but expand is still true, so `not` still fails. stateManager.setState({ fullscreen: false }); - expect(manager.getEvaluation().result).toBeTruthy(); + expect(manager.getEvaluation().result).toBeFalsy(); + // Both sub-conditions are false again, so `not` passes. stateManager.setState({ expand: false }); expect(manager.getEvaluation().result).toBeTruthy();