diff --git a/gallery/src/demos/demo-integration-card.ts b/gallery/src/demos/demo-integration-card.ts index 4da42f1279..5067d6e455 100644 --- a/gallery/src/demos/demo-integration-card.ts +++ b/gallery/src/demos/demo-integration-card.ts @@ -206,6 +206,7 @@ const createDeviceRegistryEntries = ( model: "Mock Device", name: "Tag Reader", sw_version: null, + hw_version: "1.0.0", id: "mock-device-id", identifiers: [], via_device_id: null, diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index e887991ec0..91e072ba72 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -133,6 +133,7 @@ class HassioAddonInfo extends LitElement { .narrow=${this.narrow} .supervisor=${this.supervisor} .addonSlug=${this.addon.slug} + @update-complete=${this._updateComplete} > ` : ""} @@ -865,6 +866,15 @@ class HassioAddonInfo extends LitElement { } } + private _updateComplete() { + const eventdata = { + success: true, + response: undefined, + path: "install", + }; + fireEvent(this, "hass-api-called", eventdata); + } + private async _installClicked(ev: CustomEvent): Promise { const button = ev.currentTarget as any; button.progress = true; diff --git a/hassio/src/components/supervisor-backup-content.ts b/hassio/src/components/supervisor-backup-content.ts index 58ca3bcb94..d1422521b8 100644 --- a/hassio/src/components/supervisor-backup-content.ts +++ b/hassio/src/components/supervisor-backup-content.ts @@ -350,9 +350,7 @@ export class SupervisorBackupContent extends LitElement { if (folders?.length) { data.folders = folders; } - if (this.homeAssistant) { - data.homeassistant = this.homeAssistant; - } + data.homeassistant = this.homeAssistant; return data; } diff --git a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts index 03f1fc0395..b9707f5986 100644 --- a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts +++ b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts @@ -1,5 +1,6 @@ +import "@polymer/paper-tooltip/paper-tooltip"; import "@material/mwc-button/mwc-button"; -import { mdiDelete } from "@mdi/js"; +import { mdiDelete, mdiDeleteOff } from "@mdi/js"; import "@polymer/paper-input/paper-input"; import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import "@polymer/paper-item/paper-item"; @@ -15,6 +16,7 @@ import { createCloseHeading } from "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-icon-button"; import { fetchHassioAddonsInfo, + HassioAddonInfo, HassioAddonRepository, } from "../../../../src/data/hassio/addon"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; @@ -60,11 +62,24 @@ class HassioRepositoriesDialog extends LitElement { .sort((a, b) => caseInsensitiveStringCompare(a.name, b.name)) ); + private _filteredUsedRepositories = memoizeOne( + (repos: HassioAddonRepository[], addons: HassioAddonInfo[]) => + repos + .filter((repo) => + addons.some((addon) => addon.repository === repo.slug) + ) + .map((repo) => repo.slug) + ); + protected render(): TemplateResult { if (!this._dialogParams?.supervisor || this._repositories === undefined) { return html``; } const repositories = this._filteredRepositories(this._repositories); + const usedRepositories = this._filteredUsedRepositories( + repositories, + this._dialogParams.supervisor.supervisor.addons + ); return html` ${repo.maintainer}
${repo.url}
- +
+ + + + ${this._dialogParams!.supervisor.localize( + usedRepositories.includes(repo.slug) + ? "dialog.repositories.used" + : "dialog.repositories.remove" + )} + +
` ) - : html` No repositories `} + : html` No repositories `}
` : ""} + ${this.device.hw_version + ? html` +
+ ${this.hass.localize( + "ui.panel.config.integrations.config_entry.hardware", + "version", + this.device.hw_version + )} +
+ ` + : ""} @@ -107,9 +118,9 @@ export class HaDeviceCard extends LitElement { word-wrap: break-word; } .manuf, - .entity-id, .model { color: var(--secondary-text-color); + word-wrap: break-word; } `; } diff --git a/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts b/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts index d95402bc51..290ff126ac 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts @@ -218,9 +218,11 @@ class ZHADeviceCard extends SubscribeMixin(LitElement) { .device .manuf { color: var(--secondary-text-color); margin-bottom: 20px; + word-wrap: break-word; } .extra-info { margin-top: 8px; + word-wrap: break-word; } state-badge { cursor: pointer; diff --git a/src/panels/lovelace/cards/hui-entities-card.ts b/src/panels/lovelace/cards/hui-entities-card.ts index 2e9cca12a4..d5bbd66b3d 100644 --- a/src/panels/lovelace/cards/hui-entities-card.ts +++ b/src/panels/lovelace/cards/hui-entities-card.ts @@ -163,16 +163,16 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard { if (!this._config || !this._hass) { return; } - const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + const oldHass = changedProps.get("_hass") as HomeAssistant | undefined; const oldConfig = changedProps.get("_config") as | EntitiesCardConfig | undefined; if ( - !oldHass || - !oldConfig || - oldHass.themes !== this.hass.themes || - oldConfig.theme !== this._config.theme + (changedProps.has("_hass") && + (!oldHass || oldHass.themes !== this._hass.themes)) || + (changedProps.has("_config") && + (!oldConfig || oldConfig.theme !== this._config.theme)) ) { applyThemesOnElement(this, this._hass.themes, this._config.theme); } diff --git a/src/panels/lovelace/components/hui-generic-entity-row.ts b/src/panels/lovelace/components/hui-generic-entity-row.ts index 5f185bf46e..b87c39f5a7 100644 --- a/src/panels/lovelace/components/hui-generic-entity-row.ts +++ b/src/panels/lovelace/components/hui-generic-entity-row.ts @@ -33,6 +33,13 @@ class HuiGenericEntityRow extends LitElement { @property({ type: Boolean }) public hideName = false; + // Allows to control if this row should capture the user interaction, e.g. with its + // toggle switch, button or input field. Some domains dynamically decide what to show + // => static determination will not work => the caller has to pass the desired value in. + // Same applies for custom components that want to override the default behavior. + // Default behavior is controlled by DOMAINS_INPUT_ROW. + @property({ type: Boolean }) public catchInteraction?; + protected render(): TemplateResult { if (!this.hass || !this.config) { return html``; @@ -50,8 +57,11 @@ class HuiGenericEntityRow extends LitElement { } const domain = computeDomain(this.config.entity); + // By default, we always show a pointer, since if there is no explicit configuration provided, + // the frontend always assumes "more-info" in the action handler. We only need to hide the pointer + // if the tap action is explicitly set to "none". const pointer = !( - this.config.tap_action && this.config.tap_action.action !== "none" + this.config.tap_action && this.config.tap_action.action === "none" ); const hasSecondary = this.secondaryText || this.config.secondary_info; @@ -144,7 +154,7 @@ class HuiGenericEntityRow extends LitElement { : ""} ` : html``} - ${!DOMAINS_INPUT_ROW.includes(domain) + ${this.catchInteraction ?? !DOMAINS_INPUT_ROW.includes(domain) ? html`
- ${stateObj.state === "on" || - stateObj.state === "off" || - UNAVAILABLE_STATES.includes(stateObj.state) + + ${showToggle ? html`