More statistics validation (#10146)

Co-authored-by: Philip Allgaier <mail@spacegaier.de>
This commit is contained in:
Bram Kragten 2021-10-04 23:21:21 +02:00 committed by GitHub
parent dc3bad56f2
commit 6f4593508b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 246 additions and 44 deletions

View File

@ -77,18 +77,42 @@ export interface StatisticsMetaData {
} }
export type StatisticsValidationResult = export type StatisticsValidationResult =
| StatisticsValidationResultUnsupportedUnit | StatisticsValidationResultEntityNotRecorded
| StatisticsValidationResultUnitsChanged; | StatisticsValidationResultUnsupportedStateClass
| StatisticsValidationResultUnitsChanged
| StatisticsValidationResultUnsupportedUnitMetadata
| StatisticsValidationResultUnsupportedUnitState;
export interface StatisticsValidationResultUnsupportedUnit { export interface StatisticsValidationResultEntityNotRecorded {
type: "unsupported_unit"; type: "entity_not_recorded";
data: { statistic_id: string; device_class: string; state_unit: string }; data: { statistic_id: string };
}
export interface StatisticsValidationResultUnsupportedStateClass {
type: "unsupported_state_class";
data: { statistic_id: string; state_class: string };
} }
export interface StatisticsValidationResultUnitsChanged { export interface StatisticsValidationResultUnitsChanged {
type: "units_changed"; type: "units_changed";
data: { statistic_id: string; state_unit: string; metadata_unit: string }; 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 { export interface StatisticsValidationResults {
[statisticId: string]: StatisticsValidationResult[]; [statisticId: string]: StatisticsValidationResult[];
} }

View File

@ -17,7 +17,15 @@ import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed"; 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") @customElement("developer-tools-statistics")
class HaPanelDevStatistics extends LitElement { class HaPanelDevStatistics extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -119,49 +127,116 @@ class HaPanelDevStatistics extends LitElement {
validateStatistics(this.hass), validateStatistics(this.hass),
]); ]);
this._data = statisticIds.map((statistic) => ({ const statsIds = new Set();
this._data = statisticIds.map((statistic) => {
statsIds.add(statistic.statistic_id);
return {
...statistic, ...statistic,
state: this.hass.states[statistic.statistic_id], state: this.hass.states[statistic.statistic_id],
issues: issues[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) { private _fixIssue(ev) {
const issue = ev.currentTarget.data[0] as StatisticsValidationResult; const issues = (ev.currentTarget.data as StatisticsValidationResult[]).sort(
if (issue.type === "unsupported_unit") { (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. <br /><br />You
probably excluded this entity, or have just included some
entities.<br /><br />See the
<a
href="https://www.home-assistant.io/integrations/recorder/#configure-filter"
target="_blank"
rel="noreferrer noopener"
>
recorder documentation</a
>
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. <br />Statistics can not be generated until this
entity has a supported state class.<br /><br />If this state class
was provided by an integration, this is a bug. Please report an
issue.<br /><br />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
<a
href="https://developers.home-assistant.io/docs/core/entity/sensor/#long-term-statistics"
target="_blank"
rel="noreferrer noopener"
>
developer documentation</a
>.`,
});
break;
case "unsupported_unit_metadata":
showFixStatisticsUnsupportedUnitMetadataDialog(this, {
issue,
fixedCallback: () => {
this._validateStatistics();
},
});
break;
case "unsupported_unit_state":
showAlertDialog(this, { showAlertDialog(this, {
title: "Unsupported unit", title: "Unsupported unit",
text: html`The unit of your entity is not a supported unit for the text: html`The unit of your entity is not a supported unit for the
device class of the entity, ${issue.data.device_class}. device class of the entity, ${issue.data.device_class}.
<br />Statistics can not be generated until this entity has a <br />Statistics can not be generated until this entity has a
supported unit.<br /><br />If this unit was provided by an supported unit.<br /><br />If this unit was provided by an
integration, this is a bug. Please report an issue.<br /><br />If you integration, this is a bug. Please report an issue.<br /><br />If
have set this unit yourself, and want to have statistics generated, you have set this unit yourself, and want to have statistics
make sure the unit matched the device class. The supported units are generated, make sure the unit matches the device class. The
documented in the supported units are documented in the
<a <a
href="https://developers.home-assistant.io/docs/core/entity/sensor" href="https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes"
target="_blank" target="_blank"
rel="noreferrer noopener"
> >
developer documentation</a developer documentation</a
>.`, >.`,
}); });
return; break;
} case "units_changed":
if (issue.type === "units_changed") {
showFixStatisticsUnitsChangedDialog(this, { showFixStatisticsUnitsChangedDialog(this, {
issue, issue,
fixedCallback: () => { fixedCallback: () => {
this._validateStatistics(); this._validateStatistics();
}, },
}); });
return; break;
} default:
showAlertDialog(this, { showAlertDialog(this, {
title: "Fix issue", title: "Fix issue",
text: "Fixing this issue is not supported yet.", text: "Fixing this issue is not supported yet.",
}); });
} }
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [

View File

@ -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`
<ha-dialog
open
@closed=${this.closeDialog}
heading="Unsupported unit in recorded statistics"
>
<p>
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}.
</p>
<p>
Do you want to update the unit of the history statistics to
${this._params.issue.data.supported_unit}?
</p>
<mwc-button slot="primaryAction" @click=${this._fixIssue}>
Fix
</mwc-button>
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
</mwc-button>
</ha-dialog>
`;
}
private async _fixIssue(): Promise<void> {
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;
}
}

View File

@ -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,
});
};

View File

@ -3728,7 +3728,10 @@
"issue": "Issue", "issue": "Issue",
"issues": { "issues": {
"units_changed": "The unit of this entity changed from ''{metadata_unit}'' to ''{state_unit}''.", "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": { "fix_issue": {
"units_changed": { "units_changed": {