) {
ev.stopPropagation();
const newValue = ev.detail.value;
+
+ if (newValue.startsWith(CREATE_ID)) {
+ const domain = newValue.substring(CREATE_ID.length);
+ showHelperDetailDialog(this, {
+ domain,
+ dialogClosedCallback: (item) => {
+ if (item.entityId) this._setValue(item.entityId);
+ },
+ });
+ return;
+ }
+
if (newValue !== this._value) {
this._setValue(newValue);
}
diff --git a/src/components/ha-selector/ha-selector-target.ts b/src/components/ha-selector/ha-selector-target.ts
index fafc97b62beb..a37557c1bd91 100644
--- a/src/components/ha-selector/ha-selector-target.ts
+++ b/src/components/ha-selector/ha-selector-target.ts
@@ -82,6 +82,7 @@ export class HaTargetSelector extends LitElement {
.deviceFilter=${this._filterDevices}
.entityFilter=${this._filterEntities}
.disabled=${this.disabled}
+ .createDomains=${this.selector.target?.create_domains}
>`;
}
diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts
index 8310aee8f4c5..83da022e296a 100644
--- a/src/components/ha-service-control.ts
+++ b/src/components/ha-service-control.ts
@@ -33,6 +33,7 @@ import {
expandFloorTarget,
expandLabelTarget,
Selector,
+ TargetSelector,
} from "../data/selector";
import { HomeAssistant, ValueChangedEvent } from "../types";
import { documentationUrl } from "../util/documentation-url";
@@ -43,6 +44,7 @@ import "./ha-service-picker";
import "./ha-settings-row";
import "./ha-yaml-editor";
import type { HaYamlEditor } from "./ha-yaml-editor";
+import { isHelperDomain } from "../panels/config/helpers/const";
const attributeFilter = (values: any[], attribute: any) => {
if (typeof attribute === "object") {
@@ -363,6 +365,15 @@ export class HaServiceControl extends LitElement {
return false;
}
+ private _targetSelector = memoizeOne(
+ (targetSelector: TargetSelector | null | undefined, domain?: string) => {
+ const create_domains = isHelperDomain(domain) ? [domain] : undefined;
+ return targetSelector
+ ? { target: { ...targetSelector, create_domains } }
+ : { target: { create_domains } };
+ }
+ );
+
protected render() {
const serviceData = this._getServiceInfo(
this._value?.service,
@@ -401,157 +412,152 @@ export class HaServiceControl extends LitElement {
)) ||
serviceData?.description;
- return html`
- ${this.hidePicker
- ? nothing
- : html``}
+ ${this.hideDescription
+ ? nothing
+ : html`
+
+ ${description ? html`
${description}
` : ""}
+ ${this._manifest
+ ? html`
+
+ `
+ : nothing}
+
+ `}
+ ${serviceData && "target" in serviceData
+ ? html`
+ ${hasOptional
+ ? html``
+ : ""}
+ ${this.hass.localize("ui.components.service-control.target")}
+ ${this.hass.localize(
+ "ui.components.service-control.target_description"
+ )}`}
- ${this.hideDescription
- ? nothing
- : html`
-
- ${description ? html`
${description}
` : ""}
- ${this._manifest
- ? html`
-
- `
- : nothing}
-
- `}
- ${serviceData && "target" in serviceData
- ? html`
- ${hasOptional
- ? html``
- : ""}
- ${this.hass.localize(
- "ui.components.service-control.target"
- )}
- ${this.hass.localize(
- "ui.components.service-control.target_description"
- )}`
- : entityId
- ? html``
- : ""}
- ${shouldRenderServiceDataYaml
- ? html``
+ : entityId
+ ? html``
- : filteredFields?.map((dataField) => {
- const selector = dataField?.selector ?? { text: undefined };
- const type = Object.keys(selector)[0];
- const enhancedSelector = [
- "action",
- "condition",
- "trigger",
- ].includes(type)
- ? {
- [type]: {
- ...selector[type],
- path: [dataField.key],
- },
- }
- : selector;
-
- const showOptional = showOptionalToggle(dataField);
-
- return dataField.selector &&
- (!dataField.advanced ||
- this.showAdvanced ||
- (this._value?.data &&
- this._value.data[dataField.key] !== undefined))
- ? html`
- ${!showOptional
- ? hasOptional
- ? html``
- : ""
- : html``}
- ${this.hass.localize(
- `component.${domain}.services.${serviceName}.fields.${dataField.key}.name`
- ) ||
- dataField.name ||
- dataField.key}
- ${this.hass.localize(
- `component.${domain}.services.${serviceName}.fields.${dataField.key}.description`
- ) || dataField?.description}
-
- `
- : "";
- })}
- `;
+ .disabled=${this.disabled}
+ .value=${this._value?.data?.entity_id}
+ .label=${this.hass.localize(
+ `component.${domain}.services.${serviceName}.fields.entity_id.description`
+ ) || entityId.description}
+ @value-changed=${this._entityPicked}
+ allow-custom-entity
+ >`
+ : ""}
+ ${shouldRenderServiceDataYaml
+ ? html``
+ : filteredFields?.map((dataField) => {
+ const selector = dataField?.selector ?? { text: undefined };
+ const type = Object.keys(selector)[0];
+ const enhancedSelector = ["action", "condition", "trigger"].includes(
+ type
+ )
+ ? {
+ [type]: {
+ ...selector[type],
+ path: [dataField.key],
+ },
+ }
+ : selector;
+
+ const showOptional = showOptionalToggle(dataField);
+
+ return dataField.selector &&
+ (!dataField.advanced ||
+ this.showAdvanced ||
+ (this._value?.data &&
+ this._value.data[dataField.key] !== undefined))
+ ? html`
+ ${!showOptional
+ ? hasOptional
+ ? html``
+ : ""
+ : html``}
+ ${this.hass.localize(
+ `component.${domain}.services.${serviceName}.fields.${dataField.key}.name`
+ ) ||
+ dataField.name ||
+ dataField.key}
+ ${this.hass.localize(
+ `component.${domain}.services.${serviceName}.fields.${dataField.key}.description`
+ ) || dataField?.description}
+
+ `
+ : "";
+ })} `;
}
private _localizeValueCallback = (key: string) => {
diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts
index 21d32d3ea3bc..c20a465e442e 100644
--- a/src/components/ha-target-picker.ts
+++ b/src/components/ha-target-picker.ts
@@ -65,6 +65,8 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
@property() public helper?: string;
+ @property({ type: Array }) public createDomains?: string[];
+
/**
* Show only targets with entities from specific domains.
* @type {Array}
@@ -468,6 +470,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
.excludeEntities=${ensureArray(this.value?.entity_id)}
+ .createDomains=${this.createDomains}
@value-changed=${this._targetPicked}
@click=${this._preventDefault}
allow-custom-entity
diff --git a/src/data/selector.ts b/src/data/selector.ts
index 3abb7ae6fc71..557650635a12 100644
--- a/src/data/selector.ts
+++ b/src/data/selector.ts
@@ -401,6 +401,7 @@ export interface TargetSelector {
target: {
entity?: EntitySelectorFilter | readonly EntitySelectorFilter[];
device?: DeviceSelectorFilter | readonly DeviceSelectorFilter[];
+ create_domains?: string[];
} | null;
}
diff --git a/src/panels/config/helpers/dialog-helper-detail.ts b/src/panels/config/helpers/dialog-helper-detail.ts
index 5e9a6e07d63c..54488dccec60 100644
--- a/src/panels/config/helpers/dialog-helper-detail.ts
+++ b/src/panels/config/helpers/dialog-helper-detail.ts
@@ -32,7 +32,7 @@ import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-c
import { haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
-import { Helper, HelperDomain } from "./const";
+import { Helper, HelperDomain, isHelperDomain } from "./const";
import type { ShowDialogHelperDetailParams } from "./show-dialog-helper-detail";
type HelperCreators = {
@@ -96,7 +96,7 @@ export class DialogHelperDetail extends LitElement {
@state() private _opened = false;
- @state() private _domain?: HelperDomain;
+ @state() private _domain?: string;
@state() private _error?: string;
@@ -114,8 +114,12 @@ export class DialogHelperDetail extends LitElement {
this._params = params;
this._domain = params.domain;
this._item = undefined;
+ if (this._domain && this._domain in HELPERS) {
+ await HELPERS[this._domain].import();
+ }
this._opened = true;
await this.updateComplete;
+ this.hass.loadFragmentTranslation("config");
Promise.all([
getConfigFlowHandlers(this.hass, ["helper"]),
// Ensure the titles are loaded before we render the flows.
@@ -141,7 +145,7 @@ export class DialogHelperDetail extends LitElement {
if (this._domain) {
content = html`