Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
3f8a333
Initial implementation of blueprint editor
jpitlor Mar 30, 2025
bd1a339
Some bug fixes
jpitlor Apr 10, 2025
4f279ff
Remove unneeded changes
jpitlor Apr 10, 2025
2559877
Add blueprint input sections
jpitlor May 1, 2025
bc0aed3
Remove remaining errors
jpitlor May 1, 2025
cd1e691
Remove remaining errors
jpitlor May 1, 2025
9ec09f4
Remove comments and TODOs
jpitlor May 1, 2025
1a08110
Merge `dev`
jpitlor May 1, 2025
77a5749
Remove unused imports
jpitlor May 2, 2025
e2e9463
Remove invalid selector types
jpitlor May 28, 2025
68efa04
Fix incorrect redirect after saving blueprint bug
jpitlor May 28, 2025
9b52197
Add TODOs for inputs in YAML
jpitlor May 28, 2025
24d8c0e
Add support for arbitrary inputs in YAML editors for blueprints
jpitlor May 29, 2025
43ec029
Remove unused translations for blueprints
jpitlor May 29, 2025
9abcd09
Fix YAML related error
jpitlor May 29, 2025
2f5bd91
Fix Save Blueprint API call
jpitlor May 29, 2025
8cd1a3d
Merge branch 'dev' into blueprint-editor
jpitlor May 29, 2025
c1a2872
Utilize selector selector
jpitlor May 29, 2025
2caf3e7
Merge remote-tracking branch 'origin/blueprint-editor' into blueprint…
jpitlor May 29, 2025
4c2e56a
Merge branch 'dev' into blueprint-editor
jpitlor Jun 6, 2025
e297c96
Make strings sentence case
jpitlor Jun 6, 2025
6a7f1ed
Merge branch 'dev' into blueprint-editor
jpitlor Jun 15, 2025
b4f24e2
Merge branch 'dev' into blueprint-editor
jpitlor Jun 20, 2025
493e1ba
Fix lint errors
jpitlor Jun 20, 2025
9416a69
Update src/translations/en.json
jpitlor Jun 20, 2025
22ecfe2
Merge branch 'dev' into blueprint-editor
jpitlor Jun 20, 2025
9726405
Fix lint errors
jpitlor Jun 21, 2025
03f8efc
Merge branch 'dev' into blueprint-editor
jpitlor Jun 21, 2025
9ed16b1
Move blueprint editor to new developer tools tab
jpitlor Jul 11, 2025
ea04c47
Move add metadata to the editor
jpitlor Jul 13, 2025
69b9191
Merge branch 'refs/heads/dev' into blueprint-editor
jpitlor Sep 12, 2025
81f554d
Made some initial progress on moving the blueprint editor to develope…
jpitlor Sep 12, 2025
ed5e831
Merge branch 'dev' into blueprint-editor
jpitlor Sep 12, 2025
37a5cbb
More progress on Blueprint Editor
jpitlor Sep 16, 2025
a45f954
Merge branch 'refs/heads/dev' into blueprint-editor
jpitlor Sep 27, 2025
2b4eaa8
Finish moving blueprint editor to developer tools
jpitlor Oct 9, 2025
c7f0d47
Merge branch 'dev' into blueprint-editor
jpitlor Oct 9, 2025
77592a2
Remove old changes that are no longer needed
jpitlor Oct 14, 2025
7eb647d
Fix bug where blueprint never loads
jpitlor Oct 18, 2025
35ea7fc
Merge branch 'dev' into blueprint-editor
jpitlor Oct 18, 2025
6062838
Merge branch 'dev' into blueprint-editor
jpitlor Oct 22, 2025
fd7147b
Fix lint error
jpitlor Oct 24, 2025
3756a8c
Merge remote-tracking branch 'origin/blueprint-editor' into blueprint…
jpitlor Oct 24, 2025
ce7f25d
Merge branch 'dev' into blueprint-editor
jpitlor Oct 24, 2025
81ff174
Merge branch 'dev' into blueprint-editor
jpitlor Nov 5, 2025
0bf26b9
Fix lint errors
jpitlor Nov 13, 2025
0655441
Merge remote-tracking branch 'origin/blueprint-editor' into blueprint…
jpitlor Nov 13, 2025
bca2419
Merge branch 'dev' into blueprint-editor
jpitlor Nov 13, 2025
81f96ce
Update src/panels/config/automation/condition/ha-automation-condition…
jpitlor Nov 24, 2025
cf87080
Update src/panels/config/automation/trigger/ha-automation-trigger-row.ts
jpitlor Nov 24, 2025
8d9f819
Update src/panels/config/automation/trigger/ha-automation-trigger-row.ts
jpitlor Nov 24, 2025
a36917c
Blueprint Editor:
jpitlor Nov 28, 2025
b46dfe6
Merge branch 'dev' into blueprint-editor
jpitlor Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 149 additions & 4 deletions src/data/blueprint.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,84 @@
import {
mdiAlpha,
mdiCalendar,
mdiCalendarClock,
mdiChat,
mdiClock,
mdiCodeBraces,
mdiCog,
mdiCube,
mdiDetails,
mdiDevices,
mdiExclamation,
mdiFile,
mdiFloorPlan,
mdiFormatColorFill,
mdiFormDropdown,
mdiGestureDoubleTap,
mdiGestureTap,
mdiGlobeModel,
mdiHeadQuestion,
mdiLabel,
mdiMicrophone,
mdiNumeric,
mdiPalette,
mdiPin,
mdiPlayOutline,
mdiPuzzle,
mdiQrcode,
mdiShape,
mdiSimpleIcons,
mdiSofa,
mdiStateMachine,
mdiTarget,
mdiText,
mdiThermostat,
mdiTimer,
mdiToggleSwitch,
} from "@mdi/js";
import type { Schema } from "js-yaml";
import yaml from "js-yaml";
import { createContext } from "@lit/context";
import type { HomeAssistant } from "../types";
import type { ManualAutomationConfig } from "./automation";
import type { AutomationClipboard, ManualAutomationConfig } from "./automation";
import type { ManualScriptConfig } from "./script";
import type { Selector } from "./selector";
import { createSearchParam } from "../common/url/search-params";
import { navigate } from "../common/navigate";

