diff --git a/src/data/recorder.ts b/src/data/recorder.ts index 23b63a6d1cb4..f4ab0cb80498 100644 --- a/src/data/recorder.ts +++ b/src/data/recorder.ts @@ -47,6 +47,14 @@ export interface StatisticsMetaData { unit_class: string | null; } +export const STATISTIC_TYPES: StatisticsValidationResult["type"][] = [ + "entity_not_recorded", + "entity_no_longer_recorded", + "unsupported_state_class", + "units_changed", + "no_state", +]; + export type StatisticsValidationResult = | StatisticsValidationResultNoState | StatisticsValidationResultEntityNotRecorded diff --git a/src/panels/config/repairs/ha-config-repairs.ts b/src/panels/config/repairs/ha-config-repairs.ts index 4e46d47741ab..3446fb305b21 100644 --- a/src/panels/config/repairs/ha-config-repairs.ts +++ b/src/panels/config/repairs/ha-config-repairs.ts @@ -12,11 +12,16 @@ import { fetchRepairsIssueData, type RepairsIssue, } from "../../../data/repairs"; +import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; import type { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; +import { fixStatisticsIssue } from "../../developer-tools/statistics/fix-statistics"; import { showRepairsFlowDialog } from "./show-dialog-repair-flow"; import { showRepairsIssueDialog } from "./show-repair-issue-dialog"; -import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; +import { + STATISTIC_TYPES, + StatisticsValidationResult, +} from "../../../data/recorder"; @customElement("ha-config-repairs") class HaConfigRepairs extends LitElement { @@ -130,6 +135,31 @@ class HaConfigRepairs extends LitElement { continueFlowId: data.issue_data.flow_id as string, }); } + } else if ( + issue.domain === "sensor" && + issue.translation_key && + STATISTIC_TYPES.includes(issue.translation_key as any) + ) { + const localize = + await this.hass.loadFragmentTranslation("developer-tools"); + const data = await fetchRepairsIssueData( + this.hass.connection, + issue.domain, + issue.issue_id + ); + if ("issue_type" in data.issue_data) { + await fixStatisticsIssue( + this, + this.hass, + localize || this.hass.localize, + { + type: data.issue_data + .issue_type as StatisticsValidationResult["type"], + data: data.issue_data as any, + } + ); + this.hass.callWS({ type: "recorder/update_statistics_issues" }); + } } else { showRepairsIssueDialog(this, { issue, diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts index 8d40dc0b42a9..a169eeb1799a 100644 --- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts +++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts @@ -5,28 +5,22 @@ 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 { LocalizeFunc } from "../../../common/translations/localize"; 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 { fixStatisticsIssue } from "./fix-statistics"; 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, @@ -264,162 +258,30 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) { }); } - private _fixIssue = (ev) => { + 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) ); 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" - )}

