Compare commits

...
Author SHA1 Message Date
Petar Petrov aecd763f80 Refresh voice expose table when an entity alias changes 2026-09-17 14:57:46 +03:00
Przemysław Kłys 6a9fca2cb9 Fix logbook labels for restored button states (#54203)
* Fix logbook labels for unavailable timestamp entities

* Fix restored button context classification
2026-09-17 06:40:09 +00:00
Petar Petrov 55ba59074e Fix energy distribution home ring overflowing to 100% grid (#54191)
* Fix energy distribution home ring overflowing to 100% grid

Size the home circle from allocated used_solar, used_battery, and
used_grid instead of net used_total so misaligned hours cannot produce
a negative grid arc.

* Add gallery demo for energy distribution home ring

Mock the issue 54185 daily totals with realistic hourly PV, battery,
and grid statistics, including an export-lagged card that overflows
the old ring math.

* Remove gallery energy distribution demo from this PR

Keep the home-ring fix and its unit tests; drop the gallery page
that was only used for local browser verification.

* Keep a zero-consumption home ring as a plain border

When solar is configured but allocated flows are all zero, leave the
arcs unset so the card can show the home outline instead of zero-length
dashes on a borderless circle.

* Keep a defined zero low-carbon home arc

The energy distribution card mounts the home SVG when solar or
lowCarbon is defined. Omitting a 0 leftover hid grid and battery
arcs on a no-solar 100% high-carbon ring.
2026-09-16 15:49:29 +02:00
Maarten Lakerveld eeb674dd5a Show the selected light control on light group members (#54167)
* Show the selected light control on light group members

When a light group's more-info dialog is switched to color temperature, the
member tiles listed below now show a color temperature slider instead of
brightness, so the children can be adjusted on the same axis as the group.

The light control fires light-main-control-changed whenever its main control
changes, and more-info-content picks the member tile feature from that.
Members that do not support color temperature keep their brightness slider,
and the color mode keeps brightness since there is no tile feature for it.
Legacy group.* entities are unchanged.

* Hide member light control when group is in RGB color mode

There is no RGB tile feature, so showing a brightness slider on group
members while the group control is set to color is inconsistent. Show no
control in that case instead, per review feedback.
2026-09-16 15:44:11 +02:00
Jan-Philipp Benecke afe6e84e3c Make trace pages resizable two-pane layouts (#54216)
* Make trace pages resizable two-pane layouts

* Remove cast

* Remove cast
2026-09-16 16:06:56 +03:00
Bruno Pantaleão Gonçalves 3ddbc8f4e3 Communicate entity control to external bus (#54215)
* Communicate entity control to external bus

* Prefer target entity ids and report disconnecting calls

* Split and lowercase entity ids like Core does

* Keep entity control reporting best effort

* Assert wildcard entity ids are dropped
2026-09-16 15:25:32 +03:00
Jan-Philipp Benecke 216468cf50 Strip unreferenced duplicate trigger IDs (#54214) 2026-09-16 13:52:04 +02:00
Bram KragtenandClaude Opus 5 0ef3e9c71a Resolve statistic labels with structured entity names (#54202)
* Resolve statistic labels with structured entity names

getStatisticLabel resolved names through computeStateName, so every
statistic-shaped label — the energy dashboard cards, the sankey cards,
hui-statistic-card, chart series names and the energy configuration
dialogs — showed the flat friendly_name attribute while the rest of the
UI composed names from the device, area and floor.

It now asks hass.formatEntityName for the default composition, which
also makes the entity branch of computeEnergyLabel redundant.

The metadata fallback stays: external statistics have no entity to
resolve a name against.

Co-Authored-By: Claude Opus 5 <[email protected]>

* Keep the entity name shape out of the core chunk

recorder.ts sits in the core entry chunk, so importing DEFAULT_ENTITY_NAME
from compute_entity_name_display dragged the registry-aware formatters and
their dependencies in with it and blew the core bundle budget by 4.1%.

The constant and the name types have no runtime dependencies, so they move
to their own module; compute_entity_name_display re-exports them so nothing
else has to change.

Co-Authored-By: Claude Opus 5 <[email protected]>

---------

Co-authored-by: Claude Opus 5 <[email protected]>
2026-09-16 09:59:12 +03:00
Bram KragtenandClaude Opus 5 1266b0c87a Remove unused element registration imports (#54206)
ESLint cannot judge side-effect imports like `import "../../components/ha-card";`,
so dead component registrations accumulate unnoticed. A repo-wide scan matching
every side-effect import against the tag its module registers found 308 imports
whose element is never referenced anywhere in the importing file.

This removes the 84 that carry no registration risk: no other file instantiates
the tag without directly importing the defining module (checked for `<tag>`,
createElement/dynamicElement/customElements.get/whenDefined, and `custom:<tag>`
in Lovelace configs), and every affected module keeps at least one other
importer.

hui-weather-entity-row got ha-state-icon transitively through the dropped
hui-generic-entity-row import, so it now imports ha-state-icon directly, which
is the element it actually renders.

The gauge gallery page is left alone even though its .ts never names the tag: a
gallery page exists to register what its sibling .markdown demos render, and
that import is the registration.

The remaining 224 are deliberately out of scope. Their component is rendered
somewhere else without its own import, so they need the matching import added in
the same change.

Co-authored-by: Claude Opus 5 <[email protected]>
2026-09-16 09:52:42 +03: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
138 changed files with 1931 additions and 1487 deletions
-1
View File
@@ -3,7 +3,6 @@ import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-card";
import "../../../src/dialogs/more-info/more-info-content";
import "../../../src/state-summary/state-card-content";
import "../ha-demo-options";
import type { HomeAssistant } from "../../../src/types";
import { computeShowNewMoreInfo } from "../../../src/dialogs/more-info/const";
-2
View File
@@ -1,9 +1,7 @@
import { html, css, LitElement } from "lit";
import { customElement } from "lit/decorators";
import "../../src/components/ha-icon-button";
import "../../src/managers/notification-manager";
import { haStyle } from "../../src/resources/styles";
import "./components/page-description";
@customElement("ha-demo-options")
class HaDemoOptions extends LitElement {
+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,37 +19,16 @@ import {
getEntityContext,
getEntityEntryContext,
} from "./context/get_entity_context";
import type { EntityNameItem, EntityNameOptions } from "./entity_name_config";
const DEFAULT_SEPARATOR = " ";
export const DEFAULT_ENTITY_NAME = [
{ type: "parent_device" },
{ type: "device" },
{ type: "entity" },
] satisfies EntityNameItem[];
export const ENTITY_NAME_TYPES = [
"floor",
"area",
"parent_device",
"device",
"entity",
] as const;
export type EntityNameType = (typeof ENTITY_NAME_TYPES)[number];
export type EntityNameItem =
| {
type: EntityNameType;
}
| {
type: "text";
text: string;
};
export interface EntityNameOptions {
separator?: string;
}
export { DEFAULT_ENTITY_NAME, ENTITY_NAME_TYPES } from "./entity_name_config";
export type {
EntityNameItem,
EntityNameOptions,
EntityNameType,
} from "./entity_name_config";
// Joins items that need no entity context. Returns undefined as soon as one
// item references the registries (device, area, ...).
+34
View File
@@ -0,0 +1,34 @@
/**
* Shape of a composed entity name, kept free of runtime dependencies so that
* modules in the core entry chunk can name an entity without pulling in the
* registry-aware formatters from `compute_entity_name_display`.
*/
export const ENTITY_NAME_TYPES = [
"floor",
"area",
"parent_device",
"device",
"entity",
] as const;
export type EntityNameType = (typeof ENTITY_NAME_TYPES)[number];
export type EntityNameItem =
| {
type: EntityNameType;
}
| {
type: "text";
text: string;
};
export interface EntityNameOptions {
separator?: string;
}
export const DEFAULT_ENTITY_NAME = [
{ type: "parent_device" },
{ type: "device" },
{ type: "entity" },
] satisfies EntityNameItem[];
@@ -22,7 +22,6 @@ import "./ha-list";
import "./ha-list-item";
import "./voice-assistant-brand-icon";
import { voiceAssistants } from "../data/expose";
import "../panels/config/voice-assistants/expose/expose-assistant-icon";
@customElement("ha-filter-voice-assistants")
export class HaFilterVoiceAssistants extends LitElement {
@@ -3,7 +3,6 @@ import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import "../ha-check-list-item";
import "../ha-checkbox";
import type { HaCheckbox } from "../ha-checkbox";
import "../ha-dropdown";
@@ -33,7 +33,6 @@ import "../ha-list";
import "../ha-spinner";
import "../ha-svg-icon";
import "../ha-tip";
import "./ha-media-player-browse";
import "./ha-media-upload-button";
import type { MediaManageDialogParams } from "./show-media-manage-dialog";
@@ -19,7 +19,6 @@ import type { HassDialog } from "../../../dialogs/make-dialog-manager";
import type { HomeAssistant } from "../../../types";
import type { HaDevicePickerDeviceFilterFunc } from "../../device/ha-device-picker";
import "../../ha-adaptive-dialog";
import "../../ha-dialog-header";
import "../../ha-icon-button";
import "../../ha-icon-next";
import "../../ha-svg-icon";
-7
View File
@@ -21,7 +21,6 @@ import {
} from "../common/datetime/calc_date";
import type { DateRange } from "../common/datetime/calc_date_range";
import { calcDateRange } from "../common/datetime/calc_date_range";
import { DEFAULT_ENTITY_NAME } from "../common/entity/compute_entity_name_display";
import { formatNumber } from "../common/number/format_number";
import { normalizeValueBySIPrefix } from "../common/number/normalize-by-si-prefix";
import { groupBy } from "../common/util/group-by";
@@ -328,12 +327,6 @@ export const computeEnergyLabel = (
return customName;
}
const stateObj = hass.states[statisticId];
if (stateObj) {
return hass.formatEntityName(stateObj, DEFAULT_ENTITY_NAME);
}
return getStatisticLabel(hass, statisticId, statisticsMetaData);
};
+17 -3
View File
@@ -5,6 +5,7 @@ import { computeDomain } from "../common/entity/compute_domain";
import { computeStateDomain } from "../common/entity/compute_state_domain";
import type { LocalizeFunc } from "../common/translations/localize";
import type { HomeAssistant } from "../types";
import { UNAVAILABLE, UNKNOWN } from "./entity/entity";
import { isNumericEntity } from "./history";
const LOGBOOK_LOCALIZE_PATH = "ui.components.logbook.messages";
@@ -280,17 +281,22 @@ const STATE_ACTION_MESSAGES: Record<
radio_frequency: "command_sent",
};
const RESTORABLE_BUTTON_DOMAINS = new Set(["button", "input_button"]);
export const localizeStateMessage = (
hass: HomeAssistant,
state: string,
stateObj: HassEntity,
domain: string,
attributes?: LogbookEntry["attributes"]
entry?: LogbookEntry
): string => {
if (state === UNKNOWN || state === UNAVAILABLE) {
return hass.formatEntityState(stateObj, state);
}
// Events show the triggered event type, falling back to a generic label when
// the type is unknown (the timestamp state is meaningless on its own).
if (domain === "event") {
const eventType = attributes?.event_type;
const eventType = entry?.attributes?.event_type;
if (eventType != null) {
return hass.formatEntityAttributeValue(stateObj, "event_type", eventType);
}
@@ -298,7 +304,15 @@ export const localizeStateMessage = (
}
const actionKey: LogbookActionMessage | undefined =
STATE_ACTION_MESSAGES[domain as keyof typeof STATE_ACTION_MESSAGES];
if (actionKey) {
const stateTimestamp = Date.parse(state);
const matchesEntryTime =
entry === undefined ||
(Number.isFinite(stateTimestamp) &&
Math.abs(stateTimestamp - entry.when * 1000) < 1000);
if (
actionKey &&
(!RESTORABLE_BUTTON_DOMAINS.has(domain) || matchesEntryTime)
) {
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.${actionKey}`);
}
// Every other domain reuses the backend state translation, so the logbook
+3 -2
View File
@@ -1,5 +1,5 @@
import type { Connection } from "home-assistant-js-websocket";
import { computeStateName } from "../common/entity/compute_state_name";
import { DEFAULT_ENTITY_NAME } from "../common/entity/entity_name_config";
import type { HaDurationData } from "../components/ha-duration-input";
import type { HomeAssistant } from "../types";
import { firstWeekday } from "../common/datetime/first_weekday";
@@ -348,8 +348,9 @@ export const getStatisticLabel = (
): string => {
const entity = hass.states[statisticsId];
if (entity) {
return computeStateName(entity);
return hass.formatEntityName(entity, DEFAULT_ENTITY_NAME);
}
// External statistics have no entity to resolve a name against.
return statisticsMetaData?.name || statisticsId;
};
+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)
),
];
@@ -17,7 +17,6 @@ import type { LocalizeFunc } from "../../../../common/translations/localize";
import { throttle } from "../../../../common/util/throttle";
import "../../../../components/ha-hs-color-picker";
import "../../../../components/ha-icon";
import "../../../../components/ha-icon-button-prev";
import "../../../../components/ha-labeled-slider";
import { apiContext } from "../../../../data/context";
import type { LightColor, LightEntity } from "../../../../data/light";
@@ -4,7 +4,6 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { transform } from "../../../common/decorators/transform";
import "../../../components/ha-date-input";
import "../../../components/ha-time-input";
import { apiContext, internationalizationContext } from "../../../data/context";
import { setDateValue } from "../../../data/date";
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity/entity";
@@ -11,6 +11,7 @@ import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
import { fireEvent, type HASSDomEvent } from "../../../common/dom/fire_event";
import { supportsFeature } from "../../../common/entity/supports-feature";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/ha-attribute-icon";
@@ -38,7 +39,6 @@ import "../../../state-control/light/ha-state-control-light-brightness";
import { apiContext, formattersContext } from "../../../data/context";
import "../components/ha-more-info-control-select-container";
import "../components/ha-more-info-state-header";
import "../components/lights/ha-favorite-color-button";
import "../components/lights/ha-more-info-light-favorite-colors";
import "../components/lights/light-color-rgb-picker";
import "../components/lights/light-color-temp-picker";
@@ -86,12 +86,26 @@ class MoreInfoLight extends LitElement {
private _setMainControl(ev: any) {
ev.stopPropagation();
this._mainControl = ev.currentTarget.control;
this._changeMainControl(ev.currentTarget.control);
}
private _resetMainControl(ev: any) {
ev.stopPropagation();
this._mainControl = "brightness";
this._changeMainControl("brightness");
}
public connectedCallback(): void {
super.connectedCallback();
// A container that outlives this control (e.g. more-info-content when the
// dialog moves between entities) resyncs with the default control.
fireEvent(this, "light-main-control-changed", {
control: this._mainControl,
});
}
private _changeMainControl(control: MainControl) {
this._mainControl = control;
fireEvent(this, "light-main-control-changed", { control });
}
private get _stateOverride() {
@@ -400,4 +414,14 @@ declare global {
interface HTMLElementTagNameMap {
"more-info-light": MoreInfoLight;
}
interface HASSDomEvents {
"light-main-control-changed": { control: MainControl };
}
interface HTMLElementEventMap {
"light-main-control-changed": HASSDomEvent<
HASSDomEvents["light-main-control-changed"]
>;
}
}
@@ -10,7 +10,6 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/ha-control-button";
import "../../../components/ha-control-button-group";
import "../../../components/ha-outlined-icon-button";
import "../../../components/ha-state-icon";
import { apiContext } from "../../../data/context";
import type { LockEntity } from "../../../data/lock";
@@ -31,7 +31,6 @@ import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-list-item";
import "../../../components/ha-marquee-text";
import "../../../components/ha-select";
import type { HaSlider } from "../../../components/ha-slider";
import "../../../components/ha-svg-icon";
import "../../../components/ha-tooltip";
@@ -5,11 +5,9 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { listenMediaQuery } from "../../../common/dom/media_query";
import { computeObjectId } from "../../../common/entity/compute_object_id";
import "../../../components/entity/state-info";
import "../../../components/ha-control-button";
import "../../../components/ha-control-button-group";
import "../../../components/ha-markdown";
import "../../../components/ha-relative-time";
import "../../../components/ha-service-control";
import { UNAVAILABLE } from "../../../data/entity/entity";
import type { ExtEntityRegistryEntry } from "../../../data/entity/entity_registry";
@@ -3,7 +3,6 @@ import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { transform } from "../../../common/decorators/transform";
import "../../../components/ha-date-input";
import "../../../components/ha-time-input";
import { apiContext, internationalizationContext } from "../../../data/context";
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity/entity";
@@ -6,10 +6,8 @@ import { consumeLocalize } from "../../../common/decorators/consume-context-entr
import { supportsFeature } from "../../../common/entity/supports-feature";
import type { LocalizeFunc } from "../../../common/translations/localize";
import { sanitizeHttpUrl } from "../../../common/url/sanitize-http-url";
import "../../../components/buttons/ha-progress-button";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-faded";
import "../../../components/ha-markdown";
import "../../../components/ha-spinner";
import "../../../components/progress/ha-progress-bar";
+115 -84
View File
@@ -1,6 +1,7 @@
import type { HassEntity } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { computeEntityName } from "../../common/entity/compute_entity_name";
@@ -11,6 +12,7 @@ import "../../components/ha-badge";
import type { ExtEntityRegistryEntry } from "../../data/entity/entity_registry";
import { supportsCoverPositionCardFeature } from "../../panels/lovelace/card-features/hui-cover-position-card-feature";
import { supportsLightBrightnessCardFeature } from "../../panels/lovelace/card-features/hui-light-brightness-card-feature";
import { supportsLightColorTempCardFeature } from "../../panels/lovelace/card-features/hui-light-color-temp-card-feature";
import type { LovelaceCardFeatureConfig } from "../../panels/lovelace/card-features/types";
import type { TileCardConfig } from "../../panels/lovelace/cards/types";
import { importMoreInfoControl } from "../../panels/lovelace/custom-card-helpers";
@@ -25,6 +27,8 @@ interface EntityInfo {
deviceId: string | undefined;
}
type LightMainControl = HASSDomEvents["light-main-control-changed"]["control"];
@customElement("more-info-content")
class MoreInfoContent extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@@ -37,6 +41,17 @@ class MoreInfoContent extends LitElement {
@property({ attribute: false }) public data?: Record<string, any>;
// Mirrors the mode selected in the light control so group members
// show the matching slider.
@state() private _lightMainControl: LightMainControl = "brightness";
protected firstUpdated(changedProps: PropertyValues<this>) {
super.firstUpdated(changedProps);
this.addEventListener("light-main-control-changed", (ev) => {
this._lightMainControl = ev.detail.control;
});
}
protected render() {
let moreInfoType: string | undefined;
@@ -73,7 +88,10 @@ class MoreInfoContent extends LitElement {
? html`
<hui-section
.hass=${this.hass}
.config=${this._entitiesSectionConfig(memberIds)}
.config=${this._entitiesSectionConfig(
memberIds,
this._lightMainControl
)}
>
</hui-section>
`
@@ -104,97 +122,110 @@ class MoreInfoContent extends LitElement {
}
);
private _entitiesSectionConfig = memoizeOne((entityIds: string[]) => {
const hass = this.hass!;
private _entitiesSectionConfig = memoizeOne(
(entityIds: string[], lightMainControl: LightMainControl) => {
const hass = this.hass!;
// Get entity names and areas for all visible entities
const entityInfos = entityIds
.map<EntityInfo | null>((entityId) => {
const entry = hass.entities[entityId];
if (entry?.hidden) {
return null;
}
const stateObj = hass.states[entityId];
if (!stateObj) {
return null;
}
const entityName = computeEntityName(
stateObj,
hass.entities,
hass.devices
);
const { area, device } = getEntityContext(
stateObj,
hass.entities,
hass.devices,
hass.areas,
hass.floors
);
const areaId = area?.area_id;
const deviceId = device?.id;
return { entityId, entityName, areaId, deviceId };
})
.filter(Boolean) as EntityInfo[];
// Get entity names and areas for all visible entities
const entityInfos = entityIds
.map<EntityInfo | null>((entityId) => {
const entry = hass.entities[entityId];
if (entry?.hidden) {
return null;
}
const stateObj = hass.states[entityId];
if (!stateObj) {
return null;
}
const entityName = computeEntityName(
stateObj,
hass.entities,
hass.devices
);
const { area, device } = getEntityContext(
stateObj,
hass.entities,
hass.devices,
hass.areas,
hass.floors
);
const areaId = area?.area_id;
const deviceId = device?.id;
return { entityId, entityName, areaId, deviceId };
})
.filter(Boolean) as EntityInfo[];
// Check if all entities have the same entity name
const entityNames = new Set(entityInfos.map((info) => info.entityName));
const allSameEntityName = entityNames.size === 1;
// Check if all entities have the same entity name
const entityNames = new Set(entityInfos.map((info) => info.entityName));
const allSameEntityName = entityNames.size === 1;
// Check if all entities have the same area
const areaIds = new Set(entityInfos.map((info) => info.areaId));
const allSameArea = areaIds.size === 1;
// Check if all entities have the same area
const areaIds = new Set(entityInfos.map((info) => info.areaId));
const allSameArea = areaIds.size === 1;
// Check if all entities belong to the same device
const deviceIds = new Set(entityInfos.map((info) => info.deviceId));
const allSameDevice = deviceIds.size === 1;
// Check if all entities belong to the same device
const deviceIds = new Set(entityInfos.map((info) => info.deviceId));
const allSameDevice = deviceIds.size === 1;
// Build name and state content config based on conditions. The device name
// is redundant when every member belongs to the same device, so omit it
// (and fall back to the entity name so the tile still has a label).
const name: EntityNameItem[] = [];
// Build name and state content config based on conditions. The device name
// is redundant when every member belongs to the same device, so omit it
// (and fall back to the entity name so the tile still has a label).
const name: EntityNameItem[] = [];
if (!allSameDevice) {
name.push({ type: "device" });
}
if (!allSameEntityName || allSameDevice) {
name.push({ type: "entity" });
}
const stateContent = ["state"];
if (!allSameArea) {
stateContent.push("area_name");
}
const cards = entityInfos.map(({ entityId }) => {
const features: LovelaceCardFeatureConfig[] = [];
const context = { entity_id: entityId };
if (supportsCoverPositionCardFeature(hass, context)) {
features.push({
type: "cover-position",
});
} else if (supportsLightBrightnessCardFeature(hass, context)) {
features.push({
type: "light-brightness",
});
if (!allSameDevice) {
name.push({ type: "device" });
}
return {
type: "tile",
entity: entityId,
name,
state_content: stateContent,
features_position: "inline",
features,
grid_options: { columns: 12 },
} as TileCardConfig;
});
if (!allSameEntityName || allSameDevice) {
name.push({ type: "entity" });
}
return {
type: "grid",
cards,
};
});
const stateContent = ["state"];
if (!allSameArea) {
stateContent.push("area_name");
}
const cards = entityInfos.map(({ entityId }) => {
const features: LovelaceCardFeatureConfig[] = [];
const context = { entity_id: entityId };
if (supportsCoverPositionCardFeature(hass, context)) {
features.push({
type: "cover-position",
});
} else if (lightMainControl === "color") {
// There is no RGB tile feature yet, so when the group is set to
// color the members show no control rather than a mismatched
// brightness slider.
} else if (
lightMainControl === "color_temp" &&
supportsLightColorTempCardFeature(hass, context)
) {
features.push({
type: "light-color-temp",
});
} else if (supportsLightBrightnessCardFeature(hass, context)) {
features.push({
type: "light-brightness",
});
}
return {
type: "tile",
entity: entityId,
name,
state_content: stateContent,
features_position: "inline",
features,
grid_options: { columns: 12 },
} as TileCardConfig;
});
return {
type: "grid",
cards,
};
}
);
static styles = css`
hui-section {
@@ -27,7 +27,6 @@ import { updateDeviceRegistryEntry } from "../../data/device/device_registry";
import type { InputSelectEntity } from "../../data/input_select";
import { setSelectOption } from "../../data/select";
import { showVoiceAssistantPipelineDetailDialog } from "../../panels/config/voice-assistants/show-dialog-voice-assistant-pipeline-detail";
import "../../panels/lovelace/entity-rows/hui-select-entity-row";
import type { HomeAssistant } from "../../types";
import { getTranslation } from "../../util/common-translation";
import { AssistantSetupStyles } from "./styles";
@@ -6,7 +6,6 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import "../../components/ha-button";
import "../../components/ha-dialog-header";
import "../../components/ha-spinner";
import type { AssistSatelliteConfiguration } from "../../data/assist_satellite";
import { interceptWakeWord } from "../../data/assist_satellite";
+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;
@@ -12,15 +12,12 @@ import { isDate } from "../../common/string/is_date";
import "../../components/entity/state-info";
import "../../components/ha-alert";
import "../../components/ha-button";
import "../../components/ha-date-input";
import "../../components/ha-dialog-footer";
import "../../components/ha-time-input";
import "../../components/ha-dialog";
import type { CalendarEventMutableParams } from "../../data/calendar";
import { deleteCalendarEvent } from "../../data/calendar";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import "../lovelace/components/hui-generic-entity-row";
import { renderRRuleAsText } from "./recurrence";
import { showConfirmEventDialog } from "./show-confirm-event-dialog-box";
import type { CalendarEventDetailDialogParams } from "./show-dialog-calendar-event-detail";
@@ -42,7 +42,6 @@ import {
import { DirtyStateProviderMixin } from "../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import "../lovelace/components/hui-generic-entity-row";
import "./ha-recurrence-rule-editor";
import { showConfirmEventDialog } from "./show-confirm-event-dialog-box";
import type { CalendarEventEditDialogParams } from "./show-dialog-calendar-event-editor";
@@ -8,9 +8,7 @@ import type {
LocalizeKeys,
} from "../../../../../common/translations/localize";
import { CONDITION_ICONS } from "../../../../../components/ha-condition-icon";
import "../../../../../components/ha-dropdown-item";
import type { PickerComboBoxItem } from "../../../../../components/ha-picker-combo-box";
import "../../../../../components/ha-select";
import {
DYNAMIC_PREFIX,
getValueFromDynamic,
@@ -3,7 +3,6 @@ import { html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-picker";
import "../../../../../components/ha-service-picker";
import "../../../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../../components/ha-yaml-editor";
import "../../../../../components/input/ha-input";
@@ -27,7 +27,6 @@ import {
import "../../../../components/chips/ha-chip-set";
import "../../../../components/chips/ha-filter-chip";
import "../../../../components/entity/state-badge";
import "../../../../components/ha-button-toggle-group";
import "../../../../components/ha-combo-box-item";
import "../../../../components/ha-domain-icon";
import "../../../../components/ha-floor-icon";
@@ -13,9 +13,13 @@ import type { CSSResultGroup, TemplateResult, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { fireEvent } from "../../../common/dom/fire_event";
import {
fireEvent,
type HASSDomTargetEvent,
} from "../../../common/dom/fire_event";
import { navigate, replaceCurrentUrl } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-button";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
@@ -49,9 +53,14 @@ import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { fileDownload } from "../../../util/file_download";
import "../../../components/ha-trace-picker";
import "../../../components/ha-split-panel";
import type { HaSplitPanel } from "../../../components/ha-split-panel";
const TABS = ["details", "timeline", "logbook", "automation_config"] as const;
const STORAGE_KEY_SPLIT_POSITION = "automation-trace-split-position";
const DEFAULT_SPLIT_POSITION = 20;
@customElement("ha-automation-trace")
export class HaAutomationTrace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -84,6 +93,8 @@ export class HaAutomationTrace extends LitElement {
@state() private _view: (typeof TABS)[number] | "blueprint" = "details";
@state() private _splitPosition = DEFAULT_SPLIT_POSITION;
@query("hat-script-graph") private _graph?: HatScriptGraph;
protected render(): TemplateResult {
@@ -91,10 +102,6 @@ export class HaAutomationTrace extends LitElement {
? this.hass.states[this._entityId]
: undefined;
const graph = this._graph;
const trackedNodes = graph?.trackedNodes;
const renderedNodes = graph?.renderedNodes;
const title = stateObj?.attributes.friendly_name || this._entityId;
let devButtons: TemplateResult | string = "";
@@ -240,94 +247,19 @@ export class HaAutomationTrace extends LitElement {
? ""
: html`
<div class="main">
<div class="graph">
<hat-script-graph
.trace=${this._trace}
.selected=${this._selected?.path}
@graph-node-selected=${this._pickNode}
></hat-script-graph>
</div>
<div class="info">
<ha-tab-group @wa-tab-show=${this._handleTabChanged}>
${TABS.map(
(view) => html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === view}
.panel=${view}
${
this.narrow
? this._renderPanes()
: html`
<ha-split-panel
class="split"
.position=${this._splitPosition}
@wa-reposition=${this._splitRepositioned}
>
${this.hass!.localize(
`ui.panel.config.automation.trace.tabs.${view}`
)}
</ha-tab-group-tab>
${this._renderPanes()}
</ha-split-panel>
`
)}
${
this._trace.blueprint_inputs
? html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === "blueprint"}
panel="blueprint"
>
${this.hass!.localize(
`ui.panel.config.automation.trace.tabs.blueprint_config`
)}
</ha-tab-group-tab>
`
: ""
}
</ha-tab-group>
${
this._selected === undefined ||
this._logbookEntries === undefined ||
trackedNodes === undefined
? nothing
: this._view === "details"
? html`
<ha-trace-path-details
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.selected=${this._selected}
.logbookEntries=${this._logbookEntries}
.trackedNodes=${trackedNodes}
.renderedNodes=${renderedNodes!}
></ha-trace-path-details>
`
: this._view === "automation_config"
? html`
<ha-trace-config
.trace=${this._trace}
></ha-trace-config>
`
: this._view === "logbook"
? html`
<ha-trace-logbook
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
></ha-trace-logbook>
`
: this._view === "blueprint"
? html`
<ha-trace-blueprint-config
.trace=${this._trace}
></ha-trace-blueprint-config>
`
: html`
<ha-trace-timeline
.hass=${this.hass}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
.selected=${this._selected}
@value-changed=${this._timelinePathPicked}
></ha-trace-timeline>
`
}
</div>
}
</div>
`
}
@@ -335,12 +267,115 @@ export class HaAutomationTrace extends LitElement {
`;
}
private _renderPanes(): TemplateResult {
const graph = this._graph;
const trackedNodes = graph?.trackedNodes;
const renderedNodes = graph?.renderedNodes;
return html`
<div class="graph" slot="start">
<hat-script-graph
.trace=${this._trace}
.selected=${this._selected?.path}
@graph-node-selected=${this._pickNode}
></hat-script-graph>
</div>
<div class="info" slot="end">
<ha-tab-group @wa-tab-show=${this._handleTabChanged}>
${TABS.map(
(view) => html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === view}
.panel=${view}
>
${this.hass!.localize(
`ui.panel.config.automation.trace.tabs.${view}`
)}
</ha-tab-group-tab>
`
)}
${
this._trace!.blueprint_inputs
? html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === "blueprint"}
panel="blueprint"
>
${this.hass!.localize(
`ui.panel.config.automation.trace.tabs.blueprint_config`
)}
</ha-tab-group-tab>
`
: ""
}
</ha-tab-group>
${
this._selected === undefined ||
this._logbookEntries === undefined ||
trackedNodes === undefined
? nothing
: this._view === "details"
? html`
<ha-trace-path-details
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.selected=${this._selected}
.logbookEntries=${this._logbookEntries}
.trackedNodes=${trackedNodes}
.renderedNodes=${renderedNodes!}
></ha-trace-path-details>
`
: this._view === "automation_config"
? html`
<ha-trace-config .trace=${this._trace}></ha-trace-config>
`
: this._view === "logbook"
? html`
<ha-trace-logbook
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
></ha-trace-logbook>
`
: this._view === "blueprint"
? html`
<ha-trace-blueprint-config
.trace=${this._trace}
></ha-trace-blueprint-config>
`
: html`
<ha-trace-timeline
.hass=${this.hass}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
.selected=${this._selected}
@value-changed=${this._timelinePathPicked}
></ha-trace-timeline>
`
}
</div>
`;
}
protected firstUpdated(changedProps: PropertyValues<this>) {
super.firstUpdated(changedProps);
this.hass.loadBackendTranslation("triggers");
this.hass.loadBackendTranslation("conditions");
const storedPosition = localStorage?.[STORAGE_KEY_SPLIT_POSITION];
if (storedPosition) {
const parsed = parseFloat(storedPosition);
if (!isNaN(parsed) && parsed > 0 && parsed < 100) {
this._splitPosition = parsed;
}
}
if (!this.automationId) {
return;
}
@@ -428,6 +463,19 @@ export class HaAutomationTrace extends LitElement {
this._selected = ev.detail;
}
private _splitRepositioned(ev: HASSDomTargetEvent<HaSplitPanel>) {
this._splitPosition = ev.target.position;
this._storeSplitPosition();
}
private _storeSplitPosition = debounce(
() => {
localStorage[STORAGE_KEY_SPLIT_POSITION] = String(this._splitPosition);
},
500,
false
);
private _refreshTraces() {
this._loadTraces();
}
@@ -618,13 +666,22 @@ export class HaAutomationTrace extends LitElement {
padding: 16px;
}
ha-split-panel.split {
flex: 1;
min-height: 0;
min-width: 0;
--ha-split-panel-min: 10%;
--ha-split-panel-max: 80%;
--ha-split-panel-divider-hit-area: var(--ha-space-4);
}
.graph {
border-right: 1px solid var(--divider-color);
max-width: 50%;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
min-height: 0;
}
hat-script-graph {
flex: 1;
@@ -642,6 +699,8 @@ export class HaAutomationTrace extends LitElement {
}
.info {
flex: 1;
min-width: 0;
min-height: 0;
overflow-y: auto;
background-color: var(--card-background-color);
}
@@ -454,9 +454,11 @@ export const cleanupRemovedGeneratedTriggerReferences = (
};
/**
* Assign a fresh generated ID to every leaf sharing a stored ID, including manual IDs.
* Assign a fresh generated ID to every referenced leaf sharing a stored ID.
* Expand trigger-condition references to all replacements for the old ID, preserving
* their original "any of these triggers" meaning. Return the original config if IDs are unique.
* their original "any of these triggers" meaning. Duplicate IDs that no trigger
* condition references are stripped instead, so the fix does not create generated
* IDs nobody uses. Return the original config if IDs are unique.
* Templates and action data that inspect trigger.id directly are not rewritten;
* the controller's confirmation dialog warns about that limitation.
*/
@@ -470,6 +472,21 @@ export const makeDuplicateTriggerIdsUnique = (
return config;
}
const referencedIds = new Set<string>();
new AutomationTriggerConditionMapper((condition) =>
mapReferencedTriggerIds(condition, (id) => {
referencedIds.add(id);
return id;
})
).map(config);
const referencedDuplicates = new Set(
[...duplicates].filter((id) => referencedIds.has(id))
);
const unreferencedDuplicates = new Set(
[...duplicates].filter((id) => !referencedIds.has(id))
);
const reservedIds = new Set(ids.filter((id) => !duplicates.has(id)));
const generatedIds = new Set<string>();
const assignments = new Map<Trigger, string>();
@@ -479,7 +496,7 @@ export const makeDuplicateTriggerIdsUnique = (
// trigger its own ID, references to the old ID must expand to every new ID.
flattenTriggers(config.triggers).forEach((trigger) => {
const id = getTriggerId(trigger);
if (!id || !duplicates.has(id)) {
if (!id || !referencedDuplicates.has(id)) {
return;
}
const generatedId = getGeneratedTriggerId(reservedIds, generatedIds);
@@ -487,14 +504,32 @@ export const makeDuplicateTriggerIdsUnique = (
replacementIds.set(id, [...(replacementIds.get(id) || []), generatedId]);
});
const triggers = walkLeafTriggers(config.triggers, (trigger) => {
if (assignments.has(trigger)) {
return { ...trigger, id: assignments.get(trigger) };
}
if (isTriggerList(trigger)) {
return trigger;
}
const id = getTriggerId(trigger);
if (id && unreferencedDuplicates.has(id)) {
const { id: _id, ...rest } = trigger;
return rest as Trigger;
}
return trigger;
}) as Trigger | Trigger[];
if (!referencedDuplicates.size) {
return {
...config,
triggers,
};
}
return {
...new AutomationTriggerConditionMapper((condition) =>
mapReferencedTriggerIds(condition, (id) => replacementIds.get(id) ?? id)
).map(config),
triggers: walkLeafTriggers(config.triggers, (trigger) =>
assignments.has(trigger)
? { ...trigger, id: assignments.get(trigger) }
: trigger
) as Trigger | Trigger[],
triggers,
};
};
@@ -4,7 +4,6 @@ import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../../../common/translations/localize";
import "../../../../../components/ha-check-list-item";
import "../../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../../components/ha-form/types";
import "../../../../../components/ha-icon-button";
@@ -8,7 +8,6 @@ import "../../../../components/ha-button";
import "../../../../components/ha-dialog";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-button-prev";
import "../../../../components/item/ha-row-item";
import { downloadEmergencyKit } from "../../../../data/backup";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
@@ -34,7 +34,6 @@ import "../../../components/ha-dropdown-item";
import "../../../components/ha-filter-states";
import "../../../components/ha-icon";
import "../../../components/ha-icon-next";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-spinner";
import "../../../components/ha-svg-icon";
import type {
@@ -11,7 +11,6 @@ import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon";
import "../../../components/ha-icon-next";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-spinner";
import "../../../components/ha-svg-icon";
import type {
@@ -29,7 +28,6 @@ import {
import type { ManagerStateEvent } from "../../../data/backup_manager";
import type { CloudStatus } from "../../../data/cloud";
import "../../../layouts/hass-subpage";
import "../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { showAlertDialog } from "../../lovelace/custom-card-helpers";
@@ -22,7 +22,6 @@ import type { CloudStatus } from "../../../data/cloud";
import { subscribeConfigEntries } from "../../../data/config_entries";
import type { RouterOptions } from "../../../layouts/hass-router-page";
import { HassRouterPage } from "../../../layouts/hass-router-page";
import "../../../layouts/hass-tabs-subpage-data-table";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../types";
import { showToast } from "../../../util/toast";
@@ -27,7 +27,6 @@ import type {
RowClickedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-button";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-overflow-menu";
@@ -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 {
@@ -6,7 +6,6 @@ import "../../../../components/ha-button";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-markdown-element";
import "../../../../components/ha-dialog";
import "../../../../components/ha-select";
import "../../../../components/ha-spinner";
import { fetchSupportPackage } from "../../../../data/cloud";
import type { HomeAssistant } from "../../../../types";
@@ -43,11 +43,9 @@ import "../../../components/data-table/ha-data-table-labels";
import "../../../components/entity/ha-battery-icon";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-check-list-item";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-filter-devices";
import "../../../components/ha-filter-floor-areas";
import "../../../components/ha-filter-integrations";
import "../../../components/ha-filter-labels";
@@ -27,7 +27,6 @@ import "../../../helpers/forms/ha-input_select-form";
import "../../../helpers/forms/ha-input_text-form";
import "../../../helpers/forms/ha-schedule-form";
import "../../../helpers/forms/ha-timer-form";
import "../../../voice-assistants/entity-voice-settings";
import "../../entity-registry-settings-editor";
import type { EntityRegistrySettingsEditor } from "../../entity-registry-settings-editor";
import { getDeleteConfirmationText } from "../../get-delete-confirmation-text";
@@ -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,23 @@ 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 +45,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 +627,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 +643,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 +960,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 +977,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 +1006,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 +1114,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 +1137,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 +1154,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 +1171,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 +1212,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 +1805,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 +1820,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;
@@ -54,7 +54,6 @@ import type {
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-check-list-item";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
@@ -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>` : ""}
@@ -48,7 +48,6 @@ import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-filter-categories";
import "../../../components/ha-filter-devices";
import "../../../components/ha-filter-entities";
import "../../../components/ha-filter-floor-areas";
import "../../../components/ha-filter-labels";
import "../../../components/ha-filter-voice-assistants";
@@ -20,7 +20,6 @@ import { navigate } from "../../../common/navigate";
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/ha-dialog";
import "../../../components/ha-domain-icon";
import "../../../components/ha-icon-button-prev";
import "../../../components/ha-icon-next";
import "../../../components/ha-svg-icon";
@@ -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>
`;
}
@@ -37,7 +37,6 @@ import {
showConfirmationDialog,
} from "../../lovelace/custom-card-helpers";
import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail";
import "./ha-config-sub-entry-row";
@customElement("ha-config-entry-device-row")
class HaConfigEntryDeviceRow extends LitElement {
@@ -68,7 +68,6 @@ import { QUALITY_SCALE_MAP } from "../../../data/integration_quality_scale";
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
import { showSubConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-sub-config-flow";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-error-screen";
import "../../../layouts/hass-subpage";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { multiTermSearch } from "../../../resources/fuseMultiTerm";
@@ -10,7 +10,6 @@ import {
} from "../../../data/config_flow";
import type { DataEntryFlowProgress } from "../../../data/data_entry_flow";
import { domainToName } from "../../../data/integration";
import "../../../layouts/hass-loading-screen";
import { ChildPanelReady } from "../../../layouts/panel-ready";
import type { RouterOptions } from "../../../layouts/hass-router-page";
import { HassRouterPage } from "../../../layouts/hass-router-page";
@@ -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,8 +9,6 @@ import { extractSearchParamsObject } from "../../../../../common/url/search-para
import type { DataTableColumnContainer } from "../../../../../components/data-table/ha-data-table";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-metric";
import "../../../../../components/ha-relative-time";
import type {
BluetoothAllocationsData,
BluetoothConnectionData,
@@ -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>
`;
}
@@ -31,7 +31,6 @@ import { computeRTL } from "../../../common/util/compute_rtl";
import { promiseTimeout } from "../../../common/util/promise-timeout";
import { afterNextRender } from "../../../common/util/render-status";
import "../../../components/device/ha-device-picker";
import "../../../components/entity/ha-entities-picker";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
+154 -95
View File
@@ -13,8 +13,12 @@ import type { CSSResultGroup, TemplateResult, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { fireEvent } from "../../../common/dom/fire_event";
import {
fireEvent,
type HASSDomTargetEvent,
} from "../../../common/dom/fire_event";
import { navigate, replaceCurrentUrl } from "../../../common/navigate";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-button";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
@@ -45,9 +49,14 @@ import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { fileDownload } from "../../../util/file_download";
import "../../../components/ha-trace-picker";
import "../../../components/ha-split-panel";
import type { HaSplitPanel } from "../../../components/ha-split-panel";
const TABS = ["details", "timeline", "logbook", "config"] as const;
const STORAGE_KEY_SPLIT_POSITION = "script-trace-split-position";
const DEFAULT_SPLIT_POSITION = 20;
@customElement("ha-script-trace")
export class HaScriptTrace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -80,6 +89,8 @@ export class HaScriptTrace extends LitElement {
@state() private _view: (typeof TABS)[number] | "blueprint" = "details";
@state() private _splitPosition = DEFAULT_SPLIT_POSITION;
@query("hat-script-graph") private _graph?: HatScriptGraph;
protected render(): TemplateResult {
@@ -87,10 +98,6 @@ export class HaScriptTrace extends LitElement {
? this.hass.states[this._entityId]
: undefined;
const graph = this._graph;
const trackedNodes = graph?.trackedNodes;
const renderedNodes = graph?.renderedNodes;
const title = stateObj?.attributes.friendly_name || this._entityId;
let devButtons: TemplateResult | string = "";
@@ -220,96 +227,19 @@ export class HaScriptTrace extends LitElement {
? ""
: html`
<div class="main">
<div class="graph">
<hat-script-graph
.trace=${this._trace}
.selected=${this._selected?.path}
@graph-node-selected=${this._pickNode}
></hat-script-graph>
</div>
<div class="info">
<ha-tab-group @wa-tab-show=${this._handleTabChanged}>
${TABS.map(
(view) => html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === view}
.panel=${view}
${
this.narrow
? this._renderPanes()
: html`
<ha-split-panel
class="split"
.position=${this._splitPosition}
@wa-reposition=${this._splitRepositioned}
>
${this.hass.localize(
`ui.panel.config.automation.trace.tabs.${
view === "config" ? "script_config" : view
}`
)}
</ha-tab-group-tab>
${this._renderPanes()}
</ha-split-panel>
`
)}
${
this._trace.blueprint_inputs
? html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === "blueprint"}
panel="blueprint"
>
${this.hass!.localize(
`ui.panel.config.automation.trace.tabs.blueprint_config`
)}
</ha-tab-group-tab>
`
: ""
}
</ha-tab-group>
${
this._selected === undefined ||
this._logbookEntries === undefined ||
trackedNodes === undefined
? ""
: this._view === "details"
? html`
<ha-trace-path-details
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.selected=${this._selected}
.logbookEntries=${this._logbookEntries}
.trackedNodes=${trackedNodes}
.renderedNodes=${renderedNodes!}
></ha-trace-path-details>
`
: this._view === "config"
? html`
<ha-trace-config
.trace=${this._trace}
></ha-trace-config>
`
: this._view === "logbook"
? html`
<ha-trace-logbook
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
></ha-trace-logbook>
`
: this._view === "blueprint"
? html`
<ha-trace-blueprint-config
.trace=${this._trace}
></ha-trace-blueprint-config>
`
: html`
<ha-trace-timeline
.hass=${this.hass}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
.selected=${this._selected}
@value-changed=${this._timelinePathPicked}
></ha-trace-timeline>
`
}
</div>
}
</div>
`
}
@@ -317,9 +247,114 @@ export class HaScriptTrace extends LitElement {
`;
}
private _renderPanes(): TemplateResult {
const graph = this._graph;
const trackedNodes = graph?.trackedNodes;
const renderedNodes = graph?.renderedNodes;
return html`
<div class="graph" slot="start">
<hat-script-graph
.trace=${this._trace}
.selected=${this._selected?.path}
@graph-node-selected=${this._pickNode}
></hat-script-graph>
</div>
<div class="info" slot="end">
<ha-tab-group @wa-tab-show=${this._handleTabChanged}>
${TABS.map(
(view) => html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === view}
.panel=${view}
>
${this.hass.localize(
`ui.panel.config.automation.trace.tabs.${
view === "config" ? "script_config" : view
}`
)}
</ha-tab-group-tab>
`
)}
${
this._trace!.blueprint_inputs
? html`
<ha-tab-group-tab
slot="nav"
.active=${this._view === "blueprint"}
panel="blueprint"
>
${this.hass!.localize(
`ui.panel.config.automation.trace.tabs.blueprint_config`
)}
</ha-tab-group-tab>
`
: ""
}
</ha-tab-group>
${
this._selected === undefined ||
this._logbookEntries === undefined ||
trackedNodes === undefined
? ""
: this._view === "details"
? html`
<ha-trace-path-details
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.selected=${this._selected}
.logbookEntries=${this._logbookEntries}
.trackedNodes=${trackedNodes}
.renderedNodes=${renderedNodes!}
></ha-trace-path-details>
`
: this._view === "config"
? html`
<ha-trace-config .trace=${this._trace}></ha-trace-config>
`
: this._view === "logbook"
? html`
<ha-trace-logbook
.hass=${this.hass}
.narrow=${this.narrow}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
></ha-trace-logbook>
`
: this._view === "blueprint"
? html`
<ha-trace-blueprint-config
.trace=${this._trace}
></ha-trace-blueprint-config>
`
: html`
<ha-trace-timeline
.hass=${this.hass}
.trace=${this._trace}
.logbookEntries=${this._logbookEntries}
.selected=${this._selected}
@value-changed=${this._timelinePathPicked}
></ha-trace-timeline>
`
}
</div>
`;
}
protected firstUpdated(changedProps: PropertyValues<this>) {
super.firstUpdated(changedProps);
const storedPosition = localStorage?.[STORAGE_KEY_SPLIT_POSITION];
if (storedPosition) {
const parsed = parseFloat(storedPosition);
if (!isNaN(parsed) && parsed > 0 && parsed < 100) {
this._splitPosition = parsed;
}
}
if (!this.scriptId) {
return;
}
@@ -409,6 +444,19 @@ export class HaScriptTrace extends LitElement {
this._selected = ev.detail;
}
private _splitRepositioned(ev: HASSDomTargetEvent<HaSplitPanel>) {
this._splitPosition = ev.target.position;
this._storeSplitPosition();
}
private _storeSplitPosition = debounce(
() => {
localStorage[STORAGE_KEY_SPLIT_POSITION] = String(this._splitPosition);
},
500,
false
);
private _refreshTraces() {
this._loadTraces();
}
@@ -594,13 +642,22 @@ export class HaScriptTrace extends LitElement {
padding: 16px;
}
ha-split-panel.split {
flex: 1;
min-height: 0;
min-width: 0;
--ha-split-panel-min: 10%;
--ha-split-panel-max: 80%;
--ha-split-panel-divider-hit-area: var(--ha-space-4);
}
.graph {
border-right: 1px solid var(--divider-color);
max-width: 50%;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
min-height: 0;
}
hat-script-graph {
flex: 1;
@@ -618,6 +675,8 @@ export class HaScriptTrace extends LitElement {
}
.info {
flex: 1;
min-width: 0;
min-height: 0;
overflow-y: auto;
background-color: var(--card-background-color);
}
@@ -46,7 +46,6 @@ import "../../../layouts/hass-subpage";
import { panelIsReady } from "../../../layouts/panel-ready";
import type { HomeAssistant, Route } from "../../../types";
import { bytesToString } from "../../../util/bytes-to-string";
import "../core/ha-config-analytics";
import { showMoveDatadiskDialog } from "./show-dialog-move-datadisk";
import { showMountViewDialog } from "./show-dialog-view-mount";
import "./storage-breakdown-chart";
@@ -25,7 +25,6 @@ import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-input-helper-text";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-tip";
import "../../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import "../../../../components/input/ha-input";
@@ -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;
@@ -31,7 +31,6 @@ import { DialogMixin } from "../../../dialogs/dialog-mixin";
import { DirtyStateProviderMixin } from "../../../mixins/dirty-state-provider-mixin";
import { haStyle, haStyleScrollbar } from "../../../resources/styles";
import { loadVirtualizer } from "../../../resources/virtualizer";
import "./entity-voice-settings";
import type { ExposeEntityDialogParams } from "./show-dialog-expose-entity";
@customElement("dialog-expose-entity")
@@ -24,7 +24,6 @@ import "./assist-pipeline-detail/assist-pipeline-detail-conversation";
import "./assist-pipeline-detail/assist-pipeline-detail-stt";
import "./assist-pipeline-detail/assist-pipeline-detail-tts";
import "./assist-pipeline-detail/assist-pipeline-detail-wakeword";
import "./debug/assist-render-pipeline-events";
import type { VoiceAssistantPipelineDetailsDialogParams } from "./show-dialog-voice-assistant-pipeline-detail";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
@@ -78,6 +78,7 @@ class DialogVoiceSettings extends LitElement {
private _entityEntryUpdated(ev: CustomEvent) {
this._params!.extEntityReg = ev.detail;
this._params!.entityEntryUpdated?.(ev.detail);
}
private _exposedEntitiesChanged() {
@@ -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;
@@ -58,7 +58,6 @@ import {
getAssistantsTableColumn,
} from "./expose/assistants-table-column";
import { getAvailableAssistants } from "./expose/available-assistants";
import "./expose/expose-assistant-icon";
import { voiceAssistantTabs } from "./ha-config-voice-assistants";
import { showExposeEntityDialog } from "./show-dialog-expose-entity";
import { showVoiceSettingsDialog } from "./show-dialog-voice-settings";
@@ -712,6 +711,9 @@ export class VoiceAssistantsExpose extends LitElement {
exposedEntitiesChanged: () => {
fireEvent(this, "exposed-entities-changed");
},
entityEntryUpdated: (entry) => {
this._extEntities = { ...this._extEntities, [entityId]: entry };
},
});
}
@@ -7,6 +7,7 @@ export interface VoiceSettingsDialogParams {
exposed: ExposeEntitySettings;
extEntityReg?: ExtEntityRegistryEntry;
exposedEntitiesChanged?: () => void;
entityEntryUpdated?: (entry: ExtEntityRegistryEntry) => void;
}
export const loadVoiceSettingsDialog = () => import("./dialog-voice-settings");
-3
View File
@@ -6,7 +6,6 @@ import { sanitizeNavigationPath } from "../../common/url/sanitize-navigation-pat
import "../../components/ha-alert";
import "../../components/ha-icon-button-arrow-prev";
import "../../components/ha-menu-button";
import "../../components/ha-top-app-bar-fixed";
import type { EnergyFrontendSystemData } from "../../data/frontend";
import { fetchFrontendSystemData } from "../../data/frontend";
import type { LovelaceConfig } from "../../data/lovelace/config/types";
@@ -15,8 +14,6 @@ import type { HomeAssistant, PanelInfo } from "../../types";
import { generateLovelaceDashboardStrategy } from "../lovelace/strategies/get-strategy";
import "../lovelace/hui-root";
import type { Lovelace } from "../lovelace/types";
import "../lovelace/views/hui-view";
import "../lovelace/views/hui-view-container";
import { DEFAULT_POWER_COLLECTION_KEY } from "../../data/energy";
@customElement("ha-panel-energy")
+1 -7
View File
@@ -320,13 +320,7 @@ const computeLogbookValue = (
if (item.entity_id && item.state) {
return {
text: stateObj
? localizeStateMessage(
hass,
item.state,
stateObj,
domain!,
item.attributes
)
? localizeStateMessage(hass, item.state, stateObj, domain!, item)
: item.state,
type: "state",
};
@@ -12,7 +12,6 @@ import { computeStateDomain } from "../../../common/entity/compute_state_domain"
import { stateActive } from "../../../common/entity/state_active";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-badge";
import "../../../components/ha-ripple";
import "../../../components/ha-state-icon";
import "../../../components/ha-svg-icon";
import { cameraUrlWithWidthHeight } from "../../../data/camera";

Some files were not shown because too many files have changed in this diff Show More