diff --git a/pyproject.toml b/pyproject.toml index 7aa35c5c66ec..92bef3e682fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20240403.0" +version = "20240403.1" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index 28cd56dfc52a..ecb40f5b7177 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -1,4 +1,5 @@ import { consume } from "@lit-labs/context"; +import { ResizeController } from "@lit-labs/observers/resize-controller"; import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; import "@material/web/divider/divider"; import { @@ -153,6 +154,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { @query("#overflow-menu") private _overflowMenu!: HaMenu; + private _sizeController = new ResizeController(this, { + callback: (entries) => entries[0]?.contentRect.width, + }); + private _automations = memoizeOne( ( automations: AutomationEntity[], @@ -373,7 +378,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { - +
${this.hass.localize("ui.panel.config.category.editor.add")}
@@ -409,12 +414,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
`; })} - +
${this.hass.localize("ui.panel.config.labels.add_label")}
`; - + const labelsInOverflow = + (this._sizeController.value && this._sizeController.value < 700) || + (!this._sizeController.value && this.hass.dockedSidebar === "docked"); return html` ${categoryItems} - ${this.hass.dockedSidebar === "docked" + ${labelsInOverflow ? nothing : html` + this.narrow || labelsInOverflow + ? html`
${this.hass.localize( @@ -1082,6 +1089,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { private async _handleBulkCategory(ev) { const category = ev.currentTarget.value; + this._bulkAddCategory(category); + } + + private async _bulkAddCategory(category: string) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1096,6 +1107,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { private async _handleBulkLabel(ev) { const label = ev.currentTarget.value; const action = ev.currentTarget.action; + this._bulkLabel(label, action); + } + + private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1128,17 +1143,28 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { await Promise.all(promises); } - private _createCategory() { + private async _bulkCreateCategory() { showCategoryRegistryDetailDialog(this, { scope: "automation", - createEntry: (values) => - createCategoryRegistryEntry(this.hass, "automation", values), + createEntry: async (values) => { + const category = await createCategoryRegistryEntry( + this.hass, + "automation", + values + ); + this._bulkAddCategory(category.category_id); + return category; + }, }); } - private _createLabel() { + private _bulkCreateLabel() { showLabelDetailDialog(this, { - createEntry: (values) => createLabelRegistryEntry(this.hass, values), + createEntry: async (values) => { + const label = await createLabelRegistryEntry(this.hass, values); + this._bulkLabel(label.label_id, "add"); + return label; + }, }); } @@ -1146,6 +1172,9 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { return [ haStyle, css` + :host { + display: block; + } hass-tabs-subpage-data-table { --data-table-row-height: 60px; } diff --git a/src/panels/config/devices/ha-config-devices-dashboard.ts b/src/panels/config/devices/ha-config-devices-dashboard.ts index ba9630e2fa7e..f9f2fd16e6e6 100644 --- a/src/panels/config/devices/ha-config-devices-dashboard.ts +++ b/src/panels/config/devices/ha-config-devices-dashboard.ts @@ -575,7 +575,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { `; })} - +
${this.hass.localize("ui.panel.config.labels.add_label")}
[] = []; this._selected.forEach((deviceId) => { promises.push( @@ -817,9 +821,13 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { await Promise.all(promises); } - private _createLabel() { + private _bulkCreateLabel() { showLabelDetailDialog(this, { - createEntry: (values) => createLabelRegistryEntry(this.hass, values), + createEntry: async (values) => { + const label = await createLabelRegistryEntry(this.hass, values); + this._bulkLabel(label.label_id, "add"); + return label; + }, }); } diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 910448ac3a4a..5a7f5461aa06 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -88,6 +88,10 @@ import { configSections } from "../ha-panel-config"; import "../integrations/ha-integration-overflow-menu"; import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + EntitySources, + fetchEntitySourcesWithCache, +} from "../../../data/entity_sources"; export interface StateEntity extends Omit { @@ -141,6 +145,8 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { @state() _labels!: LabelRegistryEntry[]; + @state() private _entitySources?: EntitySources; + @query("hass-tabs-subpage-data-table", true) private _dataTable!: HaTabsSubpageDataTable; @@ -405,10 +411,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { const entryIds = entries .filter((entry) => filter.value!.includes(entry.domain)) .map((entry) => entry.entry_id); + filteredEntities = filteredEntities.filter( (entity) => - entity.config_entry_id && - entryIds.includes(entity.config_entry_id) + filter.value?.includes(entity.platform) || + (entity.config_entry_id && + entryIds.includes(entity.config_entry_id)) ); filter.value!.forEach((domain) => filteredDomains.add(domain)); } else if (key === "ha-filter-labels" && filter.value?.length) { @@ -547,7 +555,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
`; })} - +
${this.hass.localize("ui.panel.config.labels.add_label")}
{ + this._entitySources = sources; + }); } private _setFiltersFromUrl() { @@ -865,14 +876,18 @@ ${ this._filters = {}; } - public willUpdate(changedProps: PropertyValues): void { + public willUpdate(changedProps: PropertyValues): void { super.willUpdate(changedProps); const oldHass = changedProps.get("hass"); let changed = false; if (!this.hass || !this._entities) { return; } - if (changedProps.has("hass") || changedProps.has("_entities")) { + if ( + changedProps.has("hass") || + changedProps.has("_entities") || + changedProps.has("_entitySources") + ) { const stateEntities: StateEntity[] = []; const regEntityIds = new Set( this._entities.map((entity) => entity.entity_id) @@ -883,6 +898,7 @@ ${ } if ( !oldHass || + changedProps.has("_entitySources") || this.hass.states[entityId] !== oldHass.states[entityId] ) { changed = true; @@ -890,7 +906,8 @@ ${ stateEntities.push({ name: computeStateName(this.hass.states[entityId]), entity_id: entityId, - platform: computeDomain(entityId), + platform: + this._entitySources?.[entityId]?.domain || computeDomain(entityId), disabled_by: null, hidden_by: null, area_id: null, @@ -1027,6 +1044,10 @@ ${ private async _handleBulkLabel(ev) { const label = ev.currentTarget.value; const action = ev.currentTarget.action; + await this._bulkLabel(label, action); + } + + private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; this._selected.forEach((entityId) => { const entityReg = @@ -1047,6 +1068,16 @@ ${ await Promise.all(promises); } + private _bulkCreateLabel() { + showLabelDetailDialog(this, { + createEntry: async (values) => { + const label = await createLabelRegistryEntry(this.hass, values); + this._bulkLabel(label.label_id, "add"); + return label; + }, + }); + } + private _removeSelected() { const removeableEntities = this._selected.filter((entity) => { const stateObj = this.hass.states[entity]; @@ -1123,12 +1154,6 @@ ${ }); } - private _createLabel() { - showLabelDetailDialog(this, { - createEntry: (values) => createLabelRegistryEntry(this.hass, values), - }); - } - static get styles(): CSSResultGroup { return [ haStyle, diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index a63b0c2a01e2..b32fa2075034 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -1,4 +1,5 @@ import { consume } from "@lit-labs/context"; +import { ResizeController } from "@lit-labs/observers/resize-controller"; import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; import { mdiAlertCircle, @@ -83,12 +84,12 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; +import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; import "../integrations/ha-integration-overflow-menu"; +import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; import { isHelperDomain } from "./const"; import { showHelperDetailDialog } from "./show-dialog-helper-detail"; -import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; -import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; type HelperItem = { id: string; @@ -163,6 +164,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { @state() private _filteredStateItems?: string[] | null; + private _sizeController = new ResizeController(this, { + callback: (entries) => entries[0]?.contentRect.width, + }); + public hassSubscribe() { return [ subscribeConfigEntries( @@ -375,6 +380,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { type: configEntry.domain, configEntry, entity: undefined, + selectable: false, })); return [...states, ...entries] @@ -437,7 +443,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
- +
${this.hass.localize("ui.panel.config.category.editor.add")}
@@ -472,12 +478,14 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
`; })} - +
${this.hass.localize("ui.panel.config.labels.add_label")}
`; - + const labelsInOverflow = + (this._sizeController.value && this._sizeController.value < 700) || + (!this._sizeController.value && this.hass.dockedSidebar === "docked"); return html` ${categoryItems}
- ${this.hass.dockedSidebar === "docked" + ${labelsInOverflow ? nothing : html` `}` : nothing} - ${this.narrow || this.hass.dockedSidebar === "docked" + ${this.narrow || labelsInOverflow ? html` ${ @@ -771,6 +779,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { private async _handleBulkCategory(ev) { const category = ev.currentTarget.value; + this._bulkAddCategory(category); + } + + private async _bulkAddCategory(category: string) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -785,6 +797,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { private async _handleBulkLabel(ev) { const label = ev.currentTarget.value; const action = ev.currentTarget.action; + this._bulkLabel(label, action); + } + + private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -939,17 +955,28 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { showHelperDetailDialog(this, {}); } - private _createCategory() { + private async _bulkCreateCategory() { showCategoryRegistryDetailDialog(this, { scope: "helpers", - createEntry: (values) => - createCategoryRegistryEntry(this.hass, "helpers", values), + createEntry: async (values) => { + const category = await createCategoryRegistryEntry( + this.hass, + "helpers", + values + ); + this._bulkAddCategory(category.category_id); + return category; + }, }); } - private _createLabel() { + private _bulkCreateLabel() { showLabelDetailDialog(this, { - createEntry: (values) => createLabelRegistryEntry(this.hass, values), + createEntry: async (values) => { + const label = await createLabelRegistryEntry(this.hass, values); + this._bulkLabel(label.label_id, "add"); + return label; + }, }); } @@ -957,6 +984,9 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { return [ haStyle, css` + :host { + display: block; + } hass-tabs-subpage-data-table { --data-table-row-height: 60px; } diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 990ab42656c7..c07d31f7be73 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -1,7 +1,9 @@ import { consume } from "@lit-labs/context"; +import { ResizeController } from "@lit-labs/observers/resize-controller"; import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; import { mdiChevronRight, + mdiCog, mdiContentDuplicate, mdiDelete, mdiDotsVertical, @@ -82,6 +84,7 @@ import { showAlertDialog, showConfirmationDialog, } from "../../../dialogs/generic/show-dialog-box"; +import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog"; import "../../../layouts/hass-tabs-subpage-data-table"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; @@ -137,6 +140,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { @consume({ context: fullEntitiesContext, subscribe: true }) _entityReg!: EntityRegistryEntry[]; + private _sizeController = new ResizeController(this, { + callback: (entries) => entries[0]?.contentRect.width, + }); + private _scenes = memoizeOne( ( scenes: SceneEntity[], @@ -283,6 +290,13 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { ), action: () => this._showInfo(scene), }, + { + path: mdiCog, + label: this.hass.localize( + "ui.panel.config.automation.picker.show_settings" + ), + action: () => this._openSettings(scene), + }, { path: mdiPlay, label: this.hass.localize( @@ -367,7 +381,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
- +
${this.hass.localize("ui.panel.config.category.editor.add")}
@@ -403,12 +417,14 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
`; })} - +
${this.hass.localize("ui.panel.config.labels.add_label")}
`; - + const labelsInOverflow = + (this._sizeController.value && this._sizeController.value < 700) || + (!this._sizeController.value && this.hass.dockedSidebar === "docked"); return html` ${categoryItems} - ${this.hass.dockedSidebar === "docked" + ${labelsInOverflow ? nothing : html` `}` : nothing} - ${this.narrow || this.hass.dockedSidebar === "docked" + ${this.narrow || labelsInOverflow ? html` ${ @@ -760,6 +776,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { private async _handleBulkCategory(ev) { const category = ev.currentTarget.value; + this._bulkAddCategory(category); + } + + private async _bulkAddCategory(category: string) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -774,6 +794,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { private async _handleBulkLabel(ev) { const label = ev.currentTarget.value; const action = ev.currentTarget.action; + this._bulkLabel(label, action); + } + + private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -815,6 +839,13 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { fireEvent(this, "hass-more-info", { entityId: scene.entity_id }); } + private _openSettings(scene: SceneEntity) { + showMoreInfoDialog(this, { + entityId: scene.entity_id, + view: "settings", + }); + } + private _activateScene = async (scene: SceneEntity) => { await activateScene(this.hass, scene.entity_id); showToast(this, { @@ -878,17 +909,28 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { }); } - private _createCategory() { + private async _bulkCreateCategory() { showCategoryRegistryDetailDialog(this, { scope: "scene", - createEntry: (values) => - createCategoryRegistryEntry(this.hass, "scene", values), + createEntry: async (values) => { + const category = await createCategoryRegistryEntry( + this.hass, + "scene", + values + ); + this._bulkAddCategory(category.category_id); + return category; + }, }); } - private _createLabel() { + private _bulkCreateLabel() { showLabelDetailDialog(this, { - createEntry: (values) => createLabelRegistryEntry(this.hass, values), + createEntry: async (values) => { + const label = await createLabelRegistryEntry(this.hass, values); + this._bulkLabel(label.label_id, "add"); + return label; + }, }); } @@ -896,6 +938,9 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { return [ haStyle, css` + :host { + display: block; + } hass-tabs-subpage-data-table { --data-table-row-height: 60px; } diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 4d0242e93469..2e769e83713b 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -1,6 +1,8 @@ import { consume } from "@lit-labs/context"; +import { ResizeController } from "@lit-labs/observers/resize-controller"; import { mdiChevronRight, + mdiCog, mdiContentDuplicate, mdiDelete, mdiDotsVertical, @@ -83,6 +85,7 @@ import { showAlertDialog, showConfirmationDialog, } from "../../../dialogs/generic/show-dialog-box"; +import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog"; import "../../../layouts/hass-tabs-subpage-data-table"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; @@ -141,6 +144,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { @consume({ context: fullEntitiesContext, subscribe: true }) _entityReg!: EntityRegistryEntry[]; + private _sizeController = new ResizeController(this, { + callback: (entries) => entries[0]?.contentRect.width, + }); + private _scripts = memoizeOne( ( scripts: ScriptEntity[], @@ -294,6 +301,13 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { ), action: () => this._showInfo(script), }, + { + path: mdiCog, + label: this.hass.localize( + "ui.panel.config.automation.picker.show_settings" + ), + action: () => this._openSettings(script), + }, { path: mdiTag, label: this.hass.localize( @@ -379,7 +393,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { )}
- +
${this.hass.localize("ui.panel.config.category.editor.add")}
@@ -415,12 +429,14 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
`; })} - +
${this.hass.localize("ui.panel.config.labels.add_label")}
`; - + const labelsInOverflow = + (this._sizeController.value && this._sizeController.value < 700) || + (!this._sizeController.value && this.hass.dockedSidebar === "docked"); return html` ${categoryItems} - ${this.hass.dockedSidebar === "docked" + ${labelsInOverflow ? nothing : html` `}` : nothing} - ${this.narrow || this.hass.dockedSidebar === "docked" + ${this.narrow || labelsInOverflow ? html` ${ @@ -829,6 +845,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { private async _handleBulkCategory(ev) { const category = ev.currentTarget.value; + this._bulkAddCategory(category); + } + + private async _bulkAddCategory(category: string) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -843,6 +863,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { private async _handleBulkLabel(ev) { const label = ev.currentTarget.value; const action = ev.currentTarget.action; + this._bulkLabel(label, action); + } + + private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -895,6 +919,13 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { fireEvent(this, "hass-more-info", { entityId: script.entity_id }); } + private _openSettings(script: any) { + showMoreInfoDialog(this, { + entityId: script.entity_id, + view: "settings", + }); + } + private _showTrace(script: any) { const entry = this.entityRegistry.find( (e) => e.entity_id === script.entity_id @@ -994,17 +1025,28 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { } } - private _createCategory() { + private async _bulkCreateCategory() { showCategoryRegistryDetailDialog(this, { scope: "script", - createEntry: (values) => - createCategoryRegistryEntry(this.hass, "script", values), + createEntry: async (values) => { + const category = await createCategoryRegistryEntry( + this.hass, + "script", + values + ); + this._bulkAddCategory(category.category_id); + return category; + }, }); } - private _createLabel() { + private _bulkCreateLabel() { showLabelDetailDialog(this, { - createEntry: (values) => createLabelRegistryEntry(this.hass, values), + createEntry: async (values) => { + const label = await createLabelRegistryEntry(this.hass, values); + this._bulkLabel(label.label_id, "add"); + return label; + }, }); } @@ -1012,6 +1054,9 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { return [ haStyle, css` + :host { + display: block; + } hass-tabs-subpage-data-table { --data-table-row-height: 60px; }