-
+
@@ -410,43 +478,38 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
- ${configEntries.length === 0
+ ${normalEntries.length === 0
? html`
${this.hass.localize(
- `ui.panel.config.integrations.integration_page.no_entries`
+ "ui.panel.config.integrations.integration_page.no_entries"
)}
`
: nothing}
- ${configEntries
- .filter((entry) => !ERROR_STATES.includes(entry.state))
- .sort((a, b) => {
- if (Boolean(a.disabled_by) !== Boolean(b.disabled_by)) {
- return a.disabled_by ? 1 : -1;
- }
- return caseInsensitiveStringCompare(
- a.title,
- b.title,
- this.hass.locale.language
- );
- })
- .map((item) => this._renderConfigEntry(item))}
+ ${normalEntries.map((item) => this._renderConfigEntry(item))}
+
+
+ ${this._manifest?.integration_type
+ ? this.hass.localize(
+ `ui.panel.config.integrations.integration_page.add_${this._manifest?.integration_type}`
+ )
+ : this.hass.localize(
+ `ui.panel.config.integrations.integration_page.add_entry`
+ )}
+
+
-
-
-
`;
}
@@ -464,21 +527,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
let stateTextExtra: TemplateResult | string | undefined;
let icon: string = mdiAlertCircle;
- if (item.disabled_by) {
- stateText = [
- "ui.panel.config.integrations.config_entry.disable.disabled_cause",
- "cause",
- this.hass.localize(
- `ui.panel.config.integrations.config_entry.disable.disabled_by.${item.disabled_by}`
- ) || item.disabled_by,
- ];
- if (item.state === "failed_unload") {
- stateTextExtra = html`.
- ${this.hass.localize(
- "ui.panel.config.integrations.config_entry.disable_restart_confirm"
- )}.`;
- }
- } else if (item.state === "not_loaded") {
+ if (!item.disabled_by && item.state === "not_loaded") {
stateText = ["ui.panel.config.integrations.config_entry.not_loaded"];
} else if (item.state === "setup_in_progress") {
icon = mdiProgressHelper;
@@ -508,66 +557,87 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
}
}
- const devices = this._getDevices(item, this.hass.devices);
- const services = this._getServices(item, this.hass.devices);
- const entities = this._getEntities(item, this._entities);
+ const devices = this._getConfigEntryDevices(item);
+ const services = this._getConfigEntryServices(item);
+ const entities = this._getConfigEntryEntities(item);
let devicesLine: (TemplateResult | string)[] = [];
- for (const [items, localizeKey] of [
- [devices, "devices"],
- [services, "services"],
- ] as const) {
- if (items.length === 0) {
- continue;
+ if (item.disabled_by) {
+ devicesLine.push(
+ this.hass.localize(
+ "ui.panel.config.integrations.config_entry.disable.disabled_cause",
+ {
+ cause:
+ this.hass.localize(
+ `ui.panel.config.integrations.config_entry.disable.disabled_by.${item.disabled_by}`
+ ) || item.disabled_by,
+ }
+ )
+ );
+ if (item.state === "failed_unload") {
+ devicesLine.push(`.
+ ${this.hass.localize(
+ "ui.panel.config.integrations.config_entry.disable_restart_confirm"
+ )}.`);
+ }
+ } else {
+ for (const [items, localizeKey] of [
+ [devices, "devices"],
+ [services, "services"],
+ ] as const) {
+ if (items.length === 0) {
+ continue;
+ }
+ const url =
+ items.length === 1
+ ? `/config/devices/device/${items[0].id}`
+ : `/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`;
+ devicesLine.push(
+ // no white space before/after template on purpose
+ html`
${this.hass.localize(
+ `ui.panel.config.integrations.config_entry.${localizeKey}`,
+ "count",
+ items.length
+ )}`
+ );
+ }
+
+ if (entities.length) {
+ devicesLine.push(
+ // no white space before/after template on purpose
+ html`
${this.hass.localize(
+ "ui.panel.config.integrations.config_entry.entities",
+ "count",
+ entities.length
+ )}`
+ );
+ }
+
+ if (devicesLine.length === 0) {
+ devicesLine = ["No devices or entities"];
+ } else if (devicesLine.length === 2) {
+ devicesLine = [
+ devicesLine[0],
+ ` ${this.hass.localize("ui.common.and")} `,
+ devicesLine[1],
+ ];
+ } else if (devicesLine.length === 3) {
+ devicesLine = [
+ devicesLine[0],
+ ", ",
+ devicesLine[1],
+ ` ${this.hass.localize("ui.common.and")} `,
+ devicesLine[2],
+ ];
}
- const url =
- items.length === 1
- ? `/config/devices/device/${items[0].id}`
- : `/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`;
- devicesLine.push(
- // no white space before/after template on purpose
- html`
${this.hass.localize(
- `ui.panel.config.integrations.config_entry.${localizeKey}`,
- "count",
- items.length
- )}`
- );
}
- if (entities.length) {
- devicesLine.push(
- // no white space before/after template on purpose
- html`
${this.hass.localize(
- "ui.panel.config.integrations.config_entry.entities",
- "count",
- entities.length
- )}`
- );
- }
-
- if (devicesLine.length === 0) {
- devicesLine = ["No devices or entities"];
- } else if (devicesLine.length === 2) {
- devicesLine = [
- devicesLine[0],
- ` ${this.hass.localize("ui.common.and")} `,
- devicesLine[1],
- ];
- } else if (devicesLine.length === 3) {
- devicesLine = [
- devicesLine[0],
- ", ",
- devicesLine[1],
- ` ${this.hass.localize("ui.common.and")} `,
- devicesLine[2],
- ];
- }
return html`
`
: ""}
+ ${item.disabled_by && devices.length
+ ? html`
+
+
+ ${this.hass.localize(
+ `ui.panel.config.integrations.config_entry.devices`,
+ "count",
+ devices.length
+ )}
+
+
+ `
+ : ""}
+ ${item.disabled_by && services.length
+ ? html`
+
+
+ ${this.hass.localize(
+ `ui.panel.config.integrations.config_entry.services`,
+ "count",
+ services.length
+ )}
+
+
+ `
+ : ""}
+ ${item.disabled_by && entities.length
+ ? html`
+
+
+ ${this.hass.localize(
+ `ui.panel.config.integrations.config_entry.entities`,
+ "count",
+ entities.length
+ )}
+
+
+ `
+ : ""}
${!item.disabled_by &&
RECOVERABLE_STATES.includes(item.state) &&
item.supports_unload &&
@@ -807,49 +933,77 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
private _getEntities = memoizeOne(
(
- configEntry: ConfigEntry,
+ configEntry: ConfigEntry[],
entityRegistryEntries: EntityRegistryEntry[]
): EntityRegistryEntry[] => {
if (!entityRegistryEntries) {
return [];
}
+ const entryIds = configEntry.map((entry) => entry.entry_id);
return entityRegistryEntries.filter(
- (entity) => entity.config_entry_id === configEntry.entry_id
+ (entity) =>
+ entity.config_entry_id && entryIds.includes(entity.config_entry_id)
);
}
);
private _getDevices = memoizeOne(
(
- configEntry: ConfigEntry,
+ configEntry: ConfigEntry[],
deviceRegistryEntries: HomeAssistant["devices"]
): DeviceRegistryEntry[] => {
if (!deviceRegistryEntries) {
return [];
}
- return Object.values(deviceRegistryEntries).filter(
- (device) =>
- device.config_entries.includes(configEntry.entry_id) &&
- device.entry_type !== "service"
+ const entryIds = configEntry.map((entry) => entry.entry_id);
+ return Object.values(deviceRegistryEntries).filter((device) =>
+ device.config_entries.some((entryId) => entryIds.includes(entryId))
);
}
);
- private _getServices = memoizeOne(
- (
- configEntry: ConfigEntry,
- deviceRegistryEntries: HomeAssistant["devices"]
- ): DeviceRegistryEntry[] => {
- if (!deviceRegistryEntries) {
- return [];
- }
- return Object.values(deviceRegistryEntries).filter(
- (device) =>
- device.config_entries.includes(configEntry.entry_id) &&
- device.entry_type === "service"
- );
- }
- );
+ private _getConfigEntryEntities = (
+ configEntry: ConfigEntry
+ ): EntityRegistryEntry[] => {
+ const entries = this._domainConfigEntries(
+ this.domain,
+ this._extraConfigEntries || this.configEntries
+ );
+ const entityRegistryEntries = this._getEntities(entries, this._entities);
+ return entityRegistryEntries.filter(
+ (entity) => entity.config_entry_id === configEntry.entry_id
+ );
+ };
+
+ private _getConfigEntryDevices = (
+ configEntry: ConfigEntry
+ ): DeviceRegistryEntry[] => {
+ const entries = this._domainConfigEntries(
+ this.domain,
+ this._extraConfigEntries || this.configEntries
+ );
+ const deviceRegistryEntries = this._getDevices(entries, this.hass.devices);
+ return Object.values(deviceRegistryEntries).filter(
+ (device) =>
+ device.config_entries.includes(configEntry.entry_id) &&
+ device.entry_type !== "service"
+ );
+ };
+
+ private _getConfigEntryServices = (
+ configEntry: ConfigEntry
+ ): DeviceRegistryEntry[] => {
+ const entries = this._domainConfigEntries(
+ this.domain,
+ this._extraConfigEntries || this.configEntries
+ );
+ const deviceRegistryEntries = this._getDevices(entries, this.hass.devices);
+ return Object.values(deviceRegistryEntries).filter(
+ (device) =>
+ device.config_entries.includes(configEntry.entry_id) &&
+ device.entry_type === "service"
+ );
+ };
private _showOptions(ev) {
showOptionsFlowDialog(
@@ -1175,7 +1329,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
display: flex;
justify-content: center;
}
- .card-actions {
+ .overview .card-actions {
padding: 0;
}
img {
@@ -1199,6 +1353,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
--mdc-list-item-meta-size: auto;
--mdc-list-item-meta-display: flex;
}
+ ha-button-menu ha-list-item {
+ --mdc-list-item-meta-size: 24px;
+ }
ha-list-item.config_entry::after {
position: absolute;
top: 0;
@@ -1221,6 +1378,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
.attention {
primary-color: var(--error-color);
}
+ .warning {
+ color: var(--error-color);
+ }
.state-error {
--state-message-color: var(--error-color);
--text-on-state-color: var(--text-primary-color);
diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts
index 2d4a39b486..a0e1aae130 100644
--- a/src/panels/config/integrations/ha-integration-card.ts
+++ b/src/panels/config/integrations/ha-integration-card.ts
@@ -5,6 +5,7 @@ import {
mdiCogOutline,
mdiDevices,
mdiHandExtendedOutline,
+ mdiPuzzleOutline,
mdiShapeOutline,
} from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
@@ -94,7 +95,9 @@ export class HaIntegrationCard extends LitElement {
private _renderSingleEntry(): TemplateResult {
const devices = this._getDevices(this.items, this.hass.devices);
- const entities = this._getEntities(this.items, this.entityRegistryEntries);
+ const entities = devices.length
+ ? []
+ : this._getEntities(this.items, this.entityRegistryEntries);
const services = !devices.some((device) => device.entry_type !== "service");
@@ -121,8 +124,7 @@ export class HaIntegrationCard extends LitElement {
`
- : ""}
- ${entities.length > 0
+ : entities.length > 0
? html`
@@ -139,7 +141,20 @@ export class HaIntegrationCard extends LitElement {
`
- : ""}
+ : html`
+
+
+ ${this.hass.localize(
+ `ui.panel.config.integrations.config_entry.entries`,
+ "count",
+ this.items.length
+ )}
+
+
+ `}
`;
}
diff --git a/src/panels/config/integrations/ha-integration-header.ts b/src/panels/config/integrations/ha-integration-header.ts
index fbc2217a3c..9aa8db3003 100644
--- a/src/panels/config/integrations/ha-integration-header.ts
+++ b/src/panels/config/integrations/ha-integration-header.ts
@@ -3,6 +3,7 @@ import { mdiCloud, mdiPackageVariant } from "@mdi/js";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
+import { computeRTL } from "../../../common/util/compute_rtl";
import "../../../components/ha-svg-icon";
import { domainToName, IntegrationManifest } from "../../../data/integration";
import { HomeAssistant } from "../../../types";
@@ -94,7 +95,10 @@ export class HaIntegrationHeader extends LitElement {
([icon, description]) => html`