mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-14 15:39:46 +00:00
Compare commits
10 Commits
20240403.0
...
fix-backgr
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cc1a0b24f0 | ||
![]() |
3a4e9b6856 | ||
![]() |
5f5ac5419b | ||
![]() |
92b7a3b477 | ||
![]() |
00837acdfc | ||
![]() |
7704be12b1 | ||
![]() |
712ddb531b | ||
![]() |
d52afc3f71 | ||
![]() |
92f6083e0b | ||
![]() |
5751fdbe56 |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20240403.0"
|
version = "20240403.1"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@@ -92,7 +92,7 @@ export class HaFilterIntegrations extends LitElement {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.expanded) return;
|
if (!this.expanded) return;
|
||||||
this.renderRoot.querySelector("mwc-list")!.style.height =
|
this.renderRoot.querySelector("mwc-list")!.style.height =
|
||||||
`${this.clientHeight - 49}px`;
|
`${this.clientHeight - 49 - 32}px`; // 32px is the height of the search input
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,10 @@ export class HaOutlinedTextField extends MdOutlinedTextField {
|
|||||||
md-outlined-field {
|
md-outlined-field {
|
||||||
background: var(--ha-outlined-text-field-container-color, transparent);
|
background: var(--ha-outlined-text-field-container-color, transparent);
|
||||||
opacity: var(--ha-outlined-text-field-container-opacity, 1);
|
opacity: var(--ha-outlined-text-field-container-opacity, 1);
|
||||||
|
border-start-start-radius: var(--_container-shape-start-start);
|
||||||
|
border-start-end-radius: var(--_container-shape-start-end);
|
||||||
|
border-end-end-radius: var(--_container-shape-end-end);
|
||||||
|
border-end-start-radius: var(--_container-shape-end-start);
|
||||||
}
|
}
|
||||||
.input {
|
.input {
|
||||||
font-family: Roboto, sans-serif;
|
font-family: Roboto, sans-serif;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
|
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||||
import "@material/web/divider/divider";
|
import "@material/web/divider/divider";
|
||||||
import {
|
import {
|
||||||
@@ -153,6 +154,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@query("#overflow-menu") private _overflowMenu!: HaMenu;
|
@query("#overflow-menu") private _overflowMenu!: HaMenu;
|
||||||
|
|
||||||
|
private _sizeController = new ResizeController(this, {
|
||||||
|
callback: (entries) => entries[0]?.contentRect.width,
|
||||||
|
});
|
||||||
|
|
||||||
private _automations = memoizeOne(
|
private _automations = memoizeOne(
|
||||||
(
|
(
|
||||||
automations: AutomationEntity[],
|
automations: AutomationEntity[],
|
||||||
@@ -373,7 +378,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createCategory}>
|
<ha-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
@@ -409,12 +414,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createLabel}>
|
<ha-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
const labelsInOverflow =
|
||||||
|
(this._sizeController.value && this._sizeController.value < 700) ||
|
||||||
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -538,7 +545,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-button-menu-new>
|
||||||
${this.hass.dockedSidebar === "docked"
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-button-menu-new slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
@@ -600,8 +607,8 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
${
|
${
|
||||||
this.narrow || this.hass.dockedSidebar === "docked"
|
this.narrow || labelsInOverflow
|
||||||
? html` <ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@@ -1082,6 +1089,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
private async _handleBulkCategory(ev) {
|
private async _handleBulkCategory(ev) {
|
||||||
const category = ev.currentTarget.value;
|
const category = ev.currentTarget.value;
|
||||||
|
this._bulkAddCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkAddCategory(category: string) {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -1096,6 +1107,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
private async _handleBulkLabel(ev) {
|
private async _handleBulkLabel(ev) {
|
||||||
const label = ev.currentTarget.value;
|
const label = ev.currentTarget.value;
|
||||||
const action = ev.currentTarget.action;
|
const action = ev.currentTarget.action;
|
||||||
|
this._bulkLabel(label, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkLabel(label: string, action: "add" | "remove") {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -1128,17 +1143,28 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createCategory() {
|
private async _bulkCreateCategory() {
|
||||||
showCategoryRegistryDetailDialog(this, {
|
showCategoryRegistryDetailDialog(this, {
|
||||||
scope: "automation",
|
scope: "automation",
|
||||||
createEntry: (values) =>
|
createEntry: async (values) => {
|
||||||
createCategoryRegistryEntry(this.hass, "automation", values),
|
const category = await createCategoryRegistryEntry(
|
||||||
|
this.hass,
|
||||||
|
"automation",
|
||||||
|
values
|
||||||
|
);
|
||||||
|
this._bulkAddCategory(category.category_id);
|
||||||
|
return category;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createLabel() {
|
private _bulkCreateLabel() {
|
||||||
showLabelDetailDialog(this, {
|
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 [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
hass-tabs-subpage-data-table {
|
hass-tabs-subpage-data-table {
|
||||||
--data-table-row-height: 60px;
|
--data-table-row-height: 60px;
|
||||||
}
|
}
|
||||||
|
@@ -575,7 +575,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createLabel}>
|
<ha-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
@@ -801,6 +801,10 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
private async _handleBulkLabel(ev) {
|
private async _handleBulkLabel(ev) {
|
||||||
const label = ev.currentTarget.value;
|
const label = ev.currentTarget.value;
|
||||||
const action = ev.currentTarget.action;
|
const action = ev.currentTarget.action;
|
||||||
|
this._bulkLabel(label, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkLabel(label: string, action: "add" | "remove") {
|
||||||
const promises: Promise<DeviceRegistryEntry>[] = [];
|
const promises: Promise<DeviceRegistryEntry>[] = [];
|
||||||
this._selected.forEach((deviceId) => {
|
this._selected.forEach((deviceId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -817,9 +821,13 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createLabel() {
|
private _bulkCreateLabel() {
|
||||||
showLabelDetailDialog(this, {
|
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;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -88,6 +88,10 @@ import { configSections } from "../ha-panel-config";
|
|||||||
import "../integrations/ha-integration-overflow-menu";
|
import "../integrations/ha-integration-overflow-menu";
|
||||||
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
||||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||||
|
import {
|
||||||
|
EntitySources,
|
||||||
|
fetchEntitySourcesWithCache,
|
||||||
|
} from "../../../data/entity_sources";
|
||||||
|
|
||||||
export interface StateEntity
|
export interface StateEntity
|
||||||
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
||||||
@@ -141,6 +145,8 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
@state()
|
@state()
|
||||||
_labels!: LabelRegistryEntry[];
|
_labels!: LabelRegistryEntry[];
|
||||||
|
|
||||||
|
@state() private _entitySources?: EntitySources;
|
||||||
|
|
||||||
@query("hass-tabs-subpage-data-table", true)
|
@query("hass-tabs-subpage-data-table", true)
|
||||||
private _dataTable!: HaTabsSubpageDataTable;
|
private _dataTable!: HaTabsSubpageDataTable;
|
||||||
|
|
||||||
@@ -405,10 +411,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
const entryIds = entries
|
const entryIds = entries
|
||||||
.filter((entry) => filter.value!.includes(entry.domain))
|
.filter((entry) => filter.value!.includes(entry.domain))
|
||||||
.map((entry) => entry.entry_id);
|
.map((entry) => entry.entry_id);
|
||||||
|
|
||||||
filteredEntities = filteredEntities.filter(
|
filteredEntities = filteredEntities.filter(
|
||||||
(entity) =>
|
(entity) =>
|
||||||
entity.config_entry_id &&
|
filter.value?.includes(entity.platform) ||
|
||||||
entryIds.includes(entity.config_entry_id)
|
(entity.config_entry_id &&
|
||||||
|
entryIds.includes(entity.config_entry_id))
|
||||||
);
|
);
|
||||||
filter.value!.forEach((domain) => filteredDomains.add(domain));
|
filter.value!.forEach((domain) => filteredDomains.add(domain));
|
||||||
} else if (key === "ha-filter-labels" && filter.value?.length) {
|
} else if (key === "ha-filter-labels" && filter.value?.length) {
|
||||||
@@ -547,7 +555,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createLabel}>
|
<ha-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
@@ -807,6 +815,9 @@ ${
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
this._setFiltersFromUrl();
|
this._setFiltersFromUrl();
|
||||||
|
fetchEntitySourcesWithCache(this.hass).then((sources) => {
|
||||||
|
this._entitySources = sources;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setFiltersFromUrl() {
|
private _setFiltersFromUrl() {
|
||||||
@@ -865,14 +876,18 @@ ${
|
|||||||
this._filters = {};
|
this._filters = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues<this>): void {
|
public willUpdate(changedProps: PropertyValues): void {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
const oldHass = changedProps.get("hass");
|
const oldHass = changedProps.get("hass");
|
||||||
let changed = false;
|
let changed = false;
|
||||||
if (!this.hass || !this._entities) {
|
if (!this.hass || !this._entities) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (changedProps.has("hass") || changedProps.has("_entities")) {
|
if (
|
||||||
|
changedProps.has("hass") ||
|
||||||
|
changedProps.has("_entities") ||
|
||||||
|
changedProps.has("_entitySources")
|
||||||
|
) {
|
||||||
const stateEntities: StateEntity[] = [];
|
const stateEntities: StateEntity[] = [];
|
||||||
const regEntityIds = new Set(
|
const regEntityIds = new Set(
|
||||||
this._entities.map((entity) => entity.entity_id)
|
this._entities.map((entity) => entity.entity_id)
|
||||||
@@ -883,6 +898,7 @@ ${
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
|
changedProps.has("_entitySources") ||
|
||||||
this.hass.states[entityId] !== oldHass.states[entityId]
|
this.hass.states[entityId] !== oldHass.states[entityId]
|
||||||
) {
|
) {
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -890,7 +906,8 @@ ${
|
|||||||
stateEntities.push({
|
stateEntities.push({
|
||||||
name: computeStateName(this.hass.states[entityId]),
|
name: computeStateName(this.hass.states[entityId]),
|
||||||
entity_id: entityId,
|
entity_id: entityId,
|
||||||
platform: computeDomain(entityId),
|
platform:
|
||||||
|
this._entitySources?.[entityId]?.domain || computeDomain(entityId),
|
||||||
disabled_by: null,
|
disabled_by: null,
|
||||||
hidden_by: null,
|
hidden_by: null,
|
||||||
area_id: null,
|
area_id: null,
|
||||||
@@ -1027,6 +1044,10 @@ ${
|
|||||||
private async _handleBulkLabel(ev) {
|
private async _handleBulkLabel(ev) {
|
||||||
const label = ev.currentTarget.value;
|
const label = ev.currentTarget.value;
|
||||||
const action = ev.currentTarget.action;
|
const action = ev.currentTarget.action;
|
||||||
|
await this._bulkLabel(label, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkLabel(label: string, action: "add" | "remove") {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
const entityReg =
|
const entityReg =
|
||||||
@@ -1047,6 +1068,16 @@ ${
|
|||||||
await Promise.all(promises);
|
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() {
|
private _removeSelected() {
|
||||||
const removeableEntities = this._selected.filter((entity) => {
|
const removeableEntities = this._selected.filter((entity) => {
|
||||||
const stateObj = this.hass.states[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 {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
|
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||||
import {
|
import {
|
||||||
mdiAlertCircle,
|
mdiAlertCircle,
|
||||||
@@ -83,12 +84,12 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
|||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||||
|
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import "../integrations/ha-integration-overflow-menu";
|
import "../integrations/ha-integration-overflow-menu";
|
||||||
|
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||||
import { isHelperDomain } from "./const";
|
import { isHelperDomain } from "./const";
|
||||||
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
|
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 = {
|
type HelperItem = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -163,6 +164,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _filteredStateItems?: string[] | null;
|
@state() private _filteredStateItems?: string[] | null;
|
||||||
|
|
||||||
|
private _sizeController = new ResizeController(this, {
|
||||||
|
callback: (entries) => entries[0]?.contentRect.width,
|
||||||
|
});
|
||||||
|
|
||||||
public hassSubscribe() {
|
public hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeConfigEntries(
|
subscribeConfigEntries(
|
||||||
@@ -375,6 +380,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
type: configEntry.domain,
|
type: configEntry.domain,
|
||||||
configEntry,
|
configEntry,
|
||||||
entity: undefined,
|
entity: undefined,
|
||||||
|
selectable: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return [...states, ...entries]
|
return [...states, ...entries]
|
||||||
@@ -437,7 +443,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createCategory}>
|
<ha-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
@@ -472,12 +478,14 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
</ha-label>
|
</ha-label>
|
||||||
</ha-menu-item> `;
|
</ha-menu-item> `;
|
||||||
})}<md-divider role="separator" tabindex="-1"></md-divider>
|
})}<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createLabel}>
|
<ha-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
|
const labelsInOverflow =
|
||||||
|
(this._sizeController.value && this._sizeController.value < 700) ||
|
||||||
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -569,7 +577,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-button-menu-new>
|
||||||
${this.hass.dockedSidebar === "docked"
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-button-menu-new slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
@@ -586,7 +594,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-button-menu-new>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || this.hass.dockedSidebar === "docked"
|
${this.narrow || labelsInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-button-menu-new has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
@@ -771,6 +779,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
private async _handleBulkCategory(ev) {
|
private async _handleBulkCategory(ev) {
|
||||||
const category = ev.currentTarget.value;
|
const category = ev.currentTarget.value;
|
||||||
|
this._bulkAddCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkAddCategory(category: string) {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -785,6 +797,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
private async _handleBulkLabel(ev) {
|
private async _handleBulkLabel(ev) {
|
||||||
const label = ev.currentTarget.value;
|
const label = ev.currentTarget.value;
|
||||||
const action = ev.currentTarget.action;
|
const action = ev.currentTarget.action;
|
||||||
|
this._bulkLabel(label, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkLabel(label: string, action: "add" | "remove") {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -939,17 +955,28 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
showHelperDetailDialog(this, {});
|
showHelperDetailDialog(this, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createCategory() {
|
private async _bulkCreateCategory() {
|
||||||
showCategoryRegistryDetailDialog(this, {
|
showCategoryRegistryDetailDialog(this, {
|
||||||
scope: "helpers",
|
scope: "helpers",
|
||||||
createEntry: (values) =>
|
createEntry: async (values) => {
|
||||||
createCategoryRegistryEntry(this.hass, "helpers", values),
|
const category = await createCategoryRegistryEntry(
|
||||||
|
this.hass,
|
||||||
|
"helpers",
|
||||||
|
values
|
||||||
|
);
|
||||||
|
this._bulkAddCategory(category.category_id);
|
||||||
|
return category;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createLabel() {
|
private _bulkCreateLabel() {
|
||||||
showLabelDetailDialog(this, {
|
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 [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
hass-tabs-subpage-data-table {
|
hass-tabs-subpage-data-table {
|
||||||
--data-table-row-height: 60px;
|
--data-table-row-height: 60px;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
|
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||||
import {
|
import {
|
||||||
mdiChevronRight,
|
mdiChevronRight,
|
||||||
|
mdiCog,
|
||||||
mdiContentDuplicate,
|
mdiContentDuplicate,
|
||||||
mdiDelete,
|
mdiDelete,
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
@@ -82,6 +84,7 @@ import {
|
|||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../dialogs/generic/show-dialog-box";
|
} 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 "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
@@ -137,6 +140,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
private _sizeController = new ResizeController(this, {
|
||||||
|
callback: (entries) => entries[0]?.contentRect.width,
|
||||||
|
});
|
||||||
|
|
||||||
private _scenes = memoizeOne(
|
private _scenes = memoizeOne(
|
||||||
(
|
(
|
||||||
scenes: SceneEntity[],
|
scenes: SceneEntity[],
|
||||||
@@ -283,6 +290,13 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
),
|
),
|
||||||
action: () => this._showInfo(scene),
|
action: () => this._showInfo(scene),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: mdiCog,
|
||||||
|
label: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.picker.show_settings"
|
||||||
|
),
|
||||||
|
action: () => this._openSettings(scene),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: mdiPlay,
|
path: mdiPlay,
|
||||||
label: this.hass.localize(
|
label: this.hass.localize(
|
||||||
@@ -367,7 +381,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createCategory}>
|
<ha-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
@@ -403,12 +417,14 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createLabel}>
|
<ha-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
const labelsInOverflow =
|
||||||
|
(this._sizeController.value && this._sizeController.value < 700) ||
|
||||||
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -516,7 +532,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-button-menu-new>
|
||||||
${this.hass.dockedSidebar === "docked"
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-button-menu-new slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
@@ -533,7 +549,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-button-menu-new>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || this.hass.dockedSidebar === "docked"
|
${this.narrow || labelsInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-button-menu-new has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
@@ -760,6 +776,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
private async _handleBulkCategory(ev) {
|
private async _handleBulkCategory(ev) {
|
||||||
const category = ev.currentTarget.value;
|
const category = ev.currentTarget.value;
|
||||||
|
this._bulkAddCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkAddCategory(category: string) {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -774,6 +794,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
private async _handleBulkLabel(ev) {
|
private async _handleBulkLabel(ev) {
|
||||||
const label = ev.currentTarget.value;
|
const label = ev.currentTarget.value;
|
||||||
const action = ev.currentTarget.action;
|
const action = ev.currentTarget.action;
|
||||||
|
this._bulkLabel(label, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkLabel(label: string, action: "add" | "remove") {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -815,6 +839,13 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
fireEvent(this, "hass-more-info", { entityId: scene.entity_id });
|
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) => {
|
private _activateScene = async (scene: SceneEntity) => {
|
||||||
await activateScene(this.hass, scene.entity_id);
|
await activateScene(this.hass, scene.entity_id);
|
||||||
showToast(this, {
|
showToast(this, {
|
||||||
@@ -878,17 +909,28 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createCategory() {
|
private async _bulkCreateCategory() {
|
||||||
showCategoryRegistryDetailDialog(this, {
|
showCategoryRegistryDetailDialog(this, {
|
||||||
scope: "scene",
|
scope: "scene",
|
||||||
createEntry: (values) =>
|
createEntry: async (values) => {
|
||||||
createCategoryRegistryEntry(this.hass, "scene", values),
|
const category = await createCategoryRegistryEntry(
|
||||||
|
this.hass,
|
||||||
|
"scene",
|
||||||
|
values
|
||||||
|
);
|
||||||
|
this._bulkAddCategory(category.category_id);
|
||||||
|
return category;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createLabel() {
|
private _bulkCreateLabel() {
|
||||||
showLabelDetailDialog(this, {
|
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 [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
hass-tabs-subpage-data-table {
|
hass-tabs-subpage-data-table {
|
||||||
--data-table-row-height: 60px;
|
--data-table-row-height: 60px;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
|
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||||
import {
|
import {
|
||||||
mdiChevronRight,
|
mdiChevronRight,
|
||||||
|
mdiCog,
|
||||||
mdiContentDuplicate,
|
mdiContentDuplicate,
|
||||||
mdiDelete,
|
mdiDelete,
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
@@ -83,6 +85,7 @@ import {
|
|||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../dialogs/generic/show-dialog-box";
|
} 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 "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
@@ -141,6 +144,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
private _sizeController = new ResizeController(this, {
|
||||||
|
callback: (entries) => entries[0]?.contentRect.width,
|
||||||
|
});
|
||||||
|
|
||||||
private _scripts = memoizeOne(
|
private _scripts = memoizeOne(
|
||||||
(
|
(
|
||||||
scripts: ScriptEntity[],
|
scripts: ScriptEntity[],
|
||||||
@@ -294,6 +301,13 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
),
|
),
|
||||||
action: () => this._showInfo(script),
|
action: () => this._showInfo(script),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: mdiCog,
|
||||||
|
label: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.picker.show_settings"
|
||||||
|
),
|
||||||
|
action: () => this._openSettings(script),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: mdiTag,
|
path: mdiTag,
|
||||||
label: this.hass.localize(
|
label: this.hass.localize(
|
||||||
@@ -379,7 +393,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
)}
|
)}
|
||||||
</div> </ha-menu-item
|
</div> </ha-menu-item
|
||||||
><md-divider role="separator" tabindex="-1"></md-divider>
|
><md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createCategory}>
|
<ha-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
@@ -415,12 +429,14 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._createLabel}>
|
<ha-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
const labelsInOverflow =
|
||||||
|
(this._sizeController.value && this._sizeController.value < 700) ||
|
||||||
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -542,7 +558,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-button-menu-new>
|
||||||
${this.hass.dockedSidebar === "docked"
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-button-menu-new slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
@@ -559,7 +575,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-button-menu-new>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || this.hass.dockedSidebar === "docked"
|
${this.narrow || labelsInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-button-menu-new has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
@@ -829,6 +845,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
private async _handleBulkCategory(ev) {
|
private async _handleBulkCategory(ev) {
|
||||||
const category = ev.currentTarget.value;
|
const category = ev.currentTarget.value;
|
||||||
|
this._bulkAddCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkAddCategory(category: string) {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -843,6 +863,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
private async _handleBulkLabel(ev) {
|
private async _handleBulkLabel(ev) {
|
||||||
const label = ev.currentTarget.value;
|
const label = ev.currentTarget.value;
|
||||||
const action = ev.currentTarget.action;
|
const action = ev.currentTarget.action;
|
||||||
|
this._bulkLabel(label, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkLabel(label: string, action: "add" | "remove") {
|
||||||
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@@ -895,6 +919,13 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
fireEvent(this, "hass-more-info", { entityId: script.entity_id });
|
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) {
|
private _showTrace(script: any) {
|
||||||
const entry = this.entityRegistry.find(
|
const entry = this.entityRegistry.find(
|
||||||
(e) => e.entity_id === script.entity_id
|
(e) => e.entity_id === script.entity_id
|
||||||
@@ -994,17 +1025,28 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createCategory() {
|
private async _bulkCreateCategory() {
|
||||||
showCategoryRegistryDetailDialog(this, {
|
showCategoryRegistryDetailDialog(this, {
|
||||||
scope: "script",
|
scope: "script",
|
||||||
createEntry: (values) =>
|
createEntry: async (values) => {
|
||||||
createCategoryRegistryEntry(this.hass, "script", values),
|
const category = await createCategoryRegistryEntry(
|
||||||
|
this.hass,
|
||||||
|
"script",
|
||||||
|
values
|
||||||
|
);
|
||||||
|
this._bulkAddCategory(category.category_id);
|
||||||
|
return category;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createLabel() {
|
private _bulkCreateLabel() {
|
||||||
showLabelDetailDialog(this, {
|
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 [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
hass-tabs-subpage-data-table {
|
hass-tabs-subpage-data-table {
|
||||||
--data-table-row-height: 60px;
|
--data-table-row-height: 60px;
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import { property, query, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { ensureArray } from "../../common/array/ensure-array";
|
import { ensureArray } from "../../common/array/ensure-array";
|
||||||
import { storage } from "../../common/decorators/storage";
|
import { storage } from "../../common/decorators/storage";
|
||||||
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import { constructUrlCurrentPath } from "../../common/url/construct-url";
|
import { constructUrlCurrentPath } from "../../common/url/construct-url";
|
||||||
import {
|
import {
|
||||||
@@ -27,37 +28,29 @@ import "../../components/ha-menu-button";
|
|||||||
import "../../components/ha-target-picker";
|
import "../../components/ha-target-picker";
|
||||||
import "../../components/ha-top-app-bar-fixed";
|
import "../../components/ha-top-app-bar-fixed";
|
||||||
import {
|
import {
|
||||||
AreaDeviceLookup,
|
|
||||||
AreaEntityLookup,
|
|
||||||
getAreaDeviceLookup,
|
|
||||||
getAreaEntityLookup,
|
|
||||||
} from "../../data/area_registry";
|
|
||||||
import {
|
|
||||||
DeviceEntityLookup,
|
|
||||||
getDeviceEntityLookup,
|
|
||||||
subscribeDeviceRegistry,
|
|
||||||
} from "../../data/device_registry";
|
|
||||||
import { subscribeEntityRegistry } from "../../data/entity_registry";
|
|
||||||
import {
|
|
||||||
HistoryResult,
|
|
||||||
computeHistory,
|
|
||||||
subscribeHistory,
|
|
||||||
HistoryStates,
|
|
||||||
EntityHistoryState,
|
EntityHistoryState,
|
||||||
|
HistoryResult,
|
||||||
|
HistoryStates,
|
||||||
|
LineChartState,
|
||||||
LineChartUnit,
|
LineChartUnit,
|
||||||
computeGroupKey,
|
computeGroupKey,
|
||||||
LineChartState,
|
computeHistory,
|
||||||
|
subscribeHistory,
|
||||||
} from "../../data/history";
|
} from "../../data/history";
|
||||||
import { fetchStatistics, Statistics } from "../../data/recorder";
|
import { Statistics, fetchStatistics } from "../../data/recorder";
|
||||||
|
import {
|
||||||
|
expandAreaTarget,
|
||||||
|
expandDeviceTarget,
|
||||||
|
expandFloorTarget,
|
||||||
|
expandLabelTarget,
|
||||||
|
} from "../../data/selector";
|
||||||
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../resources/styles";
|
import { haStyle } from "../../resources/styles";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { fileDownload } from "../../util/file_download";
|
import { fileDownload } from "../../util/file_download";
|
||||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
|
||||||
|
|
||||||
class HaPanelHistory extends SubscribeMixin(LitElement) {
|
class HaPanelHistory extends LitElement {
|
||||||
@property({ attribute: false }) hass!: HomeAssistant;
|
@property({ attribute: false }) hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ reflect: true, type: Boolean }) public narrow = false;
|
@property({ reflect: true, type: Boolean }) public narrow = false;
|
||||||
@@ -83,12 +76,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _statisticsHistory?: HistoryResult;
|
@state() private _statisticsHistory?: HistoryResult;
|
||||||
|
|
||||||
@state() private _deviceEntityLookup?: DeviceEntityLookup;
|
|
||||||
|
|
||||||
@state() private _areaEntityLookup?: AreaEntityLookup;
|
|
||||||
|
|
||||||
@state() private _areaDeviceLookup?: AreaDeviceLookup;
|
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private _showBack?: boolean;
|
private _showBack?: boolean;
|
||||||
|
|
||||||
@@ -123,18 +110,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
this._unsubscribeHistory();
|
this._unsubscribeHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
|
||||||
return [
|
|
||||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
|
||||||
this._deviceEntityLookup = getDeviceEntityLookup(entities);
|
|
||||||
this._areaEntityLookup = getAreaEntityLookup(entities);
|
|
||||||
}),
|
|
||||||
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
|
||||||
this._areaDeviceLookup = getAreaDeviceLookup(devices);
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _goBack(): void {
|
private _goBack(): void {
|
||||||
history.back();
|
history.back();
|
||||||
}
|
}
|
||||||
@@ -332,7 +307,9 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
const entityIds = searchParams.entity_id;
|
const entityIds = searchParams.entity_id;
|
||||||
const deviceIds = searchParams.device_id;
|
const deviceIds = searchParams.device_id;
|
||||||
const areaIds = searchParams.area_id;
|
const areaIds = searchParams.area_id;
|
||||||
if (entityIds || deviceIds || areaIds) {
|
const floorIds = searchParams.floor_id;
|
||||||
|
const labelsIds = searchParams.label_id;
|
||||||
|
if (entityIds || deviceIds || areaIds || floorIds || labelsIds) {
|
||||||
this._targetPickerValue = {};
|
this._targetPickerValue = {};
|
||||||
}
|
}
|
||||||
if (entityIds) {
|
if (entityIds) {
|
||||||
@@ -347,6 +324,14 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
const splitIds = areaIds.split(",");
|
const splitIds = areaIds.split(",");
|
||||||
this._targetPickerValue!.area_id = splitIds;
|
this._targetPickerValue!.area_id = splitIds;
|
||||||
}
|
}
|
||||||
|
if (floorIds) {
|
||||||
|
const splitIds = floorIds.split(",");
|
||||||
|
this._targetPickerValue!.floor_id = splitIds;
|
||||||
|
}
|
||||||
|
if (labelsIds) {
|
||||||
|
const splitIds = labelsIds.split(",");
|
||||||
|
this._targetPickerValue!.label_id = splitIds;
|
||||||
|
}
|
||||||
|
|
||||||
const startDate = searchParams.start_date;
|
const startDate = searchParams.start_date;
|
||||||
if (startDate) {
|
if (startDate) {
|
||||||
@@ -522,95 +507,77 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
private _getEntityIds(): string[] {
|
private _getEntityIds(): string[] {
|
||||||
return this.__getEntityIds(
|
return this.__getEntityIds(
|
||||||
this._targetPickerValue,
|
this._targetPickerValue,
|
||||||
this._deviceEntityLookup,
|
this.hass.entities,
|
||||||
this._areaEntityLookup,
|
this.hass.devices,
|
||||||
this._areaDeviceLookup
|
this.hass.areas
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private __getEntityIds = memoizeOne(
|
private __getEntityIds = memoizeOne(
|
||||||
(
|
(
|
||||||
targetPickerValue: HassServiceTarget,
|
targetPickerValue: HassServiceTarget,
|
||||||
deviceEntityLookup: DeviceEntityLookup | undefined,
|
entities: HomeAssistant["entities"],
|
||||||
areaEntityLookup: AreaEntityLookup | undefined,
|
devices: HomeAssistant["devices"],
|
||||||
areaDeviceLookup: AreaDeviceLookup | undefined
|
areas: HomeAssistant["areas"]
|
||||||
): string[] => {
|
): string[] => {
|
||||||
if (
|
if (!targetPickerValue) {
|
||||||
!targetPickerValue ||
|
|
||||||
deviceEntityLookup === undefined ||
|
|
||||||
areaEntityLookup === undefined ||
|
|
||||||
areaDeviceLookup === undefined
|
|
||||||
) {
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const entityIds = new Set<string>();
|
const targetSelector = { target: {} };
|
||||||
let {
|
const targetEntities = new Set(ensureArray(targetPickerValue.entity_id));
|
||||||
area_id: searchingAreaId,
|
const targetDevices = new Set(ensureArray(targetPickerValue.device_id));
|
||||||
device_id: searchingDeviceId,
|
const targetAreas = new Set(ensureArray(targetPickerValue.area_id));
|
||||||
entity_id: searchingEntityId,
|
const targetFloors = new Set(ensureArray(targetPickerValue.floor_id));
|
||||||
} = targetPickerValue;
|
const targetLabels = new Set(ensureArray(targetPickerValue.label_id));
|
||||||
|
|
||||||
if (searchingAreaId) {
|
targetLabels.forEach((labelId) => {
|
||||||
searchingAreaId = ensureArray(searchingAreaId);
|
const expanded = expandLabelTarget(
|
||||||
for (const singleSearchingAreaId of searchingAreaId) {
|
this.hass,
|
||||||
const foundEntities = areaEntityLookup[singleSearchingAreaId];
|
labelId,
|
||||||
if (foundEntities?.length) {
|
areas,
|
||||||
for (const foundEntity of foundEntities) {
|
devices,
|
||||||
if (foundEntity.entity_category === null) {
|
entities,
|
||||||
entityIds.add(foundEntity.entity_id);
|
targetSelector
|
||||||
}
|
);
|
||||||
}
|
expanded.devices.forEach((id) => targetDevices.add(id));
|
||||||
}
|
expanded.entities.forEach((id) => targetEntities.add(id));
|
||||||
|
expanded.areas.forEach((id) => targetAreas.add(id));
|
||||||
|
});
|
||||||
|
|
||||||
const foundDevices = areaDeviceLookup[singleSearchingAreaId];
|
targetFloors.forEach((floorId) => {
|
||||||
if (!foundDevices?.length) {
|
const expanded = expandFloorTarget(
|
||||||
continue;
|
this.hass,
|
||||||
}
|
floorId,
|
||||||
|
areas,
|
||||||
|
targetSelector
|
||||||
|
);
|
||||||
|
expanded.areas.forEach((id) => targetAreas.add(id));
|
||||||
|
});
|
||||||
|
|
||||||
for (const foundDevice of foundDevices) {
|
targetAreas.forEach((areaId) => {
|
||||||
const foundDeviceEntities = deviceEntityLookup[foundDevice.id];
|
const expanded = expandAreaTarget(
|
||||||
if (!foundDeviceEntities?.length) {
|
this.hass,
|
||||||
continue;
|
areaId,
|
||||||
}
|
devices,
|
||||||
|
entities,
|
||||||
|
targetSelector
|
||||||
|
);
|
||||||
|
expanded.devices.forEach((id) => targetDevices.add(id));
|
||||||
|
expanded.entities.forEach((id) => targetEntities.add(id));
|
||||||
|
});
|
||||||
|
|
||||||
for (const foundDeviceEntity of foundDeviceEntities) {
|
targetDevices.forEach((deviceId) => {
|
||||||
if (
|
const expanded = expandDeviceTarget(
|
||||||
(!foundDeviceEntity.area_id ||
|
this.hass,
|
||||||
foundDeviceEntity.area_id === singleSearchingAreaId) &&
|
deviceId,
|
||||||
foundDeviceEntity.entity_category === null
|
entities,
|
||||||
) {
|
targetSelector
|
||||||
entityIds.add(foundDeviceEntity.entity_id);
|
);
|
||||||
}
|
expanded.entities.forEach((id) => targetEntities.add(id));
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchingDeviceId) {
|
return Array.from(targetEntities);
|
||||||
searchingDeviceId = ensureArray(searchingDeviceId);
|
|
||||||
for (const singleSearchingDeviceId of searchingDeviceId) {
|
|
||||||
const foundEntities = deviceEntityLookup[singleSearchingDeviceId];
|
|
||||||
if (!foundEntities?.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const foundEntity of foundEntities) {
|
|
||||||
if (foundEntity.entity_category === null) {
|
|
||||||
entityIds.add(foundEntity.entity_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchingEntityId) {
|
|
||||||
searchingEntityId = ensureArray(searchingEntityId);
|
|
||||||
for (const singleSearchingEntityId of searchingEntityId) {
|
|
||||||
entityIds.add(singleSearchingEntityId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [...entityIds];
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -639,6 +606,12 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
","
|
","
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (this._targetPickerValue.label_id) {
|
||||||
|
params.label_id = ensureArray(this._targetPickerValue.label_id).join(",");
|
||||||
|
}
|
||||||
|
if (this._targetPickerValue.floor_id) {
|
||||||
|
params.floor_id = ensureArray(this._targetPickerValue.floor_id).join(",");
|
||||||
|
}
|
||||||
if (this._targetPickerValue.area_id) {
|
if (this._targetPickerValue.area_id) {
|
||||||
params.area_id = ensureArray(this._targetPickerValue.area_id).join(",");
|
params.area_id = ensureArray(this._targetPickerValue.area_id).join(",");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user