diff --git a/src/components/device/ha-device-picker.ts b/src/components/device/ha-device-picker.ts
index e041f79fb7..c1e86dd761 100644
--- a/src/components/device/ha-device-picker.ts
+++ b/src/components/device/ha-device-picker.ts
@@ -1,4 +1,5 @@
-import "../ha-icon-button";
+import "../ha-svg-icon";
+import "@material/mwc-icon-button/mwc-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
@@ -37,6 +38,7 @@ import {
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types";
+import { mdiClose, mdiMenuUp, mdiMenuDown } from "@mdi/js";
interface Device {
name: string;
@@ -115,17 +117,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
- public open() {
- this.updateComplete.then(() => {
- (this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
- });
- }
-
- public focus() {
- this.updateComplete.then(() => {
- this.shadowRoot?.querySelector("paper-input")?.focus();
- });
- }
+ private _init = false;
private _getDevices = memoizeOne(
(
@@ -138,7 +130,13 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
deviceFilter: this["deviceFilter"]
): Device[] => {
if (!devices.length) {
- return [];
+ return [
+ {
+ id: "",
+ area: "",
+ name: this.hass.localize("ui.components.device-picker.no_devices"),
+ },
+ ];
}
const deviceEntityLookup: DeviceEntityLookup = {};
@@ -229,6 +227,15 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
: this.hass.localize("ui.components.device-picker.no_area"),
};
});
+ if (!outputDevices.length) {
+ return [
+ {
+ id: "",
+ area: "",
+ name: this.hass.localize("ui.components.device-picker.no_match"),
+ },
+ ];
+ }
if (outputDevices.length === 1) {
return outputDevices;
}
@@ -236,6 +243,18 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
}
);
+ public open() {
+ this.updateComplete.then(() => {
+ (this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
+ });
+ }
+
+ public focus() {
+ this.updateComplete.then(() => {
+ this.shadowRoot?.querySelector("paper-input")?.focus();
+ });
+ }
+
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
@@ -251,7 +270,11 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
}
protected updated(changedProps: PropertyValues) {
- if (changedProps.has("_opened") && this._opened) {
+ if (
+ (!this._init && this.devices && this.areas && this.entities) ||
+ (changedProps.has("_opened") && this._opened)
+ ) {
+ this._init = true;
(this._comboBox as any).items = this._getDevices(
this.devices!,
this.areas!,
@@ -290,30 +313,30 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
>
${this.value
? html`
-
- Clear
-
+
+
`
: ""}
-
- Toggle
-
+
+
`;
@@ -350,7 +373,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
static get styles(): CSSResult {
return css`
- paper-input > ha-icon-button {
+ paper-input > mwc-icon-button {
--mdc-icon-button-size: 24px;
padding: 2px;
color: var(--secondary-text-color);
diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts
index c83c6a44e3..b24c5c227c 100644
--- a/src/components/entity/ha-entity-picker.ts
+++ b/src/components/entity/ha-entity-picker.ts
@@ -165,6 +165,24 @@ export class HaEntityPicker extends LitElement {
);
}
+ if (!states.length) {
+ return [
+ {
+ entity_id: "",
+ state: "",
+ last_changed: "",
+ last_updated: "",
+ context: { id: "", user_id: null },
+ attributes: {
+ friendly_name: this.hass!.localize(
+ "ui.components.entity.entity-picker.no_match"
+ ),
+ icon: "mdi:magnify",
+ },
+ },
+ ];
+ }
+
return states;
}
);
@@ -215,7 +233,6 @@ export class HaEntityPicker extends LitElement {
.label=${this.label === undefined
? this.hass.localize("ui.components.entity.entity-picker.entity")
: this.label}
- .value=${this._value}
.disabled=${this.disabled}
class="input"
autocapitalize="none"
diff --git a/src/components/ha-area-picker.ts b/src/components/ha-area-picker.ts
index 7a9cb710d1..9ad7391448 100644
--- a/src/components/ha-area-picker.ts
+++ b/src/components/ha-area-picker.ts
@@ -1,4 +1,5 @@
-import "./ha-icon-button";
+import "./ha-svg-icon";
+import "@material/mwc-icon-button/mwc-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
@@ -14,6 +15,8 @@ import {
property,
internalProperty,
TemplateResult,
+ PropertyValues,
+ query,
} from "lit-element";
import { fireEvent } from "../common/dom/fire_event";
import {
@@ -40,6 +43,7 @@ import {
} from "../data/entity_registry";
import { computeDomain } from "../common/entity/compute_domain";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
+import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
const rowRenderer = (
root: HTMLElement,
@@ -121,6 +125,10 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
@internalProperty() private _opened?: boolean;
+ @query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
+
+ private _init = false;
+
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeAreaRegistry(this.hass.connection!, (areas) => {
@@ -159,6 +167,15 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
entityFilter: this["entityFilter"],
noAdd: this["noAdd"]
): AreaRegistryEntry[] => {
+ if (!areas.length) {
+ return [
+ {
+ area_id: "",
+ name: this.hass.localize("ui.components.area-picker.no_areas"),
+ },
+ ];
+ }
+
const deviceEntityLookup: DeviceEntityLookup = {};
let inputDevices: DeviceRegistryEntry[] | undefined;
let inputEntities: EntityRegistryEntry[] | undefined;
@@ -243,7 +260,9 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
}
if (entityFilter) {
- entities = entities.filter((entity) => entityFilter!(entity));
+ inputEntities = inputEntities!.filter((entity) =>
+ entityFilter!(entity)
+ );
}
let outputAreas = areas;
@@ -268,6 +287,15 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
outputAreas = areas.filter((area) => areaIds!.includes(area.area_id));
}
+ if (!outputAreas.length) {
+ outputAreas = [
+ {
+ area_id: "",
+ name: this.hass.localize("ui.components.area-picker.no_match"),
+ },
+ ];
+ }
+
return noAdd
? outputAreas
: [
@@ -280,27 +308,35 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
}
);
+ protected updated(changedProps: PropertyValues) {
+ if (
+ (!this._init && this._devices && this._areas && this._entities) ||
+ (changedProps.has("_opened") && this._opened)
+ ) {
+ this._init = true;
+ (this._comboBox as any).items = this._getAreas(
+ this._areas!,
+ this._devices!,
+ this._entities!,
+ this.includeDomains,
+ this.excludeDomains,
+ this.includeDeviceClasses,
+ this.deviceFilter,
+ this.entityFilter,
+ this.noAdd
+ );
+ }
+ }
+
protected render(): TemplateResult {
if (!this._devices || !this._areas || !this._entities) {
return html``;
}
- const areas = this._getAreas(
- this._areas,
- this._devices,
- this._entities,
- this.includeDomains,
- this.excludeDomains,
- this.includeDeviceClasses,
- this.deviceFilter,
- this.entityFilter,
- this.noAdd
- );
return html`
${this.value
? html`
-
- ${this.hass.localize("ui.components.area-picker.clear")}
-
- `
- : ""}
- ${areas.length > 0
- ? html`
-
- ${this.hass.localize("ui.components.area-picker.toggle")}
-
+
+
`
: ""}
+
+
+
+
`;
@@ -424,7 +454,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
static get styles(): CSSResult {
return css`
- paper-input > ha-icon-button {
+ paper-input > mwc-icon-button {
--mdc-icon-button-size: 24px;
padding: 2px;
color: var(--secondary-text-color);
diff --git a/src/components/ha-selector/ha-selector-area.ts b/src/components/ha-selector/ha-selector-area.ts
index d3f7d2f6a7..903626440b 100644
--- a/src/components/ha-selector/ha-selector-area.ts
+++ b/src/components/ha-selector/ha-selector-area.ts
@@ -77,7 +77,8 @@ export class HaAreaSelector extends LitElement {
}
if (this.selector.area.device?.integration) {
if (
- !this._configEntries?.some((entry) =>
+ this._configEntries &&
+ !this._configEntries.some((entry) =>
device.config_entries.includes(entry.entry_id)
)
) {
diff --git a/src/components/ha-selector/ha-selector-device.ts b/src/components/ha-selector/ha-selector-device.ts
index 98a83ba562..f3f2c1ff35 100644
--- a/src/components/ha-selector/ha-selector-device.ts
+++ b/src/components/ha-selector/ha-selector-device.ts
@@ -63,7 +63,8 @@ export class HaDeviceSelector extends LitElement {
}
if (this.selector.device.integration) {
if (
- !this._configEntries?.some((entry) =>
+ this._configEntries &&
+ !this._configEntries.some((entry) =>
device.config_entries.includes(entry.entry_id)
)
) {
diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts
index 68a6e0701e..52f1ae550f 100644
--- a/src/components/ha-target-picker.ts
+++ b/src/components/ha-target-picker.ts
@@ -435,10 +435,19 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
type: string,
id: string
): this["value"] {
- return {
- ...value,
- [type]: ensureArray(value![type])!.filter((val) => val !== id),
- };
+ const newVal = ensureArray(value![type])!.filter((val) => val !== id);
+ if (newVal.length) {
+ return {
+ ...value,
+ [type]: newVal,
+ };
+ }
+ const val = { ...value }!;
+ delete val[type];
+ if (Object.keys(val).length) {
+ return val;
+ }
+ return undefined;
}
private _deviceMeetsFilter(device: DeviceRegistryEntry): boolean {
diff --git a/src/panels/config/automation/blueprint-automation-editor.ts b/src/panels/config/automation/blueprint-automation-editor.ts
index b7f6d46f05..570d4cee7b 100644
--- a/src/panels/config/automation/blueprint-automation-editor.ts
+++ b/src/panels/config/automation/blueprint-automation-editor.ts
@@ -18,9 +18,6 @@ import "@polymer/paper-input/paper-textarea";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "../../../components/entity/ha-entity-toggle";
import "@material/mwc-button/mwc-button";
-import "./trigger/ha-automation-trigger";
-import "./condition/ha-automation-condition";
-import "./action/ha-automation-action";
import { fireEvent } from "../../../common/dom/fire_event";
import { haStyle } from "../../../resources/styles";
import { HassEntity } from "home-assistant-js-websocket";
@@ -151,7 +148,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
There is an error in this Blueprint: ${blueprint.error}
`
: html`${blueprint?.metadata.description
- ? html`
+ ? html`
${blueprint.metadata.description}
`
: ""}
@@ -227,12 +224,18 @@ export class HaBlueprintAutomationEditor extends LitElement {
) {
return;
}
+ const input = { ...this.config.use_blueprint.input, [key]: value };
+
+ if (value === "" || value === undefined) {
+ delete input[key];
+ }
+
fireEvent(this, "value-changed", {
value: {
...this.config!,
use_blueprint: {
...this.config.use_blueprint,
- input: { ...this.config.use_blueprint.input, [key]: value },
+ input,
},
},
});
@@ -264,6 +267,9 @@ export class HaBlueprintAutomationEditor extends LitElement {
.padding {
padding: 16px;
}
+ .pre-line {
+ white-space: pre-line;
+ }
.blueprint-picker-container {
padding: 16px;
}
diff --git a/src/panels/config/blueprint/dialog-import-blueprint.ts b/src/panels/config/blueprint/dialog-import-blueprint.ts
index 1abb786fec..ed65a04157 100644
--- a/src/panels/config/blueprint/dialog-import-blueprint.ts
+++ b/src/panels/config/blueprint/dialog-import-blueprint.ts
@@ -12,6 +12,7 @@ import {
internalProperty,
query,
TemplateResult,
+ css,
} from "lit-element";
import "../../../components/ha-dialog";
import { haStyleDialog } from "../../../resources/styles";
@@ -73,7 +74,9 @@ class DialogImportBlueprint extends LitElement {
this._result.blueprint.metadata.domain
)}
- ${this._result.blueprint.metadata.description}
+
+ ${this._result.blueprint.metadata.description}
+
${this._result.validation_errors
? html`
@@ -199,8 +202,15 @@ class DialogImportBlueprint extends LitElement {
}
}
- static get styles(): CSSResult {
- return haStyleDialog;
+ static get styles(): CSSResult[] {
+ return [
+ haStyleDialog,
+ css`
+ .pre-line {
+ white-space: pre-line;
+ }
+ `,
+ ];
}
}
diff --git a/src/panels/config/blueprint/ha-blueprint-overview.ts b/src/panels/config/blueprint/ha-blueprint-overview.ts
index b6e538440a..76009e48fd 100644
--- a/src/panels/config/blueprint/ha-blueprint-overview.ts
+++ b/src/panels/config/blueprint/ha-blueprint-overview.ts
@@ -195,7 +195,10 @@ class HaBlueprintOverview extends LitElement {
${this.hass.localize("ui.panel.config.blueprint.overview.introduction")}
diff --git a/src/translations/en.json b/src/translations/en.json
index d09621d41b..aa10caf715 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -328,6 +328,7 @@
"entity-picker": {
"entity": "Entity",
"clear": "Clear",
+ "no_match": "No matching entities found",
"show_entities": "Show entities"
},
"entity-attribute-picker": {
@@ -359,6 +360,8 @@
"clear": "Clear",
"toggle": "Toggle",
"show_devices": "Show devices",
+ "no_devices": "You don't have any devices",
+ "no_match": "No matching devices found",
"device": "Device",
"no_area": "No area"
},
@@ -367,6 +370,8 @@
"show_areas": "Show areas",
"area": "Area",
"add_new": "Add new area…",
+ "no_areas": "You don't have any areas",
+ "no_match": "No matching areas found",
"add_dialog": {
"title": "Add new area",
"text": "Enter the name of the new area.",
@@ -1456,8 +1461,8 @@
"description": "Manage blueprints",
"overview": {
"header": "Blueprint Editor",
- "introduction": "The blueprint editor allows you to create and edit blueprints.",
- "learn_more": "Learn more about blueprints",
+ "introduction": "The blueprint configuration allows you to import and manage your blueprints.",
+ "learn_more": "Learn more about using blueprints",
"headers": {
"name": "Name",
"domain": "Domain",