export type BlueprintDomain = "automation" | "script";

export type Blueprints = Record<string, BlueprintOrError>;

export type BlueprintOrError = Blueprint | { error: string };
export interface Blueprint {

export interface BlueprintBase {
blueprint?: BlueprintMetaData;
metadata: BlueprintMetaData;
}

export interface AutomationBlueprint
extends ManualAutomationConfig,
BlueprintBase {}
export interface ScriptBlueprint extends ManualScriptConfig, BlueprintBase {}
export type Blueprint = AutomationBlueprint | ScriptBlueprint;

export interface BlueprintMetaDataEditorSchema {
name: string;
path: string;
description: string;
author: string;
minimum_version: string;
}

export interface BlueprintMetaData {
domain: BlueprintDomain;
name: string;
input?: Record<string, BlueprintInput | BlueprintInputSection | null>;
description?: string;
source_url?: string;
author?: string;
homeassistant?: { min_version: string };
}

export interface BlueprintInput {
Expand Down Expand Up @@ -49,25 +109,35 @@ export interface BlueprintSubstituteResults {
script: { substituted_config: ManualScriptConfig };
}

export interface BlueprintGetResult {
yaml: string;
}

export const fetchBlueprints = (hass: HomeAssistant, domain: BlueprintDomain) =>
hass.callWS<Blueprints>({ type: "blueprint/list", domain });

export const getBlueprint = (
hass: HomeAssistant,
domain: BlueprintDomain,
path: string
) => hass.callWS<BlueprintGetResult>({ type: "blueprint/get", domain, path });

export const importBlueprint = (hass: HomeAssistant, url: string) =>
hass.callWS<BlueprintImportResult>({ type: "blueprint/import", url });

export const saveBlueprint = (
hass: HomeAssistant,
domain: BlueprintDomain,
path: string,
yaml: string,
yamlSource: string,
source_url?: string,
allow_override?: boolean
) =>
hass.callWS({
type: "blueprint/save",
domain,
path,
yaml,
yaml: yamlSource,
source_url,
allow_override,
});
Expand Down Expand Up @@ -113,3 +183,78 @@ export const substituteBlueprint = <
path,
input,
});

let initialBlueprintEditorData: Partial<Blueprint> | undefined;

export const showBlueprintEditor = (
domain: BlueprintDomain,
data?: Partial<Blueprint>,
expanded?: boolean
) => {
initialBlueprintEditorData = data;

const params: Record<string, string> = {};
if (expanded) {
params.expanded = "1";
}

navigate(`/config/blueprint/edit/${domain}/new?${createSearchParam(params)}`);
};

