Compare commits

...

1 Commits

Author SHA1 Message Date
Wendelin
cdf53cfcdd Implement config entry subscription handling in automation and script editors 2025-12-22 11:24:32 +01:00
5 changed files with 94 additions and 32 deletions

View File

@@ -206,3 +206,29 @@ export const sortConfigEntries = (
);
return [primaryEntry, ...otherEntries];
};
export const handleConfigEntrySubscriptionMessages = (
currentEntries: ConfigEntry[],
messages: ConfigEntryUpdate[]
): ConfigEntry[] => {
let entries = [...currentEntries];
messages.forEach((message) => {
if (message.type === null || message.type === "added") {
entries.push(message.entry);
return;
}
if (message.type === "removed") {
entries = entries.filter(
(entry) => entry.entry_id !== message.entry.entry_id
);
return;
}
if (message.type === "updated") {
const newEntry = message.entry;
entries = entries.map((entry) =>
entry.entry_id === newEntry.entry_id ? newEntry : entry
);
}
});
return entries;
};

View File

@@ -3,6 +3,7 @@ import type { HassConfig } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../types";
import type { EntityRegistryEntry } from "./entity/entity_registry";
import type { LabelRegistryEntry } from "./label/label_registry";
import type { ConfigEntry } from "./config_entries";
export const connectionContext =
createContext<HomeAssistant["connection"]>("connection");
@@ -30,3 +31,5 @@ export const fullEntitiesContext =
export const floorsContext = createContext<HomeAssistant["floors"]>("floors");
export const labelsContext = createContext<LabelRegistryEntry[]>("labels");
export const configEntries = createContext<ConfigEntry[]>("configEntries");

View File

@@ -1,5 +1,6 @@
import { ContextProvider } from "@lit/context";
import { mdiContentSave, mdiHelpCircle } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { load } from "js-yaml";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -46,7 +47,13 @@ import {
isTrigger,
normalizeAutomationConfig,
} from "../../../data/automation";
import {
handleConfigEntrySubscriptionMessages,
subscribeConfigEntries,
} from "../../../data/config_entries";
import { configEntries } from "../../../data/context";
import { getActionType, type Action } from "../../../data/script";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
@@ -80,7 +87,7 @@ const automationConfigStruct = union([
export const SIDEBAR_DEFAULT_WIDTH = 500;
@customElement("manual-automation-editor")
export class HaManualAutomationEditor extends LitElement {
export class HaManualAutomationEditor extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
@@ -117,6 +124,11 @@ export class HaManualAutomationEditor extends LitElement {
HaAutomationAction | HaAutomationCondition
>;
private _configEntries = new ContextProvider(this, {
context: configEntries,
initialValue: [],
});
private _prevSidebarWidthPx?: number;
public connectedCallback() {
@@ -124,6 +136,19 @@ export class HaManualAutomationEditor extends LitElement {
window.addEventListener("paste", this._handlePaste);
}
public hassSubscribe(): Promise<UnsubscribeFunc>[] {
return [
subscribeConfigEntries(this.hass, (messages) => {
this._configEntries.setValue(
handleConfigEntrySubscriptionMessages(
this._configEntries.value,
messages
)
);
}),
];
}
public disconnectedCallback() {
window.removeEventListener("paste", this._handlePaste);
super.disconnectedCallback();

View File

@@ -1,17 +1,15 @@
import { consume } from "@lit/context";
import { mdiAlert, mdiFormatListBulleted, mdiShape } from "@mdi/js";
import type { HassServiceTarget } from "home-assistant-js-websocket";
import { LitElement, css, html, nothing, type TemplateResult } from "lit";
import { css, html, LitElement, type nothing, type TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import { ensureArray } from "../../../../common/array/ensure-array";
import { transform } from "../../../../common/decorators/transform";
import "../../../../components/ha-svg-icon";
import {
getConfigEntries,
type ConfigEntry,
} from "../../../../data/config_entries";
import type { ConfigEntry } from "../../../../data/config_entries";
import {
areasContext,
configEntries,
devicesContext,
floorsContext,
labelsContext,
@@ -55,6 +53,13 @@ export class HaAutomationRowTargets extends LitElement {
@consume({ context: labelsContext, subscribe: true })
private _labelRegistry!: LabelRegistryEntry[];
@state()
@consume({ context: configEntries, subscribe: true })
@transform<ConfigEntry[], Record<string, ConfigEntry>>({
transformer: function (value) {
return Object.fromEntries(value.map((entry) => [entry.entry_id, entry]));
},
})
private _configEntryLookup?: Record<string, ConfigEntry>;
protected render() {
@@ -149,13 +154,6 @@ export class HaAutomationRowTargets extends LitElement {
</div>`;
}
private async _loadConfigEntries() {
const configEntries = await getConfigEntries(this.hass);
this._configEntryLookup = Object.fromEntries(
configEntries.map((entry) => [entry.entry_id, entry])
);
}
private _renderTarget(
targetType: "floor" | "area" | "device" | "entity" | "label",
targetId: string
@@ -178,22 +176,6 @@ export class HaAutomationRowTargets extends LitElement {
);
}
if (targetType === "device" && !this._configEntryLookup) {
const loadConfigEntries = this._loadConfigEntries().then(() =>
this._renderTargetBadge(
getTargetIcon(
this.hass,
targetType,
targetId,
this._configEntryLookup!
),
getTargetText(this.hass, targetType, targetId)
)
);
return html`${until(loadConfigEntries, nothing)}`;
}
return this._renderTargetBadge(
getTargetIcon(
this.hass,

View File

@@ -1,4 +1,6 @@
import { ContextProvider } from "@lit/context";
import { mdiContentSave, mdiHelpCircle } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { load } from "js-yaml";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -35,6 +37,11 @@ import type {
ActionSidebarConfig,
SidebarConfig,
} from "../../../data/automation";
import {
handleConfigEntrySubscriptionMessages,
subscribeConfigEntries,
} from "../../../data/config_entries";
import { configEntries } from "../../../data/context";
import type {
Action,
Fields,
@@ -46,6 +53,7 @@ import {
MODES,
normalizeScriptConfig,
} from "../../../data/script";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
@@ -70,7 +78,7 @@ const scriptConfigStruct = object({
});
@customElement("manual-script-editor")
export class HaManualScriptEditor extends LitElement {
export class HaManualScriptEditor extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
@@ -108,6 +116,11 @@ export class HaManualScriptEditor extends LitElement {
HaAutomationAction | HaScriptFields
>;
private _configEntries = new ContextProvider(this, {
context: configEntries,
initialValue: [],
});
private _openFields = false;
private _prevSidebarWidthPx?: number;
@@ -129,6 +142,19 @@ export class HaManualScriptEditor extends LitElement {
});
}
public hassSubscribe(): Promise<UnsubscribeFunc>[] {
return [
subscribeConfigEntries(this.hass, (messages) => {
this._configEntries.setValue(
handleConfigEntrySubscriptionMessages(
this._configEntries.value,
messages
)
);
}),
];
}
protected updated(changedProps) {
if (this._openFields && changedProps.has("config")) {
this._openFields = false;