diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index d11b038beb0b..c7c8837ae1ce 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -15,6 +15,7 @@ import { mdiPlus, mdiRobotHappy, mdiTag, + mdiTextureBox, mdiToggleSwitch, mdiToggleSwitchOffOutline, mdiTransitConnection, @@ -69,6 +70,7 @@ import type { HaMenu } from "../../../components/ha-menu"; import "../../../components/ha-menu-item"; import "../../../components/ha-sub-menu"; import "../../../components/ha-svg-icon"; +import { createAreaRegistryEntry } from "../../../data/area_registry"; import { AutomationEntity, deleteAutomation, @@ -106,6 +108,7 @@ import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route, ServiceCallResponse } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { turnOnOffEntity } from "../../lovelace/common/entity/turn-on-off-entity"; +import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; @@ -403,6 +406,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { ${this.hass.localize("ui.panel.config.category.editor.add")} `; + const labelItems = html`${this._labels?.map((label) => { const color = label.color ? computeCssColor(label.color) : undefined; const selected = this._selected.every((entityId) => @@ -440,10 +444,45 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { `; - const labelsInOverflow = - (this._sizeController.value && this._sizeController.value < 700) || + const areaItems = html`${Object.values(this.hass.areas).map( + (area) => + html` + ${area.icon + ? html`` + : html``} +
${area.name}
+
` + )} + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.no_area" + )} +
+
+ + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.add_area" + )} +
+
`; + + const areasInOverflow = + (this._sizeController.value && this._sizeController.value < 900) || (!this._sizeController.value && this.hass.dockedSidebar === "docked"); + const labelsInOverflow = + areasInOverflow && + (!this._sizeController.value || this._sizeController.value < 700); + const automations = this._automations( this.automations, this._entityReg, @@ -598,6 +637,22 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { > ${labelItems} + `} + ${areasInOverflow + ? nothing + : html` + + + + ${areaItems} `}` : nothing } @@ -662,6 +717,24 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { ` : nothing } + ${ + this.narrow || areasInOverflow + ? html` + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.move_area" + )} +
+ +
+ ${areaItems} +
` + : nothing + }
@@ -1191,6 +1264,46 @@ ${rejected } } + private async _handleBulkArea(ev) { + const area = ev.currentTarget.value; + this._bulkAddArea(area); + } + + private async _bulkAddArea(area: string) { + const promises: Promise[] = []; + this._selected.forEach((entityId) => { + promises.push( + updateEntityRegistryEntry(this.hass, entityId, { + area_id: area, + }) + ); + }); + const result = await Promise.allSettled(promises); + if (hasRejectedItems(result)) { + const rejected = rejectedItems(result); + showAlertDialog(this, { + title: this.hass.localize("ui.panel.config.common.multiselect.failed", { + number: rejected.length, + }), + text: html`
+${rejected
+            .map((r) => r.reason.message || r.reason.code || r.reason)
+            .join("\r\n")}
`, + }); + } + } + + private async _bulkCreateArea() { + showAreaRegistryDetailDialog(this, { + createEntry: async (values) => { + const area = await createAreaRegistryEntry(this.hass, values); + this._bulkAddArea(area.area_id); + return area; + }, + }); + } + private async _handleBulkEnable() { const promises: Promise[] = []; this._selected.forEach((entityId) => { diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index c77bb1fb503c..2a56d90f1cbe 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -15,6 +15,7 @@ import { mdiPlay, mdiPlus, mdiTag, + mdiTextureBox, } from "@mdi/js"; import { differenceInDays } from "date-fns"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; @@ -61,6 +62,7 @@ import "../../../components/ha-menu-item"; import "../../../components/ha-state-icon"; import "../../../components/ha-sub-menu"; import "../../../components/ha-svg-icon"; +import { createAreaRegistryEntry } from "../../../data/area_registry"; import { CategoryRegistryEntry, createCategoryRegistryEntry, @@ -97,6 +99,7 @@ import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { showToast } from "../../../util/toast"; +import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; @@ -406,6 +409,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { ${this.hass.localize("ui.panel.config.category.editor.add")}
`; + const labelItems = html` ${this._labels?.map((label) => { const color = label.color ? computeCssColor(label.color) : undefined; const selected = this._selected.every((entityId) => @@ -442,9 +446,46 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { ${this.hass.localize("ui.panel.config.labels.add_label")} `; - const labelsInOverflow = - (this._sizeController.value && this._sizeController.value < 700) || + + const areaItems = html`${Object.values(this.hass.areas).map( + (area) => + html` + ${area.icon + ? html`` + : html``} +
${area.name}
+
` + )} + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.no_area" + )} +
+
+ + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.add_area" + )} +
+
`; + + const areasInOverflow = + (this._sizeController.value && this._sizeController.value < 900) || (!this._sizeController.value && this.hass.dockedSidebar === "docked"); + + const labelsInOverflow = + areasInOverflow && + (!this._sizeController.value || this._sizeController.value < 700); + const scenes = this._scenes( this.scenes, this._entityReg, @@ -453,6 +494,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { this._labels, this._filteredScenes ); + return html` ${labelItems} + `} + ${areasInOverflow + ? nothing + : html` + + + + ${areaItems} `}` : nothing} - ${this.narrow || labelsInOverflow + ${this.narrow || areasInOverflow ? html` ${ @@ -630,8 +688,8 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { : nothing } ${ - this.narrow || this.hass.dockedSidebar === "docked" - ? html` + this.narrow || labelsInOverflow + ? html`
${this.hass.localize( @@ -647,6 +705,24 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { ` : nothing } + ${ + this.narrow || areasInOverflow + ? html` + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.move_area" + )} +
+ +
+ ${areaItems} +
` + : nothing + } ` : nothing} ${!this.scenes.length @@ -875,6 +951,46 @@ ${rejected } } + private async _handleBulkArea(ev) { + const area = ev.currentTarget.value; + this._bulkAddArea(area); + } + + private async _bulkAddArea(area: string) { + const promises: Promise[] = []; + this._selected.forEach((entityId) => { + promises.push( + updateEntityRegistryEntry(this.hass, entityId, { + area_id: area, + }) + ); + }); + const result = await Promise.allSettled(promises); + if (hasRejectedItems(result)) { + const rejected = rejectedItems(result); + showAlertDialog(this, { + title: this.hass.localize("ui.panel.config.common.multiselect.failed", { + number: rejected.length, + }), + text: html`
+${rejected
+            .map((r) => r.reason.message || r.reason.code || r.reason)
+            .join("\r\n")}
`, + }); + } + } + + private async _bulkCreateArea() { + showAreaRegistryDetailDialog(this, { + createEntry: async (values) => { + const area = await createAreaRegistryEntry(this.hass, values); + this._bulkAddArea(area.area_id); + return area; + }, + }); + } + private _editCategory(scene: any) { const entityReg = this._entityReg.find( (reg) => reg.entity_id === scene.entity_id diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 2d8f5e70c114..c2435b6ac90e 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -13,6 +13,7 @@ import { mdiPlus, mdiScriptText, mdiTag, + mdiTextureBox, mdiTransitConnection, } from "@mdi/js"; import { differenceInDays } from "date-fns"; @@ -61,6 +62,7 @@ import "../../../components/ha-icon-overflow-menu"; import "../../../components/ha-menu-item"; import "../../../components/ha-sub-menu"; import "../../../components/ha-svg-icon"; +import { createAreaRegistryEntry } from "../../../data/area_registry"; import { CategoryRegistryEntry, createCategoryRegistryEntry, @@ -98,6 +100,7 @@ import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { showToast } from "../../../util/toast"; +import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail"; import { showNewAutomationDialog } from "../automation/show-dialog-new-automation"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; @@ -418,6 +421,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { ${this.hass.localize("ui.panel.config.category.editor.add")}
`; + const labelItems = html`${this._labels?.map((label) => { const color = label.color ? computeCssColor(label.color) : undefined; const selected = this._selected.every((entityId) => @@ -454,9 +458,46 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { ${this.hass.localize("ui.panel.config.labels.add_label")} `; - const labelsInOverflow = - (this._sizeController.value && this._sizeController.value < 700) || + + const areaItems = html`${Object.values(this.hass.areas).map( + (area) => + html` + ${area.icon + ? html`` + : html``} +
${area.name}
+
` + )} + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.no_area" + )} +
+
+ + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.add_area" + )} +
+
`; + + const areasInOverflow = + (this._sizeController.value && this._sizeController.value < 900) || (!this._sizeController.value && this.hass.dockedSidebar === "docked"); + + const labelsInOverflow = + areasInOverflow && + (!this._sizeController.value || this._sizeController.value < 700); + const scripts = this._scripts( this.scripts, this._entityReg, @@ -608,9 +649,25 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { > ${labelItems} +
`} + ${areasInOverflow + ? nothing + : html` + + + + ${areaItems} `}` : nothing} - ${this.narrow || labelsInOverflow + ${this.narrow || areasInOverflow ? html` ${ @@ -656,8 +713,8 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { : nothing } ${ - this.narrow || this.hass.dockedSidebar === "docked" - ? html` + this.narrow || labelsInOverflow + ? html`
${this.hass.localize( @@ -673,6 +730,24 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { ` : nothing } + ${ + this.narrow || areasInOverflow + ? html` + +
+ ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.move_area" + )} +
+ +
+ ${areaItems} +
` + : nothing + } ` : nothing} ${!this.scripts.length @@ -1111,6 +1186,46 @@ ${rejected }); } + private async _handleBulkArea(ev) { + const area = ev.currentTarget.value; + this._bulkAddArea(area); + } + + private async _bulkAddArea(area: string) { + const promises: Promise[] = []; + this._selected.forEach((entityId) => { + promises.push( + updateEntityRegistryEntry(this.hass, entityId, { + area_id: area, + }) + ); + }); + const result = await Promise.allSettled(promises); + if (hasRejectedItems(result)) { + const rejected = rejectedItems(result); + showAlertDialog(this, { + title: this.hass.localize("ui.panel.config.common.multiselect.failed", { + number: rejected.length, + }), + text: html`
+${rejected
+            .map((r) => r.reason.message || r.reason.code || r.reason)
+            .join("\r\n")}
`, + }); + } + } + + private async _bulkCreateArea() { + showAreaRegistryDetailDialog(this, { + createEntry: async (values) => { + const area = await createAreaRegistryEntry(this.hass, values); + this._bulkAddArea(area.area_id); + return area; + }, + }); + } + private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; }