Compare commits

..

2 Commits

Author SHA1 Message Date
Wendelin 8af5908682 Fix add T/C/A floor auto open; Target details adaptive dialog. (#52001)
* Auto open single floor

* Use adaptive dialog for target details

* review
2026-05-12 19:28:24 +03:00
George Caliment 60e95b886c Fixed how ha-entity-toggle sets ha-switch styles var (#51984) 2026-05-12 16:46:01 +02:00
5 changed files with 90 additions and 186 deletions
+11 -3
View File
@@ -22,6 +22,14 @@ const isOn = (stateObj?: HassEntity) =>
!STATES_OFF.includes(stateObj.state) &&
!isUnavailableState(stateObj.state);
/**
* @element ha-entity-toggle
*
* @cssprop --ha-entity-toggle-switch-width - Width of the switch track. Defaults to `38px`.
* @cssprop --ha-entity-toggle-switch-size - Height of the switch track. Defaults to `20px`.
* @cssprop --ha-entity-toggle-switch-thumb-size - Size of the switch thumb. Defaults to `14px`.
*/
@customElement("ha-entity-toggle")
export class HaEntityToggle extends LitElement {
// hass is not a property so that we only re-render on stateObj changes
@@ -165,9 +173,9 @@ export class HaEntityToggle extends LitElement {
white-space: nowrap;
}
ha-switch {
--ha-switch-width: 38px;
--ha-switch-size: 20px;
--ha-switch-thumb-size: 14px;
--ha-switch-width: var(--ha-entity-toggle-switch-width, 38px);
--ha-switch-size: var(--ha-entity-toggle-switch-size, 20px);
--ha-switch-thumb-size: var(--ha-entity-toggle-switch-thumb-size, 14px);
}
ha-icon-button {
--ha-icon-button-size: 40px;
@@ -18,7 +18,7 @@ import {
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
import type { HomeAssistant } from "../../../types";
import type { HaDevicePickerDeviceFilterFunc } from "../../device/ha-device-picker";
import "../../ha-dialog";
import "../../ha-adaptive-dialog";
import "../../ha-dialog-header";
import "../../ha-icon-button";
import "../../ha-icon-next";
@@ -153,7 +153,7 @@ class DialogTargetDetails extends LitElement implements HassDialog {
!this._entitySourcesLoaded;
return html`
<ha-dialog
<ha-adaptive-dialog
.open=${this._opened}
header-title=${this.hass.localize(
"ui.components.target-picker.target_details"
@@ -187,7 +187,7 @@ class DialogTargetDetails extends LitElement implements HassDialog {
`}
</ha-list-base>
</div>
</ha-dialog>
</ha-adaptive-dialog>
`;
}
+4 -7
View File
@@ -154,7 +154,7 @@ export const getRecorderInfo = (conn: Connection) =>
});
export const getStatisticIds = (
hass: Pick<HomeAssistant, "callWS">,
hass: HomeAssistant,
statistic_type?: "mean" | "sum"
) =>
hass.callWS<StatisticsMetaData[]>({
@@ -227,7 +227,7 @@ export const fetchStatistic = (
rolling_window: period.rolling_window,
});
export const validateStatistics = (hass: Pick<HomeAssistant, "callWS">) =>
export const validateStatistics = (hass: HomeAssistant) =>
hass.callWS<StatisticsValidationResults>({
type: "recorder/validate_statistics",
});
@@ -245,10 +245,7 @@ export const updateStatisticsMetadata = (
unit_class,
});
export const clearStatistics = (
hass: Pick<HomeAssistant, "callWS">,
statistic_ids: string[]
) =>
export const clearStatistics = (hass: HomeAssistant, statistic_ids: string[]) =>
hass.callWS<undefined>({
type: "recorder/clear_statistics",
statistic_ids,
@@ -372,5 +369,5 @@ export const getDisplayUnit = (
export const isExternalStatistic = (statisticsId: string): boolean =>
statisticsId.includes(":");
export const updateStatisticsIssues = (hass: Pick<HomeAssistant, "callWS">) =>
export const updateStatisticsIssues = (hass: HomeAssistant) =>
hass.callWS<undefined>({ type: "recorder/update_statistics_issues" });
@@ -866,10 +866,13 @@ export default class HaAutomationAddFromTarget extends LitElement {
undefined
);
const filteredFloors = this._floorAreas.filter(
({ id, areas }) => id !== undefined && areas.length
);
this._floorAreas.forEach((floor) => {
this._entries[floor.id || `floor${TARGET_SEPARATOR}`] = {
// auto expand if only one floor is present
open: this._floorAreas.length === 1,
open: filteredFloors.length === 1 && filteredFloors[0].id === floor.id,
areas: {},
};
@@ -12,18 +12,11 @@ import {
import "@home-assistant/webawesome/dist/components/divider/divider";
import type { HassEntity } from "home-assistant-js-websocket";
import { consume, type ContextType } from "@lit/context";
import { css, type CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeAreaName } from "../../../../common/entity/compute_area_name";
import {
computeDeviceName,
getDuplicatedDeviceNames,
} from "../../../../common/entity/compute_device_name";
import { computeEntityEntryName } from "../../../../common/entity/compute_entity_name";
import { computeStateName } from "../../../../common/entity/compute_state_name";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/chips/ha-assist-chip";
@@ -31,7 +24,6 @@ import "../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
HaDataTable,
RowClickedEvent,
SelectionChangedEvent,
SortingDirection,
} from "../../../../components/data-table/ha-data-table";
@@ -53,16 +45,9 @@ import {
updateStatisticsIssues,
validateStatistics,
} from "../../../../data/recorder";
import {
apiContext,
internationalizationContext,
registriesContext,
statesContext,
} from "../../../../data/context";
import { getAreaTableColumn } from "../../common/data-table-columns";
import { KeyboardShortcutMixin } from "../../../../mixins/keyboard-shortcut-mixin";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistantRegistries } from "../../../../types";
import type { HomeAssistant } from "../../../../types";
import { showConfirmationDialog } from "../../../lovelace/custom-card-helpers";
import { fixStatisticsIssue } from "./fix-statistics";
import { showStatisticsAdjustSumDialog } from "./show-dialog-statistics-adjust-sum";
@@ -92,14 +77,13 @@ type StatisticData = StatisticsMetaData & {
type DisplayedStatisticData = StatisticData & {
displayName: string;
device?: string;
device_full?: string;
area?: string;
issues_string?: string;
};
@customElement("developer-tools-statistics")
class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean, reflect: true }) public narrow = false;
@state() private _data: StatisticData[] = [] as StatisticsMetaData[];
@@ -122,22 +106,6 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
@state() private _selectMode = false;
@state()
@consume({ context: apiContext, subscribe: true })
private _api!: ContextType<typeof apiContext>;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: registriesContext, subscribe: true })
private _registries!: ContextType<typeof registriesContext>;
@state()
@consume({ context: statesContext, subscribe: true })
private _states!: ContextType<typeof statesContext>;
@query("ha-data-table", true) private _dataTable!: HaDataTable;
@query("ha-input-search") private _searchInput!: HaInputSearch;
@@ -147,55 +115,22 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
}
private _displayData = memoizeOne(
(
data: StatisticData[],
localize: LocalizeFunc,
entities: HomeAssistantRegistries["entities"],
devices: HomeAssistantRegistries["devices"],
areas: HomeAssistantRegistries["areas"]
): DisplayedStatisticData[] => {
const duplicatedDeviceNames = getDuplicatedDeviceNames(devices);
return data.map((item) => {
const entry = entities[item.statistic_id];
const device = entry?.device_id ? devices[entry.device_id] : undefined;
const areaId = entry?.area_id || device?.area_id;
const area = areaId ? areas[areaId] : undefined;
const entityName = entry
? computeEntityEntryName(entry, devices, item.state)
: undefined;
const deviceName = device ? computeDeviceName(device) : undefined;
const areaName = area ? computeAreaName(area) : undefined;
const deviceFullName = deviceName
? duplicatedDeviceNames.has(deviceName) && areaName
? `${deviceName} (${areaName})`
: deviceName
: undefined;
return {
...item,
displayName:
entityName ||
deviceName ||
(item.state ? computeStateName(item.state) : undefined) ||
item.name ||
item.statistic_id,
device: deviceName,
device_full: deviceFullName,
area: areaName,
issues_string: item.issues
?.map(
(issue) =>
localize(
`ui.panel.config.developer-tools.tabs.statistics.issues.${issue.type}`,
issue.data
) || issue.type
)
.join(" "),
};
});
}
(data: StatisticData[], localize: LocalizeFunc): DisplayedStatisticData[] =>
data.map((item) => ({
...item,
displayName: item.state
? computeStateName(item.state)
: item.name || item.statistic_id,
issues_string: item.issues
?.map(
(issue) =>
localize(
`ui.panel.config.developer-tools.tabs.statistics.issues.${issue.type}`,
issue.data
) || issue.type
)
.join(" "),
}))
);
private _columns = memoizeOne(
@@ -211,18 +146,6 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
filterable: true,
flex: 2,
},
device: {
title: localize("ui.panel.config.entities.picker.headers.device"),
sortable: true,
template: (entry) => entry.device || "—",
},
device_full: {
title: localize("ui.panel.config.entities.picker.headers.device"),
filterable: true,
groupable: true,
hidden: true,
},
area: getAreaTableColumn(localize),
statistic_id: {
title: localize(
"ui.panel.config.developer-tools.tabs.statistics.data_table.statistic_id"
@@ -264,7 +187,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
},
fix: {
title: "",
label: localize(
label: this.hass.localize(
"ui.panel.config.developer-tools.tabs.statistics.fix_issue.fix"
),
type: "icon",
@@ -308,20 +231,21 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
@click=${this._showStatisticsAdjustSumDialog}
></ha-icon-button>
`
: nothing,
: "",
},
})
);
protected render() {
const columns = this._columns(this._i18n.localize);
const localize = this.hass.localize;
const columns = this._columns(this.hass.localize);
const selectModeBtn = !this._selectMode
? html`<ha-assist-chip
class="has-dropdown select-mode-chip"
.active=${this._selectMode}
@click=${this._enableSelectMode}
.title=${this._i18n.localize(
.title=${localize(
"ui.components.subpage-data-table.enter_selection_mode"
)}
>
@@ -341,14 +265,11 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
<ha-dropdown @wa-select=${this._handleSortBy}>
<ha-assist-chip
slot="trigger"
.label=${this._i18n.localize(
"ui.components.subpage-data-table.sort_by",
{
sortColumn: this._sortColumn
? ` ${columns[this._sortColumn]?.title || columns[this._sortColumn]?.label}`
: "",
}
)}
.label=${localize("ui.components.subpage-data-table.sort_by", {
sortColumn: this._sortColumn
? ` ${columns[this._sortColumn]?.title || columns[this._sortColumn]?.label}`
: "",
})}
>
<ha-svg-icon
slot="trailing-icon"
@@ -386,14 +307,11 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
<ha-dropdown @wa-select=${this._handleOverflowGroupBy}>
<ha-assist-chip
slot="trigger"
.label=${this._i18n.localize(
"ui.components.subpage-data-table.group_by",
{
groupColumn: this._groupColumn
? ` ${columns[this._groupColumn].title || columns[this._groupColumn].label}`
: "",
}
)}
.label=${localize("ui.components.subpage-data-table.group_by", {
groupColumn: this._groupColumn
? ` ${columns[this._groupColumn].title || columns[this._groupColumn].label}`
: "",
})}
>
<ha-svg-icon
slot="trailing-icon"
@@ -416,9 +334,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
value="none"
.selected=${this._groupColumn === undefined}
>
${this._i18n.localize(
"ui.components.subpage-data-table.dont_group_by"
)}
${localize("ui.components.subpage-data-table.dont_group_by")}
</ha-dropdown-item>
<wa-divider></wa-divider>
<ha-dropdown-item
@@ -430,7 +346,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
slot="icon"
.path=${mdiUnfoldLessHorizontal}
></ha-svg-icon>
${this._i18n.localize(
${localize(
"ui.components.subpage-data-table.collapse_all_groups"
)}
</ha-dropdown-item>
@@ -442,9 +358,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
slot="icon"
.path=${mdiUnfoldMoreHorizontal}
></ha-svg-icon>
${this._i18n.localize(
"ui.components.subpage-data-table.expand_all_groups"
)}
${localize("ui.components.subpage-data-table.expand_all_groups")}
</ha-dropdown-item>
</ha-dropdown>
`
@@ -453,7 +367,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
const settingsButton = html`<ha-assist-chip
class="has-dropdown select-mode-chip"
@click=${this._openSettings}
.title=${this._i18n.localize("ui.components.subpage-data-table.settings")}
.title=${localize("ui.components.subpage-data-table.settings")}
>
<ha-svg-icon slot="icon" .path=${mdiTableCog}></ha-svg-icon>
</ha-assist-chip>`;
@@ -466,13 +380,13 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
<ha-icon-button
.path=${mdiClose}
@click=${this._disableSelectMode}
.label=${this._i18n.localize(
.label=${localize(
"ui.components.subpage-data-table.exit_selection_mode"
)}
></ha-icon-button>
<ha-dropdown>
<ha-assist-chip
.label=${this._i18n.localize(
.label=${localize(
"ui.components.subpage-data-table.select"
)}
slot="trigger"
@@ -487,41 +401,34 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
></ha-svg-icon
></ha-assist-chip>
<ha-dropdown-item @click=${this._selectAll}>
${this._i18n.localize(
"ui.components.subpage-data-table.select_all"
)}
${localize("ui.components.subpage-data-table.select_all")}
</ha-dropdown-item>
<ha-dropdown-item @click=${this._selectAllIssues}>
${this._i18n.localize(
${localize(
"ui.panel.config.developer-tools.tabs.statistics.data_table.select_all_issues"
)}
</ha-dropdown-item>
<ha-dropdown-item @click=${this._selectNone}>
${this._i18n.localize(
"ui.components.subpage-data-table.select_none"
)}
${localize("ui.components.subpage-data-table.select_none")}
</ha-dropdown-item>
<wa-divider></wa-divider>
<ha-dropdown-item @click=${this._disableSelectMode}>
${this._i18n.localize(
${localize(
"ui.components.subpage-data-table.exit_selection_mode"
)}
</ha-dropdown-item>
</ha-dropdown>
<p>
${this._i18n.localize(
"ui.components.subpage-data-table.selected",
{
selected: this._selected.length,
}
)}
${localize("ui.components.subpage-data-table.selected", {
selected: this._selected.length,
})}
</p>
</div>
<div class="center-vertical">
<slot name="selection-bar"></slot>
</div>
<ha-assist-chip
.label=${this._i18n.localize(
.label=${localize(
"ui.panel.config.developer-tools.tabs.statistics.delete_selected"
)}
.disabled=${!this._selected.length}
@@ -541,18 +448,12 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
</slot>
</div>
`
: nothing}
: ""}
<ha-data-table
.narrow=${this.narrow}
.columns=${columns}
.data=${this._displayData(
this._data,
this._i18n.localize,
this._registries.entities,
this._registries.devices,
this._registries.areas
)}
.noDataText=${this._i18n.localize(
.data=${this._displayData(this._data, this.hass.localize)}
.noDataText=${this.hass.localize(
"ui.panel.config.developer-tools.tabs.statistics.data_table.no_statistics"
)}
.filter=${this.filter}
@@ -650,7 +551,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
private _openSettings() {
showDataTableSettingsDialog(this, {
columns: this._columns(this._i18n.localize),
columns: this._columns(this.hass.localize),
hiddenColumns: this.hiddenColumns,
columnOrder: this.columnOrder,
onUpdate: (
@@ -660,7 +561,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
this.columnOrder = columnOrder;
this.hiddenColumns = hiddenColumns;
},
localizeFunc: this._i18n.localize,
localizeFunc: this.hass.localize,
});
}
@@ -698,29 +599,27 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
);
}
private _showStatisticsAdjustSumDialog(ev: Event) {
private _showStatisticsAdjustSumDialog(ev) {
ev.stopPropagation();
showStatisticsAdjustSumDialog(this, {
statistic: (
ev.currentTarget as HTMLElement & { statistic: StatisticData }
).statistic,
statistic: ev.currentTarget.statistic,
});
}
private _rowClicked(ev: HASSDomEvent<RowClickedEvent>) {
private _rowClicked(ev) {
const id = ev.detail.id;
if (id in this._states) {
if (id in this.hass.states) {
fireEvent(this, "hass-more-info", { entityId: id });
}
}
private async _validateStatistics() {
const [statisticIds, issues] = await Promise.all([
getStatisticIds(this._api),
validateStatistics(this._api),
getStatisticIds(this.hass),
validateStatistics(this.hass),
]);
updateStatisticsIssues(this._api);
updateStatisticsIssues(this.hass);
const statsIds = new Set();
@@ -728,7 +627,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
statsIds.add(statistic.statistic_id);
return {
...statistic,
state: this._states[statistic.statistic_id],
state: this.hass.states[statistic.statistic_id],
issues: issues[statistic.statistic_id],
};
});
@@ -739,7 +638,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
statistic_id: statisticId,
statistics_unit_of_measurement: "",
source: "",
state: this._states[statisticId],
state: this.hass.states[statisticId],
issues: issues[statisticId],
mean_type: StatisticMeanType.NONE,
has_sum: false,
@@ -757,28 +656,25 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
const deletableIds = this._selected;
await showConfirmationDialog(this, {
title: this._i18n.localize(
title: this.hass.localize(
"ui.panel.config.developer-tools.tabs.statistics.multi_delete.title"
),
text: html`${this._i18n.localize(
text: html`${this.hass.localize(
"ui.panel.config.developer-tools.tabs.statistics.multi_delete.info_text",
{ statistic_count: deletableIds.length }
)}`,
confirmText: this._i18n.localize("ui.common.delete"),
confirmText: this.hass.localize("ui.common.delete"),
destructive: true,
confirm: async () => {
await clearStatistics(this._api, deletableIds);
await clearStatistics(this.hass, deletableIds);
this._validateStatistics();
this._dataTable.clearSelection();
},
});
};
private _fixIssue = async (ev: Event) => {
const issues = (
(ev.currentTarget as HTMLElement & { data: StatisticsValidationResult[] })
.data as StatisticsValidationResult[]
).sort(
private _fixIssue = async (ev) => {
const issues = (ev.currentTarget.data as StatisticsValidationResult[]).sort(
(itemA, itemB) =>
(FIX_ISSUES_ORDER[itemA.type] ?? 99) -
(FIX_ISSUES_ORDER[itemB.type] ?? 99)