Compare commits

...

1 Commits

Author SHA1 Message Date
Bram Kragten 8d7ce043d9 Hide "for" on state condition when matching an attribute
The legacy state condition measures the `for` duration against
`last_changed`, which only updates on state changes, not attribute
changes. So `for` together with `attribute` is unreliable and core is
moving to reject it. Omit the `for` field when an attribute is selected,
drop any lingering duration value, and explain why via a helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:31:06 +02:00
2 changed files with 56 additions and 27 deletions
@@ -1,6 +1,7 @@
import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import {
array,
assert,
@@ -34,32 +35,40 @@ const stateConditionStruct = object({
enabled: optional(boolean()),
});
const SCHEMA = [
{ name: "entity_id", required: true, selector: { entity: {} } },
{
name: "attribute",
selector: {
attribute: {
hide_attributes: STATE_CONDITION_HIDDEN_ATTRIBUTES,
const SCHEMA = memoizeOne(
(hasAttribute: boolean) =>
[
{ name: "entity_id", required: true, selector: { entity: {} } },
{
name: "attribute",
selector: {
attribute: {
hide_attributes: STATE_CONDITION_HIDDEN_ATTRIBUTES,
},
},
context: {
filter_entity: "entity_id",
},
},
},
context: {
filter_entity: "entity_id",
},
},
{
name: "state",
required: true,
selector: {
state: { multiple: true },
},
context: {
filter_entity: "entity_id",
filter_attribute: "attribute",
},
},
{ name: "for", selector: { duration: {} } },
] as const;
{
name: "state",
required: true,
selector: {
state: { multiple: true },
},
context: {
filter_entity: "entity_id",
filter_attribute: "attribute",
},
},
// `for` is not supported together with `attribute`: the legacy state
// condition measures the duration against `last_changed`, which only
// updates on state changes, not attribute changes.
...(hasAttribute
? ([] as const)
: ([{ name: "for", selector: { duration: {} } }] as const)),
] as const
);
@customElement("ha-automation-condition-state")
export class HaStateCondition extends LitElement implements ConditionElement {
@@ -86,6 +95,7 @@ export class HaStateCondition extends LitElement implements ConditionElement {
}
protected render() {
const hasAttribute = !!this.condition.attribute;
const trgFor = createDurationData(this.condition.for);
const data = {
...this.condition,
@@ -97,10 +107,11 @@ export class HaStateCondition extends LitElement implements ConditionElement {
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${SCHEMA}
.schema=${SCHEMA(hasAttribute)}
.disabled=${this.disabled}
@value-changed=${this._valueChanged}
.computeLabel=${this._computeLabelCallback}
.computeHelper=${this._computeHelperCallback}
></ha-form>
`;
}
@@ -115,6 +126,12 @@ export class HaStateCondition extends LitElement implements ConditionElement {
: {}
);
// `for` is not supported together with `attribute` for the legacy state
// condition, so drop any lingering duration when an attribute is set.
if (newCondition.attribute) {
delete newCondition.for;
}
// Ensure `state` stays an array for multi-select. If absent, set to []
if (newCondition.state === undefined || newCondition.state === "") {
newCondition.state = [];
@@ -124,7 +141,7 @@ export class HaStateCondition extends LitElement implements ConditionElement {
}
private _computeLabelCallback = (
schema: SchemaUnion<typeof SCHEMA>
schema: SchemaUnion<ReturnType<typeof SCHEMA>>
): string => {
switch (schema.name) {
case "entity_id":
@@ -144,6 +161,17 @@ export class HaStateCondition extends LitElement implements ConditionElement {
}
};
private _computeHelperCallback = (
schema: SchemaUnion<ReturnType<typeof SCHEMA>>
): string | undefined => {
if (schema.name === "attribute" && this.condition.attribute) {
return this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.state.attribute_no_for"
);
}
return undefined;
};
static styles = css`
:host {
display: block;
+1
View File
@@ -5575,6 +5575,7 @@
"state": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
"state": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
"attribute_no_for": "A duration isn't available when matching on an attribute.",
"description": {
"picker": "Tests if an entity (or attribute) is in a specific state.",
"no_entity": "If state confirmed",