mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
Group config entries by integration (#5646)
This commit is contained in:
parent
462c1f94d6
commit
2abfd0392d
@ -12,7 +12,7 @@ import {
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import * as Fuse from "fuse.js";
|
||||
import { compare } from "../../../common/string/compare";
|
||||
import { caseInsensitiveCompare } from "../../../common/string/compare";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import {
|
||||
afterNextRender,
|
||||
@ -25,7 +25,6 @@ import {
|
||||
ConfigEntry,
|
||||
deleteConfigEntry,
|
||||
getConfigEntries,
|
||||
updateConfigEntry,
|
||||
} from "../../../data/config_entries";
|
||||
import {
|
||||
DISCOVERY_SOURCES,
|
||||
@ -44,29 +43,44 @@ import {
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entry-system-options/show-dialog-config-entry-system-options";
|
||||
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
||||
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
showPromptDialog,
|
||||
} from "../../../dialogs/generic/show-dialog-box";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "../../../common/search/search-input";
|
||||
import "./ha-integration-card";
|
||||
import type {
|
||||
ConfigEntryRemovedEvent,
|
||||
ConfigEntryUpdatedEvent,
|
||||
HaIntegrationCard,
|
||||
} from "./ha-integration-card";
|
||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
interface DataEntryFlowProgressExtended extends DataEntryFlowProgress {
|
||||
localized_title?: string;
|
||||
}
|
||||
|
||||
interface ConfigEntryExtended extends ConfigEntry {
|
||||
export interface ConfigEntryExtended extends ConfigEntry {
|
||||
localized_domain_name?: string;
|
||||
}
|
||||
|
||||
const groupByIntegration = (
|
||||
entries: ConfigEntryExtended[]
|
||||
): Map<string, ConfigEntryExtended[]> => {
|
||||
const result = new Map();
|
||||
entries.forEach((entry) => {
|
||||
if (result.has(entry.domain)) {
|
||||
result.get(entry.domain).push(entry);
|
||||
} else {
|
||||
result.set(entry.domain, [entry]);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
@customElement("ha-config-integrations")
|
||||
class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@ -145,6 +159,25 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
);
|
||||
|
||||
private _filterGroupConfigEntries = memoizeOne(
|
||||
(
|
||||
configEntries: ConfigEntryExtended[],
|
||||
filter?: string
|
||||
): [Map<string, ConfigEntryExtended[]>, ConfigEntryExtended[]] => {
|
||||
const filteredConfigEnties = this._filterConfigEntries(
|
||||
configEntries,
|
||||
filter
|
||||
);
|
||||
const ignored: ConfigEntryExtended[] = [];
|
||||
filteredConfigEnties.forEach((item, index) => {
|
||||
if (item.source === "ignore") {
|
||||
ignored.push(filteredConfigEnties.splice(index, 1)[0]);
|
||||
}
|
||||
});
|
||||
return [groupByIntegration(filteredConfigEnties), ignored];
|
||||
}
|
||||
);
|
||||
|
||||
private _filterConfigEntriesInProgress = memoizeOne(
|
||||
(
|
||||
configEntriesInProgress: DataEntryFlowProgressExtended[],
|
||||
@ -185,22 +218,32 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
this._configEntries.length
|
||||
) {
|
||||
afterNextRender(() => {
|
||||
const card = this.shadowRoot!.getElementById(
|
||||
this._searchParms.get("config_entry")!
|
||||
const entryId = this._searchParms.get("config_entry")!;
|
||||
const configEntry = this._configEntries.find(
|
||||
(entry) => entry.entry_id === entryId
|
||||
);
|
||||
if (!configEntry) {
|
||||
return;
|
||||
}
|
||||
const card: HaIntegrationCard = this.shadowRoot!.querySelector(
|
||||
`[data-domain=${configEntry?.domain}]`
|
||||
) as HaIntegrationCard;
|
||||
if (card) {
|
||||
card.scrollIntoView();
|
||||
card.scrollIntoView({
|
||||
block: "center",
|
||||
});
|
||||
card.classList.add("highlight");
|
||||
card.selectedConfigEntryId = entryId;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const configEntries = this._filterConfigEntries(
|
||||
this._configEntries,
|
||||
this._filter
|
||||
);
|
||||
const [
|
||||
groupedConfigEntries,
|
||||
ignoredConfigEntries,
|
||||
] = this._filterGroupConfigEntries(this._configEntries, this._filter);
|
||||
const configEntriesInProgress = this._filterConfigEntriesInProgress(
|
||||
this._configEntriesInProgress,
|
||||
this._filter
|
||||
@ -265,11 +308,13 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
`
|
||||
: ""}
|
||||
|
||||
<div class="container">
|
||||
<div
|
||||
class="container"
|
||||
@entry-removed=${this._handleRemoved}
|
||||
@entry-updated=${this._handleUpdated}
|
||||
>
|
||||
${this._showIgnored
|
||||
? configEntries
|
||||
.filter((item) => item.source === "ignore")
|
||||
.map(
|
||||
? ignoredConfigEntries.map(
|
||||
(item: ConfigEntryExtended) => html`
|
||||
<ha-card class="ignored">
|
||||
<div class="header">
|
||||
@ -352,119 +397,18 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
`
|
||||
)
|
||||
: ""}
|
||||
${configEntries.length
|
||||
? configEntries.map((item: ConfigEntryExtended) => {
|
||||
const devices = this._getDevices(item);
|
||||
const entities = this._getEntities(item);
|
||||
return item.source === "ignore"
|
||||
? ""
|
||||
: html`
|
||||
<ha-card
|
||||
class="integration"
|
||||
.configEntry=${item}
|
||||
.id=${item.entry_id}
|
||||
>
|
||||
<div class="card-content">
|
||||
<div class="image">
|
||||
<img
|
||||
src="https://brands.home-assistant.io/${item.domain}/logo.png"
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
@load=${this._onImageLoad}
|
||||
/>
|
||||
</div>
|
||||
<h1>
|
||||
${item.localized_domain_name}
|
||||
</h1>
|
||||
<h2>
|
||||
${item.localized_domain_name === item.title
|
||||
? html` `
|
||||
: item.title}
|
||||
</h2>
|
||||
${devices.length || entities.length
|
||||
? html`
|
||||
<div>
|
||||
${devices.length
|
||||
? html`
|
||||
<a
|
||||
href=${`/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.devices",
|
||||
"count",
|
||||
devices.length
|
||||
)}</a
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
${devices.length && entities.length
|
||||
? "and"
|
||||
: ""}
|
||||
${entities.length
|
||||
? html`
|
||||
<a
|
||||
href=${`/config/entities?historyBack=1&config_entry=${item.entry_id}`}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.entities",
|
||||
"count",
|
||||
entities.length
|
||||
)}</a
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<div>
|
||||
<mwc-button @click=${this._editEntryName}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.rename"
|
||||
)}</mwc-button
|
||||
>
|
||||
${item.supports_options
|
||||
? html`
|
||||
<mwc-button @click=${this._showOptions}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.options"
|
||||
)}</mwc-button
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<paper-menu-button
|
||||
horizontal-align="right"
|
||||
vertical-align="top"
|
||||
vertical-offset="40"
|
||||
close-on-activate
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
aria-label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.edit_card.options"
|
||||
)}
|
||||
></paper-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item @tap=${this._showSystemOptions}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.system_options"
|
||||
)}</paper-item
|
||||
>
|
||||
<paper-item
|
||||
class="warning"
|
||||
@tap=${this._removeIntegration}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.delete"
|
||||
)}</paper-item
|
||||
>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
})
|
||||
${groupedConfigEntries.size
|
||||
? Array.from(groupedConfigEntries.entries()).map(
|
||||
([domain, items]) =>
|
||||
html`<ha-integration-card
|
||||
data-domain=${domain}
|
||||
.hass=${this.hass}
|
||||
.domain=${domain}
|
||||
.items=${items}
|
||||
.entityRegistryEntries=${this._entityRegistryEntries}
|
||||
.deviceRegistryEntries=${this._deviceRegistryEntries}
|
||||
></ha-integration-card>`
|
||||
)
|
||||
: !this._configEntries.length
|
||||
? html`
|
||||
<ha-card>
|
||||
@ -488,7 +432,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
: ""}
|
||||
${this._filter &&
|
||||
!configEntriesInProgress.length &&
|
||||
!configEntries.length &&
|
||||
!groupedConfigEntries.size &&
|
||||
this._configEntries.length
|
||||
? html`
|
||||
<div class="none-found">
|
||||
@ -522,9 +466,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
private _loadConfigEntries() {
|
||||
getConfigEntries(this.hass).then((configEntries) => {
|
||||
this._configEntries = configEntries
|
||||
.sort((conf1, conf2) =>
|
||||
compare(conf1.domain + conf1.title, conf2.domain + conf2.title)
|
||||
)
|
||||
.map(
|
||||
(entry: ConfigEntry): ConfigEntryExtended => ({
|
||||
...entry,
|
||||
@ -533,10 +474,31 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
entry.domain
|
||||
),
|
||||
})
|
||||
)
|
||||
.sort((conf1, conf2) =>
|
||||
caseInsensitiveCompare(
|
||||
conf1.localized_domain_name + conf1.title,
|
||||
conf2.localized_domain_name + conf2.title
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private _handleRemoved(ev: HASSDomEvent<ConfigEntryRemovedEvent>) {
|
||||
this._configEntries = this._configEntries.filter(
|
||||
(entry) => entry.entry_id !== ev.detail.entryId
|
||||
);
|
||||
}
|
||||
|
||||
private _handleUpdated(ev: HASSDomEvent<ConfigEntryUpdatedEvent>) {
|
||||
const newEntry = ev.detail.entry;
|
||||
this._configEntries = this._configEntries!.map((entry) =>
|
||||
entry.entry_id === newEntry.entry_id
|
||||
? { ...newEntry, localized_domain_name: entry.localized_domain_name }
|
||||
: entry
|
||||
);
|
||||
}
|
||||
|
||||
private _createFlow() {
|
||||
showConfigFlowDialog(this, {
|
||||
dialogClosedCallback: () => {
|
||||
@ -612,22 +574,8 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
});
|
||||
}
|
||||
|
||||
private _getEntities(configEntry: ConfigEntry): EntityRegistryEntry[] {
|
||||
if (!this._entityRegistryEntries) {
|
||||
return [];
|
||||
}
|
||||
return this._entityRegistryEntries.filter(
|
||||
(entity) => entity.config_entry_id === configEntry.entry_id
|
||||
);
|
||||
}
|
||||
|
||||
private _getDevices(configEntry: ConfigEntry): DeviceRegistryEntry[] {
|
||||
if (!this._deviceRegistryEntries) {
|
||||
return [];
|
||||
}
|
||||
return this._deviceRegistryEntries.filter((device) =>
|
||||
device.config_entries.includes(configEntry.entry_id)
|
||||
);
|
||||
private _handleSearchChange(ev: CustomEvent) {
|
||||
this._filter = ev.detail.value;
|
||||
}
|
||||
|
||||
private _onImageLoad(ev) {
|
||||
@ -638,68 +586,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
ev.target.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
private _showOptions(ev) {
|
||||
showOptionsFlowDialog(this, ev.target.closest("ha-card").configEntry);
|
||||
}
|
||||
|
||||
private _showSystemOptions(ev) {
|
||||
showConfigEntrySystemOptionsDialog(this, {
|
||||
entry: ev.target.closest("ha-card").configEntry,
|
||||
});
|
||||
}
|
||||
|
||||
private async _editEntryName(ev) {
|
||||
const configEntry = ev.target.closest("ha-card").configEntry;
|
||||
const newName = await showPromptDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.integrations.rename_dialog"),
|
||||
defaultValue: configEntry.title,
|
||||
inputLabel: this.hass.localize(
|
||||
"ui.panel.config.integrations.rename_input_label"
|
||||
),
|
||||
});
|
||||
if (newName === null) {
|
||||
return;
|
||||
}
|
||||
const newEntry = await updateConfigEntry(this.hass, configEntry.entry_id, {
|
||||
title: newName,
|
||||
});
|
||||
this._configEntries = this._configEntries!.map((entry) =>
|
||||
entry.entry_id === newEntry.entry_id ? newEntry : entry
|
||||
);
|
||||
}
|
||||
|
||||
private async _removeIntegration(ev) {
|
||||
const entryId = ev.target.closest("ha-card").configEntry.entry_id;
|
||||
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.delete_confirm"
|
||||
),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteConfigEntry(this.hass, entryId).then((result) => {
|
||||
this._configEntries = this._configEntries.filter(
|
||||
(entry) => entry.entry_id !== entryId
|
||||
);
|
||||
|
||||
if (result.require_restart) {
|
||||
showAlertDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.restart_confirm"
|
||||
),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _handleSearchChange(ev: CustomEvent) {
|
||||
this._filter = ev.detail.value;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
@ -717,9 +603,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
ha-card.highlight {
|
||||
border: 1px solid var(--accent-color);
|
||||
}
|
||||
.discovered {
|
||||
border: 1px solid var(--primary-color);
|
||||
}
|
||||
@ -742,21 +625,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
ha-card.integration .card-content {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
.card-actions {
|
||||
border-top: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -787,11 +655,12 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
top: 2px;
|
||||
}
|
||||
img {
|
||||
max-height: 60px;
|
||||
max-height: 100%;
|
||||
max-width: 90%;
|
||||
}
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
.none-found {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
@ -821,10 +690,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
left: 24px;
|
||||
right: auto;
|
||||
}
|
||||
paper-menu-button {
|
||||
color: var(--secondary-text-color);
|
||||
padding: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
404
src/panels/config/integrations/ha-integration-card.ts
Normal file
404
src/panels/config/integrations/ha-integration-card.ts
Normal file
@ -0,0 +1,404 @@
|
||||
import {
|
||||
customElement,
|
||||
LitElement,
|
||||
property,
|
||||
html,
|
||||
CSSResult,
|
||||
css,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { ConfigEntryExtended } from "./ha-config-integrations";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import {
|
||||
ConfigEntry,
|
||||
updateConfigEntry,
|
||||
deleteConfigEntry,
|
||||
} from "../../../data/config_entries";
|
||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
|
||||
import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entry-system-options/show-dialog-config-entry-system-options";
|
||||
import {
|
||||
showPromptDialog,
|
||||
showConfirmationDialog,
|
||||
showAlertDialog,
|
||||
} from "../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import "../../../components/ha-icon-next";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
export interface ConfigEntryUpdatedEvent {
|
||||
entry: ConfigEntry;
|
||||
}
|
||||
|
||||
export interface ConfigEntryRemovedEvent {
|
||||
entryId: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"entry-updated": ConfigEntryUpdatedEvent;
|
||||
"entry-removed": ConfigEntryRemovedEvent;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-integration-card")
|
||||
export class HaIntegrationCard extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public domain!: string;
|
||||
|
||||
@property() public items!: ConfigEntryExtended[];
|
||||
|
||||
@property() public entityRegistryEntries!: EntityRegistryEntry[];
|
||||
|
||||
@property() public deviceRegistryEntries!: DeviceRegistryEntry[];
|
||||
|
||||
@property() public selectedConfigEntryId?: string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.items.length === 1) {
|
||||
return this._renderSingleEntry(this.items[0]);
|
||||
}
|
||||
if (this.selectedConfigEntryId) {
|
||||
const configEntry = this.items.find(
|
||||
(entry) => entry.entry_id === this.selectedConfigEntryId
|
||||
);
|
||||
if (configEntry) {
|
||||
return this._renderSingleEntry(configEntry);
|
||||
}
|
||||
}
|
||||
return this._renderGroupedIntegration();
|
||||
}
|
||||
|
||||
private _renderGroupedIntegration(): TemplateResult {
|
||||
return html`
|
||||
<ha-card class="group">
|
||||
<div class="group-header">
|
||||
<img
|
||||
src="https://brands.home-assistant.io/${this.domain}/icon.png"
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
@load=${this._onImageLoad}
|
||||
/>
|
||||
<h1>
|
||||
${domainToName(this.hass.localize, this.domain)}
|
||||
</h1>
|
||||
</div>
|
||||
<paper-listbox>
|
||||
${this.items.map(
|
||||
(item) =>
|
||||
html`<paper-item
|
||||
.entryId=${item.entry_id}
|
||||
@click=${this._selectConfigEntry}
|
||||
><paper-item-body>${item.title}</paper-item-body
|
||||
><ha-icon-next></ha-icon-next
|
||||
></paper-item>`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderSingleEntry(item: ConfigEntryExtended): TemplateResult {
|
||||
const devices = this._getDevices(item);
|
||||
const entities = this._getEntities(item);
|
||||
return html`
|
||||
<ha-card
|
||||
class="single integration"
|
||||
.configEntry=${item}
|
||||
.id=${item.entry_id}
|
||||
>
|
||||
${this.items.length > 1
|
||||
? html`<paper-icon-button
|
||||
class="back-btn"
|
||||
icon="hass:chevron-left"
|
||||
@click=${this._back}
|
||||
></paper-icon-button>`
|
||||
: ""}
|
||||
<div class="card-content">
|
||||
<div class="image">
|
||||
<img
|
||||
src="https://brands.home-assistant.io/${item.domain}/logo.png"
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
@load=${this._onImageLoad}
|
||||
/>
|
||||
</div>
|
||||
<h1>
|
||||
${item.localized_domain_name}
|
||||
</h1>
|
||||
<h2>
|
||||
${item.localized_domain_name === item.title ? "" : item.title}
|
||||
</h2>
|
||||
${devices.length || entities.length
|
||||
? html`
|
||||
<div>
|
||||
${devices.length
|
||||
? html`
|
||||
<a
|
||||
href=${`/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.devices",
|
||||
"count",
|
||||
devices.length
|
||||
)}</a
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
${devices.length && entities.length ? "and" : ""}
|
||||
${entities.length
|
||||
? html`
|
||||
<a
|
||||
href=${`/config/entities?historyBack=1&config_entry=${item.entry_id}`}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.entities",
|
||||
"count",
|
||||
entities.length
|
||||
)}</a
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<div>
|
||||
<mwc-button @click=${this._editEntryName}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.rename"
|
||||
)}</mwc-button
|
||||
>
|
||||
${item.supports_options
|
||||
? html`
|
||||
<mwc-button @click=${this._showOptions}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.options"
|
||||
)}</mwc-button
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<paper-menu-button
|
||||
horizontal-align="right"
|
||||
vertical-align="top"
|
||||
vertical-offset="40"
|
||||
close-on-activate
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
aria-label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.edit_card.options"
|
||||
)}
|
||||
></paper-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item @tap=${this._showSystemOptions}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.system_options"
|
||||
)}</paper-item
|
||||
>
|
||||
<paper-item class="warning" @tap=${this._removeIntegration}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.delete"
|
||||
)}</paper-item
|
||||
>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _selectConfigEntry(ev: Event) {
|
||||
this.selectedConfigEntryId = (ev.currentTarget as any).entryId;
|
||||
}
|
||||
|
||||
private _back() {
|
||||
this.selectedConfigEntryId = undefined;
|
||||
this.classList.remove("highlight");
|
||||
}
|
||||
|
||||
private _getEntities(configEntry: ConfigEntry): EntityRegistryEntry[] {
|
||||
if (!this.entityRegistryEntries) {
|
||||
return [];
|
||||
}
|
||||
return this.entityRegistryEntries.filter(
|
||||
(entity) => entity.config_entry_id === configEntry.entry_id
|
||||
);
|
||||
}
|
||||
|
||||
private _getDevices(configEntry: ConfigEntry): DeviceRegistryEntry[] {
|
||||
if (!this.deviceRegistryEntries) {
|
||||
return [];
|
||||
}
|
||||
return this.deviceRegistryEntries.filter((device) =>
|
||||
device.config_entries.includes(configEntry.entry_id)
|
||||
);
|
||||
}
|
||||
|
||||
private _onImageLoad(ev) {
|
||||
ev.target.style.visibility = "initial";
|
||||
}
|
||||
|
||||
private _onImageError(ev) {
|
||||
ev.target.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
private _showOptions(ev) {
|
||||
showOptionsFlowDialog(this, ev.target.closest("ha-card").configEntry);
|
||||
}
|
||||
|
||||
private _showSystemOptions(ev) {
|
||||
showConfigEntrySystemOptionsDialog(this, {
|
||||
entry: ev.target.closest("ha-card").configEntry,
|
||||
});
|
||||
}
|
||||
|
||||
private async _editEntryName(ev) {
|
||||
const configEntry = ev.target.closest("ha-card").configEntry;
|
||||
const newName = await showPromptDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.integrations.rename_dialog"),
|
||||
defaultValue: configEntry.title,
|
||||
inputLabel: this.hass.localize(
|
||||
"ui.panel.config.integrations.rename_input_label"
|
||||
),
|
||||
});
|
||||
if (newName === null) {
|
||||
return;
|
||||
}
|
||||
const newEntry = await updateConfigEntry(this.hass, configEntry.entry_id, {
|
||||
title: newName,
|
||||
});
|
||||
fireEvent(this, "entry-updated", { entry: newEntry });
|
||||
}
|
||||
|
||||
private async _removeIntegration(ev) {
|
||||
const entryId = ev.target.closest("ha-card").configEntry.entry_id;
|
||||
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.delete_confirm"
|
||||
),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
deleteConfigEntry(this.hass, entryId).then((result) => {
|
||||
fireEvent(this, "entry-removed", { entryId });
|
||||
|
||||
if (result.require_restart) {
|
||||
showAlertDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.restart_confirm"
|
||||
),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
max-width: 500px;
|
||||
}
|
||||
ha-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
ha-card.single {
|
||||
justify-content: space-between;
|
||||
}
|
||||
:host(.highlight) ha-card {
|
||||
border: 1px solid var(--accent-color);
|
||||
}
|
||||
.card-content {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
ha-card.integration .card-content {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
.card-actions {
|
||||
border-top: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.group-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
padding: 16px 16px 8px 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.group-header h1 {
|
||||
margin: 0;
|
||||
}
|
||||
.group-header img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 60px;
|
||||
margin-bottom: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
img {
|
||||
max-height: 100%;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.none-found {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
min-height: 24px;
|
||||
}
|
||||
paper-menu-button {
|
||||
color: var(--secondary-text-color);
|
||||
padding: 0;
|
||||
}
|
||||
@media (min-width: 563px) {
|
||||
paper-listbox {
|
||||
max-height: 150px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
min-height: 35px;
|
||||
}
|
||||
.back-btn {
|
||||
position: absolute;
|
||||
background: #ffffffe0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-integration-card": HaIntegrationCard;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user