export const getBlueprintEditorInitData = () => {
const data = initialBlueprintEditorData;
initialBlueprintEditorData = undefined;
return data;
};

interface BlueprintClipboardBase {
input?: string;
}
type AutomationBlueprintClipboard = BlueprintClipboardBase &
AutomationClipboard;
export type BlueprintClipboard = AutomationBlueprintClipboard;

export const INPUT_ICONS = {
action: mdiGestureTap,
addOn: mdiPuzzle,
area: mdiSofa,
assistPipeline: mdiChat,
attribute: mdiDetails,
backupLocation: mdiPin,
boolean: mdiToggleSwitch,
colorTemperature: mdiThermostat,
condition: mdiHeadQuestion,
configEntry: mdiCog,
constant: mdiExclamation,
conversationAgent: mdiMicrophone,
country: mdiGlobeModel,
date: mdiCalendar,
dateAndTime: mdiCalendarClock,
device: mdiDevices,
duration: mdiTimer,
entity: mdiShape,
file: mdiFile,
floor: mdiFloorPlan,
icon: mdiSimpleIcons,
label: mdiLabel,
language: mdiAlpha,
location: mdiPin,
media: mdiPlayOutline,
number: mdiNumeric,
object: mdiCube,
qrCode: mdiQrcode,
rgbColor: mdiFormatColorFill,
select: mdiFormDropdown,
state: mdiStateMachine,
target: mdiTarget,
template: mdiCodeBraces,
text: mdiText,
theme: mdiPalette,
time: mdiClock,
trigger: mdiGestureDoubleTap,
} as const;

const inputTag = new yaml.Type("!input", { kind: "scalar" });
export const BlueprintYamlSchema = yaml.DEFAULT_SCHEMA.extend([inputTag]);