${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" - )}

${this.hass.localize( - "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_2" - )}

- - ${this.hass.localize( - "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_3_link" - )}`, - }); - 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" - )} - - ${this.hass.localize( - "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_3_link" - )}

- ${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 } - )}

- ${this.hass.localize( - "ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_2" - )} - - ${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" - ), - }); + const result = await fixStatisticsIssue( + this, + this.hass, + this.hass.localize, + issue + ); + if ( + result && + [ + "no_state", + "entity_no_longer_recorded", + "unsupported_state_class", + ].includes(issue.type) + ) { + this._deletedStatistics.add(issue.data.statistic_id); } + this._validateStatistics(); }; static get styles(): CSSResultGroup { diff --git a/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts b/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts index 682fa58b2cbe..e2bb70720898 100644 --- a/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts +++ b/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts @@ -7,6 +7,7 @@ import "../../../components/ha-formfield"; import "../../../components/ha-radio"; import { clearStatistics, + getStatisticLabel, updateStatisticsMetadata, } from "../../../data/recorder"; import { haStyle, haStyleDialog } from "../../../resources/styles"; @@ -27,6 +28,10 @@ export class DialogStatisticsFixUnitsChanged extends LitElement { } public closeDialog(): void { + this._cancel(); + } + + private _closeDialog(): void { this._params = undefined; this._action = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); @@ -40,7 +45,9 @@ export class DialogStatisticsFixUnitsChanged extends LitElement { return html` - + ${this.hass.localize("ui.common.close")} @@ -109,6 +121,11 @@ export class DialogStatisticsFixUnitsChanged extends LitElement { this._action = ev.target.value; } + private _cancel(): void { + this._params?.cancelCallback!(); + this._closeDialog(); + } + private async _fixIssue(): Promise { if (this._action === "clear") { await clearStatistics(this.hass, [this._params!.issue.data.statistic_id]); @@ -119,8 +136,8 @@ export class DialogStatisticsFixUnitsChanged extends LitElement { this._params!.issue.data.state_unit ); } - this._params?.fixedCallback(); - this.closeDialog(); + this._params?.fixedCallback!(); + this._closeDialog(); } static get styles(): CSSResultGroup { diff --git a/src/panels/developer-tools/statistics/fix-statistics.ts b/src/panels/developer-tools/statistics/fix-statistics.ts new file mode 100644 index 000000000000..6b2d6a7affca --- /dev/null +++ b/src/panels/developer-tools/statistics/fix-statistics.ts @@ -0,0 +1,169 @@ +import { html } from "lit"; +import { + clearStatistics, + getStatisticLabel, + StatisticsValidationResult, +} from "../../../data/recorder"; +import { documentationUrl } from "../../../util/documentation-url"; +import { + showConfirmationDialog, + showAlertDialog, +} from "../../lovelace/custom-card-helpers"; +import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed"; +import { LocalizeFunc } from "../../../common/translations/localize"; +import { HomeAssistant } from "../../../types"; + +export const fixStatisticsIssue = async ( + element: HTMLElement, + hass: HomeAssistant, + localize: LocalizeFunc, + issue: StatisticsValidationResult +) => { + switch (issue.type) { + case "no_state": + return showConfirmationDialog(element, { + title: localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.no_state.title" + ), + text: html`${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.no_state.info_text_1", + { + name: getStatisticLabel(hass, issue.data.statistic_id, undefined), + } + )}

${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.no_state.info_text_2", + { statistic_id: issue.data.statistic_id } + )}`, + confirmText: localize("ui.common.delete"), + destructive: true, + confirm: async () => { + await clearStatistics(hass, [issue.data.statistic_id]); + }, + }); + case "entity_not_recorded": + return showAlertDialog(element, { + title: localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.title" + ), + text: html`${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_1", + { + name: getStatisticLabel(hass, issue.data.statistic_id, undefined), + } + )}

${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_2" + )}

+ + ${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_not_recorded.info_text_3_link" + )}`, + }); + case "entity_no_longer_recorded": + return showConfirmationDialog(element, { + title: localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.title" + ), + text: html`${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_1", + { + name: getStatisticLabel(hass, issue.data.statistic_id, undefined), + } + )} + ${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_2" + )} + + ${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_3_link" + )}

+ ${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.entity_no_longer_recorded.info_text_4" + )}`, + confirmText: localize("ui.common.delete"), + destructive: true, + confirm: async () => { + await clearStatistics(hass, [issue.data.statistic_id]); + }, + }); + case "unsupported_state_class": + return showConfirmationDialog(element, { + title: localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.title" + ), + text: html`${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_1", + { + name: getStatisticLabel(hass, issue.data.statistic_id, undefined), + state_class: issue.data.state_class, + } + )}

