Compare commits

...
Author SHA1 Message Date
Bruno Pantaleão c9edfaed69 Assert wildcard entity ids are dropped 2026-09-15 21:07:30 +02:00
Bruno Pantaleão 2797a49a6d Keep entity control reporting best effort 2026-09-15 21:01:30 +02:00
Bruno Pantaleão 0c4c834a88 Split and lowercase entity ids like Core does 2026-09-15 20:56:58 +02:00
Bruno Pantaleão d2852a1cd1 Prefer target entity ids and report disconnecting calls 2026-09-15 20:45:11 +02:00
Bruno Pantaleão Gonçalves c67368f89f Merge branch 'dev' into entity-control-external-bus 2026-09-15 20:31:26 +02:00
Bruno Pantaleão d1ec250b12 Communicate entity control to external bus 2026-09-15 20:25:10 +02:00
Wendelin a695710beb Migrate md lists to new lists in 20 files - 2 (#54208)
* migrate to ha-list-base in 10 files

* migrate lists in 10 files
2026-09-15 19:23:46 +02:00
Wendelin f991efbc0a Migrate md lists to ha-list-base in 20 files (#54197)
* Migrated 10 files from md list to new lists

* migrate 10 files

* copilot review

* fix prettier
2026-09-15 19:06:53 +02:00
renovate[bot] 47306d1afd Update dependency intl-messageformat to v11.2.15 (#54213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-15 16:33:19 +00:00
karwostsandCopilot Autofix powered by AI 1692067392 Fix create helper dialog (#54211)
* Fix create helper dialog

* Ensure loading state resets after import

Wrap import and dirty tracking in try-finally to ensure loading state is reset.

Co-authored-by: Copilot Autofix powered by AI <[email protected]>

---------

Co-authored-by: Copilot Autofix powered by AI <[email protected]>
2026-09-15 18:26:53 +02:00
49 changed files with 979 additions and 1035 deletions
+1 -1
View File
@@ -106,7 +106,7 @@
"hls.js": "1.7.3",
"home-assistant-js-websocket": "9.6.0",
"idb-keyval": "6.3.0",
"intl-messageformat": "11.2.14",
"intl-messageformat": "11.2.15",
"js-yaml": "5.4.1",
"leaflet": "1.9.4",
"leaflet.markercluster": "1.5.3",
+6
View File
@@ -1 +1,7 @@
export const stopPropagation = (ev) => ev.stopPropagation();
export const stopKeydownEnterSpacePropagation = (ev: KeyboardEvent) => {
if (ev.key === "Enter" || ev.key === " " || ev.key === "Spacebar") {
ev.stopPropagation();
}
};
+19 -1
View File
@@ -1,4 +1,6 @@
import type { Context, HomeAssistant } from "../types";
import { ensureArray } from "../common/array/ensure-array";
import { isValidEntityId } from "../common/entity/valid_entity_id";
import type { Context, HomeAssistant, ServiceCallRequest } from "../types";
import type { Action } from "./script";
export const callExecuteScript = (
@@ -22,3 +24,19 @@ export const serviceCallWillDisconnect = (
"update.home_assistant_core_update",
"update.home_assistant_operating_system_update",
].includes(serviceData?.entity_id));
// Core merges the target into the service data, so a target entity_id
// replaces the legacy service data one rather than adding to it. Its schema
// also accepts comma separated ids and lowercases them.
export const getServiceCallEntityIds = (
serviceData?: ServiceCallRequest["serviceData"],
target?: ServiceCallRequest["target"]
): string[] => [
...new Set(
(ensureArray(target?.entity_id ?? serviceData?.entity_id) ?? [])
.filter((id): id is string => typeof id === "string")
.flatMap((id) => id.split(","))
.map((id) => id.trim().toLowerCase())
.filter(isValidEntityId)
),
];
+10
View File
@@ -184,6 +184,15 @@ interface EMOutgoingMessageAddEntityTo extends EMMessage {
};
}
interface EMOutgoingMessageEntityControlled extends EMMessage {
type: "entity/controlled";
payload: {
entity_ids: string[];
domain: string;
service: string;
};
}
interface EMOutgoingMessageMoreInfoOpened extends EMMessage {
type: "more_info/opened";
payload: {
@@ -239,6 +248,7 @@ type EMOutgoingMessageWithoutAnswer =
| EMOutgoingMessageImprovScan
| EMOutgoingMessageImprovConfigureDevice
| EMOutgoingMessageAddEntityTo
| EMOutgoingMessageEntityControlled
| EMOutgoingMessageFocusElement
| EMOutgoingMessageReloadAndClearCache
| EMOutgoingMessageAssistSettings;
@@ -9,9 +9,9 @@ import {
mdiVideo,
mdiWebhook,
} from "@mdi/js";
import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { TemplateResult } from "lit";
import { formatDate } from "../../../../common/datetime/format_date";
import { relativeTime } from "../../../../common/datetime/relative_time";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -20,11 +20,13 @@ import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-svg-icon";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/item/ha-list-item-button";
import "../../../../components/list/ha-list-base";
import "../../../../components/list/ha-list-nav";
import type { BackupConfig } from "../../../../data/backup";
import { cloudBackupHealth, cloudBackupEnabled } from "../../../../data/backup";
import { cloudBackupEnabled, cloudBackupHealth } from "../../../../data/backup";
import type {
CloudStatusLoggedIn,
SubscriptionInfo,
@@ -260,8 +262,8 @@ export class CloudAccountOverview extends LitElement {
/>
</div>
<div class="card-content">
<ha-md-list>
<ha-md-list-item>
<ha-list-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.email"
@@ -283,16 +285,16 @@ export class CloudAccountOverview extends LitElement {
@click=${this._toggleEmail}
></ha-icon-button>
</span>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.subscription"
)}</span
>
<span slot="supporting-text">${this._subscriptionDetail()}</span>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-base>
</ha-list-base>
</div>
<div class="card-actions split">
<ha-button
@@ -348,7 +350,11 @@ export class CloudAccountOverview extends LitElement {
"ui.panel.config.cloud.account.overview.features_intro"
)}
</p>
<ha-md-list>
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.cloud.account.overview.features_title"
)}
>
${this._featureRow(
mdiEarth,
this.hass.localize(
@@ -411,7 +417,7 @@ export class CloudAccountOverview extends LitElement {
webhookStatus,
"/config/cloud/webhooks"
)}
</ha-md-list>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -424,12 +430,12 @@ export class CloudAccountOverview extends LitElement {
href: string
): TemplateResult {
return html`
<ha-md-list-item type="link" href=${href}>
<ha-list-item-button href=${href}>
<ha-svg-icon slot="start" .path=${icon}></ha-svg-icon>
<span slot="headline">${title}</span>
<span slot="supporting-text">${supporting}</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`;
}
@@ -661,9 +667,6 @@ export class CloudAccountOverview extends LitElement {
width: 36px;
height: auto;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.muted {
color: var(--secondary-text-color);
}
@@ -692,10 +695,8 @@ export class CloudAccountOverview extends LitElement {
.status-dot.disabled {
background-color: var(--error-color);
}
ha-md-list {
padding: 0;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
ha-list-base {
--ha-row-item-padding-inline: 0;
}
.extras-header {
display: flex;
@@ -721,14 +722,12 @@ export class CloudAccountOverview extends LitElement {
padding-inline: var(--ha-space-4);
margin-top: 0;
}
.extras-content ha-md-list {
.extras-content ha-list-nav {
padding-top: 0;
--md-list-item-leading-space: var(--ha-space-4);
--md-list-item-trailing-space: var(--ha-space-4);
--ha-row-item-padding-inline: var(--ha-space-4);
}
.extras-content ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
.extras-content ha-list-item-button {
--ha-row-item-padding-block: var(--ha-space-2);
}
.extras-content ha-svg-icon[slot="start"],
.extras-content ha-icon-next {
@@ -11,9 +11,9 @@ import { relativeTime } from "../../../../common/datetime/relative_time";
import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-spinner";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-svg-icon";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import type { BackupConfig, BackupInfo } from "../../../../data/backup";
import {
CLOUD_AGENT,
@@ -137,8 +137,8 @@ export class CloudBackupPref extends LitElement {
)}
>
<div class="card-content">
<ha-md-list>
<ha-md-list-item>
<ha-list-base>
<ha-list-item-base>
<ha-svg-icon slot="start" .path=${mdiBackupRestore}></ha-svg-icon>
<span slot="headline">
${
@@ -159,11 +159,11 @@ export class CloudBackupPref extends LitElement {
)
}
</span>
</ha-md-list-item>
</ha-list-item-base>
${
cloudAgent
? html`
<ha-md-list-item>
<ha-list-item-base>
<ha-svg-icon
slot="start"
.path=${mdiHarddisk}
@@ -171,8 +171,8 @@ export class CloudBackupPref extends LitElement {
<span slot="headline">
${bytesToString(cloudAgent.size)}
</span>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<ha-svg-icon
slot="start"
.path=${mdiShieldLock}
@@ -188,11 +188,11 @@ export class CloudBackupPref extends LitElement {
)
}
</span>
</ha-md-list-item>
</ha-list-item-base>
`
: nothing
}
<ha-md-list-item>
<ha-list-item-base>
<ha-svg-icon slot="start" .path=${mdiCalendar}></ha-svg-icon>
<span slot="headline">
${
@@ -213,8 +213,8 @@ export class CloudBackupPref extends LitElement {
)
}
</span>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-base>
</ha-list-base>
</div>
<div class="card-actions">
<ha-button appearance="filled" href="/config/backup?historyBack=1">
@@ -308,15 +308,12 @@ export class CloudBackupPref extends LitElement {
display: flex;
justify-content: flex-end;
}
ha-md-list {
background: none;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
ha-list-base {
--ha-row-item-padding-inline: 0;
}
ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
--md-list-item-one-line-container-height: 40px;
ha-list-item-base {
--ha-row-item-padding-block: var(--ha-space-2);
--ha-row-item-min-height: 40px;
}
ha-svg-icon[slot="start"] {
color: var(--secondary-text-color);
@@ -4,10 +4,10 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-switch";
import "../../../../components/ha-tip";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import { formatDate } from "../../../../common/datetime/format_date";
import type { HaSwitch } from "../../../../components/ha-switch";
@@ -179,8 +179,8 @@ export class CloudRemotePref extends LitElement {
)}
>
<div class="card-content">
<ha-md-list>
<ha-md-list-item>
<ha-list-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.external_activation"
@@ -196,8 +196,8 @@ export class CloudRemotePref extends LitElement {
.checked=${remote_allow_remote_enable}
@change=${this._toggleAllowRemoteEnabledChanged}
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.certificate_info"
@@ -232,8 +232,8 @@ export class CloudRemotePref extends LitElement {
"ui.panel.config.cloud.account.remote.more_info"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-base>
</ha-list-base>
</div>
</ha-card>
<ha-tip .hass=${this.hass}>
@@ -318,13 +318,8 @@ export class CloudRemotePref extends LitElement {
display: flex;
justify-content: flex-end;
}
ha-md-list {
background: none;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
ha-list-base {
--ha-row-item-padding-inline: 0;
}
ha-tip {
max-width: 600px;
@@ -5,26 +5,26 @@ import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../../common/entity/compute_state_name";
import { supportsFeature } from "../../../../common/entity/supports-feature";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-language-picker";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-select";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-textarea";
import "../../../../components/ha-tip";
import "../../../../components/voice-assistant-brand-icon";
import type {
HaSelectOption,
HaSelectSelectEvent,
} from "../../../../components/ha-select";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-textarea";
import type { HaTextArea } from "../../../../components/ha-textarea";
import "../../../../components/ha-tip";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import "../../../../components/voice-assistant-brand-icon";
import { showAutomationEditor } from "../../../../data/automation";
import type { CloudStatusLoggedIn } from "../../../../data/cloud";
import { updateCloudPref } from "../../../../data/cloud";
@@ -153,8 +153,8 @@ export class CloudTTSPref extends LitElement {
)}
>
<div class="card-content">
<ha-md-list>
<ha-md-list-item>
<ha-list-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.cloud.account.tts.default_language"
@@ -176,8 +176,8 @@ export class CloudTTSPref extends LitElement {
@value-changed=${this._handleLanguageChange}
>
</ha-language-picker>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.cloud.account.tts.default_voice"
@@ -199,8 +199,8 @@ export class CloudTTSPref extends LitElement {
}))}
>
</ha-select>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.components.media-browser.tts.selected_voice_id"
@@ -215,8 +215,8 @@ export class CloudTTSPref extends LitElement {
)}
@click=${this._copyVoiceId}
></ha-icon-button>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-base>
</ha-list-base>
<div class="try-tts">
<span class="try-heading">
${this.hass.localize(
@@ -541,13 +541,8 @@ export class CloudTTSPref extends LitElement {
gap: var(--ha-space-2);
flex-wrap: wrap;
}
ha-md-list {
background: none;
--md-list-item-leading-space: var(--ha-space-4);
--md-list-item-trailing-space: var(--ha-space-4);
}
ha-md-list-item {
--md-item-overflow: visible;
ha-list-base {
--ha-row-item-padding-inline: var(--ha-space-4);
}
ha-language-picker,
ha-select {
@@ -1,4 +1,5 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume } from "@lit/context";
import { mdiContentCopy, mdiRestore } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues } from "lit";
@@ -6,7 +7,6 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import memoizeOne from "memoize-one";
import { consume } from "@lit/context";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeObjectId } from "../../../common/entity/compute_object_id";
@@ -19,23 +19,24 @@ import type {
LocalizeKeys,
} from "../../../common/translations/localize";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import "../../../components/entity/ha-entity-picker";
import "../../../components/ha-alert";
import "../../../components/ha-area-picker";
import "../../../components/ha-color-picker";
import "../../../components/ha-dropdown-item";
import "../../../components/entity/ha-entity-picker";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button-next";
import "../../../components/ha-icon-picker";
import "../../../components/ha-labels-picker";
import "../../../components/ha-list-item";
import "../../../components/ha-md-list-item";
import "../../../components/ha-select";
import type { HaSelectSelectEvent } from "../../../components/ha-select";
import "../../../components/ha-state-icon";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/input/ha-input";
import "../../../components/item/ha-list-item-button";
import "../../../components/item/ha-row-item";
import "../../../components/list/ha-list-base";
import {
CAMERA_ORIENTATIONS,
CameraEntityFeature,
@@ -45,16 +46,16 @@ import {
STREAM_TYPE_HLS,
updateCameraPrefs,
} from "../../../data/camera";
import {
dirtyStateContext,
type DirtyStateContext,
} from "../../../data/context/dirty-state";
import type { ConfigEntry } from "../../../data/config_entries";
import { deleteConfigEntry } from "../../../data/config_entries";
import {
createConfigFlow,
handleConfigFlowStep,
} from "../../../data/config_flow";
import {
dirtyStateContext,
type DirtyStateContext,
} from "../../../data/context/dirty-state";
import type { DataEntryFlowStepCreateEntry } from "../../../data/data_entry_flow";
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
import { updateDeviceRegistryEntry } from "../../../data/device/device_registry";
@@ -627,7 +628,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
${
SWITCH_AS_DOMAINS_INVERT.includes(this._switchAsDomain)
? html`
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.invert.label"
@@ -643,7 +644,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
.checked=${!!this.entry.options?.switch_as_x?.invert}
@change=${this._switchAsInvertChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
`
: nothing
} `
@@ -960,7 +961,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
${
this._cameraPrefs
? html`
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.stream.preload_stream"
@@ -977,8 +978,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
.disabled=${this.disabled}
@change=${this._handleCameraPrefsChanged}
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-row-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.stream.stream_orientation"
@@ -1006,103 +1007,97 @@ export class EntityRegistrySettingsEditor extends LitElement {
.value=${this._cameraPrefs.orientation.toString()}
>
</ha-select>
</ha-md-list-item>
</ha-row-item>
`
: nothing
}
${
this.helperConfigEntry &&
this.helperConfigEntry.supports_options &&
this.helperConfigEntry.domain !== "switch_as_x"
? html`
<ha-list-item
class="menu-item"
twoline
hasMeta
.disabled=${this.disabled}
@click=${this._showOptionsFlow}
>
<span
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
<ha-list-base>
${
this.helperConfigEntry &&
this.helperConfigEntry.supports_options &&
this.helperConfigEntry.domain !== "switch_as_x"
? html`
<ha-list-item-button
class="menu-item"
.disabled=${this.disabled}
@click=${this._showOptionsFlow}
>
<span slot="secondary"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state_secondary",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`
: nothing
}
<ha-list-item
class="menu-item"
twoline
hasMeta
.disabled=${this.disabled}
@click=${this._handleVoiceAssistantsClicked}
>
<span
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.voice_assistants"
)}</span
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
>
<span slot="supporting-text"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state_secondary",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
`
: nothing
}
<ha-list-item-button
class="menu-item"
.disabled=${this.disabled}
@click=${this._handleVoiceAssistantsClicked}
>
<span slot="secondary">
${
this.entry.aliases.filter((a) => a !== null).length
? this.entry.aliases
.filter((a): a is string => a !== null)
.join(", ")
: this.hass.localize(
"ui.dialogs.entity_registry.editor.no_aliases"
)
}
</span>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
${
domain === "vacuum" &&
stateObj &&
supportsFeature(stateObj, VacuumEntityFeature.CLEAN_AREA)
? html`
<ha-list-item
class="menu-item"
twoline
hasMeta
.disabled=${this.disabled}
@click=${this._handleVacuumSegmentMappingClicked}
>
<span
>${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.title"
)}</span
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.voice_assistants"
)}</span
>
<span slot="supporting-text">
${
this.entry.aliases.filter((a) => a !== null).length
? this.entry.aliases
.filter((a): a is string => a !== null)
.join(", ")
: this.hass.localize(
"ui.dialogs.entity_registry.editor.no_aliases"
)
}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
${
domain === "vacuum" &&
stateObj &&
supportsFeature(stateObj, VacuumEntityFeature.CLEAN_AREA)
? html`
<ha-list-item-button
class="menu-item"
.disabled=${this.disabled}
@click=${this._handleVacuumSegmentMappingClicked}
>
<span slot="secondary">
${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.description"
)}
</span>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`
: nothing
}
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.title"
)}</span
>
<span slot="supporting-text">
${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.description"
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
`
: nothing
}
</ha-list-base>
${
this._disabledBy &&
this._disabledBy !== "user" &&
@@ -1120,7 +1115,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
: nothing
}
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.enabled_label"
@@ -1143,7 +1138,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
}
@change=${this._enabledChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
${
this._hiddenBy && this._hiddenBy !== "user"
@@ -1160,7 +1155,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
: nothing
}
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.visible_label"
@@ -1177,12 +1172,12 @@ export class EntityRegistrySettingsEditor extends LitElement {
.disabled=${this.disabled || this._disabledBy}
@change=${this._hiddenChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
${
this.entry.device_id
? html`
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.use_device_area"
@@ -1218,7 +1213,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
.disabled=${this.disabled}
@change=${this._useDeviceAreaChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
${
this._areaId || this._noDeviceArea
? html`<ha-area-picker
@@ -1811,7 +1806,10 @@ export class EntityRegistrySettingsEditor extends LitElement {
margin-inline-start: 0;
}
ha-md-list-item ha-select {
ha-row-item {
--ha-row-item-padding-inline: 0;
}
ha-row-item ha-select {
width: auto;
}
ha-input,
@@ -1823,11 +1821,11 @@ export class EntityRegistrySettingsEditor extends LitElement {
width: 100%;
}
.menu-item {
--ha-row-item-padding-inline: 0;
border-radius: var(--ha-border-radius-sm);
margin-top: 3px;
margin-bottom: 3px;
overflow: hidden;
--mdc-list-side-padding: 13px;
}
.entityId {
direction: ltr;
@@ -9,15 +9,16 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { round } from "../../../common/number/round";
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
import { sanitizeHttpUrl } from "../../../common/url/sanitize-http-url";
import "../../../components/animation/ha-fade-in";
import "../../../components/chart/ha-chart-base";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/animation/ha-fade-in";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-md-list-item";
import "../../../components/ha-spinner";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../data/config_entries";
import { subscribeConfigEntries } from "../../../data/config_entries";
import type {
@@ -305,24 +306,29 @@ class HaConfigHardwareOverview extends SubscribeMixin(LitElement) {
${
documentationURL
? html`
<ha-md-list-item
.href=${documentationURL}
type="link"
target="_blank"
rel="noopener noreferrer"
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.hardware.documentation"
)}
>
<span
>${this.hass.localize(
"ui.panel.config.hardware.documentation"
)}</span
<ha-list-item-button
.href=${documentationURL}
target="_blank"
rel="noopener noreferrer"
>
<span slot="supporting-text"
>${this.hass.localize(
"ui.panel.config.hardware.documentation_description"
)}</span
>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.hardware.documentation"
)}</span
>
<span slot="supporting-text"
>${this.hass.localize(
"ui.panel.config.hardware.documentation_description"
)}</span
>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
`
: nothing
}
@@ -140,8 +140,13 @@ export class DialogHelperDetail extends DirtyStateProviderMixin<
this._domain = params.domain;
this._item = undefined;
if (this._domain && this._domain in HELPERS) {
await HELPERS[this._domain].import();
this._initDirtyTracking({ type: "deep" }, undefined);
this._loading = true;
try {
await HELPERS[this._domain].import();
this._initDirtyTracking({ type: "deep" }, undefined);
} finally {
this._loading = false;
}
}
this._open = true;
await this.updateComplete;
@@ -185,7 +190,7 @@ export class DialogHelperDetail extends DirtyStateProviderMixin<
let content: TemplateResult;
let footer: TemplateResult | typeof nothing = nothing;
if (this._domain) {
if (this._domain && !this._loading) {
content = html`
<div class="form" @value-changed=${this._valueChanged}>
${this._error ? html`<div class="error">${this._error}</div>` : ""}
@@ -1,9 +1,9 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import "../../../components/ha-dialog";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-base";
import { ERROR_STATES, RECOVERABLE_STATES } from "../../../data/config_entries";
import type { HomeAssistant } from "../../../types";
import type { PickConfigEntryDialogParams } from "./show-pick-config-entry-dialog";
@@ -43,21 +43,22 @@ export class DialogPickConfigEntry extends LitElement {
header-title=${title}
@closed=${this._dialogClosed}
>
<ha-md-list>
<ha-list-base>
${this._params.configEntries.map(
(entry) =>
html`<ha-md-list-item
type="button"
html`<ha-list-item-button
@click=${this._itemPicked}
.entry=${entry}
.disabled=${
!ERROR_STATES.includes(entry.state) &&
!RECOVERABLE_STATES.includes(entry.state)
}
>${entry.title}</ha-md-list-item
><span slot="headline"
>${entry.title}</span
></ha-list-item-button
>`
)}
</ha-md-list>
</ha-list-base>
</ha-dialog>
`;
}
@@ -4,15 +4,17 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { stopKeydownEnterSpacePropagation } from "../../../../../common/dom/stop_propagation";
import { computeDeviceName } from "../../../../../common/entity/compute_device_name";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import type {
BluetoothAllocationsData,
BluetoothScannerState,
@@ -191,7 +193,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
>
<div class="container">
<ha-card>
<ha-md-list>
<ha-list-base>
${this._renderAdaptersList(enabledDevices)}
${disabledDevices.map(({ device, entry }) => {
const areaName =
@@ -205,7 +207,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
.filter(Boolean)
.join(" · ");
return html`
<ha-md-list-item class="disabled">
<ha-list-item-base class="disabled">
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
<div slot="headline">
${computeDeviceName(device) || entry.title}
@@ -218,12 +220,12 @@ export class BluetoothAdapterInfoPage extends LitElement {
>
${this.hass.localize("ui.common.enable")}
</ha-button>
</ha-md-list-item>
</ha-list-item-base>
`;
})}
${disabledEntriesWithoutDevice.map(
(entry) => html`
<ha-md-list-item class="disabled">
<ha-list-item-base class="disabled">
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
<div slot="headline">${entry.title}</div>
<div slot="supporting-text">
@@ -238,10 +240,10 @@ export class BluetoothAdapterInfoPage extends LitElement {
>
${this.hass.localize("ui.common.enable")}
</ha-button>
</ha-md-list-item>
</ha-list-item-base>
`
)}
</ha-md-list>
</ha-list-base>
<div class="card-actions">
<ha-button
appearance="plain"
@@ -262,13 +264,13 @@ export class BluetoothAdapterInfoPage extends LitElement {
devices: { device: DeviceRegistryEntry; entry: ConfigEntry }[]
) {
if (devices.length === 0) {
return html`<ha-md-list-item>
return html`<ha-list-item-base>
<div slot="headline">
${this.hass.localize(
"ui.panel.config.bluetooth.no_scanner_state_available"
)}
</div>
</ha-md-list-item>`;
</ha-list-item-base>`;
}
return devices.map(({ device, entry }) => {
@@ -314,8 +316,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
);
return html`
<ha-md-list-item
type="link"
<ha-list-item-button
href=${`/config/bluetooth/connection-monitor?source=${btAddress}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -328,6 +329,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
.path=${mdiCogOutline}
.entry=${entry}
@click=${this._openOptionFlow}
@keydown=${stopKeydownEnterSpacePropagation}
.label=${this.hass.localize(
"ui.panel.config.bluetooth.option_flow"
)}
@@ -335,7 +337,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
: nothing
}
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
${
hasMismatch && scannerDetails
? this._renderScannerMismatchWarning(
@@ -499,22 +501,14 @@ export class BluetoothAdapterInfoPage extends LitElement {
margin: 0 auto var(--ha-space-4);
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-md-list-item ha-svg-icon[slot="start"] {
ha-list-item-button ha-svg-icon[slot="start"],
ha-list-item-base ha-svg-icon[slot="start"] {
color: var(--secondary-text-color);
}
ha-md-list-item.disabled ha-svg-icon[slot="start"],
ha-md-list-item.disabled [slot="headline"],
ha-md-list-item.disabled [slot="supporting-text"] {
ha-list-item-base.disabled ha-svg-icon[slot="start"],
ha-list-item-base.disabled [slot="headline"],
ha-list-item-base.disabled [slot="supporting-text"] {
opacity: 0.5;
}
@@ -7,16 +7,16 @@ import {
mdiLinkVariant,
mdiVectorPolyline,
} from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import type {
BluetoothAllocationsData,
BluetoothDeviceData,
@@ -213,11 +213,12 @@ export class BluetoothConfigDashboard extends LitElement {
</ha-button>
</div>
<div class="card-content network-card-content">
<ha-md-list>
<ha-md-list-item
type="link"
href="/config/bluetooth/adapter-info"
>
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.bluetooth.my_network"
)}
>
<ha-list-item-button href="/config/bluetooth/adapter-info">
<ha-svg-icon
slot="start"
.path=${mdiAccessPoint}
@@ -229,10 +230,9 @@ export class BluetoothConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
<ha-md-list-item
type="link"
<ha-list-item-button
href="/config/bluetooth/connection-monitor"
>
<ha-svg-icon
@@ -246,10 +246,9 @@ export class BluetoothConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
<ha-md-list-item
type="link"
<ha-list-item-button
href="/config/bluetooth/advertisement-monitor"
>
<ha-svg-icon slot="start" .path=${mdiBroadcast}></ha-svg-icon>
@@ -260,8 +259,8 @@ export class BluetoothConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-nav>
</div>
</ha-card>
</div>
@@ -286,11 +285,6 @@ export class BluetoothConfigDashboard extends LitElement {
margin-top: var(--ha-space-6);
}
ha-md-list {
background: none;
padding: 0;
}
.network-card {
overflow: hidden;
}
@@ -9,9 +9,9 @@ import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import type { InfraredDevice } from "../../../../../data/infrared";
import "../../../../../layouts/hass-subpage";
import { haStyle } from "../../../../../resources/styles";
@@ -82,8 +82,12 @@ export class InfraredConfigDashboard extends LitElement {
${this.hass.localize("ui.panel.config.infrared.my_devices")}
</div>
<div class="card-content network-card-content">
<ha-md-list>
<ha-md-list-item type="link" href="/config/infrared/devices">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.infrared.my_devices"
)}
>
<ha-list-item-button href="/config/infrared/devices">
<ha-svg-icon slot="start" .path=${mdiRemote}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -92,8 +96,8 @@ export class InfraredConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-nav>
</div>
</ha-card>
</div>
@@ -118,11 +122,6 @@ export class InfraredConfigDashboard extends LitElement {
margin-top: var(--ha-space-6);
}
ha-md-list {
background: none;
padding: 0;
}
.network-card {
overflow: hidden;
}
@@ -2,25 +2,26 @@ import { mdiDelete, mdiLock, mdiPlus } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { stopKeydownEnterSpacePropagation } from "../../../../../common/dom/stop_propagation";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-dialog";
import "../../../../../components/ha-dialog-footer";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/ha-dialog";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import type {
MatterLockInfo,
MatterLockUser,
} from "../../../../../data/matter-lock";
import {
clearMatterLockUser,
getMatterLockInfo,
getMatterLockUsers,
clearMatterLockUser,
} from "../../../../../data/matter-lock";
import {
showAlertDialog,
@@ -162,11 +163,10 @@ class DialogMatterLockManage extends LitElement {
)}
</p>`
: html`
<ha-md-list>
<ha-list-base>
${occupiedUsers.map(
(user) => html`
<ha-md-list-item
type="button"
<ha-list-item-button
.user=${user}
@click=${this._handleUserClick}
>
@@ -191,11 +191,12 @@ class DialogMatterLockManage extends LitElement {
.path=${mdiDelete}
.user=${user}
@click=${this._handleDeleteUserClick}
@keydown=${stopKeydownEnterSpacePropagation}
></ha-icon-button>
</ha-md-list-item>
</ha-list-item-button>
`
)}
</ha-md-list>
</ha-list-base>
`
}
${
@@ -3,8 +3,8 @@ import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../../common/dom/fire_event";
import "../../../../../../components/ha-icon-next";
import "../../../../../../components/ha-md-list";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/item/ha-list-item-button";
import "../../../../../../components/list/ha-list-base";
import type { HomeAssistant } from "../../../../../../types";
import type { MatterAddDeviceStep } from "../dialog-matter-add-device";
import { sharedStyles } from "./matter-add-device-shared-styles";
@@ -23,14 +23,8 @@ class MatterAddDeviceExisting extends LitElement {
</p>
</div>
<ha-md-list>
<ha-md-list-item
interactive
type="button"
.step=${"google_home"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
<ha-list-base>
<ha-list-item-button .step=${"google_home"} @click=${this._onItemClick}>
<img
src="/static/images/logo_google_home.png"
alt=""
@@ -43,14 +37,8 @@ class MatterAddDeviceExisting extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
.step=${"apple_home"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
</ha-list-item-button>
<ha-list-item-button .step=${"apple_home"} @click=${this._onItemClick}>
<img
src="/static/images/logo_apple_home.png"
alt=""
@@ -63,14 +51,8 @@ class MatterAddDeviceExisting extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
.step=${"generic"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
</ha-list-item-button>
<ha-list-item-button .step=${"generic"} @click=${this._onItemClick}>
<div class="logo" slot="start">
<ha-svg-icon path=${mdiHomeAutomation}></ha-svg-icon>
</div>
@@ -80,15 +62,12 @@ class MatterAddDeviceExisting extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-base>
`;
}
private _onItemClick(ev) {
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
return;
}
const item = ev.currentTarget as any;
const step = item.step as MatterAddDeviceStep;
fireEvent(this, "step-selected", { step });
@@ -2,8 +2,8 @@ import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../../common/dom/fire_event";
import "../../../../../../components/ha-icon-next";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/ha-md-list";
import "../../../../../../components/item/ha-list-item-button";
import "../../../../../../components/list/ha-list-base";
import type { HomeAssistant } from "../../../../../../types";
import { sharedStyles } from "./matter-add-device-shared-styles";
@@ -18,14 +18,8 @@ class MatterAddDeviceMain extends LitElement {
${this.hass.localize(`ui.dialogs.matter-add-device.main.question`)}
</p>
</div>
<ha-md-list>
<ha-md-list-item
interactive
type="button"
.step=${"new"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
<ha-list-base>
<ha-list-item-button .step=${"new"} @click=${this._onItemClick}>
<span slot="headline">
${this.hass.localize(
`ui.dialogs.matter-add-device.main.answer_new`
@@ -37,14 +31,8 @@ class MatterAddDeviceMain extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
.step=${"existing"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
</ha-list-item-button>
<ha-list-item-button .step=${"existing"} @click=${this._onItemClick}>
<span slot="headline">
${this.hass.localize(
`ui.dialogs.matter-add-device.main.answer_existing`
@@ -56,15 +44,12 @@ class MatterAddDeviceMain extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-base>
`;
}
private _onItemClick(ev) {
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
return;
}
const item = ev.currentTarget as any;
const step = item.step as "new" | "existing";
fireEvent(this, "step-selected", { step });
@@ -23,10 +23,9 @@ export const sharedStyles = css`
cursor: pointer;
text-decoration: underline;
}
ha-md-list {
ha-list-base {
padding: 0;
--md-list-item-leading-space: var(--horizontal-padding, 16px);
--md-list-item-trailing-space: var(--horizontal-padding, 16px);
--ha-row-item-padding-inline: var(--horizontal-padding, 16px);
margin-bottom: 16px;
}
ha-input {
@@ -16,9 +16,9 @@ import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import { fetchMatterNetworkTopology } from "../../../../../data/matter";
@@ -167,9 +167,12 @@ export class MatterConfigDashboard extends LitElement {
</ha-button>
</div>
<div class="card-content">
<ha-md-list>
<ha-md-list-item
type="link"
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.matter.panel.my_network_title"
)}
>
<ha-list-item-button
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -180,9 +183,8 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
type="link"
</ha-list-item-button>
<ha-list-item-button
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -193,8 +195,8 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -204,8 +206,12 @@ export class MatterConfigDashboard extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-md-list>
<ha-md-list-item type="link" href="/config/matter/options">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.matter.panel.options_title"
)}
>
<ha-list-item-button href="/config/matter/options">
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -218,10 +224,10 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
${
isComponentLoaded(this.hass.config, "thread")
? html`<ha-md-list-item type="link" href="/config/thread">
? html`<ha-list-item-button href="/config/thread">
<ha-svg-icon
slot="start"
.path=${THREAD_ICON}
@@ -237,10 +243,10 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>`
</ha-list-item-button>`
: nothing
}
</ha-md-list>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -307,15 +313,6 @@ export class MatterConfigDashboard extends LitElement {
margin-top: var(--ha-space-6);
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.network-status div.heading {
display: flex;
align-items: center;
@@ -1,12 +1,11 @@
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/item/ha-row-item";
import {
acceptSharedMatterDevice,
canCommissionMatterExternal,
@@ -55,122 +54,120 @@ class MatterOptionsPage extends LitElement {
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing
}
<ha-md-list>
${
canCommissionMatterExternal(this.hass)
? html`<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._startMobileCommissioning}
>
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_action"
)}
</ha-button>
</ha-md-list-item>`
: nothing
}
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._commission}
>
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._acceptSharedDevice}
>
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setWifi}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setThread}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_action"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>
${
canCommissionMatterExternal(this.hass)
? html`<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._startMobileCommissioning}
>
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_action"
)}
</ha-button>
</ha-row-item>`
: nothing
}
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._commission}
>
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_action"
)}
</ha-button>
</ha-row-item>
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._acceptSharedDevice}
>
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_action"
)}
</ha-button>
</ha-row-item>
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setWifi}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_action"
)}
</ha-button>
</ha-row-item>
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setThread}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_action"
)}
</ha-button>
</ha-row-item>
</ha-card>
</div>
</hass-subpage>
@@ -323,15 +320,6 @@ class MatterOptionsPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
`,
];
}
@@ -16,10 +16,11 @@ import "../../../../../components/ha-alert";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import { domainToName } from "../../../../../data/integration";
@@ -187,22 +188,21 @@ export class ModbusConfigDashboard extends LitElement {
// removed while the connection was listed has nowhere to link to
if (!holder.entry) {
return html`
<ha-md-list-item class="holder">
<ha-list-item-base class="holder">
<ha-svg-icon
slot="start"
.path=${source === "yaml" ? mdiFileDocumentOutline : mdiPuzzle}
></ha-svg-icon>
<div slot="headline">${holder.id}</div>
${this._renderUnits(holder)}
</ha-md-list-item>
</ha-list-item-base>
`;
}
const { domain, title } = holder.entry;
return html`
<ha-md-list-item
type="link"
<ha-list-item-button
class="holder"
href=${`/config/integrations/integration/${domain}#config_entry=${holder.id}`}
>
@@ -225,19 +225,19 @@ export class ModbusConfigDashboard extends LitElement {
</div>
${this._renderUnits(holder)}
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`;
}
private _renderSerialPortLink(): TemplateResult {
return html`
<ha-md-list-item type="link" class="holder" href="/config/serial">
<ha-list-item-button class="holder" href="/config/serial">
<ha-svg-icon slot="start" .path=${mdiCableData}></ha-svg-icon>
<div slot="headline">
${this.hass.localize("ui.panel.config.modbus.view_serial_port")}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`;
}
@@ -256,12 +256,12 @@ export class ModbusConfigDashboard extends LitElement {
private _renderConnectionItem(item: ConnectionListItem): TemplateResult {
return html`
<ha-md-list-item class="connection">
<ha-list-item-base class="connection">
<ha-svg-icon slot="start" .path=${item.icon}></ha-svg-icon>
<div slot="headline">${item.primary}</div>
<div slot="supporting-text">${item.secondary}</div>
${this._renderState(item)}
</ha-md-list-item>
</ha-list-item-base>
${item.holders.map((holder) => this._renderHolder(holder, item.source))}
${
item.serialDevice && isComponentLoaded(this.hass.config, "usb")
@@ -283,9 +283,9 @@ export class ModbusConfigDashboard extends LitElement {
"ui.panel.config.modbus.connections_description"
)}
</div>
<ha-md-list>
<ha-list-base>
${items.map((item) => this._renderConnectionItem(item))}
</ha-md-list>
</ha-list-base>
</div>
</ha-card>
`;
@@ -474,23 +474,16 @@ export class ModbusConfigDashboard extends LitElement {
color: var(--secondary-text-color);
}
ha-md-list {
background: none;
padding: 0;
ha-list-item-base,
ha-list-item-button {
--ha-row-item-padding-block: var(--ha-space-2);
--ha-row-item-min-height: 0;
}
ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
--md-list-item-one-line-container-height: 0;
--md-list-item-two-line-container-height: 0;
--md-list-item-three-line-container-height: 0;
}
ha-md-list-item.connection:not(:first-child) {
ha-list-item-base.connection:not(:first-child) {
border-top: 1px solid var(--divider-color);
margin-top: var(--ha-space-2);
--md-list-item-top-space: var(--ha-space-4);
--ha-row-item-padding-block: var(--ha-space-4) var(--ha-space-2);
}
.state {
@@ -512,11 +505,11 @@ export class ModbusConfigDashboard extends LitElement {
background-color: var(--success-color);
}
ha-md-list-item.holder {
--md-list-item-leading-space: var(--ha-space-14);
.holder {
--ha-row-item-padding-inline: var(--ha-space-14) var(--ha-space-4);
}
ha-md-list-item.holder img[slot="start"] {
.holder img[slot="start"] {
width: 24px;
height: 24px;
}
@@ -1,7 +1,3 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import {
mdiAlertCircleOutline,
mdiCheck,
@@ -9,32 +5,37 @@ import {
mdiShape,
mdiTune,
} from "@mdi/js";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { storage } from "../../../../../common/decorators/storage";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-code-editor";
import "../../../../../components/ha-formfield";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import type { HaSelectSelectEvent } from "../../../../../components/ha-select";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/ha-switch";
import "../../../../../components/input/ha-input";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import { fetchIntegrationManifest } from "../../../../../data/integration";
import type { Action } from "../../../../../data/script";
import { callExecuteScript } from "../../../../../data/service";
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
import { showOptionsFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-options-flow";
import "../../../../../layouts/hass-subpage";
import { mdiMqttLogo } from "../../../../../resources/mqtt-logo-svg";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import { brandsUrl } from "../../../../../util/brands-url";
import { showToast } from "../../../../../util/toast";
import "./mqtt-subscribe-card";
import { brandsUrl } from "../../../../../util/brands-url";
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
import { fetchIntegrationManifest } from "../../../../../data/integration";
import { mdiMqttLogo } from "../../../../../resources/mqtt-logo-svg";
const qosLevel = ["0", "1", "2"];
@@ -186,9 +187,12 @@ export class MQTTConfigPanel extends LitElement {
${this.hass.localize("ui.panel.config.mqtt.my_network_title")}
</div>
<div class="card-content">
<ha-md-list>
<ha-md-list-item
type="link"
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.mqtt.my_network_title"
)}
>
<ha-list-item-button
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -198,9 +202,8 @@ export class MQTTConfigPanel extends LitElement {
})}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
type="link"
</ha-list-item-button>
<ha-list-item-button
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -210,8 +213,8 @@ export class MQTTConfigPanel extends LitElement {
})}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -221,8 +224,8 @@ export class MQTTConfigPanel extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-md-list>
<ha-md-list-item type="link" @click=${this._openOptionFlow}>
<ha-list-base>
<ha-list-item-button @click=${this._openOptionFlow}>
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${this.hass.localize("ui.panel.config.mqtt.option_flow")}
@@ -233,8 +236,8 @@ export class MQTTConfigPanel extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item type="link" @click=${this._openConfigFlow}>
</ha-list-item-button>
<ha-list-item-button @click=${this._openConfigFlow}>
<ha-svg-icon slot="start" .path=${mdiMqttLogo}></ha-svg-icon>
<div slot="headline">
${this.hass.localize("ui.panel.config.mqtt.config_flow")}
@@ -245,8 +248,8 @@ export class MQTTConfigPanel extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-base>
</div>
</ha-card>
`;
@@ -404,13 +407,6 @@ export class MQTTConfigPanel extends LitElement {
margin: 0 auto var(--ha-space-4);
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-select {
width: 96px;
margin: 0 8px;
@@ -4,9 +4,9 @@ import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import { UNAVAILABLE } from "../../../../../data/entity/entity";
import { FALLBACK_DOMAIN_ICONS } from "../../../../../data/icons";
import type { RadioFrequencyTransmitter } from "../../../../../data/radio_frequency";
@@ -73,11 +73,12 @@ export class RadioFrequencyConfigDashboard extends LitElement {
<ha-card class="network-card">
<div class="card-content">
<ha-md-list>
<ha-md-list-item
type="link"
href="/config/radio-frequency/devices"
>
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.radio_frequency.title"
)}
>
<ha-list-item-button href="/config/radio-frequency/devices">
<ha-svg-icon
slot="start"
.path=${FALLBACK_DOMAIN_ICONS[DOMAIN]}
@@ -89,8 +90,8 @@ export class RadioFrequencyConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-nav>
</div>
</ha-card>
</div>
@@ -111,11 +112,6 @@ export class RadioFrequencyConfigDashboard extends LitElement {
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
.network-card {
overflow: hidden;
}
@@ -22,10 +22,11 @@ import "../../../../../components/ha-app-icon";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import {
domainToName,
getConfigPanelPath,
@@ -42,13 +43,13 @@ import type {
} from "../../../../../data/usb";
import { listSerialPortsWithUsage } from "../../../../../data/usb";
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
import { mdiEsphomeLogo } from "../../../../../resources/esphome-logo-svg";
import { showSerialPortInfoDialog } from "./show-dialog-serial-port-info";
import "../../../../../layouts/hass-subpage";
import { panelIsReady } from "../../../../../layouts/panel-ready";
import { mdiEsphomeLogo } from "../../../../../resources/esphome-logo-svg";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant, Route } from "../../../../../types";
import { brandsUrl } from "../../../../../util/brands-url";
import { showSerialPortInfoDialog } from "./show-dialog-serial-port-info";
const ESPHOME_HASS_SCHEME = "esphome-hass://";
@@ -270,7 +271,7 @@ export class SerialConfigDashboard extends LitElement {
const href = this._consumerHref(consumer);
return html`
<ha-md-list-item type="link" href=${href} class="consumer">
<ha-list-item-button href=${href} class="consumer">
${
consumer.kind === "config_entry"
? html`<img
@@ -302,14 +303,13 @@ export class SerialConfigDashboard extends LitElement {
}
<div slot="headline">${this._consumerName(consumer)}</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`;
}
private _renderDiscoveryFlow(flow: SerialPortDiscoveryFlow): TemplateResult {
return html`
<ha-md-list-item
type="button"
<ha-list-item-button
class="consumer"
.flowId=${flow.flow_id}
@click=${this._continueFlow}
@@ -334,13 +334,13 @@ export class SerialConfigDashboard extends LitElement {
})}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`;
}
private _renderModbusLink(): TemplateResult {
return html`
<ha-md-list-item type="link" class="consumer" href="/config/modbus">
<ha-list-item-button class="consumer" href="/config/modbus">
<ha-svg-icon
slot="start"
.path=${mdiTransitConnectionVariant}
@@ -349,7 +349,7 @@ export class SerialConfigDashboard extends LitElement {
${this.hass.localize("ui.panel.config.serial.used_by_modbus")}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`;
}
@@ -378,7 +378,7 @@ export class SerialConfigDashboard extends LitElement {
: undefined;
return html`
<ha-md-list-item class="port">
<ha-list-item-base class="port">
<ha-svg-icon
slot="start"
class=${item.port.present ? "" : "disconnected"}
@@ -415,7 +415,7 @@ export class SerialConfigDashboard extends LitElement {
></ha-icon-button>`
: nothing
}
</ha-md-list-item>
</ha-list-item-base>
${item.consumers.map((consumer) => this._renderConsumer(consumer))}
${this._usedByModbus(item.port) ? this._renderModbusLink() : nothing}
${item.discoveryFlows.map((flow) => this._renderDiscoveryFlow(flow))}
@@ -438,9 +438,9 @@ export class SerialConfigDashboard extends LitElement {
<div class="card-header">${header}</div>
<div class="card-content">
<div class="description">${description}</div>
<ha-md-list>
<ha-list-base>
${items.map((item) => this._renderPortItem(item))}
</ha-md-list>
</ha-list-base>
</div>
</ha-card>
`;
@@ -688,35 +688,28 @@ export class SerialConfigDashboard extends LitElement {
color: var(--secondary-text-color);
}
ha-md-list {
background: none;
padding: 0;
ha-list-item-base,
ha-list-item-button {
--ha-row-item-padding-block: var(--ha-space-2);
--ha-row-item-min-height: 0;
}
ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
--md-list-item-one-line-container-height: 0;
--md-list-item-two-line-container-height: 0;
--md-list-item-three-line-container-height: 0;
}
ha-md-list-item.port:not(:first-child) {
ha-list-item-base.port:not(:first-child) {
border-top: 1px solid var(--divider-color);
margin-top: var(--ha-space-2);
--md-list-item-top-space: var(--ha-space-4);
--ha-row-item-padding-block: var(--ha-space-4) var(--ha-space-2);
}
ha-md-list-item .disconnected {
ha-list-item-base .disconnected {
color: var(--disabled-text-color);
}
ha-md-list-item.consumer {
--md-list-item-leading-space: var(--ha-space-14);
.consumer {
--ha-row-item-padding-inline: var(--ha-space-14) var(--ha-space-4);
}
ha-md-list-item.consumer img[slot="start"],
ha-md-list-item.consumer ha-app-icon[slot="start"] {
.consumer img[slot="start"],
.consumer ha-app-icon[slot="start"] {
width: 24px;
height: 24px;
}
@@ -16,18 +16,20 @@ import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { navigate } from "../../../../../common/navigate";
import { animationStyles } from "../../../../../resources/theme/animations.globals";
import "../../../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button";
import { animationStyles } from "../../../../../resources/theme/animations.globals";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import type {
@@ -219,9 +221,12 @@ class ZHAConfigDashboard extends LitElement {
</ha-button>
</div>
<div class="card-content">
<ha-md-list>
<ha-md-list-item
type="link"
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zha.configuration_page.my_network_title"
)}
>
<ha-list-item-button
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -232,9 +237,8 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
type="link"
</ha-list-item-button>
<ha-list-item-button
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -245,8 +249,8 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item type="link" href="/config/zha/groups">
</ha-list-item-button>
<ha-list-item-button href="/config/zha/groups">
<ha-svg-icon
slot="start"
.path=${mdiFolderMultipleOutline}
@@ -271,8 +275,8 @@ class ZHAConfigDashboard extends LitElement {
}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -288,8 +292,12 @@ class ZHAConfigDashboard extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-md-list>
<ha-md-list-item type="link" href="/config/zha/options">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zha.configuration_page.options_title"
)}
>
<ha-list-item-button href="/config/zha/options">
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -302,8 +310,8 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item type="link" href="/config/zha/network-info">
</ha-list-item-button>
<ha-list-item-button href="/config/zha/network-info">
<ha-svg-icon
slot="start"
.path=${mdiInformationOutline}
@@ -319,13 +327,10 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
${dynamicSections.map(
(section) => html`
<ha-md-list-item
type="link"
href=${`/config/zha/section/${section}`}
>
<ha-list-item-button href=${`/config/zha/section/${section}`}>
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${
@@ -335,10 +340,10 @@ class ZHAConfigDashboard extends LitElement {
}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`
)}
</ha-md-list>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -348,8 +353,8 @@ class ZHAConfigDashboard extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-md-list>
<ha-md-list-item>
<ha-list-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zha.configuration_page.download_backup"
@@ -372,8 +377,8 @@ class ZHAConfigDashboard extends LitElement {
"ui.panel.config.zha.configuration_page.download_backup_action"
)}
</ha-progress-button>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zha.configuration_page.migrate_radio"
@@ -394,8 +399,8 @@ class ZHAConfigDashboard extends LitElement {
"ui.panel.config.zha.configuration_page.migrate_radio_action"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-base>
</ha-list-base>
</div>
</ha-card>
`;
@@ -532,15 +537,6 @@ class ZHAConfigDashboard extends LitElement {
padding: var(--ha-space-12);
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.network-status div.heading {
display: flex;
align-items: center;
@@ -4,8 +4,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import type { ZHANetworkSettings } from "../../../../../data/zha";
import { fetchZHANetworkSettings } from "../../../../../data/zha";
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
@@ -53,8 +53,8 @@ class ZHANetworkInfoPage extends LitElement {
<ha-card>
${
this._networkSettings
? html`<ha-md-list>
<ha-md-list-item>
? html`<ha-list-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.channel_label"
@@ -73,16 +73,16 @@ class ZHANetworkInfoPage extends LitElement {
.path=${mdiPencil}
@click=${this._showChannelMigrationDialog}
></ha-icon-button>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">PAN ID</span>
<span slot="supporting-text"
>${
this._networkSettings.settings.network_info.pan_id
}</span
>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">Extended PAN ID</span>
<span slot="supporting-text"
>${
@@ -90,14 +90,14 @@ class ZHANetworkInfoPage extends LitElement {
.extended_pan_id
}</span
>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">Coordinator IEEE</span>
<span slot="supporting-text"
>${this._networkSettings.settings.node_info.ieee}</span
>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.radio_type"
@@ -106,8 +106,8 @@ class ZHANetworkInfoPage extends LitElement {
<span slot="supporting-text"
>${this._networkSettings.radio_type}</span
>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.serial_port"
@@ -116,12 +116,12 @@ class ZHANetworkInfoPage extends LitElement {
<span slot="supporting-text"
>${this._networkSettings.device.path}</span
>
</ha-md-list-item>
</ha-list-item-base>
${
this._networkSettings.device.baudrate &&
!this._networkSettings.device.path.startsWith("socket://")
? html`
<ha-md-list-item>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.baudrate"
@@ -130,11 +130,11 @@ class ZHANetworkInfoPage extends LitElement {
<span slot="supporting-text"
>${this._networkSettings.device.baudrate}</span
>
</ha-md-list-item>
</ha-list-item-base>
`
: nothing
}
</ha-md-list>`
</ha-list-base>`
: nothing
}
</ha-card>
@@ -174,19 +174,6 @@ class ZHANetworkInfoPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
--md-list-item-supporting-text-size: var(
--md-list-item-label-text-size,
var(--md-sys-typescale-body-large-size, 1rem)
);
}
`,
];
}
@@ -4,13 +4,13 @@ import { customElement, property, state } from "lit/decorators";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-select";
import "../../../../../components/input/ha-input";
import type { HaInput } from "../../../../../components/input/ha-input";
import "../../../../../components/ha-switch";
import type { HaSwitch } from "../../../../../components/ha-switch";
import "../../../../../components/input/ha-input";
import type { HaInput } from "../../../../../components/input/ha-input";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import type { ZHAConfiguration } from "../../../../../data/zha";
import {
fetchZHAConfiguration,
@@ -115,8 +115,8 @@ class ZHAOptionsPage extends LitElement {
${
this._configuration
? html`
<ha-md-list>
<ha-md-list-item>
<ha-list-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.enable_identify_on_join_label"
@@ -135,8 +135,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._enableIdentifyOnJoinChanged}
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.default_light_transition_label"
@@ -160,8 +160,8 @@ class ZHAOptionsPage extends LitElement {
>
<span slot="end">s</span>
</ha-input>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.enhanced_light_transition_label"
@@ -180,8 +180,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._enhancedLightTransitionChanged}
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.light_transitioning_flag_label"
@@ -200,8 +200,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._lightTransitioningFlagChanged}
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.group_members_assume_state_label"
@@ -220,8 +220,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._groupMembersAssumeStateChanged}
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.consider_unavailable_mains_label"
@@ -242,11 +242,11 @@ class ZHAOptionsPage extends LitElement {
.options=${this._getUnavailableTimeoutOptions(7200)}
@selected=${this._mainsUnavailableChanged}
></ha-select>
</ha-md-list-item>
</ha-list-item-base>
${
this._customMains
? html`
<ha-md-list-item>
<ha-list-item-base>
<ha-input
slot="end"
type="number"
@@ -261,11 +261,11 @@ class ZHAOptionsPage extends LitElement {
>
<span slot="end">s</span>
</ha-input>
</ha-md-list-item>
</ha-list-item-base>
`
: nothing
}
<ha-md-list-item>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.consider_unavailable_battery_label"
@@ -286,11 +286,11 @@ class ZHAOptionsPage extends LitElement {
.options=${this._getUnavailableTimeoutOptions(21600)}
@selected=${this._batteryUnavailableChanged}
></ha-select>
</ha-md-list-item>
</ha-list-item-base>
${
this._customBattery
? html`
<ha-md-list-item>
<ha-list-item-base>
<ha-input
slot="end"
type="number"
@@ -305,11 +305,11 @@ class ZHAOptionsPage extends LitElement {
>
<span slot="end">s</span>
</ha-input>
</ha-md-list-item>
</ha-list-item-base>
`
: nothing
}
<ha-md-list-item>
<ha-list-item-base>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.enable_mains_startup_polling_label"
@@ -328,8 +328,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._enableMainsStartupPollingChanged}
></ha-switch>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-base>
</ha-list-base>
<div class="card-actions">
<ha-progress-button
appearance="filled"
@@ -463,15 +463,6 @@ class ZHAOptionsPage extends LitElement {
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-select,
ha-input {
min-width: 210px;
@@ -1,12 +1,12 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { css, html, LitElement, nothing } from "lit";
import { fireEvent } from "../../../../../../common/dom/fire_event";
import type { HomeAssistant } from "../../../../../../types";
import "../../../../../../components/ha-md-list";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/ha-alert";
import "../../../../../../components/ha-icon-next";
import "../../../../../../components/item/ha-list-item-button";
import "../../../../../../components/list/ha-list-base";
@customElement("zwave-js-add-node-select-method")
export class ZWaveJsAddNodeSelectMethod extends LitElement {
@@ -26,12 +26,10 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
>`
: nothing
}
<ha-md-list>
<ha-list-base>
${
!this.hideQrWebcam
? html`<ha-md-list-item
interactive
type="button"
? html`<ha-list-item-button
@click=${this._selectMethod}
.value=${"qr_code_webcam"}
.disabled=${!window.isSecureContext}
@@ -47,12 +45,10 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>`
</ha-list-item-button>`
: nothing
}
<ha-md-list-item
interactive
type="button"
<ha-list-item-button
@click=${this._selectMethod}
.value=${"qr_code_manual"}
>
@@ -67,10 +63,8 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
</ha-list-item-button>
<ha-list-item-button
@click=${this._selectMethod}
.value=${"search_device"}
>
@@ -85,8 +79,8 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-base>
`;
}
@@ -96,12 +90,6 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
fireEvent(this, "z-wave-method-selected", { method });
}
}
static styles = css`
ha-md-list {
padding: 0;
}
`;
}
declare global {
@@ -2,32 +2,35 @@ import { mdiAccountKey, mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
import { fireEvent } from "../../../../../common/dom/fire_event";
import {
stopKeydownEnterSpacePropagation,
stopPropagation,
} from "../../../../../common/dom/stop_propagation";
import type { LocalizeKeys } from "../../../../../common/translations/localize";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-dialog";
import "../../../../../components/ha-dialog-footer";
import "../../../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../../../components/ha-dropdown";
import "../../../../../components/ha-dropdown-item";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/ha-dialog";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import type {
ZwaveCredentialCapabilities,
ZwaveUser,
} from "../../../../../data/zwave_js-credentials";
import {
canAddZwaveUser,
compatibleUserTypes,
deleteZwaveAllUsers,
deleteZwaveUser,
getZwaveCredentialCapabilities,
getZwaveUsers,
compatibleUserTypes,
} from "../../../../../data/zwave_js-credentials";
import {
showAlertDialog,
@@ -220,11 +223,10 @@ class DialogZwaveCredentialManage extends LitElement {
)}
</p>`
: html`
<ha-md-list>
<ha-list-base>
${activeUsers.map(
(user) => html`
<ha-md-list-item
type="button"
<ha-list-item-button
data-user-id=${user.user_id}
@click=${this._handleUserClick}
>
@@ -264,11 +266,12 @@ class DialogZwaveCredentialManage extends LitElement {
data-user-id=${user.user_id}
?disabled=${this._busy}
@click=${this._handleDeleteUserClick}
@keydown=${stopKeydownEnterSpacePropagation}
></ha-icon-button>
</ha-md-list-item>
</ha-list-item-button>
`
)}
</ha-md-list>
</ha-list-base>
`
}
</div>
@@ -8,14 +8,14 @@ import { computeAreaName } from "../../../../../../common/entity/compute_area_na
import { computeDeviceNameDisplay } from "../../../../../../common/entity/compute_device_name";
import { getDeviceArea } from "../../../../../../common/entity/context/get_device_context";
import { caseInsensitiveStringCompare } from "../../../../../../common/string/compare";
import "../../../../../../components/animation/ha-fade-in";
import "../../../../../../components/ha-button";
import "../../../../../../components/ha-dialog";
import "../../../../../../components/ha-dialog-footer";
import "../../../../../../components/ha-domain-icon";
import "../../../../../../components/animation/ha-fade-in";
import "../../../../../../components/ha-md-list";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/ha-spinner";
import "../../../../../../components/item/ha-list-item-base";
import "../../../../../../components/list/ha-list-base";
import {
configEntriesContext,
devicesContext,
@@ -115,13 +115,13 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
)}
</p>`
: this._zwaveDevices
? html`<ha-md-list>
? html`<ha-list-base>
${this._filteredDevices(
this._progress,
this._zwaveDevices
).map(
(device) => html`
<ha-md-list-item>
<ha-list-item-base>
<ha-domain-icon
slot="start"
.domain=${device.domain}
@@ -129,10 +129,10 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
></ha-domain-icon>
<span slot="headline">${device.name}</span>
<span slot="supporting-text">${device.areaName}</span>
</ha-md-list-item>
</ha-list-item-base>
`
)}
</ha-md-list>`
</ha-list-base>`
: nothing
}
</ha-dialog>
@@ -208,16 +208,14 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
static get styles(): CSSResultGroup {
return [
css`
ha-md-list {
gap: var(--ha-space-2);
ha-list-base {
--ha-list-gap: var(--ha-space-2);
min-height: 300px;
}
ha-md-list-item {
--md-list-item-two-line-container-height: 0;
--md-list-item-top-space: var(--ha-space-1);
--md-list-item-bottom-space: var(--ha-space-1);
--md-list-item-leading-space: var(--ha-space-2);
--md-list-item-trailing-space: var(--ha-space-2);
ha-list-item-base {
--ha-row-item-min-height: 0;
--ha-row-item-padding-block: var(--ha-space-1);
--ha-row-item-padding-inline: var(--ha-space-2);
border: var(--ha-border-width-sm) solid
var(--ha-color-border-neutral-normal);
border-radius: var(--ha-border-radius-lg);
@@ -23,10 +23,12 @@ import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/list/ha-list-nav";
import "../../../../../components/progress/ha-progress-ring";
import type { ConfigEntry } from "../../../../../data/config_entries";
import {
@@ -305,9 +307,12 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
</ha-button>
</div>
<div class="card-content network-card-content">
<ha-md-list>
<ha-md-list-item
type="link"
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.network_card_title"
)}
>
<ha-list-item-button
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -318,9 +323,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
type="link"
</ha-list-item-button>
<ha-list-item-button
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -331,11 +335,10 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
${
this._provisioningEntries?.length
? html`<ha-md-list-item
type="link"
? html`<ha-list-item-button
href=${`provisioned?config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiQrcode}></ha-svg-icon>
@@ -346,10 +349,10 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>`
</ha-list-item-button>`
: nothing
}
</ha-md-list>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -359,9 +362,12 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-md-list>
<ha-md-list-item
type="link"
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zwave_js.navigation.general"
)}
>
<ha-list-item-button
href=${`options?config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
@@ -376,9 +382,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
type="link"
</ha-list-item-button>
<ha-list-item-button
href=${`statistics?config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiPoll}></ha-svg-icon>
@@ -393,9 +398,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
type="link"
</ha-list-item-button>
<ha-list-item-button
href=${`logs?config_entry=${this.configEntryId}`}
>
<ha-svg-icon
@@ -413,8 +417,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item type="link" href="/config/analytics?section=zwave">
</ha-list-item-button>
<ha-list-item-button href="/config/analytics?section=zwave">
<ha-svg-icon slot="start" .path=${mdiChartBox}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -436,9 +440,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item
type="link"
</ha-list-item-button>
<ha-list-item-button
href=${`network-info?config_entry=${this.configEntryId}`}
>
<ha-svg-icon
@@ -456,8 +459,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</ha-list-item-button>
</ha-list-nav>
</div>
</ha-card>
`;
@@ -490,8 +493,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
${this._restoreProgress}%
</div>`
: html`<ha-md-list>
<ha-md-list-item>
: html`<ha-list-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.nvm_backup.download_backup"
@@ -516,8 +519,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
"ui.panel.config.zwave_js.dashboard.nvm_backup.download_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.nvm_backup.restore_backup"
@@ -545,8 +548,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
@change=${this._handleRestoreFileSelected}
style="display: none"
/>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.nvm_backup.migrate"
@@ -567,8 +570,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
"ui.panel.config.zwave_js.dashboard.nvm_backup.migrate_action"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>`
</ha-list-item-base>
</ha-list-base>`
}
</div>
</ha-card>
@@ -905,15 +908,6 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.network-card .card-header {
display: flex;
align-items: center;
@@ -3,8 +3,8 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import type { ZWaveJSControllerStatisticsUpdatedMessage } from "../../../../../data/zwave_js";
import { subscribeZwaveControllerStatistics } from "../../../../../data/zwave_js";
import "../../../../../layouts/hass-subpage";
@@ -60,7 +60,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
>
<div class="container">
<ha-card>
<ha-md-list>
<ha-list-base>
${this._renderStat("messages_tx")}
${this._renderStat("messages_rx")}
${this._renderStat("messages_dropped_tx")}
@@ -69,7 +69,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
${this._renderStat("timeout_ack")}
${this._renderStat("timeout_response")}
${this._renderStat("timeout_callback")}
</ha-md-list>
</ha-list-base>
</ha-card>
</div>
</hass-subpage>
@@ -78,7 +78,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
private _renderStat(key: string) {
return html`
<ha-md-list-item>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
`ui.panel.config.zwave_js.dashboard.statistics.${key}.label`
@@ -90,7 +90,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
)}
</span>
<span slot="end">${this._statistics?.[key] ?? 0}</span>
</ha-md-list-item>
</ha-list-item-base>
`;
}
@@ -104,15 +104,6 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
span[slot="end"] {
font-size: 0.95em;
color: var(--primary-text-color);
@@ -2,8 +2,8 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import type { ZWaveJSNetwork } from "../../../../../data/zwave_js";
import { fetchZwaveNetworkStatus } from "../../../../../data/zwave_js";
import "../../../../../layouts/hass-subpage";
@@ -49,8 +49,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<ha-card>
${
this._network
? html`<ha-md-list>
<ha-md-list-item>
? html`<ha-list-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.home_id"
@@ -59,8 +59,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${formatHomeIdAsHex(this._network.controller.home_id)}
</span>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.driver_version"
@@ -69,8 +69,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${this._network.client.driver_version}
</span>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.server_version"
@@ -79,8 +79,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${this._network.client.server_version}
</span>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.server_url"
@@ -89,8 +89,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${this._network.client.ws_server_url}
</span>
</ha-md-list-item>
</ha-md-list>`
</ha-list-item-base>
</ha-list-base>`
: nothing
}
</ha-card>
@@ -111,19 +111,6 @@ class ZWaveJSNetworkInfoPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
--md-list-item-supporting-text-size: var(
--md-list-item-label-text-size,
var(--md-sys-typescale-body-large-size, 1rem)
);
}
`,
];
}
@@ -3,8 +3,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import type {
ZWaveJSClient,
ZWaveJSNetwork,
@@ -61,8 +61,8 @@ class ZWaveJSOptionsPage extends LitElement {
<ha-card>
${
this._network
? html`<ha-md-list>
<ha-md-list-item>
? html`<ha-list-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.common.rebuild_network_routes"
@@ -84,8 +84,8 @@ class ZWaveJSOptionsPage extends LitElement {
"ui.panel.config.zwave_js.dashboard.rebuild_routes_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
</ha-list-item-base>
<ha-list-item-base>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.common.remove_node"
@@ -113,8 +113,8 @@ class ZWaveJSOptionsPage extends LitElement {
"ui.panel.config.zwave_js.dashboard.remove_node_action"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>`
</ha-list-item-base>
</ha-list-base>`
: nothing
}
</ha-card>
@@ -149,15 +149,6 @@ class ZWaveJSOptionsPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
`,
];
}
@@ -4,12 +4,11 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { relativeTime } from "../../../common/datetime/relative_time";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-dialog";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/item/ha-row-item";
import type { BackupConfig } from "../../../data/backup";
import { fetchBackupConfig } from "../../../data/backup";
import { getSupervisorUpdateConfig } from "../../../data/supervisor/update";
@@ -158,25 +157,23 @@ export class DialogLabsPreviewFeatureEnable
${
createBackupTexts
? html`
<ha-md-list>
<ha-md-list-item>
<span slot="headline">${createBackupTexts.title}</span>
${
createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing
}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-md-list-item>
</ha-md-list>
<ha-row-item>
<span slot="headline">${createBackupTexts.title}</span>
${
createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing
}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-row-item>
`
: nothing
}
@@ -211,10 +208,8 @@ export class DialogLabsPreviewFeatureEnable
color: var(--secondary-text-color);
}
ha-md-list {
background: none;
--md-list-item-leading-space: var(--ha-space-6);
--md-list-item-trailing-space: var(--ha-space-6);
ha-row-item {
--ha-row-item-padding-inline: var(--ha-space-6);
margin: 0;
padding: 0;
border-top: var(--ha-border-width-sm) solid var(--divider-color);
@@ -2,11 +2,11 @@ import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../layouts/hass-subpage";
import "../../../components/ha-card";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import "../../../components/ha-icon-next";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-nav";
import "../../../layouts/hass-subpage";
import type { HomeAssistant, Route } from "../../../types";
import "./ha-config-http-form";
import "./ha-config-network";
@@ -57,10 +57,14 @@ class HaConfigSectionNetwork extends LitElement {
"ui.panel.config.network.discovery.title"
)}
>
<ha-md-list>
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.network.discovery.title"
)}
>
${NETWORK_BROWSERS.map(
(domain) => html`
<ha-md-list-item type="link" href="/config/${domain}">
<ha-list-item-button href="/config/${domain}">
<div slot="headline">
${this.hass.localize(
`ui.panel.config.network.discovery.${domain}`
@@ -72,10 +76,10 @@ class HaConfigSectionNetwork extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-list-item-button>
`
)}
</ha-md-list>
</ha-list-nav>
</ha-card>
`
: ""
@@ -102,10 +106,6 @@ class HaConfigSectionNetwork extends LitElement {
margin-bottom: 24px;
max-width: 600px;
}
.discovery-card ha-md-list {
background: none;
padding-top: 0;
}
`;
}
@@ -7,12 +7,12 @@ import { isIPAddress } from "../../../common/string/is_ip_address";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import type { HaInput } from "../../../components/input/ha-input";
import "../../../components/input/ha-input-copy";
import type { HaInputCopy } from "../../../components/input/ha-input-copy";
import "../../../components/item/ha-row-item";
import type { CloudStatus } from "../../../data/cloud";
import { fetchCloudStatus } from "../../../data/cloud";
import { saveCoreConfig } from "../../../data/core";
@@ -161,7 +161,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
${
hasCloud
? html`
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.url.external_use_ha_cloud"
@@ -173,7 +173,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
.checked=${this._cloudChecked}
@change=${this._toggleCloud}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
`
: nothing
}
@@ -261,7 +261,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
<h4>
${this.hass.localize("ui.panel.config.url.internal_url_label")}
</h4>
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.url.internal_url_automatic"
@@ -278,7 +278,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
.checked=${!this._showCustomInternalUrl}
@change=${this._toggleInternalAutomatic}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
<div class="url-container">
<ha-input-copy
@@ -466,12 +466,10 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
flex: 1;
}
ha-md-list-item {
--md-list-item-top-space: 0;
--md-list-item-bottom-space: 0;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-list-item-two-line-container-height: 48px;
ha-row-item {
--ha-row-item-padding-block: 0;
--ha-row-item-padding-inline: 0;
--ha-row-item-min-height: 48px;
}
.no-wrap {
@@ -3,8 +3,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-card";
import "../../../components/ha-list";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-base";
import type {
IntegrationManifest,
IntegrationSetup,
@@ -40,7 +40,7 @@ class IntegrationsStartupTime extends LitElement {
}
return html`
<ha-md-list>
<ha-list-base>
${this._setups?.map((setup) => {
const manifest = this._manifests && this._manifests[setup.domain];
const docLink =
@@ -55,7 +55,7 @@ class IntegrationsStartupTime extends LitElement {
const setupSeconds = setup.seconds?.toFixed(2);
return html`
<ha-md-list-item .href=${docLink} type="link" target="_blank">
<ha-list-item-button .href=${docLink} target="_blank">
<img
alt=""
loading="lazy"
@@ -71,17 +71,17 @@ class IntegrationsStartupTime extends LitElement {
referrerpolicy="no-referrer"
slot="start"
/>
<span>
<span slot="headline">
${domainToName(this.hass.localize, setup.domain, manifest)}
</span>
<span slot="supporting-text">${setup.domain}</span>
<div slot="end">
${setupSeconds ? html`${setupSeconds} s` : ""}
</div>
</ha-md-list-item>
</ha-list-item-button>
`;
})}
</ha-md-list>
</ha-list-base>
`;
}
@@ -21,14 +21,15 @@ import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-md-list-item";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-svg-icon";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/item/ha-list-item-button";
import "../../../components/item/ha-row-item";
import "../../../components/list/ha-list-base";
import "../../../components/voice-assistant-brand-icon";
import type { AssistPipeline } from "../../../data/assist_pipeline";
import {
@@ -52,7 +53,6 @@ import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/sh
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assistant-pipeline-detail";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
@customElement("assist-pref")
export class AssistPref extends LitElement {
@@ -123,17 +123,14 @@ export class AssistPref extends LitElement {
class="icon-link"
></ha-icon-button>
</div>
<ha-list>
<ha-list-base>
${this._pipelines.map(
(pipeline) => html`
<ha-list-item
twoline
hasMeta
role="button"
<ha-list-item-button
.id=${pipeline.id}
@click=${this._editPipeline}
>
<span>
<span slot="headline">
${pipeline.name}
${
this._preferred === pipeline.id
@@ -141,13 +138,14 @@ export class AssistPref extends LitElement {
: ""
}
</span>
<span slot="secondary">
<span slot="supporting-text">
${formatLanguageCode(pipeline.language, this.hass.locale)}
</span>
<ha-dropdown
slot="meta"
slot="end"
placement="bottom-end"
@click=${stopPropagation}
@keydown=${this._handleDropdownKeydown}
@wa-select=${this._handlePipelineMenuAction}
>
<ha-icon-button
@@ -199,10 +197,10 @@ export class AssistPref extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiTrashCan}></ha-svg-icon>
</ha-dropdown-item>
</ha-dropdown>
</ha-list-item>
</ha-list-item-button>
`
)}
</ha-list>
</ha-list-base>
<ha-button
appearance="filled"
@click=${this._addPipeline}
@@ -214,7 +212,7 @@ export class AssistPref extends LitElement {
)}
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
</ha-button>
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.voice_assistants.expose.expose_new_entities"
@@ -231,7 +229,7 @@ export class AssistPref extends LitElement {
.disabled=${this._exposeNew === undefined}
@change=${this._exposeNewToggleChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
<div class="card-actions">
<ha-button
appearance="plain"
@@ -407,6 +405,12 @@ export class AssistPref extends LitElement {
});
}
private _handleDropdownKeydown(ev: KeyboardEvent) {
if (ev.key === " " || ev.key === "Enter") {
ev.stopPropagation();
}
}
static styles = css`
a {
color: var(--primary-color);
@@ -428,14 +432,7 @@ export class AssistPref extends LitElement {
direction: var(--direction);
color: var(--secondary-text-color);
}
ha-list-item {
--mdc-list-item-meta-size: auto;
--mdc-list-item-meta-display: flex;
--mdc-list-side-padding-right: 8px;
--mdc-list-side-padding-left: 16px;
}
ha-list-item span ha-svg-icon {
ha-list-item-button span ha-svg-icon {
color: currentColor;
width: 16px;
}
@@ -5,11 +5,12 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
import "../../../components/ha-alert";
import "../../../components/ha-card";
import "../../../components/ha-button";
import "../../../components/ha-md-list-item";
import "../../../components/ha-card";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/item/ha-row-item";
import "../../../components/voice-assistant-brand-icon";
import type { CloudStatusLoggedIn } from "../../../data/cloud";
import { updateCloudPref } from "../../../data/cloud";
import type { ExposeEntitySettings } from "../../../data/expose";
@@ -18,7 +19,6 @@ import {
setExposeNewEntities,
} from "../../../data/expose";
import type { HomeAssistant } from "../../../types";
import "../../../components/voice-assistant-brand-icon";
@customElement("cloud-alexa-pref")
export class CloudAlexaPref extends LitElement {
@@ -139,7 +139,7 @@ export class CloudAlexaPref extends LitElement {
</ha-alert>`
: nothing
}
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.alexa.expose_new_entities"
@@ -156,11 +156,11 @@ export class CloudAlexaPref extends LitElement {
.disabled=${this._exposeNew === undefined}
@change=${this._exposeNewToggleChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
${
alexa_registered
? html`
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.alexa.enable_state_reporting"
@@ -176,7 +176,7 @@ export class CloudAlexaPref extends LitElement {
.checked=${alexa_report_state}
@change=${this._reportToggleChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
`
: nothing
}
@@ -262,10 +262,8 @@ export class CloudAlexaPref extends LitElement {
a {
color: var(--primary-color);
}
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-item-overflow: visible;
ha-row-item {
--ha-row-item-padding-inline: 0;
}
.header-actions {
position: absolute;
@@ -5,13 +5,14 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
import "../../../components/ha-alert";
import "../../../components/ha-card";
import "../../../components/ha-button";
import "../../../components/ha-md-list-item";
import "../../../components/ha-card";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/input/ha-input";
import type { HaInput } from "../../../components/input/ha-input";
import "../../../components/item/ha-row-item";
import "../../../components/voice-assistant-brand-icon";
import type { CloudStatusLoggedIn } from "../../../data/cloud";
import { updateCloudPref } from "../../../data/cloud";
import type { ExposeEntitySettings } from "../../../data/expose";
@@ -21,7 +22,6 @@ import {
} from "../../../data/expose";
import type { HomeAssistant } from "../../../types";
import { showSaveSuccessToast } from "../../../util/toast-saved-success";
import "../../../components/voice-assistant-brand-icon";
@customElement("cloud-google-pref")
export class CloudGooglePref extends LitElement {
@@ -138,7 +138,7 @@ export class CloudGooglePref extends LitElement {
`
: nothing
}
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.google.expose_new_entities"
@@ -155,7 +155,7 @@ export class CloudGooglePref extends LitElement {
.disabled=${this._exposeNew === undefined}
@change=${this._exposeNewToggleChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
${
google_registered
? html`
@@ -183,7 +183,7 @@ export class CloudGooglePref extends LitElement {
`
: nothing
}
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.google.enable_state_reporting"
@@ -199,9 +199,9 @@ export class CloudGooglePref extends LitElement {
.checked=${google_report_state}
@change=${this._reportToggleChanged}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.google.security_devices"
@@ -213,7 +213,7 @@ export class CloudGooglePref extends LitElement {
)}</span
>
<ha-switch slot="end"></ha-switch>
</ha-md-list-item>
</ha-row-item>
<ha-input
id="google_secure_devices_pin"
@@ -344,10 +344,8 @@ export class CloudGooglePref extends LitElement {
direction: var(--direction);
color: var(--secondary-text-color);
}
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-item-overflow: visible;
ha-row-item {
--ha-row-item-padding-inline: 0;
}
ha-input {
width: 250px;
@@ -17,8 +17,8 @@ import {
import "../../../components/ha-alert";
import "../../../components/ha-aliases-editor";
import "../../../components/ha-checkbox";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import "../../../components/item/ha-row-item";
import "../../../components/voice-assistant-brand-icon";
import { fetchCloudAlexaEntity } from "../../../data/alexa";
import type { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud";
@@ -190,7 +190,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
const exposedToAssist = this.exposed.conversation;
return html`
<ha-md-list-item>
<ha-row-item>
<h3 slot="headline">
${this.hass.localize("ui.dialogs.voice-settings.expose_header")}
</h3>
@@ -200,7 +200,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
.assistants=${uiAssistants}
.checked=${anyExposed}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
${
anyExposed
? showAssistants.map((key) => {
@@ -224,7 +224,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
this._googleEntity?.might_2fa;
return html`
<ha-md-list-item>
<ha-row-item>
<voice-assistant-brand-icon
slot="start"
.voiceAssistantId=${key}
@@ -274,7 +274,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
.disabled=${manualConfig || (!exposed && !supported)}
.checked=${exposed}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
`;
})
: nothing
@@ -321,7 +321,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
)}
</ha-alert>`
: html`
<ha-md-list-item>
<ha-row-item>
<span slot="headline">
${
this.hass.states[this.entityId]
@@ -339,7 +339,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
.checked=${(this._aliases ?? this.entry.aliases).includes(null)}
@change=${this._toggleEntityNameAlias}
></ha-switch>
</ha-md-list-item>
</ha-row-item>
<ha-aliases-editor
.aliases=${(this._aliases ?? this.entry.aliases).filter(
(a): a is string => a !== null
@@ -445,10 +445,8 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
margin: 32px;
margin-top: 0;
}
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-item-overflow: visible;
ha-row-item {
--ha-row-item-padding-inline: 0;
}
img {
height: 32px;
@@ -12,11 +12,11 @@ import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-grid-size-picker";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-slider";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-switch";
import "../../../../components/ha-yaml-editor";
import "../../../../components/item/ha-row-item";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import { haStyle } from "../../../../resources/styles";
@@ -145,7 +145,7 @@ export class HuiCardLayoutEditor extends LitElement {
.columnMax=${gridOptions.max_columns}
.step=${this._preciseMode ? 1 : GRID_COLUMN_MULTIPLIER}
></ha-grid-size-picker>
<ha-md-list-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.auto_height"
@@ -162,8 +162,8 @@ export class HuiCardLayoutEditor extends LitElement {
.checked=${options.rows === "auto"}
name="auto-height"
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-row-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.full_width"
@@ -180,8 +180,8 @@ export class HuiCardLayoutEditor extends LitElement {
.checked=${options.columns === "full"}
name="full-width"
></ha-switch>
</ha-md-list-item>
<ha-md-list-item>
</ha-row-item>
<ha-row-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.precise_mode"
@@ -198,7 +198,7 @@ export class HuiCardLayoutEditor extends LitElement {
.checked=${this._preciseMode}
name="precise-mode"
></ha-switch>
</ha-md-list-item>
</ha-row-item>
`
}
`;
@@ -4,11 +4,11 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
import type { HaSwitch } from "../../../../components/ha-switch";
import "../../../../components/user/ha-user-badge";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-switch";
import type { HaSwitch } from "../../../../components/ha-switch";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import "../../../../components/user/ha-user-badge";
import type {
LovelaceViewConfig,
ShowViewConfig,
@@ -66,10 +66,10 @@ export class HuiViewVisibilityEditor extends LitElement {
"ui.panel.lovelace.editor.edit_view.visibility.select_users"
)}
</p>
<ha-md-list>
<ha-list-base>
${this._sortedUsers(this._users).map(
(user) => html`
<ha-md-list-item>
<ha-list-item-base>
<ha-user-badge slot="start" .user=${user}></ha-user-badge>
<span slot="headline">${user.name}</span>
<ha-switch
@@ -78,10 +78,10 @@ export class HuiViewVisibilityEditor extends LitElement {
@change=${this._valChange}
.checked=${this.checkUser(user.id)}
></ha-switch>
</ha-md-list-item>
</ha-list-item-base>
`
)}
</ha-md-list>
</ha-list-base>
`;
}
@@ -135,15 +135,10 @@ export class HuiViewVisibilityEditor extends LitElement {
:host {
display: block;
}
ha-md-list {
padding: 0;
}
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-list-item-top-space: var(--ha-space-1);
--md-list-item-bottom-space: var(--ha-space-1);
--md-list-item-one-line-container-height: 48px;
ha-list-item-base {
--ha-row-item-padding-inline: 0;
--ha-row-item-padding-block: var(--ha-space-1);
--ha-row-item-min-height: 48px;
}
`;
}
+48 -3
View File
@@ -18,7 +18,10 @@ import {
subscribeFrontendUserData,
} from "../data/frontend";
import { forwardHaptic } from "../data/haptics";
import { serviceCallWillDisconnect } from "../data/service";
import {
getServiceCallEntityIds,
serviceCallWillDisconnect,
} from "../data/service";
import {
DateFormat,
FirstWeekday,
@@ -32,7 +35,12 @@ import { preserveUnchangedRecord } from "../common/util/preserve-unchanged-recor
import { subscribeFloorRegistry } from "../data/ws-floor_registry";
import { subscribePanels } from "../data/ws-panels";
import { translationMetadata } from "../resources/translations-metadata";
import type { Constructor, HomeAssistant, ServiceCallResponse } from "../types";
import type {
Constructor,
HomeAssistant,
ServiceCallRequest,
ServiceCallResponse,
} from "../types";
import {
addBrandsAuth,
clearBrandsTokenRefresh,
@@ -114,7 +122,7 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
);
}
try {
return (await callService(
const response = (await callService(
conn,
domain,
service,
@@ -122,11 +130,24 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
target,
returnResponse
)) as ServiceCallResponse;
this._reportEntityControlToExternalApp(
domain,
service,
serviceData,
target
);
return response;
} catch (err: any) {
if (
err.error?.code === ERR_CONNECTION_LOST &&
serviceCallWillDisconnect(domain, service, serviceData)
) {
this._reportEntityControlToExternalApp(
domain,
service,
serviceData,
target
);
return { context: { id: "" } };
}
if (this.hass?.debugConnection) {
@@ -405,4 +426,28 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
this._updateHass({});
}
}
private _reportEntityControlToExternalApp(
domain: string,
service: string,
serviceData?: ServiceCallRequest["serviceData"],
target?: ServiceCallRequest["target"]
) {
const external = this.hass?.auth.external;
if (!external) {
return;
}
const entityIds = getServiceCallEntityIds(serviceData, target);
if (!entityIds.length) {
return;
}
try {
external.fireMessage({
type: "entity/controlled",
payload: { entity_ids: entityIds, domain, service },
});
} catch (_err) {
// Reporting is best effort and must not fail the service call.
}
}
};
+50
View File
@@ -0,0 +1,50 @@
import { describe, expect, it } from "vitest";
import { getServiceCallEntityIds } from "../../src/data/service";
describe("getServiceCallEntityIds", () => {
it("returns an empty list when no entities are targeted", () => {
expect(getServiceCallEntityIds()).toEqual([]);
expect(getServiceCallEntityIds({ brightness: 50 }, {})).toEqual([]);
expect(getServiceCallEntityIds({}, { area_id: "kitchen" })).toEqual([]);
});
it("reads a single entity from target or service data", () => {
expect(getServiceCallEntityIds({}, { entity_id: "light.a" })).toEqual([
"light.a",
]);
expect(getServiceCallEntityIds({ entity_id: "light.a" })).toEqual([
"light.a",
]);
});
it("prefers the target over the legacy service data entity ids", () => {
expect(
getServiceCallEntityIds(
{ entity_id: ["light.a", "light.b"] },
{ entity_id: ["light.b", "light.c"] }
)
).toEqual(["light.b", "light.c"]);
});
it("deduplicates entity ids", () => {
expect(
getServiceCallEntityIds({}, { entity_id: ["light.a", "light.a"] })
).toEqual(["light.a"]);
});
it("splits comma separated ids and lowercases them like Core does", () => {
expect(
getServiceCallEntityIds({}, { entity_id: "Light.A, light.b ,light.a" })
).toEqual(["light.a", "light.b"]);
});
it("ignores wildcard, malformed, and non-string entity ids", () => {
expect(getServiceCallEntityIds({ entity_id: "all" })).toEqual([]);
expect(
getServiceCallEntityIds(
{},
{ entity_id: ["all", "none", "", "light", 5] as unknown as string[] }
)
).toEqual([]);
});
});
+13 -13
View File
@@ -1,15 +1,7 @@
import { expect, test } from "@playwright/test";
import {
expectNoPageErrors,
NAVIGATION_TIMEOUT,
PANEL_TIMEOUT,
QUICK_TIMEOUT,
SHELL_TIMEOUT,
trackPageErrors,
} from "./helpers";
import {
DEMO_THEME_STORAGE_KEY,
activateDemoSidebarPanel,
DEMO_THEME_STORAGE_KEY,
demoCardSelector,
expectDemoDarkMode,
expectStoredDemoTheme,
@@ -18,6 +10,14 @@ import {
openDemoSidebar,
reloadDemo,
} from "./demo/helpers";
import {
expectNoPageErrors,
NAVIGATION_TIMEOUT,
PANEL_TIMEOUT,
QUICK_TIMEOUT,
SHELL_TIMEOUT,
trackPageErrors,
} from "./helpers";
test.describe("Home Assistant Demo", () => {
let pageErrors: ReturnType<typeof trackPageErrors>;
@@ -82,10 +82,10 @@ test.describe("Home Assistant Demo", () => {
// Adapters come from the config entries, connections and advertisements
// from the subscriptions. A count of zero means the data never arrived.
const rows = dashboard.locator("ha-md-list-item div[slot='headline']");
const rows = dashboard.locator("ha-list-item-button div[slot='headline']");
await expect(rows).toHaveCount(3, { timeout: PANEL_TIMEOUT });
// Asserted over the list as a whole so a single empty count still fails.
await expect(dashboard.locator("ha-md-list")).not.toHaveText(/\b0\b/, {
await expect(dashboard.locator("ha-list-nav")).not.toHaveText(/\b0\b/, {
timeout: QUICK_TIMEOUT,
});
@@ -96,7 +96,7 @@ test.describe("Home Assistant Demo", () => {
await loadDemo(page, "/#/config/bluetooth/adapter-info");
const adapters = page.locator(
"bluetooth-adapter-info-page ha-md-list-item"
"bluetooth-adapter-info-page ha-list-item-button"
);
await expect(adapters).toHaveCount(3, { timeout: PANEL_TIMEOUT });
await expect(adapters.locator("ha-icon-button")).toHaveCount(1, {
@@ -120,7 +120,7 @@ test.describe("Home Assistant Demo", () => {
});
// The device and entity counts come from the registries; a zero means the
// fixtures did not reach them.
await expect(dashboard.locator("ha-md-list").first()).not.toHaveText(
await expect(dashboard.locator("ha-list-nav").first()).not.toHaveText(
/\b0\b/,
{ timeout: QUICK_TIMEOUT }
);
+10 -10
View File
@@ -2798,12 +2798,12 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/icu-messageformat-parser@npm:3.5.17":
version: 3.5.17
resolution: "@formatjs/icu-messageformat-parser@npm:3.5.17"
"@formatjs/icu-messageformat-parser@npm:3.5.18":
version: 3.5.18
resolution: "@formatjs/icu-messageformat-parser@npm:3.5.18"
dependencies:
"@formatjs/icu-skeleton-parser": "npm:2.1.11"
checksum: 10/cbb9daf23f65e4ef3697eae3be4c6888eda942fcde18929dfc6e96ef6c45052409d20340ece13476eeadd062e222809c46a2f26e3b453c9cbd4979bb6c0b0ae5
checksum: 10/fc2215a16571fcff3eb9f446878ef7649d40064a79648352ab79f68f4b48ce7b196125495eec82caa02edec78a9a094d8fd6fd5db0abeb353b95afb62672c2a2
languageName: node
linkType: hard
@@ -10472,7 +10472,7 @@ __metadata:
html-minifier-terser: "npm:7.2.0"
husky: "npm:9.1.7"
idb-keyval: "npm:6.3.0"
intl-messageformat: "npm:11.2.14"
intl-messageformat: "npm:11.2.15"
js-yaml: "npm:5.4.1"
jsdom: "npm:30.0.1"
jszip: "npm:3.10.2"
@@ -10879,13 +10879,13 @@ __metadata:
languageName: node
linkType: hard
"intl-messageformat@npm:11.2.14":
version: 11.2.14
resolution: "intl-messageformat@npm:11.2.14"
"intl-messageformat@npm:11.2.15":
version: 11.2.15
resolution: "intl-messageformat@npm:11.2.15"
dependencies:
"@formatjs/fast-memoize": "npm:3.1.7"
"@formatjs/icu-messageformat-parser": "npm:3.5.17"
checksum: 10/2720155c42bfd99a00d41f7bc9e2f5c2c83c2b6cb2353006b7d8e736a0f1ed20d0147406c1aeba0748731a0ce588c67304b52eb2fe92b42b928bf1ef6bffdcdf
"@formatjs/icu-messageformat-parser": "npm:3.5.18"
checksum: 10/71d1448964e685c821b3680a89668aae3fe92f6b7774842ea5eb08830417846d4fcbcaf691930e4da1ceb2cc414eeab468c7e3dd4eb530a38ffed91530468ac5
languageName: node
linkType: hard