mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-15 07:25:54 +00:00
435 lines
14 KiB
TypeScript
435 lines
14 KiB
TypeScript
import "@material/mwc-button/mwc-button";
|
|
import { mdiSlopeUphill } from "@mdi/js";
|
|
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
import { CSSResultGroup, html, LitElement } from "lit";
|
|
import { customElement, property, state } from "lit/decorators";
|
|
import memoizeOne from "memoize-one";
|
|
import { fireEvent } from "../../../common/dom/fire_event";
|
|
import { LocalizeFunc } from "../../../common/translations/localize";
|
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
|
import "../../../components/data-table/ha-data-table";
|
|
import type { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
|
import { subscribeEntityRegistry } from "../../../data/entity_registry";
|
|
import {
|
|
clearStatistics,
|
|
getStatisticIds,
|
|
StatisticsMetaData,
|
|
StatisticsValidationResult,
|
|
validateStatistics,
|
|
} from "../../../data/recorder";
|
|
import {
|
|
showAlertDialog,
|
|
showConfirmationDialog,
|
|
} from "../../../dialogs/generic/show-dialog-box";
|
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
|
import { haStyle } from "../../../resources/styles";
|
|
import { HomeAssistant } from "../../../types";
|
|
import { showStatisticsAdjustSumDialog } from "./show-dialog-statistics-adjust-sum";
|
|
import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed";
|
|
import { documentationUrl } from "../../../util/documentation-url";
|
|
|
|
const FIX_ISSUES_ORDER = {
|
|
no_state: 0,
|
|
entity_no_longer_recorded: 1,
|
|
entity_not_recorded: 1,
|
|
unsupported_state_class: 2,
|
|
units_changed: 3,
|
|
};
|
|
|
|
type StatisticData = StatisticsMetaData & {
|
|
issues?: StatisticsValidationResult[];
|
|
state?: HassEntity;
|
|
};
|
|
|
|
type DisplayedStatisticData = StatisticData & {
|
|
displayName: string;
|
|
issues_string?: string;
|
|
};
|
|
|
|
@customElement("developer-tools-statistics")
|
|
class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
|
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
|
|
@property({ type: Boolean }) public narrow = false;
|
|
|
|
@state() private _data: StatisticData[] = [] as StatisticsMetaData[];
|
|
|
|
private _disabledEntities = new Set<string>();
|
|
|
|
private _deletedStatistics = new Set<string>();
|
|
|
|
protected firstUpdated() {
|
|
this._validateStatistics();
|
|
}
|
|
|
|
private _displayData = memoizeOne(
|
|
(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.developer-tools.tabs.statistics.issues.${issue.type}`,
|
|
issue.data
|
|
) || issue.type
|
|
)
|
|
.join(" "),
|
|
}))
|
|
);
|
|
|
|
private _columns = memoizeOne(
|
|
(
|
|
localize: LocalizeFunc
|
|
): DataTableColumnContainer<DisplayedStatisticData> => ({
|
|
displayName: {
|
|
title: localize(
|
|
"ui.panel.developer-tools.tabs.statistics.data_table.name"
|
|
),
|
|
main: true,
|
|
sortable: true,
|
|
filterable: true,
|
|
flex: 2,
|
|
},
|
|
statistic_id: {
|
|
title: localize(
|
|
"ui.panel.developer-tools.tabs.statistics.data_table.statistic_id"
|
|
),
|
|
sortable: true,
|
|
filterable: true,
|
|
hidden: this.narrow,
|
|
},
|
|
statistics_unit_of_measurement: {
|
|
title: localize(
|
|
"ui.panel.developer-tools.tabs.statistics.data_table.statistics_unit"
|
|
),
|
|
sortable: true,
|
|
filterable: true,
|
|
forceLTR: true,
|
|
},
|
|
source: {
|
|
title: localize(
|
|
"ui.panel.developer-tools.tabs.statistics.data_table.source"
|
|
),
|
|
sortable: true,
|
|
filterable: true,
|
|
},
|
|
issues_string: {
|
|
title: localize(
|
|
"ui.panel.developer-tools.tabs.statistics.data_table.issue"
|
|
),
|
|
sortable: true,
|
|
filterable: true,
|
|
direction: "asc",
|
|
flex: 2,
|
|
template: (statistic) =>
|
|
html`${statistic.issues_string ??
|
|
localize("ui.panel.developer-tools.tabs.statistics.no_issue")}`,
|
|
},
|
|
fix: {
|
|
title: "",
|
|
label: this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.fix"
|
|
),
|
|
template: (statistic) =>
|
|
html`${statistic.issues
|
|
? html`<mwc-button
|
|
@click=${this._fixIssue}
|
|
.data=${statistic.issues}
|
|
>
|
|
${localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.fix"
|
|
)}
|
|
</mwc-button>`
|
|
: "—"}`,
|
|
minWidth: "113px",
|
|
maxWidth: "113px",
|
|
showNarrow: true,
|
|
},
|
|
actions: {
|
|
title: "",
|
|
label: localize("ui.panel.developer-tools.tabs.statistics.adjust_sum"),
|
|
type: "icon-button",
|
|
showNarrow: true,
|
|
template: (statistic) =>
|
|
statistic.has_sum
|
|
? html`
|
|
<ha-icon-button
|
|
.label=${localize(
|
|
"ui.panel.developer-tools.tabs.statistics.adjust_sum"
|
|
)}
|
|
.path=${mdiSlopeUphill}
|
|
.statistic=${statistic}
|
|
@click=${this._showStatisticsAdjustSumDialog}
|
|
></ha-icon-button>
|
|
`
|
|
: "",
|
|
},
|
|
})
|
|
);
|
|
|
|
protected render() {
|
|
return html`
|
|
<ha-data-table
|
|
.hass=${this.hass}
|
|
.columns=${this._columns(this.hass.localize)}
|
|
.data=${this._displayData(this._data, this.hass.localize)}
|
|
.noDataText=${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.data_table.no_statistics"
|
|
)}
|
|
.narrow=${this.narrow}
|
|
id="statistic_id"
|
|
clickable
|
|
@row-click=${this._rowClicked}
|
|
></ha-data-table>
|
|
`;
|
|
}
|
|
|
|
private _showStatisticsAdjustSumDialog(ev) {
|
|
ev.stopPropagation();
|
|
showStatisticsAdjustSumDialog(this, {
|
|
statistic: ev.currentTarget.statistic,
|
|
});
|
|
}
|
|
|
|
private _rowClicked(ev) {
|
|
const id = ev.detail.id;
|
|
if (id in this.hass.states) {
|
|
fireEvent(this, "hass-more-info", { entityId: id });
|
|
}
|
|
}
|
|
|
|
public hassSubscribe(): UnsubscribeFunc[] {
|
|
return [
|
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
|
const disabledEntities = new Set<string>();
|
|
for (const confEnt of entities) {
|
|
if (!confEnt.disabled_by) {
|
|
continue;
|
|
}
|
|
disabledEntities.add(confEnt.entity_id);
|
|
}
|
|
// If the disabled entities changed, re-validate the statistics
|
|
if (disabledEntities !== this._disabledEntities) {
|
|
this._disabledEntities = disabledEntities;
|
|
this._validateStatistics();
|
|
}
|
|
}),
|
|
];
|
|
}
|
|
|
|
private async _validateStatistics() {
|
|
const [statisticIds, issues] = await Promise.all([
|
|
getStatisticIds(this.hass),
|
|
validateStatistics(this.hass),
|
|
]);
|
|
|
|
const statsIds = new Set();
|
|
|
|
this._data = statisticIds
|
|
.filter(
|
|
(statistic) =>
|
|
!this._disabledEntities.has(statistic.statistic_id) &&
|
|
!this._deletedStatistics.has(statistic.statistic_id)
|
|
)
|
|
.map((statistic) => {
|
|
statsIds.add(statistic.statistic_id);
|
|
return {
|
|
...statistic,
|
|
state: this.hass.states[statistic.statistic_id],
|
|
issues: issues[statistic.statistic_id],
|
|
};
|
|
});
|
|
|
|
Object.keys(issues).forEach((statisticId) => {
|
|
if (
|
|
!statsIds.has(statisticId) &&
|
|
!this._disabledEntities.has(statisticId) &&
|
|
!this._deletedStatistics.has(statisticId)
|
|
) {
|
|
this._data.push({
|
|
statistic_id: statisticId,
|
|
statistics_unit_of_measurement: "",
|
|
source: "",
|
|
state: this.hass.states[statisticId],
|
|
issues: issues[statisticId],
|
|
has_mean: false,
|
|
has_sum: false,
|
|
unit_class: null,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private _fixIssue = (ev) => {
|
|
const issues = (ev.currentTarget.data as StatisticsValidationResult[]).sort(
|
|
(itemA, itemB) =>
|
|
(FIX_ISSUES_ORDER[itemA.type] ?? 99) -
|
|
(FIX_ISSUES_ORDER[itemB.type] ?? 99)
|
|
);
|
|
const issue = issues[0];
|
|
switch (issue.type) {
|
|
case "no_state":
|
|
showConfirmationDialog(this, {
|
|
title: this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.no_state.title"
|
|
),
|
|
text: html`${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.no_state.info_text_1"
|
|
)}<br /><br />${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.no_state.info_text_2",
|
|
{ statistic_id: issue.data.statistic_id }
|
|
)}`,
|
|
confirmText: this.hass.localize("ui.common.delete"),
|
|
destructive: true,
|
|
confirm: async () => {
|
|
await clearStatistics(this.hass, [issue.data.statistic_id]);
|
|
this._deletedStatistics.add(issue.data.statistic_id);
|
|
this._validateStatistics();
|
|
},
|
|
});
|
|
break;
|
|
case "entity_not_recorded":
|
|
showAlertDialog(this, {
|
|
title: this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.title"
|
|
),
|
|
text: html`${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_1"
|
|
)}<br /><br />${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_2"
|
|
)}<br /><br />
|
|
<a
|
|
href=${documentationUrl(
|
|
this.hass,
|
|
"/integrations/recorder/#configure-filter"
|
|
)}
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_3_link"
|
|
)}</a
|
|
>`,
|
|
});
|
|
break;
|
|
case "entity_no_longer_recorded":
|
|
showConfirmationDialog(this, {
|
|
title: this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.title"
|
|
),
|
|
text: html`${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_1"
|
|
)}
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_2"
|
|
)}
|
|
<a
|
|
href=${documentationUrl(
|
|
this.hass,
|
|
"/integrations/recorder/#configure-filter"
|
|
)}
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_3_link"
|
|
)}</a
|
|
><br /><br />
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_4"
|
|
)}`,
|
|
confirmText: this.hass.localize("ui.common.delete"),
|
|
destructive: true,
|
|
confirm: async () => {
|
|
await clearStatistics(this.hass, [issue.data.statistic_id]);
|
|
this._deletedStatistics.add(issue.data.statistic_id);
|
|
this._validateStatistics();
|
|
},
|
|
});
|
|
break;
|
|
case "unsupported_state_class":
|
|
showConfirmationDialog(this, {
|
|
title: this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.title"
|
|
),
|
|
text: html`${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_1",
|
|
{ state_class: issue.data.state_class }
|
|
)}<br /><br />
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_2"
|
|
)}
|
|
<ul>
|
|
<li>
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_3"
|
|
)}
|
|
</li>
|
|
<li>
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_4"
|
|
)}
|
|
<a
|
|
href="https://developers.home-assistant.io/docs/core/entity/sensor/#long-term-statistics"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_4_link"
|
|
)}</a
|
|
>
|
|
</li>
|
|
<li>
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_5"
|
|
)}
|
|
</li>
|
|
</ul>
|
|
${this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_6",
|
|
{ statistic_id: issue.data.statistic_id }
|
|
)}`,
|
|
confirmText: this.hass.localize("ui.common.delete"),
|
|
destructive: true,
|
|
confirm: async () => {
|
|
await clearStatistics(this.hass, [issue.data.statistic_id]);
|
|
this._deletedStatistics.add(issue.data.statistic_id);
|
|
this._validateStatistics();
|
|
},
|
|
});
|
|
break;
|
|
case "units_changed":
|
|
showFixStatisticsUnitsChangedDialog(this, {
|
|
issue,
|
|
fixedCallback: () => {
|
|
this._validateStatistics();
|
|
},
|
|
});
|
|
break;
|
|
default:
|
|
showAlertDialog(this, {
|
|
title: this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.no_support.title"
|
|
),
|
|
text: this.hass.localize(
|
|
"ui.panel.developer-tools.tabs.statistics.fix_issue.no_support.info_text_1"
|
|
),
|
|
});
|
|
}
|
|
};
|
|
|
|
static get styles(): CSSResultGroup {
|
|
return haStyle;
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"developer-tools-statistics": HaPanelDevStatistics;
|
|
}
|
|
}
|