diff --git a/src/components/ha-selector/ha-selector-action.ts b/src/components/ha-selector/ha-selector-action.ts
index ce1ed97a9b..29c6b92804 100644
--- a/src/components/ha-selector/ha-selector-action.ts
+++ b/src/components/ha-selector/ha-selector-action.ts
@@ -1,7 +1,7 @@
import { consume, ContextProvider } from "@lit/context";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fullEntitiesContext } from "../../data/context";
import {
@@ -13,6 +13,7 @@ import { migrateAutomationAction } from "../../data/script";
import type { ActionSelector } from "../../data/selector";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import "../../panels/config/automation/action/ha-automation-action";
+import type HaAutomationAction from "../../panels/config/automation/action/ha-automation-action";
import type { HomeAssistant } from "../../types";
@customElement("ha-selector-action")
@@ -35,6 +36,9 @@ export class HaActionSelector extends SubscribeMixin(LitElement) {
@state() private _entitiesContext;
+ @query("ha-automation-action")
+ private _actionElement?: HaAutomationAction;
+
protected hassSubscribeRequiredHostProps = ["_entitiesContext"];
private _actions = memoizeOne((action: Action | undefined) => {
@@ -61,6 +65,14 @@ export class HaActionSelector extends SubscribeMixin(LitElement) {
];
}
+ public expandAll() {
+ this._actionElement?.expandAll();
+ }
+
+ public collapseAll() {
+ this._actionElement?.collapseAll();
+ }
+
protected render() {
return html`
${this.label ? html`` : nothing}
diff --git a/src/components/ha-selector/ha-selector-condition.ts b/src/components/ha-selector/ha-selector-condition.ts
index 5096be6144..9b0814114c 100644
--- a/src/components/ha-selector/ha-selector-condition.ts
+++ b/src/components/ha-selector/ha-selector-condition.ts
@@ -1,8 +1,9 @@
import { css, html, LitElement, nothing } from "lit";
-import { customElement, property } from "lit/decorators";
+import { customElement, property, query } from "lit/decorators";
import type { Condition } from "../../data/automation";
import type { ConditionSelector } from "../../data/selector";
import "../../panels/config/automation/condition/ha-automation-condition";
+import type HaAutomationCondition from "../../panels/config/automation/condition/ha-automation-condition";
import type { HomeAssistant } from "../../types";
@customElement("ha-selector-condition")
@@ -19,6 +20,9 @@ export class HaConditionSelector extends LitElement {
@property({ type: Boolean, reflect: true }) public disabled = false;
+ @query("ha-automation-condition")
+ private _conditionElement?: HaAutomationCondition;
+
protected render() {
return html`
${this.label ? html`` : nothing}
@@ -32,6 +36,14 @@ export class HaConditionSelector extends LitElement {
`;
}
+ public expandAll() {
+ this._conditionElement?.expandAll();
+ }
+
+ public collapseAll() {
+ this._conditionElement?.collapseAll();
+ }
+
static styles = css`
ha-automation-condition {
display: block;
diff --git a/src/data/action.ts b/src/data/action.ts
index 04a6e2e7e2..f8fe4aeb75 100644
--- a/src/data/action.ts
+++ b/src/data/action.ts
@@ -91,6 +91,15 @@ export const isService = (key: string | undefined): boolean | undefined =>
export const getService = (key: string): string =>
key.substring(SERVICE_PREFIX.length);
+export const COLLAPSIBLE_ACTION_ELEMENTS = [
+ "ha-automation-action-choose",
+ "ha-automation-action-condition",
+ "ha-automation-action-if",
+ "ha-automation-action-parallel",
+ "ha-automation-action-repeat",
+ "ha-automation-action-sequence",
+];
+
export const ACTION_BUILDING_BLOCKS = [
"choose",
"if",
diff --git a/src/data/condition.ts b/src/data/condition.ts
index 59e7828c00..8c44a3d001 100644
--- a/src/data/condition.ts
+++ b/src/data/condition.ts
@@ -52,3 +52,9 @@ export const CONDITION_GROUPS: AutomationElementGroup = {
} as const;
export const CONDITION_BUILDING_BLOCKS = ["and", "or", "not"];
+
+export const COLLAPSIBLE_CONDITION_ELEMENTS = [
+ "ha-automation-condition-and",
+ "ha-automation-condition-not",
+ "ha-automation-condition-or",
+];
diff --git a/src/panels/config/automation/action/ha-automation-action-editor.ts b/src/panels/config/automation/action/ha-automation-action-editor.ts
index e0aabf0e02..ee352f80a8 100644
--- a/src/panels/config/automation/action/ha-automation-action-editor.ts
+++ b/src/panels/config/automation/action/ha-automation-action-editor.ts
@@ -5,11 +5,15 @@ import { dynamicElement } from "../../../../common/dom/dynamic-element-directive
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
+import { COLLAPSIBLE_ACTION_ELEMENTS } from "../../../../data/action";
import { migrateAutomationAction, type Action } from "../../../../data/script";
import type { HomeAssistant } from "../../../../types";
import "../ha-automation-editor-warning";
import { editorStyles } from "../styles";
-import { getAutomationActionType } from "./ha-automation-action-row";
+import {
+ getAutomationActionType,
+ type ActionElement,
+} from "./ha-automation-action-row";
@customElement("ha-automation-action-editor")
export default class HaAutomationActionEditor extends LitElement {
@@ -34,6 +38,9 @@ export default class HaAutomationActionEditor extends LitElement {
@query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
+ @query(COLLAPSIBLE_ACTION_ELEMENTS.join(", "))
+ private _collapsibleElement?: ActionElement;
+
protected render() {
const yamlMode = this.yamlMode || !this.uiSupported;
const type = getAutomationActionType(this.action);
@@ -103,6 +110,14 @@ export default class HaAutomationActionEditor extends LitElement {
fireEvent(this, "value-changed", { value });
}
+ public expandAll() {
+ this._collapsibleElement?.expandAll?.();
+ }
+
+ public collapseAll() {
+ this._collapsibleElement?.collapseAll?.();
+ }
+
static styles = editorStyles;
}
diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts
index 48d9a7dc4d..abb3a541c3 100644
--- a/src/panels/config/automation/action/ha-automation-action-row.ts
+++ b/src/panels/config/automation/action/ha-automation-action-row.ts
@@ -109,6 +109,8 @@ export const getAutomationActionType = memoizeOne(
export interface ActionElement extends LitElement {
action: Action;
+ expandAll?: () => void;
+ collapseAll?: () => void;
}
export const handleChangeEvent = (element: ActionElement, ev: CustomEvent) => {
@@ -181,7 +183,7 @@ export default class HaAutomationActionRow extends LitElement {
@state() private _warnings?: string[];
@query("ha-automation-action-editor")
- private actionEditor?: HaAutomationActionEditor;
+ private _actionEditor?: HaAutomationActionEditor;
protected willUpdate(changedProperties: PropertyValues) {
if (changedProperties.has("yamlMode")) {
@@ -479,7 +481,7 @@ export default class HaAutomationActionRow extends LitElement {
this.openSidebar(value); // refresh sidebar
if (this._yamlMode && !this.optionsInSidebar) {
- this.actionEditor?.yamlEditor?.setValue(value);
+ this._actionEditor?.yamlEditor?.setValue(value);
}
};
@@ -582,7 +584,7 @@ export default class HaAutomationActionRow extends LitElement {
if (this._selected && this.optionsInSidebar) {
this.openSidebar(value); // refresh sidebar
} else if (this._yamlMode) {
- this.actionEditor?.yamlEditor?.setValue(value);
+ this._actionEditor?.yamlEditor?.setValue(value);
}
}
};
@@ -669,6 +671,7 @@ export default class HaAutomationActionRow extends LitElement {
yamlMode: this._yamlMode,
} satisfies ActionSidebarConfig);
this._selected = true;
+ this._collapsed = false;
if (this.narrow) {
this.scrollIntoView({
@@ -679,11 +682,39 @@ export default class HaAutomationActionRow extends LitElement {
}
public expand() {
+ if (this.optionsInSidebar) {
+ this._collapsed = false;
+ return;
+ }
+
this.updateComplete.then(() => {
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
});
}
+ public collapse() {
+ if (this.optionsInSidebar) {
+ this._collapsed = true;
+ return;
+ }
+
+ this.updateComplete.then(() => {
+ this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = false;
+ });
+ }
+
+ public expandAll() {
+ this.expand();
+
+ this._actionEditor?.expandAll();
+ }
+
+ public collapseAll() {
+ this.collapse();
+
+ this._actionEditor?.collapseAll();
+ }
+
private _uiSupported = memoizeOne(
(type: string) =>
customElements.get(`ha-automation-action-${type}`) !== undefined
diff --git a/src/panels/config/automation/action/ha-automation-action.ts b/src/panels/config/automation/action/ha-automation-action.ts
index b024e3b47f..0d7116ce93 100644
--- a/src/panels/config/automation/action/ha-automation-action.ts
+++ b/src/panels/config/automation/action/ha-automation-action.ts
@@ -2,7 +2,7 @@ import { mdiDrag, mdiPlus } from "@mdi/js";
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 { customElement, property, queryAll, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -55,6 +55,9 @@ export default class HaAutomationAction extends LitElement {
})
public _clipboard?: AutomationClipboard;
+ @queryAll("ha-automation-action-row")
+ private _actionRowElements?: HaAutomationActionRow[];
+
private _focusLastActionOnChange = false;
private _actionKeys = new WeakMap();
@@ -179,11 +182,14 @@ export default class HaAutomationAction extends LitElement {
}
public expandAll() {
- const rows = this.shadowRoot!.querySelectorAll(
- "ha-automation-action-row"
- )!;
- rows.forEach((row) => {
- row.expand();
+ this._actionRowElements?.forEach((row) => {
+ row.expandAll();
+ });
+ }
+
+ public collapseAll() {
+ this._actionRowElements?.forEach((row) => {
+ row.collapseAll();
});
}
diff --git a/src/panels/config/automation/action/types/ha-automation-action-choose.ts b/src/panels/config/automation/action/types/ha-automation-action-choose.ts
index e5cf642a84..1fb4dae95e 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-choose.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-choose.ts
@@ -1,5 +1,5 @@
import { type CSSResultGroup, LitElement, css, html } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement, property, query, state } from "lit/decorators";
import { ensureArray } from "../../../../../common/array/ensure-array";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-button";
@@ -7,7 +7,9 @@ import type { Action, ChooseAction, Option } from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import "../../option/ha-automation-option";
+import type HaAutomationOption from "../../option/ha-automation-option";
import "../ha-automation-action";
+import type HaAutomationAction from "../ha-automation-action";
import type { ActionElement } from "../ha-automation-action-row";
@customElement("ha-automation-action-choose")
@@ -24,6 +26,10 @@ export class HaChooseAction extends LitElement implements ActionElement {
@state() private _showDefault = false;
+ @query("ha-automation-option") private _optionElement?: HaAutomationOption;
+
+ @query("ha-automation-action") private _actionElement?: HaAutomationAction;
+
public static get defaultConfig(): ChooseAction {
return { choose: [{ conditions: [], sequence: [] }] };
}
@@ -104,6 +110,16 @@ export class HaChooseAction extends LitElement implements ActionElement {
fireEvent(this, "value-changed", { value: newValue });
}
+ public expandAll() {
+ this._optionElement?.expandAll();
+ this._actionElement?.expandAll();
+ }
+
+ public collapseAll() {
+ this._optionElement?.collapseAll();
+ this._actionElement?.collapseAll();
+ }
+
static get styles(): CSSResultGroup {
return [
haStyle,
diff --git a/src/panels/config/automation/action/types/ha-automation-action-condition.ts b/src/panels/config/automation/action/types/ha-automation-action-condition.ts
index 70525c8102..5e2946f5fb 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-condition.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-condition.ts
@@ -1,5 +1,5 @@
import { css, html, LitElement, nothing } from "lit";
-import { customElement, property } from "lit/decorators";
+import { customElement, property, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { stringCompare } from "../../../../../common/string/compare";
@@ -14,6 +14,7 @@ import {
} from "../../../../../data/condition";
import type { Entries, HomeAssistant } from "../../../../../types";
import "../../condition/ha-automation-condition-editor";
+import type HaAutomationConditionEditor from "../../condition/ha-automation-condition-editor";
import "../../condition/types/ha-automation-condition-and";
import "../../condition/types/ha-automation-condition-device";
import "../../condition/types/ha-automation-condition-not";
@@ -41,6 +42,9 @@ export class HaConditionAction extends LitElement implements ActionElement {
@property({ type: Boolean, attribute: "indent" }) public indent = false;
+ @query("ha-automation-condition-editor")
+ private _conditionEditor?: HaAutomationConditionEditor;
+
public static get defaultConfig(): Omit {
return { condition: "state" };
}
@@ -146,6 +150,14 @@ export class HaConditionAction extends LitElement implements ActionElement {
customElements.get(`ha-automation-condition-${type}`) !== undefined
);
+ public expandAll() {
+ this._conditionEditor?.expandAll();
+ }
+
+ public collapseAll() {
+ this._conditionEditor?.collapseAll();
+ }
+
static styles = css`
ha-select {
margin-bottom: 24px;
diff --git a/src/panels/config/automation/action/types/ha-automation-action-if.ts b/src/panels/config/automation/action/types/ha-automation-action-if.ts
index 666c50a898..0443540b0f 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-if.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-if.ts
@@ -1,13 +1,21 @@
import type { CSSResultGroup } from "lit";
import { css, html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import {
+ customElement,
+ property,
+ query,
+ queryAll,
+ state,
+} from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-textfield";
import type { Action, IfAction } from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import type { Condition } from "../../../../lovelace/common/validate-condition";
+import type HaAutomationCondition from "../../condition/ha-automation-condition";
import "../ha-automation-action";
+import type HaAutomationAction from "../ha-automation-action";
import type { ActionElement } from "../ha-automation-action-row";
@customElement("ha-automation-action-if")
@@ -24,6 +32,12 @@ export class HaIfAction extends LitElement implements ActionElement {
@state() private _showElse = false;
+ @query("ha-automation-condition")
+ private _conditionElement?: HaAutomationCondition;
+
+ @queryAll("ha-automation-action")
+ private _actionElements?: HaAutomationAction[];
+
public static get defaultConfig(): IfAction {
return {
if: [],
@@ -132,6 +146,16 @@ export class HaIfAction extends LitElement implements ActionElement {
fireEvent(this, "value-changed", { value: newValue });
}
+ public expandAll() {
+ this._conditionElement?.expandAll();
+ this._actionElements?.forEach((element) => element.expandAll?.());
+ }
+
+ public collapseAll() {
+ this._conditionElement?.collapseAll();
+ this._actionElements?.forEach((element) => element.collapseAll?.());
+ }
+
static get styles(): CSSResultGroup {
return [
haStyle,
diff --git a/src/panels/config/automation/action/types/ha-automation-action-parallel.ts b/src/panels/config/automation/action/types/ha-automation-action-parallel.ts
index 4b36e1d2cf..88a1fa15a1 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-parallel.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-parallel.ts
@@ -1,12 +1,13 @@
import type { CSSResultGroup } from "lit";
import { html, LitElement } from "lit";
-import { customElement, property } from "lit/decorators";
+import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-textfield";
import type { Action, ParallelAction } from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import "../ha-automation-action";
+import type HaAutomationAction from "../ha-automation-action";
import type { ActionElement } from "../ha-automation-action-row";
@customElement("ha-automation-action-parallel")
@@ -21,6 +22,9 @@ export class HaParallelAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public indent = false;
+ @query("ha-automation-action")
+ private _actionElement?: HaAutomationAction;
+
public static get defaultConfig(): ParallelAction {
return {
parallel: [],
@@ -53,6 +57,14 @@ export class HaParallelAction extends LitElement implements ActionElement {
});
}
+ public expandAll() {
+ this._actionElement?.expandAll();
+ }
+
+ public collapseAll() {
+ this._actionElement?.collapseAll();
+ }
+
static get styles(): CSSResultGroup {
return haStyle;
}
diff --git a/src/panels/config/automation/action/types/ha-automation-action-repeat.ts b/src/panels/config/automation/action/types/ha-automation-action-repeat.ts
index 15f492b920..0322ed6e88 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-repeat.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-repeat.ts
@@ -1,6 +1,6 @@
import type { CSSResultGroup } from "lit";
import { css, html, LitElement } from "lit";
-import { customElement, property } from "lit/decorators";
+import { customElement, property, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-textfield";
@@ -12,10 +12,14 @@ import type { ActionElement } from "../ha-automation-action-row";
import { isTemplate } from "../../../../../common/string/has-template";
import "../../../../../components/ha-form/ha-form";
+import type { HaForm } from "../../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../../components/ha-form/types";
+import type { HaSelector } from "../../../../../components/ha-selector/ha-selector";
+import type { HaActionSelector } from "../../../../../components/ha-selector/ha-selector-action";
+import type { HaConditionSelector } from "../../../../../components/ha-selector/ha-selector-condition";
const OPTIONS = ["count", "while", "until", "for_each"] as const;
type RepeatType = (typeof OPTIONS)[number];
@@ -37,6 +41,9 @@ export class HaRepeatAction extends LitElement implements ActionElement {
@property({ type: Boolean, attribute: "indent" }) public indent = false;
+ @query("ha-form")
+ private _formElement?: HaForm;
+
public static get defaultConfig(): RepeatAction {
return { repeat: { count: 2, sequence: [] } };
}
@@ -175,6 +182,41 @@ export class HaRepeatAction extends LitElement implements ActionElement {
];
}
+ private _getSelectorElements() {
+ if (this._formElement) {
+ const selectors =
+ this._formElement.shadowRoot?.querySelectorAll(
+ "ha-selector"
+ );
+
+ const selectorElements: (HaConditionSelector | HaActionSelector)[] = [];
+
+ selectors?.forEach((selector) => {
+ selectorElements.push(
+ ...Array.from(
+ selector.shadowRoot?.querySelectorAll<
+ HaConditionSelector | HaActionSelector
+ >("ha-selector-condition, ha-selector-action") || []
+ )
+ );
+ });
+ return selectorElements;
+ }
+ return [];
+ }
+
+ public expandAll() {
+ this._getSelectorElements().forEach((element) => {
+ element.expandAll?.();
+ });
+ }
+
+ public collapseAll() {
+ this._getSelectorElements().forEach((element) => {
+ element.collapseAll?.();
+ });
+ }
+
private _computeLabelCallback = (
schema: SchemaUnion>
): string => {
diff --git a/src/panels/config/automation/action/types/ha-automation-action-sequence.ts b/src/panels/config/automation/action/types/ha-automation-action-sequence.ts
index 50f126bd2f..9a8b93522d 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-sequence.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-sequence.ts
@@ -1,6 +1,6 @@
import type { CSSResultGroup } from "lit";
import { html, LitElement } from "lit";
-import { customElement, property } from "lit/decorators";
+import { query, customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-textfield";
import type { Action, SequenceAction } from "../../../../../data/script";
@@ -8,6 +8,7 @@ import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import "../ha-automation-action";
import type { ActionElement } from "../ha-automation-action-row";
+import type HaAutomationAction from "../ha-automation-action";
@customElement("ha-automation-action-sequence")
export class HaSequenceAction extends LitElement implements ActionElement {
@@ -21,6 +22,9 @@ export class HaSequenceAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public indent = false;
+ @query("ha-automation-action")
+ private _actionElement?: HaAutomationAction;
+
public static get defaultConfig(): SequenceAction {
return {
sequence: [],
@@ -53,6 +57,14 @@ export class HaSequenceAction extends LitElement implements ActionElement {
});
}
+ public expandAll() {
+ this._actionElement?.expandAll();
+ }
+
+ public collapseAll() {
+ this._actionElement?.collapseAll();
+ }
+
static get styles(): CSSResultGroup {
return haStyle;
}
diff --git a/src/panels/config/automation/condition/ha-automation-condition-editor.ts b/src/panels/config/automation/condition/ha-automation-condition-editor.ts
index da2660826d..88d89b8acc 100644
--- a/src/panels/config/automation/condition/ha-automation-condition-editor.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition-editor.ts
@@ -8,9 +8,11 @@ import "../../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import type { Condition } from "../../../../data/automation";
import { expandConditionWithShorthand } from "../../../../data/automation";
+import { COLLAPSIBLE_CONDITION_ELEMENTS } from "../../../../data/condition";
import type { HomeAssistant } from "../../../../types";
import "../ha-automation-editor-warning";
import { editorStyles } from "../styles";
+import type { ConditionElement } from "./ha-automation-condition-row";
@customElement("ha-automation-condition-editor")
export default class HaAutomationConditionEditor extends LitElement {
@@ -33,6 +35,9 @@ export default class HaAutomationConditionEditor extends LitElement {
@query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
+ @query(COLLAPSIBLE_CONDITION_ELEMENTS.join(", "))
+ private _collapsibleElement?: ConditionElement;
+
private _processedCondition = memoizeOne((condition) =>
expandConditionWithShorthand(condition)
);
@@ -108,6 +113,14 @@ export default class HaAutomationConditionEditor extends LitElement {
fireEvent(this, "value-changed", { value });
}
+ public expandAll() {
+ this._collapsibleElement?.expandAll?.();
+ }
+
+ public collapseAll() {
+ this._collapsibleElement?.collapseAll?.();
+ }
+
static styles = [
editorStyles,
css`
diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts
index 1156d3bbff..9560b284a3 100644
--- a/src/panels/config/automation/condition/ha-automation-condition-row.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts
@@ -70,6 +70,8 @@ import "./types/ha-automation-condition-zone";
export interface ConditionElement extends LitElement {
condition: Condition;
+ expandAll?: () => void;
+ collapseAll?: () => void;
}
export const handleChangeEvent = (
@@ -575,11 +577,32 @@ export default class HaAutomationConditionRow extends LitElement {
};
public expand() {
+ if (this.optionsInSidebar) {
+ this._collapsed = false;
+ return;
+ }
+
this.updateComplete.then(() => {
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
});
}
+ public collapse() {
+ this._collapsed = true;
+ }
+
+ public expandAll() {
+ this.expand();
+
+ this.conditionEditor?.expandAll();
+ }
+
+ public collapseAll() {
+ this.collapse();
+
+ this.conditionEditor?.collapseAll();
+ }
+
private _handleUiModeNotAvailable(ev: CustomEvent) {
this._warnings = handleStructError(this.hass, ev.detail).warnings;
if (!this._yamlMode) {
@@ -622,6 +645,7 @@ export default class HaAutomationConditionRow extends LitElement {
yamlMode: this._yamlMode,
} satisfies ConditionSidebarConfig);
this._selected = true;
+ this._collapsed = false;
if (this.narrow) {
this.scrollIntoView({
diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts
index 3604306e94..2ed3cdf401 100644
--- a/src/panels/config/automation/condition/ha-automation-condition.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition.ts
@@ -2,7 +2,7 @@ import { mdiDrag, mdiPlus } from "@mdi/js";
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 { customElement, property, queryAll, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -53,6 +53,9 @@ export default class HaAutomationCondition extends LitElement {
})
public _clipboard?: AutomationClipboard;
+ @queryAll("ha-automation-condition-row")
+ private _conditionRowElements?: HaAutomationConditionRow[];
+
private _focusLastConditionOnChange = false;
private _conditionKeys = new WeakMap();
@@ -123,11 +126,14 @@ export default class HaAutomationCondition extends LitElement {
}
public expandAll() {
- const rows = this.shadowRoot!.querySelectorAll(
- "ha-automation-condition-row"
- )!;
- rows.forEach((row) => {
- row.expand();
+ this._conditionRowElements?.forEach((row) => {
+ row.expandAll();
+ });
+ }
+
+ public collapseAll() {
+ this._conditionRowElements?.forEach((row) => {
+ row.collapseAll();
});
}
diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts b/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts
index 7cea32e31a..5b97eed512 100644
--- a/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts
+++ b/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts
@@ -1,9 +1,10 @@
import { html, LitElement } from "lit";
-import { customElement, property } from "lit/decorators";
+import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import type { LogicalCondition } from "../../../../../data/automation";
import type { HomeAssistant } from "../../../../../types";
import "../ha-automation-condition";
+import type HaAutomationCondition from "../ha-automation-condition";
import type { ConditionElement } from "../ha-automation-condition-row";
@customElement("ha-automation-condition-logical")
@@ -22,6 +23,9 @@ export abstract class HaLogicalCondition
@property({ type: Boolean, attribute: "sidebar" }) public optionsInSidebar =
false;
+ @query("ha-automation-condition")
+ private _conditionElement?: HaAutomationCondition;
+
protected render() {
return html`
+ ${!useBlueprint
+ ? html`
+
+
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.collapse_all"
+ )}
+
+
+
+
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.expand_all"
+ )}
+
+ `
+ : nothing}
+
("ha-automation-action, ha-automation-condition");
+ }
+
+ public expandAll() {
+ this._getCollapsableElements().forEach((element) => {
+ element.expandAll();
+ });
+ }
+
+ public collapseAll() {
+ this._getCollapsableElements().forEach((element) => {
+ element.collapseAll();
+ });
+ }
+
static get styles(): CSSResultGroup {
return [
saveFabStyles,
diff --git a/src/panels/config/automation/option/ha-automation-option-row.ts b/src/panels/config/automation/option/ha-automation-option-row.ts
index 050e4a8654..98e7e1017f 100644
--- a/src/panels/config/automation/option/ha-automation-option-row.ts
+++ b/src/panels/config/automation/option/ha-automation-option-row.ts
@@ -9,7 +9,7 @@ import {
} from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ensureArray } from "../../../../common/array/ensure-array";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -37,7 +37,9 @@ import {
} from "../../../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../../types";
import "../action/ha-automation-action";
+import type HaAutomationAction from "../action/ha-automation-action";
import "../condition/ha-automation-condition";
+import type HaAutomationCondition from "../condition/ha-automation-condition";
import { editorStyles, rowStyles } from "../styles";
@customElement("ha-automation-option-row")
@@ -69,6 +71,12 @@ export default class HaAutomationOptionRow extends LitElement {
@consume({ context: fullEntitiesContext, subscribe: true })
_entityReg!: EntityRegistryEntry[];
+ @query("ha-automation-condition")
+ private _conditionElement?: HaAutomationCondition;
+
+ @query("ha-automation-action")
+ private _actionElement?: HaAutomationAction;
+
private _expandedChanged(ev) {
if (ev.currentTarget.id !== "option") {
return;
@@ -355,6 +363,7 @@ export default class HaAutomationOptionRow extends LitElement {
delete: this._removeOption,
} satisfies OptionSidebarConfig);
this._selected = true;
+ this._collapsed = false;
if (this.narrow) {
this.scrollIntoView({
@@ -365,11 +374,34 @@ export default class HaAutomationOptionRow extends LitElement {
}
public expand() {
+ if (this.optionsInSidebar) {
+ this._collapsed = false;
+ return;
+ }
+
this.updateComplete.then(() => {
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
});
}
+ public collapse() {
+ this._collapsed = true;
+ }
+
+ public expandAll() {
+ this.expand();
+
+ this._conditionElement?.expandAll();
+ this._actionElement?.expandAll();
+ }
+
+ public collapseAll() {
+ this.collapse();
+
+ this._conditionElement?.collapseAll();
+ this._actionElement?.collapseAll();
+ }
+
private _toggleCollapse() {
this._collapsed = !this._collapsed;
}
diff --git a/src/panels/config/automation/option/ha-automation-option.ts b/src/panels/config/automation/option/ha-automation-option.ts
index 4d74ce7af7..38e7a96ac1 100644
--- a/src/panels/config/automation/option/ha-automation-option.ts
+++ b/src/panels/config/automation/option/ha-automation-option.ts
@@ -2,7 +2,7 @@ import { mdiDrag, mdiPlus } from "@mdi/js";
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 { customElement, property, queryAll, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -41,6 +41,9 @@ export default class HaAutomationOption extends LitElement {
})
public _clipboard?: AutomationClipboard;
+ @queryAll("ha-automation-option-row")
+ private _optionRowElements?: HaAutomationOptionRow[];
+
private _focusLastOptionOnChange = false;
private _optionsKeys = new WeakMap
+ ${!useBlueprint
+ ? html`
+
+
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.collapse_all"
+ )}
+
+
+
+
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.expand_all"
+ )}
+
+ `
+ : nothing}
+
@@ -141,10 +145,40 @@ export default class HaScriptFieldRow extends LitElement {
this._collapsed = !this._collapsed;
}
+ public expand() {
+ this._collapsed = false;
+ }
+
+ public collapse() {
+ this._collapsed = true;
+ }
+
+ public expandSelectorRow() {
+ this._selectorRowCollapsed = false;
+ }
+
+ public collapseSelectorRow() {
+ this._selectorRowCollapsed = true;
+ }
+
private _toggleSelectorRowCollapse() {
this._selectorRowCollapsed = !this._selectorRowCollapsed;
}
+ public expandAll() {
+ this.expand();
+ this.expandSelectorRow();
+
+ this._selectorEditor?.expandAll();
+ }
+
+ public collapseAll() {
+ this.collapse();
+ this.collapseSelectorRow();
+
+ this._selectorEditor?.collapseAll();
+ }
+
private _toggleSidebar(ev: Event) {
ev?.stopPropagation();
@@ -155,6 +189,7 @@ export default class HaScriptFieldRow extends LitElement {
}
this._selected = true;
+ this._collapsed = false;
this.openSidebar();
}
@@ -168,6 +203,7 @@ export default class HaScriptFieldRow extends LitElement {
}
this._selectorRowSelected = true;
+ this._selectorRowCollapsed = false;
this.openSidebar(true);
}
diff --git a/src/panels/config/script/ha-script-field-selector-editor.ts b/src/panels/config/script/ha-script-field-selector-editor.ts
index c391de7372..d29ee9829a 100644
--- a/src/panels/config/script/ha-script-field-selector-editor.ts
+++ b/src/panels/config/script/ha-script-field-selector-editor.ts
@@ -1,12 +1,16 @@
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
-import { customElement, property, state } from "lit/decorators";
+import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import type { LocalizeKeys } from "../../../common/translations/localize";
import "../../../components/ha-alert";
import "../../../components/ha-form/ha-form";
+import type { HaForm } from "../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../components/ha-form/types";
+import type { HaSelector } from "../../../components/ha-selector/ha-selector";
+import type { HaActionSelector } from "../../../components/ha-selector/ha-selector-action";
+import type { HaConditionSelector } from "../../../components/ha-selector/ha-selector-condition";
import "../../../components/ha-yaml-editor";
import type { Field } from "../../../data/script";
import { SELECTOR_SELECTOR_BUILDING_BLOCKS } from "../../../data/selector/selector_selector";
@@ -33,6 +37,9 @@ export default class HaScriptFieldSelectorEditor extends LitElement {
@state() private _yamlError?: undefined | "yaml_error" | "key_not_unique";
+ @query("ha-form")
+ private _formElement?: HaForm;
+
private _schema = memoizeOne(
(selector: any) =>
[
@@ -138,6 +145,41 @@ export default class HaScriptFieldSelectorEditor extends LitElement {
this.hass.localize(`ui.panel.config.script.editor.field.${error}` as any) ||
error;
+ private _getSelectorElements() {
+ if (this._formElement) {
+ const selectors =
+ this._formElement.shadowRoot?.querySelectorAll(
+ "ha-selector"
+ );
+
+ const selectorElements: (HaConditionSelector | HaActionSelector)[] = [];
+
+ selectors?.forEach((selector) => {
+ selectorElements.push(
+ ...Array.from(
+ selector.shadowRoot?.querySelectorAll<
+ HaConditionSelector | HaActionSelector
+ >("ha-selector-condition, ha-selector-action") || []
+ )
+ );
+ });
+ return selectorElements;
+ }
+ return [];
+ }
+
+ public expandAll() {
+ this._getSelectorElements().forEach((element) => {
+ element.expandAll?.();
+ });
+ }
+
+ public collapseAll() {
+ this._getSelectorElements().forEach((element) => {
+ element.collapseAll?.();
+ });
+ }
+
static get styles(): CSSResultGroup {
return [
haStyle,
diff --git a/src/panels/config/script/ha-script-fields.ts b/src/panels/config/script/ha-script-fields.ts
index 0f3a94ddf6..3ace088fa6 100644
--- a/src/panels/config/script/ha-script-fields.ts
+++ b/src/panels/config/script/ha-script-fields.ts
@@ -1,7 +1,7 @@
import { mdiPlus } from "@mdi/js";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
-import { customElement, property } from "lit/decorators";
+import { customElement, property, queryAll } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import "../../../components/ha-button-menu";
@@ -23,6 +23,9 @@ export default class HaScriptFields extends LitElement {
@property({ type: Boolean }) public narrow = false;
+ @queryAll("ha-script-field-row")
+ private _fieldRowElements?: HaScriptFieldRow[];
+
private _focusLastActionOnChange = false;
protected render() {
@@ -48,12 +51,7 @@ export default class HaScriptFields extends LitElement {
)}
`
: nothing}
-
+
${this.hass.localize("ui.panel.config.script.editor.field.add_field")}
@@ -144,6 +142,18 @@ export default class HaScriptFields extends LitElement {
return key;
}
+ public expandAll() {
+ this._fieldRowElements?.forEach((row) => {
+ row.expandAll();
+ });
+ }
+
+ public collapseAll() {
+ this._fieldRowElements?.forEach((row) => {
+ row.collapseAll();
+ });
+ }
+
static styles = css`
ha-script-field-row {
display: block;
diff --git a/src/panels/config/script/manual-script-editor.ts b/src/panels/config/script/manual-script-editor.ts
index 018c695465..52df2fd0c8 100644
--- a/src/panels/config/script/manual-script-editor.ts
+++ b/src/panels/config/script/manual-script-editor.ts
@@ -31,6 +31,7 @@ import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import "../automation/action/ha-automation-action";
+import type HaAutomationAction from "../automation/action/ha-automation-action";
import "../automation/ha-automation-sidebar";
import { showPasteReplaceDialog } from "../automation/paste-replace-dialog/show-dialog-paste-replace";
import { saveFabStyles } from "../automation/styles";
@@ -460,6 +461,24 @@ export class HaManualScriptEditor extends LitElement {
fireEvent(this, "save-script");
}
+ private _getCollapsableElements() {
+ return this.shadowRoot!.querySelectorAll<
+ HaAutomationAction | HaScriptFields
+ >("ha-automation-action, ha-script-fields");
+ }
+
+ public expandAll() {
+ this._getCollapsableElements().forEach((element) => {
+ element.expandAll();
+ });
+ }
+
+ public collapseAll() {
+ this._getCollapsableElements().forEach((element) => {
+ element.collapseAll();
+ });
+ }
+
static get styles(): CSSResultGroup {
return [
saveFabStyles,
diff --git a/src/translations/en.json b/src/translations/en.json
index c37af3ae7d..1517434c3b 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -3792,6 +3792,8 @@
"automation_settings": "Automation settings",
"move_up": "Move up",
"move_down": "Move down",
+ "collapse_all": "Collapse all",
+ "expand_all": "Expand all",
"description": {
"label": "Description",
"placeholder": "Optional description",