Add UOM to stats chart, fix coloring of bands (#9665)

This commit is contained in:
Bram Kragten 2021-07-31 20:36:27 +02:00 committed by GitHub
parent 0f16ba9325
commit e07ac52356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 50 deletions

View File

@ -78,7 +78,10 @@ const getDefaultFormatOptions = (
num: string | number,
options?: Intl.NumberFormatOptions
): Intl.NumberFormatOptions => {
const defaultOptions: Intl.NumberFormatOptions = options || {};
const defaultOptions: Intl.NumberFormatOptions = {
maximumFractionDigits: 2,
...options,
};
if (typeof num !== "string") {
return defaultOptions;

View File

@ -2,7 +2,10 @@ import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
import { html, LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators";
import { getColorByIndex } from "../../common/color/colors";
import { numberFormatToLocale } from "../../common/string/format_number";
import {
formatNumber,
numberFormatToLocale,
} from "../../common/string/format_number";
import { LineChartEntity, LineChartState } from "../../data/history";
import { HomeAssistant } from "../../types";
import "./ha-chart-base";
@ -85,7 +88,10 @@ class StateHistoryChartLine extends LitElement {
mode: "nearest",
callbacks: {
label: (context) =>
`${context.dataset.label}: ${context.parsed.y} ${this.unit}`,
`${context.dataset.label}: ${formatNumber(
context.parsed.y,
this.hass.locale
)} ${this.unit}`,
},
},
filler: {

View File

@ -16,10 +16,15 @@ import { customElement, property, state } from "lit/decorators";
import { getColorByIndex } from "../../common/color/colors";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { computeStateName } from "../../common/entity/compute_state_name";
import { numberFormatToLocale } from "../../common/string/format_number";
import {
formatNumber,
numberFormatToLocale,
} from "../../common/string/format_number";
import {
getStatisticIds,
Statistics,
statisticsHaveType,
StatisticsMetaData,
StatisticType,
} from "../../data/history";
import type { HomeAssistant } from "../../types";
@ -31,8 +36,12 @@ class StatisticsChart extends LitElement {
@property({ attribute: false }) public statisticsData!: Statistics;
@property({ type: Array }) public statisticIds?: StatisticsMetaData[];
@property() public names: boolean | Record<string, string> = false;
@property() public unit?: string;
@property({ attribute: false }) public endTime?: Date;
@property({ type: Array }) public statTypes: Array<StatisticType> = [
@ -46,12 +55,12 @@ class StatisticsChart extends LitElement {
@property({ type: Boolean }) public isLoadingData = false;
@state() private _chartData?: ChartData;
@state() private _chartData: ChartData = { datasets: [] };
@state() private _chartOptions?: ChartOptions;
protected shouldUpdate(changedProps: PropertyValues): boolean {
return !(changedProps.size === 1 && changedProps.has("hass"));
return changedProps.size > 1 || !changedProps.has("hass");
}
public willUpdate(changedProps: PropertyValues) {
@ -127,20 +136,31 @@ class StatisticsChart extends LitElement {
ticks: {
maxTicksLimit: 7,
},
title: {
display: this.unit,
text: this.unit,
},
},
},
plugins: {
tooltip: {
mode: "nearest",
callbacks: {
label: (context) => `${context.dataset.label}: ${context.parsed.y}`,
label: (context) =>
`${context.dataset.label}: ${formatNumber(
context.parsed.y,
this.hass.locale
)} ${
// @ts-ignore
context.dataset.unit || ""
}`,
},
},
filler: {
propagate: true,
},
legend: {
display: false,
display: true,
labels: {
usePointStyle: true,
},
@ -154,6 +174,7 @@ class StatisticsChart extends LitElement {
tension: 0.4,
borderWidth: 1.5,
},
bar: { borderWidth: 1.5, borderRadius: 4 },
point: {
hitRadius: 5,
},
@ -163,10 +184,19 @@ class StatisticsChart extends LitElement {
};
}
private _generateData() {
private async _getStatisticIds() {
this.statisticIds = await getStatisticIds(this.hass);
}
private async _generateData() {
if (!this.statisticsData) {
return;
}
if (!this.statisticIds) {
await this._getStatisticIds();
}
let colorIndex = 0;
const statisticsData = Object.values(this.statisticsData);
const totalDataSets: ChartDataset<"line">[] = [];
@ -191,6 +221,8 @@ class StatisticsChart extends LitElement {
endTime = new Date();
}
let unit: string | undefined | null;
const names = this.names || {};
statisticsData.forEach((stats) => {
const firstStat = stats[0];
@ -203,6 +235,19 @@ class StatisticsChart extends LitElement {
name = firstStat.statistic_id;
}
}
const meta = this.statisticIds!.find(
(stat) => stat.statistic_id === firstStat.statistic_id
);
if (!this.unit) {
if (unit === undefined) {
unit = meta?.unit_of_measurement;
} else if (unit !== meta?.unit_of_measurement) {
unit = null;
}
}
// array containing [value1, value2, etc]
let prevValues: Array<number | null> | null = null;
@ -237,59 +282,47 @@ class StatisticsChart extends LitElement {
const color = getColorByIndex(colorIndex);
colorIndex++;
const addDataSet = (
nameY: string,
borderColor: string,
backgroundColor: string,
step = false,
fill?: boolean | number | string
) => {
statDataSets.push({
label: nameY,
fill: fill || false,
borderColor,
backgroundColor: backgroundColor,
stepped: step ? "before" : false,
pointRadius: 0,
data: [],
});
};
const statTypes: this["statTypes"] = [];
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");
const sortedTypes = drawBands
? [...this.statTypes].sort((a, b) => {
if (a === "min" || b === "max") {
return -1;
}
if (a === "max" || b === "min") {
return +1;
}
return 0;
})
: this.statTypes;
sortedTypes.forEach((type) => {
if (statisticsHaveType(stats, type)) {
const band = drawBands && (type === "min" || type === "max");
statTypes.push(type);
addDataSet(
`${name} (${this.hass.localize(
statDataSets.push({
label: `${name} (${this.hass.localize(
`ui.components.statistics_charts.statistic_types.${type}`
)})`,
drawBands && (type === "min" || type === "max")
? color + "7F"
: color,
color + "7F",
false,
drawBands
)})
`,
fill: drawBands
? type === "min"
? "+1"
: type === "max"
? "-1"
: false
: false
);
: false,
borderColor: band ? color + "7F" : color,
backgroundColor: band ? color + "3F" : color + "7F",
pointRadius: 0,
data: [],
// @ts-ignore
unit: meta?.unit_of_measurement,
band,
});
}
});
@ -321,6 +354,19 @@ class StatisticsChart extends LitElement {
Array.prototype.push.apply(totalDataSets, statDataSets);
});
if (unit !== null) {
this._chartOptions = {
...this._chartOptions,
scales: {
...this._chartOptions!.scales,
y: {
...(this._chartOptions!.scales!.y as Record<string, unknown>),
title: { display: unit, text: unit },
},
},
};
}
this._chartData = {
datasets: totalDataSets,
};

View File

@ -27,9 +27,8 @@ class HaStatisticsPicker extends LitElement {
return html``;
}
const currentStatistics = this._currentStatistics;
return html`
${currentStatistics.map(
${this._currentStatistics.map(
(statisticId) => html`
<div>
<ha-statistic-picker

View File

@ -81,6 +81,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
? processConfigEntities(config.entities)
: [];
this._entities = [];
configEntities.forEach((entity) => {
this._entities.push(entity.entity);
if (entity.name) {