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",