diff --git a/src/data/history.ts b/src/data/history.ts index 0a225f2685..243e457960 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -77,18 +77,42 @@ export interface StatisticsMetaData { } export type StatisticsValidationResult = - | StatisticsValidationResultUnsupportedUnit - | StatisticsValidationResultUnitsChanged; + | StatisticsValidationResultEntityNotRecorded + | StatisticsValidationResultUnsupportedStateClass + | StatisticsValidationResultUnitsChanged + | StatisticsValidationResultUnsupportedUnitMetadata + | StatisticsValidationResultUnsupportedUnitState; -export interface StatisticsValidationResultUnsupportedUnit { - type: "unsupported_unit"; - data: { statistic_id: string; device_class: string; state_unit: string }; +export interface StatisticsValidationResultEntityNotRecorded { + type: "entity_not_recorded"; + data: { statistic_id: string }; +} + +export interface StatisticsValidationResultUnsupportedStateClass { + type: "unsupported_state_class"; + data: { statistic_id: string; state_class: string }; } export interface StatisticsValidationResultUnitsChanged { type: "units_changed"; data: { statistic_id: string; state_unit: string; metadata_unit: string }; } + +export interface StatisticsValidationResultUnsupportedUnitMetadata { + type: "unsupported_unit_metadata"; + data: { + statistic_id: string; + device_class: string; + metadata_unit: string; + supported_unit: string; + }; +} + +export interface StatisticsValidationResultUnsupportedUnitState { + type: "unsupported_unit_state"; + data: { statistic_id: string; device_class: string; metadata_unit: string }; +} + export interface StatisticsValidationResults { [statisticId: string]: StatisticsValidationResult[]; } diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts index a8121343c4..36236dffcc 100644 --- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts +++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts @@ -17,7 +17,15 @@ import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed"; +import { showFixStatisticsUnsupportedUnitMetadataDialog } from "./show-dialog-statistics-fix-unsupported-unit-meta"; +const FIX_ISSUES_ORDER = { + entity_not_recorded: 1, + unsupported_unit_state: 2, + unsupported_state_class: 3, + units_changed: 4, + unsupported_unit_metadata: 5, +}; @customElement("developer-tools-statistics") class HaPanelDevStatistics extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -119,48 +127,115 @@ class HaPanelDevStatistics extends LitElement { validateStatistics(this.hass), ]); - this._data = statisticIds.map((statistic) => ({ - ...statistic, - state: this.hass.states[statistic.statistic_id], - issues: issues[statistic.statistic_id], - })); + const statsIds = new Set(); + + this._data = statisticIds.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._data.push({ + statistic_id: statisticId, + unit_of_measurement: "", + state: this.hass.states[statisticId], + issues: issues[statisticId], + }); + } + }); } private _fixIssue(ev) { - const issue = ev.currentTarget.data[0] as StatisticsValidationResult; - if (issue.type === "unsupported_unit") { - showAlertDialog(this, { - title: "Unsupported unit", - text: html`The unit of your entity is not a supported unit for the - device class of the entity, ${issue.data.device_class}. -
Statistics can not be generated until this entity has a - supported unit.

If this unit was provided by an - integration, this is a bug. Please report an issue.

If you - have set this unit yourself, and want to have statistics generated, - make sure the unit matched the device class. The supported units are - documented in the - - developer documentation.`, - }); - return; + 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 "entity_not_recorded": + showAlertDialog(this, { + title: "Entity not recorded", + text: html`State changes of this entity are not recorded, therefore, + we can not track long term statistics for it.

You + probably excluded this entity, or have just included some + entities.

See the + + recorder documentation + for more information.`, + }); + break; + case "unsupported_state_class": + showAlertDialog(this, { + title: "Unsupported state class", + text: html`The state class of this entity, ${issue.data.state_class} + is not supported.
Statistics can not be generated until this + entity has a supported state class.

If this state class + was provided by an integration, this is a bug. Please report an + issue.

If you have set this state class yourself, please + correct it. The different state classes and when to use which can be + found in the + + developer documentation.`, + }); + break; + case "unsupported_unit_metadata": + showFixStatisticsUnsupportedUnitMetadataDialog(this, { + issue, + fixedCallback: () => { + this._validateStatistics(); + }, + }); + break; + case "unsupported_unit_state": + showAlertDialog(this, { + title: "Unsupported unit", + text: html`The unit of your entity is not a supported unit for the + device class of the entity, ${issue.data.device_class}. +
Statistics can not be generated until this entity has a + supported unit.

If this unit was provided by an + integration, this is a bug. Please report an issue.

If + you have set this unit yourself, and want to have statistics + generated, make sure the unit matches the device class. The + supported units are documented in the + + developer documentation.`, + }); + break; + case "units_changed": + showFixStatisticsUnitsChangedDialog(this, { + issue, + fixedCallback: () => { + this._validateStatistics(); + }, + }); + break; + default: + showAlertDialog(this, { + title: "Fix issue", + text: "Fixing this issue is not supported yet.", + }); } - if (issue.type === "units_changed") { - showFixStatisticsUnitsChangedDialog(this, { - issue, - fixedCallback: () => { - this._validateStatistics(); - }, - }); - return; - } - showAlertDialog(this, { - title: "Fix issue", - text: "Fixing this issue is not supported yet.", - }); } static get styles(): CSSResultGroup { diff --git a/src/panels/developer-tools/statistics/dialog-statistics-fix-unsupported-unit-meta.ts b/src/panels/developer-tools/statistics/dialog-statistics-fix-unsupported-unit-meta.ts new file mode 100644 index 0000000000..fd7e7ae61f --- /dev/null +++ b/src/panels/developer-tools/statistics/dialog-statistics-fix-unsupported-unit-meta.ts @@ -0,0 +1,79 @@ +import "@material/mwc-button/mwc-button"; +import { LitElement, TemplateResult, html, CSSResultGroup } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import "../../../components/ha-dialog"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { haStyle, haStyleDialog } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import { updateStatisticsMetadata } from "../../../data/history"; +import "../../../components/ha-formfield"; +import "../../../components/ha-radio"; +import { DialogStatisticsUnsupportedUnitMetaParams } from "./show-dialog-statistics-fix-unsupported-unit-meta"; + +@customElement("dialog-statistics-fix-unsupported-unit-meta") +export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _params?: DialogStatisticsUnsupportedUnitMetaParams; + + public showDialog(params: DialogStatisticsUnsupportedUnitMetaParams): void { + this._params = params; + } + + public closeDialog(): void { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render(): TemplateResult | void { + if (!this._params) { + return html``; + } + + return html` + +

+ The unit of the statistics in your database for this entity is not a + supported unit for the device class of the entity, + ${this._params.issue.data.device_class}. It should be + ${this._params.issue.data.supported_unit}. +

+

+ Do you want to update the unit of the history statistics to + ${this._params.issue.data.supported_unit}? +

+ + + Fix + + + ${this.hass.localize("ui.common.close")} + +
+ `; + } + + private async _fixIssue(): Promise { + await updateStatisticsMetadata( + this.hass, + this._params!.issue.data.statistic_id, + this._params!.issue.data.supported_unit + ); + this._params?.fixedCallback(); + this.closeDialog(); + } + + static get styles(): CSSResultGroup { + return [haStyle, haStyleDialog]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-statistics-fix-unsupported-unit-meta": DialogStatisticsFixUnsupportedUnitMetadata; + } +} diff --git a/src/panels/developer-tools/statistics/show-dialog-statistics-fix-unsupported-unit-meta.ts b/src/panels/developer-tools/statistics/show-dialog-statistics-fix-unsupported-unit-meta.ts new file mode 100644 index 0000000000..828f3094bc --- /dev/null +++ b/src/panels/developer-tools/statistics/show-dialog-statistics-fix-unsupported-unit-meta.ts @@ -0,0 +1,21 @@ +import { fireEvent } from "../../../common/dom/fire_event"; +import { StatisticsValidationResultUnsupportedUnitMetadata } from "../../../data/history"; + +export const loadFixUnsupportedUnitMetaDialog = () => + import("./dialog-statistics-fix-unsupported-unit-meta"); + +export interface DialogStatisticsUnsupportedUnitMetaParams { + issue: StatisticsValidationResultUnsupportedUnitMetadata; + fixedCallback: () => void; +} + +export const showFixStatisticsUnsupportedUnitMetadataDialog = ( + element: HTMLElement, + detailParams: DialogStatisticsUnsupportedUnitMetaParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-statistics-fix-unsupported-unit-meta", + dialogImport: loadFixUnsupportedUnitMetaDialog, + dialogParams: detailParams, + }); +}; diff --git a/src/translations/en.json b/src/translations/en.json index 237d1a460f..9757d2ffa5 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3728,7 +3728,10 @@ "issue": "Issue", "issues": { "units_changed": "The unit of this entity changed from ''{metadata_unit}'' to ''{state_unit}''.", - "unsupported_unit": "The unit (''{state_unit}'') of this entity doesn't match a unit of device class ''{device_class}''." + "unsupported_unit_state": "The unit (''{state_unit}'') of this entity doesn't match a unit of device class ''{device_class}''.", + "unsupported_unit_metadata": "The unit (''{state_unit}'') of the recorded statistics doesn't match a unit of device class ''{device_class}''.", + "unsupported_state_class": "The state class ''{state_class}'' of this entity is not supported.", + "entity_not_recorded": "This entity is excluded from being recorded." }, "fix_issue": { "units_changed": {