+ ${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_2" + )} + + ${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.unsupported_state_class.info_text_6", + { statistic_id: issue.data.statistic_id } + )}`, + confirmText: localize("ui.common.delete"), + destructive: true, + confirm: async () => { + await clearStatistics(hass, [issue.data.statistic_id]); + }, + }); + case "units_changed": + return showFixStatisticsUnitsChangedDialog(element, { + issue, + }); + default: + return showAlertDialog(element, { + title: localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.no_support.title" + ), + text: localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.no_support.info_text_1" + ), + }); + } +}; diff --git a/src/panels/developer-tools/statistics/show-dialog-statistics-fix-units-changed.ts b/src/panels/developer-tools/statistics/show-dialog-statistics-fix-units-changed.ts index 1b6d7ee116a5..3305fc897bcd 100644 --- a/src/panels/developer-tools/statistics/show-dialog-statistics-fix-units-changed.ts +++ b/src/panels/developer-tools/statistics/show-dialog-statistics-fix-units-changed.ts @@ -6,16 +6,29 @@ export const loadFixUnitsDialog = () => export interface DialogStatisticsUnitsChangedParams { issue: StatisticsValidationResultUnitsChanged; - fixedCallback: () => void; + fixedCallback?: () => void; + cancelCallback?: () => void; } export const showFixStatisticsUnitsChangedDialog = ( element: HTMLElement, detailParams: DialogStatisticsUnitsChangedParams -): void => { - fireEvent(element, "show-dialog", { - dialogTag: "dialog-statistics-fix-units-changed", - dialogImport: loadFixUnitsDialog, - dialogParams: detailParams, +) => + new Promise((resolve) => { + const origCallback = detailParams.fixedCallback; + + fireEvent(element, "show-dialog", { + dialogTag: "dialog-statistics-fix-units-changed", + dialogImport: loadFixUnitsDialog, + dialogParams: { + ...detailParams, + cancelCallback: () => { + resolve(false); + }, + fixedCallback: () => { + resolve(true); + origCallback?.(); + }, + }, + }); }); -}; diff --git a/src/translations/en.json b/src/translations/en.json index e46030363356..a56ef23928f4 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6955,25 +6955,25 @@ }, "no_state": { "title": "Entity has no state", - "info_text_1": "This entity has no state at the moment, if this is an orphaned entity, you may want to delete the long term statistics of it from your database.", + "info_text_1": "{name} has no state at the moment, if this is an orphaned entity, you may want to delete the long term statistics of it from your database.", "info_text_2": "Do you want to permanently delete the long term statistics of {statistic_id} from your database?" }, "entity_not_recorded": { - "title": "Entity not recorded", - "info_text_1": "State changes of this entity are not recorded, therefore, we cannot track long term statistics for it.", + "title": "Entity is not recorded", + "info_text_1": "State changes of {name} are not recorded, therefore, we cannot track long term statistics for it.", "info_text_2": "You probably excluded this entity, or have just included some entities.", "info_text_3_link": "See the recorder documentation for more information." }, "entity_no_longer_recorded": { - "title": "Entity no longer recorded", - "info_text_1": "We have generated statistics for this entity in the past, but state changes of this entity are no longer recorded, therefore, we cannot track long term statistics for it anymore.", + "title": "Entity is no longer recorded", + "info_text_1": "We have generated statistics for {name} in the past, but state changes of this entity are no longer recorded, therefore, we cannot track long term statistics for it anymore.", "info_text_2": "You probably excluded this entity, or have just included some entities.", "info_text_3_link": "See the recorder documentation for more information.", "info_text_4": "If you no longer wish to keep the long term statistics recorded in the past, you may delete them now." }, "unsupported_state_class": { "title": "Unsupported state class", - "info_text_1": "The state class of this entity, {state_class} is not supported.", + "info_text_1": "The state class of {name}, {state_class} is not supported.", "info_text_2": "Statistics cannot be generated until this entity has a supported state class.", "info_text_3": "If this state class was provided by an integration, this is a bug. Please report an issue.", "info_text_4": "If you have set this state class yourself, please correct it.", @@ -6982,11 +6982,11 @@ "info_text_6": "Do you want to permanently delete the long term statistics of {statistic_id} from your database?" }, "units_changed": { - "title": "The unit of this entity changed", + "title": "The unit changed", "update": "Update the unit of the historic statistic values from ''{metadata_unit}'' to ''{state_unit}'', without converting.", "clear": "Delete all old statistic data for this entity", "how_to_fix": "How do you want to fix this issue?", - "info_text_1": "The unit of this entity changed to ''{current_unit}'' which can't be converted to the previously stored unit, ''{previous_unit}''.", + "info_text_1": "The unit of {name} changed to ''{current_unit}'' which can't be converted to the previously stored unit, ''{previous_unit}''.", "info_text_2": "If the historic statistic values have a wrong unit, you can update the units of the old values. The values will not be updated.", "info_text_3": "Otherwise you can choose to delete all historic statistic values, and start over." },