From cfad45b7c22b3bed627a9d7fa1c38e68559df8de Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 30 Jul 2021 21:52:38 +0200 Subject: [PATCH] fix and finish statistics card (#9658) --- src/components/chart/ha-chart-base.ts | 2 +- src/components/chart/statistics-chart.ts | 60 ++++++-- src/components/ha-gauge.ts | 8 +- src/panels/lovelace/cards/hui-gauge-card.ts | 2 +- .../cards/hui-statistics-graph-card.ts | 5 +- .../hui-statistics-graph-card-editor.ts | 134 ++++++++++++++++-- 6 files changed, 179 insertions(+), 32 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 8c4c3cc16e..2ff0550efa 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -55,7 +55,7 @@ export default class HaChartBase extends LitElement { this._setupChart(); return; } - if (changedProps.has("type")) { + if (changedProps.has("chartType")) { this.chart.config.type = this.chartType; } if (changedProps.has("data")) { diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index 0ac2abb66a..68431696ba 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -38,8 +38,8 @@ class StatisticsChart extends LitElement { @property({ type: Array }) public statTypes: Array = [ "sum", "min", - "max", "mean", + "max", ]; @property() public chartType: ChartType = "line"; @@ -58,7 +58,7 @@ class StatisticsChart extends LitElement { if (!this.hasUpdated) { this._createOptions(); } - if (changedProps.has("statisticsData")) { + if (changedProps.has("statisticsData") || changedProps.has("statTypes")) { this._generateData(); } } @@ -164,6 +164,9 @@ class StatisticsChart extends LitElement { } private _generateData() { + if (!this.statisticsData) { + return; + } let colorIndex = 0; const statisticsData = Object.values(this.statisticsData); const totalDataSets: ChartDataset<"line">[] = []; @@ -231,21 +234,21 @@ class StatisticsChart extends LitElement { prevValues = dataValues; }; + const color = getColorByIndex(colorIndex); + colorIndex++; + const addDataSet = ( nameY: string, + borderColor: string, + backgroundColor: string, step = false, - fill = false, - color?: string + fill?: boolean | number | string ) => { - if (!color) { - color = getColorByIndex(colorIndex); - colorIndex++; - } statDataSets.push({ label: nameY, - fill: fill ? "origin" : false, - borderColor: color, - backgroundColor: color + "7F", + fill: fill || false, + borderColor, + backgroundColor: backgroundColor, stepped: step ? "before" : false, pointRadius: 0, data: [], @@ -254,20 +257,50 @@ class StatisticsChart extends LitElement { const statTypes: this["statTypes"] = []; - this.statTypes.forEach((type) => { + const sortedTypes = [...this.statTypes].sort((a, _b) => { + if (a === "min") { + return -1; + } + if (a === "max") { + return +1; + } + return 0; + }); + + const drawBands = + this.statTypes.includes("mean") && statisticsHaveType(stats, "mean"); + + sortedTypes.forEach((type) => { if (statisticsHaveType(stats, type)) { statTypes.push(type); addDataSet( `${name} (${this.hass.localize( `ui.components.statistics_charts.statistic_types.${type}` )})`, - false + drawBands && (type === "min" || type === "max") + ? color + "7F" + : color, + color + "7F", + false, + drawBands + ? type === "min" + ? "+1" + : type === "max" + ? "-1" + : false + : false ); } }); + let prevDate: Date | null = null; // Process chart data. stats.forEach((stat) => { + const date = new Date(stat.start); + if (prevDate === date) { + return; + } + prevDate = date; const dataValues: Array = []; statTypes.forEach((type) => { let val: number | null; @@ -278,7 +311,6 @@ class StatisticsChart extends LitElement { } dataValues.push(val !== null ? Math.round(val * 100) / 100 : null); }); - const date = new Date(stat.start); pushData(date, dataValues); }); diff --git a/src/components/ha-gauge.ts b/src/components/ha-gauge.ts index 2322f2e28a..81161ed64d 100644 --- a/src/components/ha-gauge.ts +++ b/src/components/ha-gauge.ts @@ -60,10 +60,14 @@ export class Gauge extends LitElement { protected render() { return svg` - + >` + : "" + } ${ this.levels diff --git a/src/panels/lovelace/cards/hui-gauge-card.ts b/src/panels/lovelace/cards/hui-gauge-card.ts index 1db4cb6334..f8ff918de2 100644 --- a/src/panels/lovelace/cards/hui-gauge-card.ts +++ b/src/panels/lovelace/cards/hui-gauge-card.ts @@ -206,7 +206,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { const sections = this._config!.severity; if (!sections) { - return []; + return [{ level: 0, stroke: severityMap.normal }]; } const sectionsArray = Object.keys(sections); diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index fd44114d3e..2b4e90522d 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -114,7 +114,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { | StatisticsGraphCardConfig | undefined; - if (oldConfig?.entities !== this._config.entities) { + if ( + oldConfig?.entities !== this._config.entities || + oldConfig?.days_to_show !== this._config.days_to_show + ) { this._getStatistics(); // statistics are created every hour clearInterval(this._interval); diff --git a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts index 831177442b..4acd31bca9 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts @@ -1,7 +1,16 @@ import "@polymer/paper-input/paper-input"; import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { array, assert, number, object, optional, string } from "superstruct"; +import { + array, + assert, + literal, + number, + object, + optional, + string, + union, +} from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { HomeAssistant } from "../../../../types"; import { StatisticsGraphCardConfig } from "../../cards/types"; @@ -11,12 +20,26 @@ import { EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; import "../../../../components/entity/ha-statistics-picker"; import { processConfigEntities } from "../../common/process-config-entities"; +import "../../../../components/ha-formfield"; +import "../../../../components/ha-checkbox"; +import { StatisticType } from "../../../../data/history"; +import "../../../../components/ha-radio"; +import type { HaRadio } from "../../../../components/ha-radio"; + +const statTypeStruct = union([ + literal("sum"), + literal("min"), + literal("max"), + literal("mean"), +]); const cardConfigStruct = object({ type: string(), entities: array(entitiesConfigStruct), title: optional(string()), days_to_show: optional(number()), + chart_type: optional(union([literal("bar"), literal("line")])), + stat_types: optional(union([array(statTypeStruct), statTypeStruct])), }); @customElement("hui-statistics-graph-card-editor") @@ -46,6 +69,18 @@ export class HuiStatisticsGraphCardEditor return this._config!.days_to_show || 30; } + get _chart_type(): StatisticsGraphCardConfig["chart_type"] { + return this._config!.chart_type || "line"; + } + + get _stat_types(): StatisticType[] { + return this._config!.stat_types + ? Array.isArray(this._config!.stat_types) + ? this._config!.stat_types + : [this._config!.stat_types] + : ["mean", "min", "max", "sum"]; + } + protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; @@ -63,19 +98,67 @@ export class HuiStatisticsGraphCardEditor .configValue=${"title"} @value-changed=${this._valueChanged} > + +

Show stat types:

- + + + + + + + + + + + + +
+
+

Chart type:

+ + + + + +
stat !== name), + }, + }); + } + private _valueChanged(ev: CustomEvent): void { if (!this._config || !this.hass) { return;