mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-20 15:56:35 +00:00
Add UOM to stats chart, fix coloring of bands (#9665)
This commit is contained in:
parent
0f16ba9325
commit
e07ac52356
@ -78,7 +78,10 @@ const getDefaultFormatOptions = (
|
|||||||
num: string | number,
|
num: string | number,
|
||||||
options?: Intl.NumberFormatOptions
|
options?: Intl.NumberFormatOptions
|
||||||
): Intl.NumberFormatOptions => {
|
): Intl.NumberFormatOptions => {
|
||||||
const defaultOptions: Intl.NumberFormatOptions = options || {};
|
const defaultOptions: Intl.NumberFormatOptions = {
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
if (typeof num !== "string") {
|
if (typeof num !== "string") {
|
||||||
return defaultOptions;
|
return defaultOptions;
|
||||||
|
@ -2,7 +2,10 @@ import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
|||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { getColorByIndex } from "../../common/color/colors";
|
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 { LineChartEntity, LineChartState } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "./ha-chart-base";
|
import "./ha-chart-base";
|
||||||
@ -85,7 +88,10 @@ class StateHistoryChartLine extends LitElement {
|
|||||||
mode: "nearest",
|
mode: "nearest",
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (context) =>
|
label: (context) =>
|
||||||
`${context.dataset.label}: ${context.parsed.y} ${this.unit}`,
|
`${context.dataset.label}: ${formatNumber(
|
||||||
|
context.parsed.y,
|
||||||
|
this.hass.locale
|
||||||
|
)} ${this.unit}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
filler: {
|
filler: {
|
||||||
|
@ -16,10 +16,15 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { getColorByIndex } from "../../common/color/colors";
|
import { getColorByIndex } from "../../common/color/colors";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
import { numberFormatToLocale } from "../../common/string/format_number";
|
|
||||||
import {
|
import {
|
||||||
|
formatNumber,
|
||||||
|
numberFormatToLocale,
|
||||||
|
} from "../../common/string/format_number";
|
||||||
|
import {
|
||||||
|
getStatisticIds,
|
||||||
Statistics,
|
Statistics,
|
||||||
statisticsHaveType,
|
statisticsHaveType,
|
||||||
|
StatisticsMetaData,
|
||||||
StatisticType,
|
StatisticType,
|
||||||
} from "../../data/history";
|
} from "../../data/history";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
@ -31,8 +36,12 @@ class StatisticsChart extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public statisticsData!: Statistics;
|
@property({ attribute: false }) public statisticsData!: Statistics;
|
||||||
|
|
||||||
|
@property({ type: Array }) public statisticIds?: StatisticsMetaData[];
|
||||||
|
|
||||||
@property() public names: boolean | Record<string, string> = false;
|
@property() public names: boolean | Record<string, string> = false;
|
||||||
|
|
||||||
|
@property() public unit?: string;
|
||||||
|
|
||||||
@property({ attribute: false }) public endTime?: Date;
|
@property({ attribute: false }) public endTime?: Date;
|
||||||
|
|
||||||
@property({ type: Array }) public statTypes: Array<StatisticType> = [
|
@property({ type: Array }) public statTypes: Array<StatisticType> = [
|
||||||
@ -46,12 +55,12 @@ class StatisticsChart extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public isLoadingData = false;
|
@property({ type: Boolean }) public isLoadingData = false;
|
||||||
|
|
||||||
@state() private _chartData?: ChartData;
|
@state() private _chartData: ChartData = { datasets: [] };
|
||||||
|
|
||||||
@state() private _chartOptions?: ChartOptions;
|
@state() private _chartOptions?: ChartOptions;
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
return !(changedProps.size === 1 && changedProps.has("hass"));
|
return changedProps.size > 1 || !changedProps.has("hass");
|
||||||
}
|
}
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues) {
|
public willUpdate(changedProps: PropertyValues) {
|
||||||
@ -127,20 +136,31 @@ class StatisticsChart extends LitElement {
|
|||||||
ticks: {
|
ticks: {
|
||||||
maxTicksLimit: 7,
|
maxTicksLimit: 7,
|
||||||
},
|
},
|
||||||
|
title: {
|
||||||
|
display: this.unit,
|
||||||
|
text: this.unit,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
mode: "nearest",
|
mode: "nearest",
|
||||||
callbacks: {
|
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: {
|
filler: {
|
||||||
propagate: true,
|
propagate: true,
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
display: false,
|
display: true,
|
||||||
labels: {
|
labels: {
|
||||||
usePointStyle: true,
|
usePointStyle: true,
|
||||||
},
|
},
|
||||||
@ -154,6 +174,7 @@ class StatisticsChart extends LitElement {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
borderWidth: 1.5,
|
borderWidth: 1.5,
|
||||||
},
|
},
|
||||||
|
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||||
point: {
|
point: {
|
||||||
hitRadius: 5,
|
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) {
|
if (!this.statisticsData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.statisticIds) {
|
||||||
|
await this._getStatisticIds();
|
||||||
|
}
|
||||||
|
|
||||||
let colorIndex = 0;
|
let colorIndex = 0;
|
||||||
const statisticsData = Object.values(this.statisticsData);
|
const statisticsData = Object.values(this.statisticsData);
|
||||||
const totalDataSets: ChartDataset<"line">[] = [];
|
const totalDataSets: ChartDataset<"line">[] = [];
|
||||||
@ -191,6 +221,8 @@ class StatisticsChart extends LitElement {
|
|||||||
endTime = new Date();
|
endTime = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let unit: string | undefined | null;
|
||||||
|
|
||||||
const names = this.names || {};
|
const names = this.names || {};
|
||||||
statisticsData.forEach((stats) => {
|
statisticsData.forEach((stats) => {
|
||||||
const firstStat = stats[0];
|
const firstStat = stats[0];
|
||||||
@ -203,6 +235,19 @@ class StatisticsChart extends LitElement {
|
|||||||
name = firstStat.statistic_id;
|
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]
|
// array containing [value1, value2, etc]
|
||||||
let prevValues: Array<number | null> | null = null;
|
let prevValues: Array<number | null> | null = null;
|
||||||
|
|
||||||
@ -237,59 +282,47 @@ class StatisticsChart extends LitElement {
|
|||||||
const color = getColorByIndex(colorIndex);
|
const color = getColorByIndex(colorIndex);
|
||||||
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 statTypes: this["statTypes"] = [];
|
||||||
|
|
||||||
const sortedTypes = [...this.statTypes].sort((a, _b) => {
|
|
||||||
if (a === "min") {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (a === "max") {
|
|
||||||
return +1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const drawBands =
|
const drawBands =
|
||||||
this.statTypes.includes("mean") && statisticsHaveType(stats, "mean");
|
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) => {
|
sortedTypes.forEach((type) => {
|
||||||
if (statisticsHaveType(stats, type)) {
|
if (statisticsHaveType(stats, type)) {
|
||||||
|
const band = drawBands && (type === "min" || type === "max");
|
||||||
statTypes.push(type);
|
statTypes.push(type);
|
||||||
addDataSet(
|
statDataSets.push({
|
||||||
`${name} (${this.hass.localize(
|
label: `${name} (${this.hass.localize(
|
||||||
`ui.components.statistics_charts.statistic_types.${type}`
|
`ui.components.statistics_charts.statistic_types.${type}`
|
||||||
)})`,
|
)})
|
||||||
drawBands && (type === "min" || type === "max")
|
`,
|
||||||
? color + "7F"
|
fill: drawBands
|
||||||
: color,
|
|
||||||
color + "7F",
|
|
||||||
false,
|
|
||||||
drawBands
|
|
||||||
? type === "min"
|
? type === "min"
|
||||||
? "+1"
|
? "+1"
|
||||||
: type === "max"
|
: type === "max"
|
||||||
? "-1"
|
? "-1"
|
||||||
: false
|
: 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);
|
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 = {
|
this._chartData = {
|
||||||
datasets: totalDataSets,
|
datasets: totalDataSets,
|
||||||
};
|
};
|
||||||
|
@ -27,9 +27,8 @@ class HaStatisticsPicker extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentStatistics = this._currentStatistics;
|
|
||||||
return html`
|
return html`
|
||||||
${currentStatistics.map(
|
${this._currentStatistics.map(
|
||||||
(statisticId) => html`
|
(statisticId) => html`
|
||||||
<div>
|
<div>
|
||||||
<ha-statistic-picker
|
<ha-statistic-picker
|
||||||
|
@ -81,6 +81,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
? processConfigEntities(config.entities)
|
? processConfigEntities(config.entities)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
this._entities = [];
|
||||||
configEntities.forEach((entity) => {
|
configEntities.forEach((entity) => {
|
||||||
this._entities.push(entity.entity);
|
this._entities.push(entity.entity);
|
||||||
if (entity.name) {
|
if (entity.name) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user