export const yamlSchemaContext = createContext<Schema>(Symbol("yaml-schema"));
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ export default class HaAutomationActionRow extends LitElement {
@consume({ context: floorsContext, subscribe: true })
_floorReg!: Record<string, FloorRegistryEntry>;

@state() private _warnings?: string[];

@state() private _uiModeAvailable = true;

@state() private _yamlMode = false;
Expand All @@ -191,8 +193,6 @@ export default class HaAutomationActionRow extends LitElement {

@state() private _collapsed = true;

@state() private _warnings?: string[];

@query("ha-automation-action-editor")
private _actionEditor?: HaAutomationActionEditor;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import type { Schema } from "js-yaml";
import { DEFAULT_SCHEMA } from "js-yaml";
import { consume } from "@lit/context";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-picker";
import "../../../../../components/ha-service-picker";
Expand All @@ -11,6 +14,7 @@ import type { EventAction } from "../../../../../data/script";
import type { HomeAssistant } from "../../../../../types";
import type { ActionElement } from "../ha-automation-action-row";
import { handleChangeEvent } from "../ha-automation-action-row";
import { yamlSchemaContext } from "../../../../../data/blueprint";

@customElement("ha-automation-action-event")
export class HaEventAction extends LitElement implements ActionElement {
Expand All @@ -22,6 +26,9 @@ export class HaEventAction extends LitElement implements ActionElement {

@query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor;

@consume({ context: yamlSchemaContext })
private _yamlSchema?: Schema;

private _actionData?: EventAction["event_data"];

public static get defaultConfig(): EventAction {
Expand Down Expand Up @@ -60,6 +67,7 @@ export class HaEventAction extends LitElement implements ActionElement {
.name=${"event_data"}
.readOnly=${this.disabled}
.defaultValue=${event_data}
.yamlSchema=${this._yamlSchema ?? DEFAULT_SCHEMA}
@value-changed=${this._dataChanged}
></ha-yaml-editor>
`;
Expand Down
8 changes: 4 additions & 4 deletions src/panels/config/automation/blueprint-automation-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import "../../../components/ha-markdown";
import "../../../components/ha-fab";
import type { BlueprintAutomationConfig } from "../../../data/automation";
import { fetchBlueprints } from "../../../data/blueprint";
import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor";
import { BlueprintGenericEditor } from "../blueprint/blueprint-generic-editor";
import { saveFabStyles } from "./styles";

@customElement("blueprint-automation-editor")
export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
export class BlueprintAutomationEditor extends BlueprintGenericEditor {
@property({ attribute: false }) public config!: BlueprintAutomationConfig;

@property({ attribute: false }) public stateObj?: HassEntity;
Expand Down Expand Up @@ -88,7 +88,7 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {

static get styles(): CSSResultGroup {
return [
HaBlueprintGenericEditor.styles,
BlueprintGenericEditor.styles,
saveFabStyles,
css`
:host {
Expand Down Expand Up @@ -118,6 +118,6 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
}
declare global {
interface HTMLElementTagNameMap {
"blueprint-automation-editor": HaBlueprintAutomationEditor;
"blueprint-automation-editor": BlueprintAutomationEditor;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { consume } from "@lit/context";
import memoizeOne from "memoize-one";
import { DEFAULT_SCHEMA } from "js-yaml";
import type { Schema } from "js-yaml";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-yaml-editor";
Expand All @@ -14,6 +17,7 @@ import type { HomeAssistant } from "../../../../types";
import "../ha-automation-editor-warning";
import { editorStyles, indentStyle } from "../styles";
import type { ConditionElement } from "./ha-automation-condition-row";
import { yamlSchemaContext } from "../../../../data/blueprint";
import "./types/ha-automation-condition-platform";

@customElement("ha-automation-condition-editor")
Expand Down Expand Up @@ -44,6 +48,9 @@ export default class HaAutomationConditionEditor extends LitElement {
@query(COLLAPSIBLE_CONDITION_ELEMENTS.join(", "))
private _collapsibleElement?: ConditionElement;

@consume({ context: yamlSchemaContext })
private _yamlSchema?: Schema;

private _processedCondition = memoizeOne((condition) =>
expandConditionWithShorthand(condition)
);
Expand Down Expand Up @@ -83,6 +90,7 @@ export default class HaAutomationConditionEditor extends LitElement {
.defaultValue=${this.condition}
@value-changed=${this._onYamlChange}
.readOnly=${this.disabled}
.yamlSchema=${this._yamlSchema ?? DEFAULT_SCHEMA}
></ha-yaml-editor>
`
: html`
Expand Down
11 changes: 10 additions & 1 deletion src/panels/config/automation/ha-automation-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import type { Schema } from "js-yaml";
import { DEFAULT_SCHEMA } from "js-yaml";
import { transform } from "../../../common/decorators/transform";
import { fireEvent } from "../../../common/dom/fire_event";
import { goBack, navigate } from "../../../common/navigate";
Expand Down Expand Up @@ -57,7 +59,10 @@ import {
showAutomationEditor,
triggerAutomationActions,
} from "../../../data/automation";
import { substituteBlueprint } from "../../../data/blueprint";
import {
substituteBlueprint,
yamlSchemaContext,
} from "../../../data/blueprint";
import { validateConfig } from "../../../data/config";
import { fullEntitiesContext } from "../../../data/context";
import { UNAVAILABLE } from "../../../data/entity";
Expand Down Expand Up @@ -157,6 +162,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
})
private _registryEntry?: EntityRegistryEntry;

@consume({ context: yamlSchemaContext })
private _yamlSchema?: Schema;

@state() private _saving = false;

@state()
Expand Down Expand Up @@ -630,6 +638,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
.hass=${this.hass}
.defaultValue=${this._preprocessYaml()}
.readOnly=${this._readOnly}
.yamlSchema=${this._yamlSchema ?? DEFAULT_SCHEMA}
@value-changed=${this._yamlChanged}
@editor-save=${this._handleSaveAutomation}
.showErrors=${false}
Expand Down
5 changes: 4 additions & 1 deletion src/panels/config/automation/manual-automation-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import "./condition/ha-automation-condition";
import type HaAutomationCondition from "./condition/ha-automation-condition";
import "./ha-automation-sidebar";
import type HaAutomationSidebar from "./ha-automation-sidebar";
import type { AutomationBlueprint } from "../../../data/blueprint";
import { showPasteReplaceDialog } from "./paste-replace-dialog/show-dialog-paste-replace";
import { manualEditorStyles, saveFabStyles } from "./styles";
import "./trigger/ha-automation-trigger";
Expand Down Expand Up @@ -91,7 +92,9 @@ export class HaManualAutomationEditor extends LitElement {

@property({ type: Boolean }) public saving = false;

@property({ attribute: false }) public config!: ManualAutomationConfig;
@property({ attribute: false }) public config!:
| ManualAutomationConfig
| AutomationBlueprint;

@property({ attribute: false }) public stateObj?: HassEntity;

Expand Down
Loading