Simplify data table template (#17825)
* Simplify data table template * Fix backup and gallery
This commit is contained in:
parent
5e107d43d7
commit
3349031cbd
|
@ -343,7 +343,7 @@ export class DemoEntityState extends LitElement {
|
|||
const columns: DataTableColumnContainer<EntityRowData> = {
|
||||
icon: {
|
||||
title: "Icon",
|
||||
template: (_, entry) => html`
|
||||
template: (entry) => html`
|
||||
<state-badge
|
||||
.stateObj=${entry.stateObj}
|
||||
.stateColor=${true}
|
||||
|
@ -360,7 +360,7 @@ export class DemoEntityState extends LitElement {
|
|||
title: "State",
|
||||
width: "20%",
|
||||
sortable: true,
|
||||
template: (_, entry) =>
|
||||
template: (entry) =>
|
||||
html`${computeStateDisplay(
|
||||
hass.localize,
|
||||
entry.stateObj,
|
||||
|
@ -371,14 +371,14 @@ export class DemoEntityState extends LitElement {
|
|||
},
|
||||
device_class: {
|
||||
title: "Device class",
|
||||
template: (dc) => html`${dc ?? "-"}`,
|
||||
template: (entry) => html`${entry.device_class ?? "-"}`,
|
||||
width: "20%",
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
domain: {
|
||||
title: "Domain",
|
||||
template: (_, entry) => html`${computeDomain(entry.entity_id)}`,
|
||||
template: (entry) => html`${computeDomain(entry.entity_id)}`,
|
||||
width: "20%",
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
|
|
|
@ -49,6 +49,10 @@ import { showHassioCreateBackupDialog } from "../dialogs/backup/show-dialog-hass
|
|||
import { supervisorTabs } from "../hassio-tabs";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
|
||||
type BackupItem = HassioBackup & {
|
||||
secondary: string;
|
||||
};
|
||||
|
||||
@customElement("hassio-backups")
|
||||
export class HassioBackups extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
@ -117,15 +121,15 @@ export class HassioBackups extends LitElement {
|
|||
}
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean): DataTableColumnContainer => ({
|
||||
(narrow: boolean): DataTableColumnContainer<BackupItem> => ({
|
||||
name: {
|
||||
title: this.supervisor.localize("backup.name"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
template: (entry: string, backup: any) =>
|
||||
html`${entry || backup.slug}
|
||||
template: (backup) =>
|
||||
html`${backup.name || backup.slug}
|
||||
<div class="secondary">${backup.secondary}</div>`,
|
||||
},
|
||||
size: {
|
||||
|
@ -134,7 +138,7 @@ export class HassioBackups extends LitElement {
|
|||
hidden: narrow,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
template: (entry: number) => Math.ceil(entry * 10) / 10 + " MB",
|
||||
template: (backup) => Math.ceil(backup.size * 10) / 10 + " MB",
|
||||
},
|
||||
location: {
|
||||
title: this.supervisor.localize("backup.location"),
|
||||
|
@ -142,8 +146,8 @@ export class HassioBackups extends LitElement {
|
|||
hidden: narrow,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
template: (entry: string | null) =>
|
||||
entry || this.supervisor.localize("backup.data_disk"),
|
||||
template: (backup) =>
|
||||
backup.location || this.supervisor.localize("backup.data_disk"),
|
||||
},
|
||||
date: {
|
||||
title: this.supervisor.localize("backup.created"),
|
||||
|
@ -152,8 +156,8 @@ export class HassioBackups extends LitElement {
|
|||
hidden: narrow,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
template: (entry: string) =>
|
||||
relativeTime(new Date(entry), this.hass.locale),
|
||||
template: (backup) =>
|
||||
relativeTime(new Date(backup.date), this.hass.locale),
|
||||
},
|
||||
secondary: {
|
||||
title: "",
|
||||
|
@ -163,7 +167,7 @@ export class HassioBackups extends LitElement {
|
|||
})
|
||||
);
|
||||
|
||||
private _backupData = memoizeOne((backups: HassioBackup[]) =>
|
||||
private _backupData = memoizeOne((backups: HassioBackup[]): BackupItem[] =>
|
||||
backups.map((backup) => ({
|
||||
...backup,
|
||||
secondary: this._computeBackupContent(backup),
|
||||
|
|
|
@ -74,7 +74,7 @@ export interface DataTableColumnData<T = any> extends DataTableSortColumnData {
|
|||
title: TemplateResult | string;
|
||||
label?: TemplateResult | string;
|
||||
type?: "numeric" | "icon" | "icon-button" | "overflow-menu" | "flex";
|
||||
template?: (data: any, row: T) => TemplateResult | string | typeof nothing;
|
||||
template?: (row: T) => TemplateResult | string | typeof nothing;
|
||||
width?: string;
|
||||
maxWidth?: string;
|
||||
grows?: boolean;
|
||||
|
@ -431,7 +431,7 @@ export class HaDataTable extends LitElement {
|
|||
})
|
||||
: ""}
|
||||
>
|
||||
${column.template ? column.template(row[key], row) : row[key]}
|
||||
${column.template ? column.template(row) : row[key]}
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
|
|
|
@ -62,17 +62,16 @@ export class HaConfigApplicationCredentials extends LitElement {
|
|||
),
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (_, entry: ApplicationCredential) => html`${entry.name}`,
|
||||
template: (entry) => html`${entry.name}`,
|
||||
},
|
||||
clientId: {
|
||||
client_id: {
|
||||
title: localize(
|
||||
"ui.panel.config.application_credentials.picker.headers.client_id"
|
||||
),
|
||||
width: "30%",
|
||||
direction: "asc",
|
||||
hidden: narrow,
|
||||
template: (_, entry: ApplicationCredential) =>
|
||||
html`${entry.client_id}`,
|
||||
template: (entry) => html`${entry.client_id}`,
|
||||
},
|
||||
application: {
|
||||
title: localize(
|
||||
|
@ -81,7 +80,7 @@ export class HaConfigApplicationCredentials extends LitElement {
|
|||
sortable: true,
|
||||
width: "30%",
|
||||
direction: "asc",
|
||||
template: (_, entry) => html`${domainToName(localize, entry.domain)}`,
|
||||
template: (entry) => html`${domainToName(localize, entry.domain)}`,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,12 @@ import { findRelated } from "../../../data/search";
|
|||
import { fetchBlueprints } from "../../../data/blueprint";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
|
||||
type AutomationItem = AutomationEntity & {
|
||||
name: string;
|
||||
last_triggered?: string | undefined;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
@customElement("ha-automation-picker")
|
||||
class HaAutomationPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
@ -79,7 +85,7 @@ class HaAutomationPicker extends LitElement {
|
|||
(
|
||||
automations: AutomationEntity[],
|
||||
filteredAutomations?: string[] | null
|
||||
) => {
|
||||
): AutomationItem[] => {
|
||||
if (filteredAutomations === null) {
|
||||
return [];
|
||||
}
|
||||
|
@ -100,14 +106,14 @@ class HaAutomationPicker extends LitElement {
|
|||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean, _locale): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
const columns: DataTableColumnContainer<AutomationItem> = {
|
||||
icon: {
|
||||
title: "",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.automation.picker.headers.state"
|
||||
),
|
||||
type: "icon",
|
||||
template: (_, automation) =>
|
||||
template: (automation) =>
|
||||
html`<ha-state-icon
|
||||
.state=${automation}
|
||||
style=${styleMap({
|
||||
|
@ -128,12 +134,12 @@ class HaAutomationPicker extends LitElement {
|
|||
direction: "asc",
|
||||
grows: true,
|
||||
template: narrow
|
||||
? (name, automation: any) => {
|
||||
? (automation) => {
|
||||
const date = new Date(automation.attributes.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
return html`
|
||||
${name}
|
||||
${automation.name}
|
||||
<div class="secondary">
|
||||
${this.hass.localize("ui.card.automation.last_triggered")}:
|
||||
${automation.attributes.last_triggered
|
||||
|
@ -156,20 +162,17 @@ class HaAutomationPicker extends LitElement {
|
|||
sortable: true,
|
||||
width: "20%",
|
||||
title: this.hass.localize("ui.card.automation.last_triggered"),
|
||||
template: (last_triggered) => {
|
||||
const date = new Date(last_triggered);
|
||||
template: (automation) => {
|
||||
if (!automation.last_triggered) {
|
||||
return this.hass.localize("ui.components.relative_time.never");
|
||||
}
|
||||
const date = new Date(automation.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
return html`
|
||||
${last_triggered
|
||||
? dayDifference > 3
|
||||
? formatShortDateTime(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: relativeTime(date, this.hass.locale)
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
${dayDifference > 3
|
||||
? formatShortDateTime(date, this.hass.locale, this.hass.config)
|
||||
: relativeTime(date, this.hass.locale)}
|
||||
`;
|
||||
},
|
||||
};
|
||||
|
@ -178,8 +181,8 @@ class HaAutomationPicker extends LitElement {
|
|||
columns.disabled = this.narrow
|
||||
? {
|
||||
title: "",
|
||||
template: (disabled: boolean) =>
|
||||
disabled
|
||||
template: (automation) =>
|
||||
automation.disabled
|
||||
? html`
|
||||
<simple-tooltip animation-delay="0" position="left">
|
||||
${this.hass.localize(
|
||||
|
@ -196,8 +199,8 @@ class HaAutomationPicker extends LitElement {
|
|||
: {
|
||||
width: "20%",
|
||||
title: "",
|
||||
template: (disabled: boolean) =>
|
||||
disabled
|
||||
template: (automation) =>
|
||||
automation.disabled
|
||||
? html`
|
||||
<ha-chip>
|
||||
${this.hass.localize(
|
||||
|
@ -212,7 +215,7 @@ class HaAutomationPicker extends LitElement {
|
|||
title: "",
|
||||
width: this.narrow ? undefined : "10%",
|
||||
type: "overflow-menu",
|
||||
template: (_: string, automation: any) => html`
|
||||
template: (automation) => html`
|
||||
<ha-icon-overflow-menu
|
||||
.hass=${this.hass}
|
||||
narrow
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { mdiDelete, mdiDownload, mdiPlus } from "@mdi/js";
|
||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||
import { mdiDelete, mdiDownload, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoize from "memoize-one";
|
||||
|
@ -48,15 +48,15 @@ class HaConfigBackup extends LitElement {
|
|||
@state() private _backupData?: BackupData;
|
||||
|
||||
private _columns = memoize(
|
||||
(narrow, _language): DataTableColumnContainer => ({
|
||||
(narrow, _language): DataTableColumnContainer<BackupContent> => ({
|
||||
name: {
|
||||
title: this.hass.localize("ui.panel.config.backup.name"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
template: (entry: string, backup: BackupContent) =>
|
||||
html`${entry}
|
||||
template: (backup) =>
|
||||
html`${backup.name}
|
||||
<div class="secondary">${backup.path}</div>`,
|
||||
},
|
||||
size: {
|
||||
|
@ -65,7 +65,7 @@ class HaConfigBackup extends LitElement {
|
|||
hidden: narrow,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
template: (entry: number) => Math.ceil(entry * 10) / 10 + " MB",
|
||||
template: (backup) => Math.ceil(backup.size * 10) / 10 + " MB",
|
||||
},
|
||||
date: {
|
||||
title: this.hass.localize("ui.panel.config.backup.created"),
|
||||
|
@ -74,15 +74,15 @@ class HaConfigBackup extends LitElement {
|
|||
hidden: narrow,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
template: (entry: string) =>
|
||||
relativeTime(new Date(entry), this.hass.locale),
|
||||
template: (backup) =>
|
||||
relativeTime(new Date(backup.date), this.hass.locale),
|
||||
},
|
||||
|
||||
actions: {
|
||||
title: "",
|
||||
width: "15%",
|
||||
type: "overflow-menu",
|
||||
template: (_: string, backup: BackupContent) =>
|
||||
template: (backup) =>
|
||||
html`<ha-icon-overflow-menu
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
|
|
|
@ -10,14 +10,14 @@ import {
|
|||
} from "@mdi/js";
|
||||
import {
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
html,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
|
@ -32,7 +32,6 @@ import "../../../components/ha-icon-overflow-menu";
|
|||
import "../../../components/ha-svg-icon";
|
||||
import { showAutomationEditor } from "../../../data/automation";
|
||||
import {
|
||||
BlueprintDomain,
|
||||
BlueprintMetaData,
|
||||
Blueprints,
|
||||
deleteBlueprint,
|
||||
|
@ -50,10 +49,12 @@ import { documentationUrl } from "../../../util/documentation-url";
|
|||
import { configSections } from "../ha-panel-config";
|
||||
import { showAddBlueprintDialog } from "./show-dialog-import-blueprint";
|
||||
|
||||
interface BlueprintMetaDataPath extends BlueprintMetaData {
|
||||
type BlueprintMetaDataPath = BlueprintMetaData & {
|
||||
path: string;
|
||||
error: boolean;
|
||||
}
|
||||
type: "automation" | "script";
|
||||
fullpath: string;
|
||||
};
|
||||
|
||||
const createNewFunctions = {
|
||||
automation: (blueprintMeta: BlueprintMetaDataPath) => {
|
||||
|
@ -86,7 +87,7 @@ class HaBlueprintOverview extends LitElement {
|
|||
>;
|
||||
|
||||
private _processedBlueprints = memoizeOne(
|
||||
(blueprints: Record<string, Blueprints>) => {
|
||||
(blueprints: Record<string, Blueprints>): BlueprintMetaDataPath[] => {
|
||||
const result: any[] = [];
|
||||
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
|
||||
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
|
||||
|
@ -125,9 +126,9 @@ class HaBlueprintOverview extends LitElement {
|
|||
direction: "asc",
|
||||
grows: true,
|
||||
template: narrow
|
||||
? (name, entity: any) => html`
|
||||
${name}<br />
|
||||
<div class="secondary">${entity.path}</div>
|
||||
? (blueprint) => html`
|
||||
${blueprint.name}<br />
|
||||
<div class="secondary">${blueprint.path}</div>
|
||||
`
|
||||
: undefined,
|
||||
},
|
||||
|
@ -135,9 +136,9 @@ class HaBlueprintOverview extends LitElement {
|
|||
title: this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.headers.type"
|
||||
),
|
||||
template: (type: BlueprintDomain) =>
|
||||
template: (blueprint) =>
|
||||
html`${this.hass.localize(
|
||||
`ui.panel.config.blueprint.overview.types.${type}`
|
||||
`ui.panel.config.blueprint.overview.types.${blueprint.type}`
|
||||
)}`,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
|
@ -163,7 +164,7 @@ class HaBlueprintOverview extends LitElement {
|
|||
title: "",
|
||||
width: this.narrow ? undefined : "10%",
|
||||
type: "overflow-menu",
|
||||
template: (_: string, blueprint) =>
|
||||
template: (blueprint) =>
|
||||
blueprint.error
|
||||
? html`<ha-svg-icon
|
||||
style="color: var(--error-color); display: block; margin-inline-end: 12px; margin-inline-start: auto;"
|
||||
|
@ -177,7 +178,7 @@ class HaBlueprintOverview extends LitElement {
|
|||
{
|
||||
path: mdiPlus,
|
||||
label: this.hass.localize(
|
||||
`ui.panel.config.blueprint.overview.create_${blueprint.domain}`
|
||||
`ui.panel.config.blueprint.overview.create_${blueprint.type}`
|
||||
),
|
||||
action: () => this._createNew(blueprint),
|
||||
},
|
||||
|
@ -324,7 +325,7 @@ class HaBlueprintOverview extends LitElement {
|
|||
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||
const blueprint = this._processedBlueprints(this.blueprints).find(
|
||||
(b) => b.fullpath === ev.detail.id
|
||||
);
|
||||
)!;
|
||||
if (blueprint.error) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.blueprint.overview.error", {
|
||||
|
|
|
@ -2,27 +2,26 @@ import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
|||
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||
import { mdiCancel, mdiFilterVariant, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import {
|
||||
protocolIntegrationPicked,
|
||||
PROTOCOL_INTEGRATIONS,
|
||||
protocolIntegrationPicked,
|
||||
} from "../../../common/integrations/protocolIntegrationPicked";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import {
|
||||
DataTableColumnContainer,
|
||||
DataTableRowData,
|
||||
RowClickedEvent,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/entity/ha-battery-icon";
|
||||
|
@ -33,9 +32,9 @@ import "../../../components/ha-icon-button";
|
|||
import { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import { ConfigEntry, sortConfigEntries } from "../../../data/config_entries";
|
||||
import {
|
||||
computeDeviceName,
|
||||
DeviceEntityLookup,
|
||||
DeviceRegistryEntry,
|
||||
computeDeviceName,
|
||||
} from "../../../data/device_registry";
|
||||
import {
|
||||
EntityRegistryEntry,
|
||||
|
@ -231,7 +230,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||
outputDevices = outputDevices.filter((device) => !device.disabled_by);
|
||||
}
|
||||
|
||||
outputDevices = outputDevices.map((device) => {
|
||||
const formattedOutputDevices = outputDevices.map((device) => {
|
||||
const deviceEntries = sortConfigEntries(
|
||||
device.config_entries
|
||||
.filter((entId) => entId in entryLookup)
|
||||
|
@ -277,156 +276,153 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||
};
|
||||
});
|
||||
|
||||
this._numHiddenDevices = startLength - outputDevices.length;
|
||||
this._numHiddenDevices = startLength - formattedOutputDevices.length;
|
||||
return {
|
||||
devicesOutput: outputDevices,
|
||||
devicesOutput: formattedOutputDevices,
|
||||
filteredConfigEntry: filterConfigEntry,
|
||||
filteredDomains,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean, showDisabled: boolean): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
icon: {
|
||||
title: "",
|
||||
type: "icon",
|
||||
template: (_icon, device) =>
|
||||
device.domains.length
|
||||
? html`<img
|
||||
alt=""
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain: device.domains[0],
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
/>`
|
||||
: "",
|
||||
},
|
||||
};
|
||||
private _columns = memoizeOne((narrow: boolean, showDisabled: boolean) => {
|
||||
type DeviceItem = ReturnType<
|
||||
typeof this._devicesAndFilterDomains
|
||||
>["devicesOutput"][number];
|
||||
|
||||
if (narrow) {
|
||||
columns.name = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.device"
|
||||
),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (name, device: DataTableRowData) => html`
|
||||
${name}
|
||||
<div class="secondary">${device.area} | ${device.integration}</div>
|
||||
`,
|
||||
};
|
||||
} else {
|
||||
columns.name = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.device"
|
||||
),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
direction: "asc",
|
||||
};
|
||||
}
|
||||
const columns: DataTableColumnContainer<DeviceItem> = {
|
||||
icon: {
|
||||
title: "",
|
||||
type: "icon",
|
||||
template: (device) =>
|
||||
device.domains.length
|
||||
? html`<img
|
||||
alt=""
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain: device.domains[0],
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
/>`
|
||||
: "",
|
||||
},
|
||||
};
|
||||
|
||||
columns.manufacturer = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.manufacturer"
|
||||
),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.model = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.model"),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.area = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.area"),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.integration = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.integration"
|
||||
),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.battery_entity = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.battery"),
|
||||
if (narrow) {
|
||||
columns.name = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.device"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: "numeric",
|
||||
width: narrow ? "95px" : "15%",
|
||||
maxWidth: "95px",
|
||||
valueColumn: "battery_level",
|
||||
template: (batteryEntityPair: DeviceRowData["battery_entity"]) => {
|
||||
const battery =
|
||||
batteryEntityPair && batteryEntityPair[0]
|
||||
? this.hass.states[batteryEntityPair[0]]
|
||||
: undefined;
|
||||
const batteryDomain = battery
|
||||
? computeStateDomain(battery)
|
||||
: undefined;
|
||||
const batteryCharging =
|
||||
batteryEntityPair && batteryEntityPair[1]
|
||||
? this.hass.states[batteryEntityPair[1]]
|
||||
: undefined;
|
||||
|
||||
return battery &&
|
||||
(batteryDomain === "binary_sensor" || !isNaN(battery.state as any))
|
||||
? html`
|
||||
${batteryDomain === "sensor"
|
||||
? this.hass.formatEntityState(battery)
|
||||
: nothing}
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass}
|
||||
.batteryStateObj=${battery}
|
||||
.batteryChargingStateObj=${batteryCharging}
|
||||
></ha-battery-icon>
|
||||
`
|
||||
: html`—`;
|
||||
},
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (device) => html`
|
||||
${device.name}
|
||||
<div class="secondary">${device.area} | ${device.integration}</div>
|
||||
`,
|
||||
};
|
||||
} else {
|
||||
columns.name = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.device"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
direction: "asc",
|
||||
};
|
||||
if (showDisabled) {
|
||||
columns.disabled_by = {
|
||||
title: "",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.disabled_by"
|
||||
),
|
||||
type: "icon",
|
||||
template: (disabled_by) =>
|
||||
disabled_by
|
||||
? html`<div
|
||||
tabindex="0"
|
||||
style="display:inline-block; position: relative;"
|
||||
>
|
||||
<ha-svg-icon .path=${mdiCancel}></ha-svg-icon>
|
||||
<simple-tooltip animation-delay="0" position="left">
|
||||
${this.hass.localize("ui.panel.config.devices.disabled")}
|
||||
</simple-tooltip>
|
||||
</div>`
|
||||
: "—",
|
||||
};
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
);
|
||||
|
||||
columns.manufacturer = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.manufacturer"
|
||||
),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.model = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.model"),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.area = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.area"),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.integration = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.integration"
|
||||
),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.battery_entity = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.battery"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: "numeric",
|
||||
width: narrow ? "95px" : "15%",
|
||||
maxWidth: "95px",
|
||||
valueColumn: "battery_level",
|
||||
template: (device) => {
|
||||
const batteryEntityPair = device.battery_entity;
|
||||
const battery =
|
||||
batteryEntityPair && batteryEntityPair[0]
|
||||
? this.hass.states[batteryEntityPair[0]]
|
||||
: undefined;
|
||||
const batteryDomain = battery ? computeStateDomain(battery) : undefined;
|
||||
const batteryCharging =
|
||||
batteryEntityPair && batteryEntityPair[1]
|
||||
? this.hass.states[batteryEntityPair[1]]
|
||||
: undefined;
|
||||
|
||||
return battery &&
|
||||
(batteryDomain === "binary_sensor" || !isNaN(battery.state as any))
|
||||
? html`
|
||||
${batteryDomain === "sensor"
|
||||
? this.hass.formatEntityState(battery)
|
||||
: nothing}
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass}
|
||||
.batteryStateObj=${battery}
|
||||
.batteryChargingStateObj=${batteryCharging}
|
||||
></ha-battery-icon>
|
||||
`
|
||||
: html`—`;
|
||||
},
|
||||
};
|
||||
if (showDisabled) {
|
||||
columns.disabled_by = {
|
||||
title: "",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.disabled_by"
|
||||
),
|
||||
type: "icon",
|
||||
template: (device) =>
|
||||
device.disabled_by
|
||||
? html`<div
|
||||
tabindex="0"
|
||||
style="display:inline-block; position: relative;"
|
||||
>
|
||||
<ha-svg-icon .path=${mdiCancel}></ha-svg-icon>
|
||||
<simple-tooltip animation-delay="0" position="left">
|
||||
${this.hass.localize("ui.panel.config.devices.disabled")}
|
||||
</simple-tooltip>
|
||||
</div>`
|
||||
: "—",
|
||||
};
|
||||
}
|
||||
return columns;
|
||||
});
|
||||
|
||||
public willUpdate(changedProps) {
|
||||
if (changedProps.has("_searchParms")) {
|
||||
|
|
|
@ -183,7 +183,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||
"ui.panel.config.entities.picker.headers.state_icon"
|
||||
),
|
||||
type: "icon",
|
||||
template: (_, entry: EntityRow) => html`
|
||||
template: (entry) => html`
|
||||
<ha-state-icon
|
||||
title=${ifDefined(entry.entity?.state)}
|
||||
slot="item-icon"
|
||||
|
@ -201,12 +201,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||
direction: "asc",
|
||||
grows: true,
|
||||
template: narrow
|
||||
? (name, entity: EntityRow) => html`
|
||||
${name}<br />
|
||||
? (entry) => html`
|
||||
${entry.name}<br />
|
||||
<div class="secondary">
|
||||
${entity.entity_id} |
|
||||
${this.hass.localize(`component.${entity.platform}.title`) ||
|
||||
entity.platform}
|
||||
${entry.entity_id} |
|
||||
${this.hass.localize(`component.${entry.platform}.title`) ||
|
||||
entry.platform}
|
||||
</div>
|
||||
`
|
||||
: undefined,
|
||||
|
@ -228,8 +228,9 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||
sortable: true,
|
||||
filterable: true,
|
||||
width: "20%",
|
||||
template: (platform) =>
|
||||
this.hass.localize(`component.${platform}.title`) || platform,
|
||||
template: (entry) =>
|
||||
this.hass.localize(`component.${entry.platform}.title`) ||
|
||||
entry.platform,
|
||||
},
|
||||
area: {
|
||||
title: this.hass.localize(
|
||||
|
@ -248,10 +249,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||
hidden: narrow || !showDisabled,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
template: (disabled_by: EntityRegistryEntry["disabled_by"]) =>
|
||||
disabled_by === null
|
||||
template: (entry) =>
|
||||
entry.disabled_by === null
|
||||
? "—"
|
||||
: this.hass.localize(`config_entry.disabled_by.${disabled_by}`),
|
||||
: this.hass.localize(
|
||||
`config_entry.disabled_by.${entry.disabled_by}`
|
||||
),
|
||||
},
|
||||
status: {
|
||||
title: this.hass.localize(
|
||||
|
@ -261,11 +264,11 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||
sortable: true,
|
||||
filterable: true,
|
||||
width: "68px",
|
||||
template: (_status, entity: EntityRow) =>
|
||||
entity.unavailable ||
|
||||
entity.disabled_by ||
|
||||
entity.hidden_by ||
|
||||
entity.readonly
|
||||
template: (entry) =>
|
||||
entry.unavailable ||
|
||||
entry.disabled_by ||
|
||||
entry.hidden_by ||
|
||||
entry.readonly
|
||||
? html`
|
||||
<div
|
||||
tabindex="0"
|
||||
|
@ -273,32 +276,32 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||
>
|
||||
<ha-svg-icon
|
||||
style=${styleMap({
|
||||
color: entity.unavailable ? "var(--error-color)" : "",
|
||||
color: entry.unavailable ? "var(--error-color)" : "",
|
||||
})}
|
||||
.path=${entity.restored
|
||||
.path=${entry.restored
|
||||
? mdiRestoreAlert
|
||||
: entity.unavailable
|
||||
: entry.unavailable
|
||||
? mdiAlertCircle
|
||||
: entity.disabled_by
|
||||
: entry.disabled_by
|
||||
? mdiCancel
|
||||
: entity.hidden_by
|
||||
: entry.hidden_by
|
||||
? mdiEyeOff
|
||||
: mdiPencilOff}
|
||||
></ha-svg-icon>
|
||||
<simple-tooltip animation-delay="0" position="left">
|
||||
${entity.restored
|
||||
${entry.restored
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.restored"
|
||||
)
|
||||
: entity.unavailable
|
||||
: entry.unavailable
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.unavailable"
|
||||
)
|
||||
: entity.disabled_by
|
||||
: entry.disabled_by
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.disabled"
|
||||
)
|
||||
: entity.hidden_by
|
||||
: entry.hidden_by
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.hidden"
|
||||
)
|
||||
|
|
|
@ -6,7 +6,10 @@ import { customElement, property, state } from "lit/decorators";
|
|||
import memoizeOne from "memoize-one";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import {
|
||||
LocalizeFunc,
|
||||
LocalizeKeys,
|
||||
} from "../../../common/translations/localize";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
import {
|
||||
DataTableColumnContainer,
|
||||
|
@ -27,6 +30,7 @@ import {
|
|||
} from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
||||
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
|
@ -38,9 +42,19 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
|||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "../integrations/ha-integration-overflow-menu";
|
||||
import { HelperDomain, isHelperDomain } from "./const";
|
||||
import { isHelperDomain } from "./const";
|
||||
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
|
||||
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
|
||||
|
||||
type HelperItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
icon?: string;
|
||||
entity_id: string;
|
||||
editable?: boolean;
|
||||
type: string;
|
||||
configEntry?: ConfigEntry;
|
||||
entity?: HassEntity;
|
||||
};
|
||||
|
||||
// This groups items by a key but only returns last entry per key.
|
||||
const groupByOne = <T>(
|
||||
|
@ -108,16 +122,16 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean, localize: LocalizeFunc): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
const columns: DataTableColumnContainer<HelperItem> = {
|
||||
icon: {
|
||||
title: "",
|
||||
label: localize("ui.panel.config.helpers.picker.headers.icon"),
|
||||
type: "icon",
|
||||
template: (icon, helper: any) =>
|
||||
template: (helper) =>
|
||||
helper.entity
|
||||
? html`<ha-state-icon .state=${helper.entity}></ha-state-icon>`
|
||||
: html`<ha-svg-icon
|
||||
.path=${icon}
|
||||
.path=${helper.icon}
|
||||
style="color: var(--error-color)"
|
||||
></ha-svg-icon>`,
|
||||
},
|
||||
|
@ -128,10 +142,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||
filterable: true,
|
||||
grows: true,
|
||||
direction: "asc",
|
||||
template: (name, item: any) => html`
|
||||
${name}
|
||||
template: (helper) => html`
|
||||
${helper.name}
|
||||
${narrow
|
||||
? html`<div class="secondary">${item.entity_id}</div> `
|
||||
? html`<div class="secondary">${helper.entity_id}</div> `
|
||||
: ""}
|
||||
`,
|
||||
},
|
||||
|
@ -149,11 +163,13 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||
sortable: true,
|
||||
width: "25%",
|
||||
filterable: true,
|
||||
template: (type: HelperDomain, row) =>
|
||||
row.configEntry
|
||||
? domainToName(localize, type)
|
||||
template: (helper) =>
|
||||
helper.configEntry
|
||||
? domainToName(localize, helper.type)
|
||||
: html`
|
||||
${localize(`ui.panel.config.helpers.types.${type}`) || type}
|
||||
${localize(
|
||||
`ui.panel.config.helpers.types.${helper.type}` as LocalizeKeys
|
||||
) || helper.type}
|
||||
`,
|
||||
};
|
||||
columns.editable = {
|
||||
|
@ -162,8 +178,8 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||
"ui.panel.config.helpers.picker.headers.editable"
|
||||
),
|
||||
type: "icon",
|
||||
template: (editable) => html`
|
||||
${!editable
|
||||
template: (helper) => html`
|
||||
${!helper.editable
|
||||
? html`
|
||||
<div
|
||||
tabindex="0"
|
||||
|
@ -189,7 +205,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||
stateItems: HassEntity[],
|
||||
entityEntries: Record<string, EntityRegistryEntry>,
|
||||
configEntries: Record<string, ConfigEntry>
|
||||
) => {
|
||||
): HelperItem[] => {
|
||||
const configEntriesCopy = { ...configEntries };
|
||||
|
||||
const states = stateItems.map((entityState) => {
|
||||
|
|
|
@ -38,7 +38,7 @@ export class ZHAClustersDataTable extends LitElement {
|
|||
});
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean): DataTableColumnContainer =>
|
||||
(narrow: boolean): DataTableColumnContainer<ClusterRowData> =>
|
||||
narrow
|
||||
? {
|
||||
name: {
|
||||
|
@ -57,7 +57,7 @@ export class ZHAClustersDataTable extends LitElement {
|
|||
},
|
||||
id: {
|
||||
title: "ID",
|
||||
template: (id: number) => html` ${formatAsPaddedHex(id)} `,
|
||||
template: (cluster) => html` ${formatAsPaddedHex(cluster.id)} `,
|
||||
sortable: true,
|
||||
width: "25%",
|
||||
},
|
||||
|
|
|
@ -67,9 +67,9 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
|||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (name, device: any) => html`
|
||||
template: (device) => html`
|
||||
<a href=${`/config/devices/device/${device.dev_id}`}>
|
||||
${name}
|
||||
${device.name}
|
||||
</a>
|
||||
`,
|
||||
},
|
||||
|
@ -86,9 +86,9 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
|||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (name, device: any) => html`
|
||||
template: (device) => html`
|
||||
<a href=${`/config/devices/device/${device.dev_id}`}>
|
||||
${name}
|
||||
${device.name}
|
||||
</a>
|
||||
`,
|
||||
},
|
||||
|
@ -102,10 +102,10 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
|||
sortable: false,
|
||||
filterable: false,
|
||||
width: "50%",
|
||||
template: (entities) => html`
|
||||
${entities.length
|
||||
? entities.length > 3
|
||||
? html`${entities
|
||||
template: (device) => html`
|
||||
${device.entities.length
|
||||
? device.entities.length > 3
|
||||
? html`${device.entities
|
||||
.slice(0, 2)
|
||||
.map(
|
||||
(entity) =>
|
||||
|
@ -115,8 +115,8 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
|||
${entity.name || entity.original_name}
|
||||
</div>`
|
||||
)}
|
||||
<div>And ${entities.length - 2} more...</div>`
|
||||
: entities.map(
|
||||
<div>And ${device.entities.length - 2} more...</div>`
|
||||
: device.entities.map(
|
||||
(entity) =>
|
||||
html`<div
|
||||
style="overflow: hidden; text-overflow: ellipsis;"
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/ha-fab";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import { fetchGroups, ZHADevice, ZHAGroup } from "../../../../../data/zha";
|
||||
import { fetchGroups, ZHAGroup } from "../../../../../data/zha";
|
||||
import "../../../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../../../types";
|
||||
|
@ -71,7 +71,7 @@ export class ZHAGroupsDashboard extends LitElement {
|
|||
});
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean): DataTableColumnContainer =>
|
||||
(narrow: boolean): DataTableColumnContainer<GroupRowData> =>
|
||||
narrow
|
||||
? {
|
||||
name: {
|
||||
|
@ -94,16 +94,14 @@ export class ZHAGroupsDashboard extends LitElement {
|
|||
title: this.hass.localize("ui.panel.config.zha.groups.group_id"),
|
||||
type: "numeric",
|
||||
width: "15%",
|
||||
template: (groupId: number) => html`
|
||||
${formatAsPaddedHex(groupId)}
|
||||
`,
|
||||
template: (group) => html` ${formatAsPaddedHex(group.group_id)} `,
|
||||
sortable: true,
|
||||
},
|
||||
members: {
|
||||
title: this.hass.localize("ui.panel.config.zha.groups.members"),
|
||||
type: "numeric",
|
||||
width: "15%",
|
||||
template: (members: ZHADevice[]) => html` ${members.length} `,
|
||||
template: (group) => html` ${group.members.length} `,
|
||||
sortable: true,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -41,15 +41,15 @@ class ZWaveJSProvisioned extends LitElement {
|
|||
}
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean): DataTableColumnContainer => ({
|
||||
(narrow: boolean): DataTableColumnContainer<ZwaveJSProvisioningEntry> => ({
|
||||
included: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.provisioned.included"
|
||||
),
|
||||
type: "icon",
|
||||
width: "100px",
|
||||
template: (_info, provisioningEntry: any) =>
|
||||
provisioningEntry.additional_properties.nodeId
|
||||
template: (entry) =>
|
||||
entry.additional_properties.nodeId
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.label=${this.hass.localize(
|
||||
|
@ -81,14 +81,16 @@ class ZWaveJSProvisioned extends LitElement {
|
|||
hidden: narrow,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
template: (securityClasses: SecurityClass[]) =>
|
||||
securityClasses
|
||||
template: (entry) => {
|
||||
const securityClasses = entry.security_classes;
|
||||
return securityClasses
|
||||
.map((secClass) =>
|
||||
this.hass.localize(
|
||||
`ui.panel.config.zwave_js.security_classes.${SecurityClass[secClass]}.title`
|
||||
)
|
||||
)
|
||||
.join(", "),
|
||||
.join(", ");
|
||||
},
|
||||
},
|
||||
unprovision: {
|
||||
title: this.hass.localize(
|
||||
|
@ -96,13 +98,13 @@ class ZWaveJSProvisioned extends LitElement {
|
|||
),
|
||||
type: "icon-button",
|
||||
width: "100px",
|
||||
template: (_info, provisioningEntry: any) => html`
|
||||
template: (entry) => html`
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.provisioned.unprovison"
|
||||
)}
|
||||
.path=${mdiDelete}
|
||||
.provisioningEntry=${provisioningEntry}
|
||||
.provisioningEntry=${entry}
|
||||
@click=${this._unprovision}
|
||||
></ha-icon-button>
|
||||
`,
|
||||
|
|
|
@ -68,12 +68,12 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||
"ui.panel.config.lovelace.dashboards.picker.headers.icon"
|
||||
),
|
||||
type: "icon",
|
||||
template: (icon: DataTableItem["icon"], dashboard) =>
|
||||
icon
|
||||
template: (dashboard) =>
|
||||
dashboard.icon
|
||||
? html`
|
||||
<ha-icon
|
||||
slot="item-icon"
|
||||
.icon=${icon}
|
||||
.icon=${dashboard.icon}
|
||||
style=${ifDefined(
|
||||
dashboard.iconColor
|
||||
? `color: ${dashboard.iconColor}`
|
||||
|
@ -91,9 +91,9 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
template: (title: DataTableItem["title"], dashboard) => {
|
||||
template: (dashboard) => {
|
||||
const titleTemplate = html`
|
||||
${title}
|
||||
${dashboard.title}
|
||||
${dashboard.default
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
|
@ -132,10 +132,10 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||
sortable: true,
|
||||
filterable: true,
|
||||
width: "20%",
|
||||
template: (mode: DataTableItem["mode"]) => html`
|
||||
template: (dashboard) => html`
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.lovelace.dashboards.conf_mode.${mode}`
|
||||
) || mode}
|
||||
`ui.panel.config.lovelace.dashboards.conf_mode.${dashboard.mode}`
|
||||
) || dashboard.mode}
|
||||
`,
|
||||
};
|
||||
if (dashboards.some((dashboard) => dashboard.filename)) {
|
||||
|
@ -155,8 +155,8 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||
sortable: true,
|
||||
type: "icon",
|
||||
width: "100px",
|
||||
template: (requireAdmin: DataTableItem["require_admin"]) =>
|
||||
requireAdmin
|
||||
template: (dashboard) =>
|
||||
dashboard.require_admin
|
||||
? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>`
|
||||
: html`—`,
|
||||
};
|
||||
|
@ -166,8 +166,8 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||
),
|
||||
type: "icon",
|
||||
width: "121px",
|
||||
template: (sidebar: DataTableItem["show_in_sidebar"]) =>
|
||||
sidebar
|
||||
template: (dashboard) =>
|
||||
dashboard.show_in_sidebar
|
||||
? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>`
|
||||
: html`—`,
|
||||
};
|
||||
|
@ -180,12 +180,12 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||
),
|
||||
filterable: true,
|
||||
width: "100px",
|
||||
template: (urlPath) =>
|
||||
template: (dashboard) =>
|
||||
narrow
|
||||
? html`
|
||||
<ha-icon-button
|
||||
.path=${mdiOpenInNew}
|
||||
.urlPath=${urlPath}
|
||||
.urlPath=${dashboard.url_path}
|
||||
@click=${this._navigate}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.lovelace.dashboards.picker.open"
|
||||
|
@ -193,7 +193,9 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||
></ha-icon-button>
|
||||
`
|
||||
: html`
|
||||
<mwc-button .urlPath=${urlPath} @click=${this._navigate}
|
||||
<mwc-button
|
||||
.urlPath=${dashboard.url_path}
|
||||
@click=${this._navigate}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.lovelace.dashboards.picker.open"
|
||||
)}</mwc-button
|
||||
|
|
|
@ -40,7 +40,7 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||
@state() private _resources: LovelaceResource[] = [];
|
||||
|
||||
private _columns = memoize(
|
||||
(_language): DataTableColumnContainer => ({
|
||||
(_language): DataTableColumnContainer<LovelaceResource> => ({
|
||||
url: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.lovelace.resources.picker.headers.url"
|
||||
|
@ -58,10 +58,10 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||
sortable: true,
|
||||
filterable: true,
|
||||
width: "30%",
|
||||
template: (type: LovelaceResource["type"]) => html`
|
||||
template: (resource) => html`
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.lovelace.resources.types.${type}`
|
||||
) || type}
|
||||
`ui.panel.config.lovelace.resources.types.${resource.type}`
|
||||
) || resource.type}
|
||||
`,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -47,6 +47,10 @@ import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
|||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { isUnavailableState } from "../../../data/entity";
|
||||
|
||||
type SceneItem = SceneEntity & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
@customElement("ha-scene-dashboard")
|
||||
class HaSceneDashboard extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
@ -66,7 +70,7 @@ class HaSceneDashboard extends LitElement {
|
|||
@state() private _filterValue?;
|
||||
|
||||
private _scenes = memoizeOne(
|
||||
(scenes: SceneEntity[], filteredScenes?: string[] | null) => {
|
||||
(scenes: SceneEntity[], filteredScenes?: string[] | null): SceneItem[] => {
|
||||
if (filteredScenes === null) {
|
||||
return [];
|
||||
}
|
||||
|
@ -83,14 +87,14 @@ class HaSceneDashboard extends LitElement {
|
|||
|
||||
private _columns = memoizeOne(
|
||||
(_language, narrow): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
const columns: DataTableColumnContainer<SceneItem> = {
|
||||
icon: {
|
||||
title: "",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.scene.picker.headers.state"
|
||||
),
|
||||
type: "icon",
|
||||
template: (_, scene) => html`
|
||||
template: (scene) => html`
|
||||
<ha-state-icon .state=${scene}></ha-state-icon>
|
||||
`,
|
||||
},
|
||||
|
@ -112,20 +116,18 @@ class HaSceneDashboard extends LitElement {
|
|||
),
|
||||
sortable: true,
|
||||
width: "30%",
|
||||
template: (last_activated) => {
|
||||
const date = new Date(last_activated);
|
||||
template: (scene) => {
|
||||
const lastActivated = scene.state;
|
||||
if (!lastActivated || isUnavailableState(lastActivated)) {
|
||||
return this.hass.localize("ui.components.relative_time.never");
|
||||
}
|
||||
const date = new Date(scene.state);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
return html`
|
||||
${last_activated && !isUnavailableState(last_activated)
|
||||
? dayDifference > 3
|
||||
? formatShortDateTime(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: relativeTime(date, this.hass.locale)
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
${dayDifference > 3
|
||||
? formatShortDateTime(date, this.hass.locale, this.hass.config)
|
||||
: relativeTime(date, this.hass.locale)}
|
||||
`;
|
||||
},
|
||||
};
|
||||
|
@ -133,7 +135,7 @@ class HaSceneDashboard extends LitElement {
|
|||
columns.only_editable = {
|
||||
title: "",
|
||||
width: "56px",
|
||||
template: (_info, scene: any) =>
|
||||
template: (scene) =>
|
||||
!scene.attributes.id
|
||||
? html`
|
||||
<simple-tooltip animation-delay="0" position="left">
|
||||
|
@ -152,7 +154,7 @@ class HaSceneDashboard extends LitElement {
|
|||
title: "",
|
||||
width: "72px",
|
||||
type: "overflow-menu",
|
||||
template: (_: string, scene: any) => html`
|
||||
template: (scene) => html`
|
||||
<ha-icon-overflow-menu
|
||||
.hass=${this.hass}
|
||||
narrow
|
||||
|
|
|
@ -7,16 +7,15 @@ import {
|
|||
mdiPlus,
|
||||
mdiTransitConnection,
|
||||
} from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { differenceInDays } from "date-fns/esm";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
|
@ -29,13 +28,18 @@ import "../../../components/ha-fab";
|
|||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-icon-overflow-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { fetchBlueprints } from "../../../data/blueprint";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import {
|
||||
ScriptEntity,
|
||||
deleteScript,
|
||||
fetchScriptFileConfig,
|
||||
getScriptStateConfig,
|
||||
showScriptEditor,
|
||||
triggerScript,
|
||||
} from "../../../data/script";
|
||||
import { findRelated } from "../../../data/search";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
|
@ -45,18 +49,18 @@ import { haStyle } from "../../../resources/styles";
|
|||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showNewAutomationDialog } from "../automation/show-dialog-new-automation";
|
||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { findRelated } from "../../../data/search";
|
||||
import { fetchBlueprints } from "../../../data/blueprint";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
|
||||
type ScriptItem = ScriptEntity & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
@customElement("ha-script-picker")
|
||||
class HaScriptPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public scripts!: HassEntity[];
|
||||
@property() public scripts!: ScriptEntity[];
|
||||
|
||||
@property() public isWide!: boolean;
|
||||
|
||||
|
@ -75,7 +79,10 @@ class HaScriptPicker extends LitElement {
|
|||
@state() private _filterValue?;
|
||||
|
||||
private _scripts = memoizeOne(
|
||||
(scripts: HassEntity[], filteredScripts?: string[] | null) => {
|
||||
(
|
||||
scripts: ScriptEntity[],
|
||||
filteredScripts?: string[] | null
|
||||
): ScriptItem[] => {
|
||||
if (filteredScripts === null) {
|
||||
return [];
|
||||
}
|
||||
|
@ -93,126 +100,136 @@ class HaScriptPicker extends LitElement {
|
|||
}
|
||||
);
|
||||
|
||||
private _columns = memoizeOne((narrow, _locale): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
icon: {
|
||||
title: "",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.headers.state"
|
||||
),
|
||||
type: "icon",
|
||||
template: (_icon, script) =>
|
||||
html`<ha-state-icon
|
||||
.state=${script}
|
||||
style=${styleMap({
|
||||
color:
|
||||
script.state === UNAVAILABLE ? "var(--error-color)" : "unset",
|
||||
})}
|
||||
></ha-state-icon>`,
|
||||
},
|
||||
name: {
|
||||
title: this.hass.localize("ui.panel.config.script.picker.headers.name"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: narrow
|
||||
? (name, script: any) => {
|
||||
const date = new Date(script.attributes.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
return html`
|
||||
${name}
|
||||
<div class="secondary">
|
||||
${this.hass.localize("ui.card.automation.last_triggered")}:
|
||||
${script.attributes.last_triggered
|
||||
? dayDifference > 3
|
||||
? formatShortDateTime(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: relativeTime(date, this.hass.locale)
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
};
|
||||
if (!narrow) {
|
||||
columns.last_triggered = {
|
||||
sortable: true,
|
||||
width: "40%",
|
||||
title: this.hass.localize("ui.card.automation.last_triggered"),
|
||||
template: (last_triggered) => {
|
||||
const date = new Date(last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
return html`
|
||||
${last_triggered
|
||||
? dayDifference > 3
|
||||
? formatShortDateTime(date, this.hass.locale, this.hass.config)
|
||||
: relativeTime(date, this.hass.locale)
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
`;
|
||||
private _columns = memoizeOne(
|
||||
(narrow, _locale): DataTableColumnContainer<ScriptItem> => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
icon: {
|
||||
title: "",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.headers.state"
|
||||
),
|
||||
type: "icon",
|
||||
template: (script) =>
|
||||
html`<ha-state-icon
|
||||
.state=${script}
|
||||
style=${styleMap({
|
||||
color:
|
||||
script.state === UNAVAILABLE ? "var(--error-color)" : "unset",
|
||||
})}
|
||||
></ha-state-icon>`,
|
||||
},
|
||||
name: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.script.picker.headers.name"
|
||||
),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: narrow
|
||||
? (script) => {
|
||||
const date = new Date(script.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
return html`
|
||||
${script.name}
|
||||
<div class="secondary">
|
||||
${this.hass.localize("ui.card.automation.last_triggered")}:
|
||||
${script.last_triggered
|
||||
? dayDifference > 3
|
||||
? formatShortDateTime(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: relativeTime(date, this.hass.locale)
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
};
|
||||
if (!narrow) {
|
||||
columns.last_triggered = {
|
||||
sortable: true,
|
||||
width: "40%",
|
||||
title: this.hass.localize("ui.card.automation.last_triggered"),
|
||||
template: (script) => {
|
||||
const date = new Date(script.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
return html`
|
||||
${script.last_triggered
|
||||
? dayDifference > 3
|
||||
? formatShortDateTime(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: relativeTime(date, this.hass.locale)
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
columns.actions = {
|
||||
title: "",
|
||||
width: this.narrow ? undefined : "10%",
|
||||
type: "overflow-menu",
|
||||
template: (script) => html`
|
||||
<ha-icon-overflow-menu
|
||||
.hass=${this.hass}
|
||||
narrow
|
||||
.items=${[
|
||||
{
|
||||
path: mdiInformationOutline,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.show_info"
|
||||
),
|
||||
action: () => this._showInfo(script),
|
||||
},
|
||||
{
|
||||
path: mdiPlay,
|
||||
label: this.hass.localize("ui.panel.config.script.picker.run"),
|
||||
action: () => this._runScript(script),
|
||||
},
|
||||
{
|
||||
path: mdiTransitConnection,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.show_trace"
|
||||
),
|
||||
action: () => this._showTrace(script),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
path: mdiContentDuplicate,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.duplicate"
|
||||
),
|
||||
action: () => this._duplicate(script),
|
||||
},
|
||||
{
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.delete"
|
||||
),
|
||||
path: mdiDelete,
|
||||
action: () => this._deleteConfirm(script),
|
||||
warning: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
</ha-icon-overflow-menu>
|
||||
`,
|
||||
};
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
columns.actions = {
|
||||
title: "",
|
||||
width: this.narrow ? undefined : "10%",
|
||||
type: "overflow-menu",
|
||||
template: (_: string, script: any) => html`
|
||||
<ha-icon-overflow-menu
|
||||
.hass=${this.hass}
|
||||
narrow
|
||||
.items=${[
|
||||
{
|
||||
path: mdiInformationOutline,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.show_info"
|
||||
),
|
||||
action: () => this._showInfo(script),
|
||||
},
|
||||
{
|
||||
path: mdiPlay,
|
||||
label: this.hass.localize("ui.panel.config.script.picker.run"),
|
||||
action: () => this._runScript(script),
|
||||
},
|
||||
{
|
||||
path: mdiTransitConnection,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.show_trace"
|
||||
),
|
||||
action: () => this._showTrace(script),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
path: mdiContentDuplicate,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.script.picker.duplicate"
|
||||
),
|
||||
action: () => this._duplicate(script),
|
||||
},
|
||||
{
|
||||
label: this.hass.localize("ui.panel.config.script.picker.delete"),
|
||||
path: mdiDelete,
|
||||
action: () => this._deleteConfirm(script),
|
||||
warning: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
</ha-icon-overflow-menu>
|
||||
`,
|
||||
};
|
||||
|
||||
return columns;
|
||||
});
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
|
|
|
@ -36,6 +36,7 @@ import { showTagDetailDialog } from "./show-dialog-tag-detail";
|
|||
import "./tag-image";
|
||||
|
||||
export interface TagRowData extends Tag {
|
||||
display_name: string;
|
||||
last_scanned_datetime: Date | null;
|
||||
}
|
||||
|
||||
|
@ -55,94 +56,90 @@ export class HaConfigTags extends SubscribeMixin(LitElement) {
|
|||
return this.hass.auth.external?.config.canWriteTag;
|
||||
}
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean, _language): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
icon: {
|
||||
title: "",
|
||||
label: this.hass.localize("ui.panel.config.tag.headers.icon"),
|
||||
type: "icon",
|
||||
template: (_icon, tag) => html`<tag-image .tag=${tag}></tag-image>`,
|
||||
},
|
||||
display_name: {
|
||||
title: this.hass.localize("ui.panel.config.tag.headers.name"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
template: (name, tag: any) =>
|
||||
html`${name}
|
||||
${narrow
|
||||
? html`<div class="secondary">
|
||||
${tag.last_scanned_datetime
|
||||
? html`<ha-relative-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${tag.last_scanned_datetime}
|
||||
capitalize
|
||||
></ha-relative-time>`
|
||||
: this.hass.localize("ui.panel.config.tag.never_scanned")}
|
||||
</div>`
|
||||
: ""}`,
|
||||
},
|
||||
};
|
||||
if (!narrow) {
|
||||
columns.last_scanned_datetime = {
|
||||
title: this.hass.localize("ui.panel.config.tag.headers.last_scanned"),
|
||||
sortable: true,
|
||||
direction: "desc",
|
||||
width: "20%",
|
||||
template: (last_scanned_datetime) => html`
|
||||
${last_scanned_datetime
|
||||
? html`<ha-relative-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${last_scanned_datetime}
|
||||
capitalize
|
||||
></ha-relative-time>`
|
||||
: this.hass.localize("ui.panel.config.tag.never_scanned")}
|
||||
`,
|
||||
};
|
||||
}
|
||||
if (this._canWriteTags) {
|
||||
columns.write = {
|
||||
title: "",
|
||||
label: this.hass.localize("ui.panel.config.tag.headers.write"),
|
||||
type: "icon-button",
|
||||
template: (_write, tag: any) =>
|
||||
html` <ha-icon-button
|
||||
.tag=${tag}
|
||||
@click=${this._handleWriteClick}
|
||||
.label=${this.hass.localize("ui.panel.config.tag.write")}
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-icon-button>`,
|
||||
};
|
||||
}
|
||||
columns.automation = {
|
||||
private _columns = memoizeOne((narrow: boolean, _language) => {
|
||||
const columns: DataTableColumnContainer<TagRowData> = {
|
||||
icon: {
|
||||
title: "",
|
||||
type: "icon-button",
|
||||
template: (_automation, tag: any) =>
|
||||
html` <ha-icon-button
|
||||
.tag=${tag}
|
||||
@click=${this._handleAutomationClick}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.tag.create_automation"
|
||||
)}
|
||||
.path=${mdiRobot}
|
||||
></ha-icon-button>`,
|
||||
label: this.hass.localize("ui.panel.config.tag.headers.icon"),
|
||||
type: "icon",
|
||||
template: (tag) => html`<tag-image .tag=${tag}></tag-image>`,
|
||||
},
|
||||
display_name: {
|
||||
title: this.hass.localize("ui.panel.config.tag.headers.name"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
template: (tag) =>
|
||||
html`${tag.name}
|
||||
${narrow
|
||||
? html`<div class="secondary">
|
||||
${tag.last_scanned_datetime
|
||||
? html`<ha-relative-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${tag.last_scanned_datetime}
|
||||
capitalize
|
||||
></ha-relative-time>`
|
||||
: this.hass.localize("ui.panel.config.tag.never_scanned")}
|
||||
</div>`
|
||||
: ""}`,
|
||||
},
|
||||
};
|
||||
if (!narrow) {
|
||||
columns.last_scanned_datetime = {
|
||||
title: this.hass.localize("ui.panel.config.tag.headers.last_scanned"),
|
||||
sortable: true,
|
||||
direction: "desc",
|
||||
width: "20%",
|
||||
template: (tag) => html`
|
||||
${tag.last_scanned_datetime
|
||||
? html`<ha-relative-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${tag.last_scanned_datetime}
|
||||
capitalize
|
||||
></ha-relative-time>`
|
||||
: this.hass.localize("ui.panel.config.tag.never_scanned")}
|
||||
`,
|
||||
};
|
||||
columns.edit = {
|
||||
title: "",
|
||||
type: "icon-button",
|
||||
template: (_settings, tag: any) =>
|
||||
html` <ha-icon-button
|
||||
.tag=${tag}
|
||||
@click=${this._handleEditClick}
|
||||
.label=${this.hass.localize("ui.panel.config.tag.edit")}
|
||||
.path=${mdiCog}
|
||||
></ha-icon-button>`,
|
||||
};
|
||||
return columns;
|
||||
}
|
||||
);
|
||||
if (this._canWriteTags) {
|
||||
columns.write = {
|
||||
title: "",
|
||||
label: this.hass.localize("ui.panel.config.tag.headers.write"),
|
||||
type: "icon-button",
|
||||
template: (tag) =>
|
||||
html` <ha-icon-button
|
||||
.tag=${tag}
|
||||
@click=${this._handleWriteClick}
|
||||
.label=${this.hass.localize("ui.panel.config.tag.write")}
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-icon-button>`,
|
||||
};
|
||||
}
|
||||
columns.automation = {
|
||||
title: "",
|
||||
type: "icon-button",
|
||||
template: (tag) =>
|
||||
html` <ha-icon-button
|
||||
.tag=${tag}
|
||||
@click=${this._handleAutomationClick}
|
||||
.label=${this.hass.localize("ui.panel.config.tag.create_automation")}
|
||||
.path=${mdiRobot}
|
||||
></ha-icon-button>`,
|
||||
};
|
||||
columns.edit = {
|
||||
title: "",
|
||||
type: "icon-button",
|
||||
template: (tag) =>
|
||||
html` <ha-icon-button
|
||||
.tag=${tag}
|
||||
@click=${this._handleEditClick}
|
||||
.label=${this.hass.localize("ui.panel.config.tag.edit")}
|
||||
.path=${mdiCog}
|
||||
></ha-icon-button>`,
|
||||
};
|
||||
return columns;
|
||||
});
|
||||
|
||||
private _data = memoizeOne((tags: Tag[]): TagRowData[] =>
|
||||
tags.map((tag) => ({
|
||||
|
|
|
@ -49,14 +49,14 @@ export class HaConfigUsers extends LitElement {
|
|||
width: "25%",
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (name, user) =>
|
||||
template: (user) =>
|
||||
narrow
|
||||
? html` ${name}<br />
|
||||
? html` ${user.name}<br />
|
||||
<div class="secondary">
|
||||
${user.username ? `${user.username} |` : ""}
|
||||
${localize(`groups.${user.group_ids[0]}`)}
|
||||
</div>`
|
||||
: html` ${name ||
|
||||
: html` ${user.name ||
|
||||
this.hass!.localize(
|
||||
"ui.panel.config.users.editor.unnamed_user"
|
||||
)}`,
|
||||
|
@ -68,7 +68,7 @@ export class HaConfigUsers extends LitElement {
|
|||
width: "20%",
|
||||
direction: "asc",
|
||||
hidden: narrow,
|
||||
template: (username) => html`${username || "—"}`,
|
||||
template: (user) => html`${user.name || "—"}`,
|
||||
},
|
||||
group_ids: {
|
||||
title: localize("ui.panel.config.users.picker.headers.group"),
|
||||
|
@ -77,8 +77,8 @@ export class HaConfigUsers extends LitElement {
|
|||
width: "20%",
|
||||
direction: "asc",
|
||||
hidden: narrow,
|
||||
template: (groupIds: User["group_ids"]) => html`
|
||||
${localize(`groups.${groupIds[0]}`)}
|
||||
template: (user) => html`
|
||||
${localize(`groups.${user.group_ids[0]}`)}
|
||||
`,
|
||||
},
|
||||
is_active: {
|
||||
|
@ -90,8 +90,8 @@ export class HaConfigUsers extends LitElement {
|
|||
filterable: true,
|
||||
width: "80px",
|
||||
hidden: narrow,
|
||||
template: (is_active) =>
|
||||
is_active
|
||||
template: (user) =>
|
||||
user.is_active
|
||||
? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>`
|
||||
: "",
|
||||
},
|
||||
|
@ -104,8 +104,8 @@ export class HaConfigUsers extends LitElement {
|
|||
filterable: true,
|
||||
width: "80px",
|
||||
hidden: narrow,
|
||||
template: (generated) =>
|
||||
generated
|
||||
template: (user) =>
|
||||
user.system_generated
|
||||
? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>`
|
||||
: "",
|
||||
},
|
||||
|
@ -118,8 +118,10 @@ export class HaConfigUsers extends LitElement {
|
|||
filterable: true,
|
||||
width: "80px",
|
||||
hidden: narrow,
|
||||
template: (local) =>
|
||||
local ? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>` : "",
|
||||
template: (user) =>
|
||||
user.local_only
|
||||
? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>`
|
||||
: "",
|
||||
},
|
||||
icons: {
|
||||
title: "",
|
||||
|
@ -131,7 +133,7 @@ export class HaConfigUsers extends LitElement {
|
|||
filterable: false,
|
||||
width: "104px",
|
||||
hidden: !narrow,
|
||||
template: (_, user) => {
|
||||
template: (user) => {
|
||||
const badges = computeUserBadges(this.hass, user, false);
|
||||
return html`${badges.map(
|
||||
([icon, tooltip]) =>
|
||||
|
|
|
@ -134,7 +134,7 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||
title: "",
|
||||
type: "icon",
|
||||
hidden: narrow,
|
||||
template: (_, entry) => html`
|
||||
template: (entry) => html`
|
||||
<ha-state-icon
|
||||
title=${ifDefined(entry.entity?.state)}
|
||||
.state=${entry.entity}
|
||||
|
@ -150,8 +150,8 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (name, entry) => html`
|
||||
${name}<br />
|
||||
template: (entry) => html`
|
||||
${entry.name}<br />
|
||||
<div class="secondary">${entry.entity_id}</div>
|
||||
`,
|
||||
},
|
||||
|
@ -172,13 +172,13 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||
filterable: true,
|
||||
width: "160px",
|
||||
type: "flex",
|
||||
template: (assistants, entry) =>
|
||||
template: (entry) =>
|
||||
html`${availableAssistants.map((key) => {
|
||||
const supported =
|
||||
!supportedEntities?.[key] ||
|
||||
supportedEntities[key].includes(entry.entity_id);
|
||||
const manual = entry.manAssistants?.includes(key);
|
||||
return assistants.includes(key)
|
||||
return entry.assistants.includes(key)
|
||||
? html`
|
||||
<voice-assistants-expose-assistant-icon
|
||||
.assistant=${key}
|
||||
|
@ -199,14 +199,14 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||
filterable: true,
|
||||
hidden: narrow,
|
||||
width: "15%",
|
||||
template: (aliases) =>
|
||||
aliases.length === 0
|
||||
template: (entry) =>
|
||||
entry.aliases.length === 0
|
||||
? "-"
|
||||
: aliases.length === 1
|
||||
? aliases[0]
|
||||
: entry.aliases.length === 1
|
||||
? entry.aliases[0]
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.expose.aliases",
|
||||
{ count: aliases.length }
|
||||
{ count: entry.aliases.length }
|
||||
),
|
||||
},
|
||||
remove: {
|
||||
|
|
|
@ -80,7 +80,9 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
|
|||
);
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(localize: LocalizeFunc): DataTableColumnContainer => ({
|
||||
(
|
||||
localize: LocalizeFunc
|
||||
): DataTableColumnContainer<DisplayedStatisticData> => ({
|
||||
displayName: {
|
||||
title: localize(
|
||||
"ui.panel.developer-tools.tabs.statistics.data_table.name"
|
||||
|
@ -123,8 +125,8 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
|
|||
filterable: true,
|
||||
direction: "asc",
|
||||
width: "30%",
|
||||
template: (issues_string) =>
|
||||
html`${issues_string ??
|
||||
template: (statistic) =>
|
||||
html`${statistic.issues_string ??
|
||||
localize("ui.panel.developer-tools.tabs.statistics.no_issue")}`,
|
||||
},
|
||||
fix: {
|
||||
|
@ -132,9 +134,12 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
|
|||
label: this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.statistics.fix_issue.fix"
|
||||
),
|
||||
template: (_, data: any) =>
|
||||
html`${data.issues
|
||||
? html`<mwc-button @click=${this._fixIssue} .data=${data.issues}>
|
||||
template: (statistic) =>
|
||||
html`${statistic.issues
|
||||
? html`<mwc-button
|
||||
@click=${this._fixIssue}
|
||||
.data=${statistic.issues}
|
||||
>
|
||||
${localize(
|
||||
"ui.panel.developer-tools.tabs.statistics.fix_issue.fix"
|
||||
)}
|
||||
|
@ -146,7 +151,7 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
|
|||
title: "",
|
||||
label: localize("ui.panel.developer-tools.tabs.statistics.adjust_sum"),
|
||||
type: "icon-button",
|
||||
template: (_info, statistic: StatisticsMetaData) =>
|
||||
template: (statistic) =>
|
||||
statistic.has_sum
|
||||
? html`
|
||||
<ha-icon-button
|
||||
|
|
|
@ -54,7 +54,7 @@ export class HuiEntityPickerTable extends LitElement {
|
|||
"ui.panel.lovelace.unused_entities.state_icon"
|
||||
),
|
||||
type: "icon",
|
||||
template: (_icon, entity: any) => html`
|
||||
template: (entity) => html`
|
||||
<state-badge
|
||||
@click=${this._handleEntityClicked}
|
||||
.hass=${this.hass!}
|
||||
|
@ -68,9 +68,9 @@ export class HuiEntityPickerTable extends LitElement {
|
|||
filterable: true,
|
||||
grows: true,
|
||||
direction: "asc",
|
||||
template: (name, entity: any) => html`
|
||||
template: (entity: any) => html`
|
||||
<div @click=${this._handleEntityClicked} style="cursor: pointer;">
|
||||
${name}
|
||||
${entity.name}
|
||||
${narrow
|
||||
? html` <div class="secondary">${entity.entity_id}</div> `
|
||||
: ""}
|
||||
|
@ -103,10 +103,10 @@ export class HuiEntityPickerTable extends LitElement {
|
|||
sortable: true,
|
||||
width: "15%",
|
||||
hidden: narrow,
|
||||
template: (lastChanged: string) => html`
|
||||
template: (entity) => html`
|
||||
<ha-relative-time
|
||||
.hass=${this.hass!}
|
||||
.datetime=${lastChanged}
|
||||
.datetime=${entity.last_changed}
|
||||
capitalize
|
||||
></ha-relative-time>
|
||||
`,
|
||||
|
|
Loading…
Reference in New Issue