mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Support displaying relative change in statistics graph cards (#13837)
* Support displaying relative change in statistics graph cards * Address review comments * Add option to display state * Drop absolute sum option * Drop period if invalid * prevent fetching stats twice * Change stat_types to supported ones for statistics Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
f9d119d33d
commit
71c43058ea
@ -30,6 +30,15 @@ import {
|
|||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "./ha-chart-base";
|
import "./ha-chart-base";
|
||||||
|
|
||||||
|
export type ExtendedStatisticType = StatisticType | "state";
|
||||||
|
|
||||||
|
export const statTypeMap: Record<ExtendedStatisticType, StatisticType> = {
|
||||||
|
mean: "mean",
|
||||||
|
min: "min",
|
||||||
|
max: "max",
|
||||||
|
sum: "sum",
|
||||||
|
state: "sum",
|
||||||
|
};
|
||||||
@customElement("statistics-chart")
|
@customElement("statistics-chart")
|
||||||
class StatisticsChart extends LitElement {
|
class StatisticsChart extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -42,7 +51,7 @@ class StatisticsChart extends LitElement {
|
|||||||
|
|
||||||
@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<ExtendedStatisticType> = [
|
||||||
"sum",
|
"sum",
|
||||||
"min",
|
"min",
|
||||||
"mean",
|
"mean",
|
||||||
@ -307,7 +316,7 @@ class StatisticsChart extends LitElement {
|
|||||||
: this.statTypes;
|
: this.statTypes;
|
||||||
|
|
||||||
sortedTypes.forEach((type) => {
|
sortedTypes.forEach((type) => {
|
||||||
if (statisticsHaveType(stats, type)) {
|
if (statisticsHaveType(stats, statTypeMap[type])) {
|
||||||
const band = drawBands && (type === "min" || type === "max");
|
const band = drawBands && (type === "min" || type === "max");
|
||||||
statTypes.push(type);
|
statTypes.push(type);
|
||||||
statDataSets.push({
|
statDataSets.push({
|
||||||
@ -335,7 +344,6 @@ class StatisticsChart extends LitElement {
|
|||||||
|
|
||||||
let prevDate: Date | null = null;
|
let prevDate: Date | null = null;
|
||||||
// Process chart data.
|
// Process chart data.
|
||||||
let initVal: number | null = null;
|
|
||||||
let prevSum: number | null = null;
|
let prevSum: number | null = null;
|
||||||
stats.forEach((stat) => {
|
stats.forEach((stat) => {
|
||||||
const date = new Date(stat.start);
|
const date = new Date(stat.start);
|
||||||
@ -347,11 +355,11 @@ class StatisticsChart extends LitElement {
|
|||||||
statTypes.forEach((type) => {
|
statTypes.forEach((type) => {
|
||||||
let val: number | null;
|
let val: number | null;
|
||||||
if (type === "sum") {
|
if (type === "sum") {
|
||||||
if (initVal === null) {
|
if (prevSum === null) {
|
||||||
initVal = val = stat.state || 0;
|
val = 0;
|
||||||
prevSum = stat.sum;
|
prevSum = stat.sum;
|
||||||
} else {
|
} else {
|
||||||
val = initVal + ((stat.sum || 0) - prevSum!);
|
val = (stat.sum || 0) - prevSum;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val = stat[type];
|
val = stat[type];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export type StatisticType = "sum" | "min" | "max" | "mean";
|
export type StatisticType = "state" | "sum" | "min" | "max" | "mean";
|
||||||
|
|
||||||
export interface Statistics {
|
export interface Statistics {
|
||||||
[statisticId: string]: StatisticValue[];
|
[statisticId: string]: StatisticValue[];
|
||||||
|
@ -38,8 +38,6 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
private _names: Record<string, string> = {};
|
private _names: Record<string, string> = {};
|
||||||
|
|
||||||
private _fetching = false;
|
|
||||||
|
|
||||||
private _interval?: number;
|
private _interval?: number;
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
@ -92,7 +90,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
if (typeof config.stat_types === "string") {
|
if (typeof config.stat_types === "string") {
|
||||||
this._config = { ...config, stat_types: [config.stat_types] };
|
this._config = { ...config, stat_types: [config.stat_types] };
|
||||||
} else if (!config.stat_types) {
|
} else if (!config.stat_types) {
|
||||||
this._config = { ...config, stat_types: ["sum", "min", "max", "mean"] };
|
this._config = {
|
||||||
|
...config,
|
||||||
|
stat_types: ["state", "sum", "min", "max", "mean"],
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
@ -156,15 +157,11 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _getStatistics(): Promise<void> {
|
private async _getStatistics(): Promise<void> {
|
||||||
if (this._fetching) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
startDate.setTime(
|
startDate.setTime(
|
||||||
startDate.getTime() -
|
startDate.getTime() -
|
||||||
1000 * 60 * 60 * (24 * (this._config!.days_to_show || 30) + 1)
|
1000 * 60 * 60 * (24 * (this._config!.days_to_show || 30) + 1)
|
||||||
);
|
);
|
||||||
this._fetching = true;
|
|
||||||
try {
|
try {
|
||||||
this._statistics = await fetchStatistics(
|
this._statistics = await fetchStatistics(
|
||||||
this.hass!,
|
this.hass!,
|
||||||
@ -173,8 +170,8 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
this._entities,
|
this._entities,
|
||||||
this._config!.period
|
this._config!.period
|
||||||
);
|
);
|
||||||
} finally {
|
} catch (err) {
|
||||||
this._fetching = false;
|
this._statistics = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +36,11 @@ import {
|
|||||||
statisticsMetaHasType,
|
statisticsMetaHasType,
|
||||||
} from "../../../../data/recorder";
|
} from "../../../../data/recorder";
|
||||||
import { deepEqual } from "../../../../common/util/deep-equal";
|
import { deepEqual } from "../../../../common/util/deep-equal";
|
||||||
|
import { statTypeMap } from "../../../../components/chart/statistics-chart";
|
||||||
|
import { ensureArray } from "../../../../common/ensure-array";
|
||||||
|
|
||||||
const statTypeStruct = union([
|
const statTypeStruct = union([
|
||||||
|
literal("state"),
|
||||||
literal("sum"),
|
literal("sum"),
|
||||||
literal("min"),
|
literal("min"),
|
||||||
literal("max"),
|
literal("max"),
|
||||||
@ -64,7 +67,7 @@ const cardConfigStruct = assign(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const periods = ["5minute", "hour", "day", "month"] as const;
|
const periods = ["5minute", "hour", "day", "month"] as const;
|
||||||
const stat_types = ["mean", "min", "max", "sum"] as const;
|
const stat_types = ["mean", "min", "max", "sum", "state"] as const;
|
||||||
|
|
||||||
@customElement("hui-statistics-graph-card-editor")
|
@customElement("hui-statistics-graph-card-editor")
|
||||||
export class HuiStatisticsGraphCardEditor
|
export class HuiStatisticsGraphCardEditor
|
||||||
@ -156,7 +159,7 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
disabled:
|
disabled:
|
||||||
!metaDatas ||
|
!metaDatas ||
|
||||||
!metaDatas?.every((metaData) =>
|
!metaDatas?.every((metaData) =>
|
||||||
statisticsMetaHasType(metaData, stat_type)
|
statisticsMetaHasType(metaData, statTypeMap[stat_type])
|
||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
@ -190,7 +193,11 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
? Array.isArray(this._config!.stat_types)
|
? Array.isArray(this._config!.stat_types)
|
||||||
? this._config!.stat_types
|
? this._config!.stat_types
|
||||||
: [this._config!.stat_types]
|
: [this._config!.stat_types]
|
||||||
: stat_types;
|
: stat_types.filter((stat_type) =>
|
||||||
|
this._metaDatas?.every((metaData) =>
|
||||||
|
statisticsMetaHasType(metaData, statTypeMap[stat_type])
|
||||||
|
)
|
||||||
|
);
|
||||||
const data = {
|
const data = {
|
||||||
chart_type: "line",
|
chart_type: "line",
|
||||||
period: "hour",
|
period: "hour",
|
||||||
@ -230,9 +237,27 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _entitiesChanged(ev: CustomEvent): void {
|
private async _entitiesChanged(ev: CustomEvent): Promise<void> {
|
||||||
|
const config = { ...this._config!, entities: ev.detail.value };
|
||||||
|
if (
|
||||||
|
config.entities?.some((statistic_id) => statistic_id.includes(":")) &&
|
||||||
|
config.period === "5minute"
|
||||||
|
) {
|
||||||
|
delete config.period;
|
||||||
|
}
|
||||||
|
if (config.stat_types && config.entities.length) {
|
||||||
|
const metadata = await getStatisticMetadata(this.hass!, config.entities);
|
||||||
|
config.stat_types = ensureArray(config.stat_types).filter((stat_type) =>
|
||||||
|
metadata.every((metaData) =>
|
||||||
|
statisticsMetaHasType(metaData, statTypeMap[stat_type])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (!config.stat_types.length) {
|
||||||
|
delete config.stat_types;
|
||||||
|
}
|
||||||
|
}
|
||||||
fireEvent(this, "config-changed", {
|
fireEvent(this, "config-changed", {
|
||||||
config: { ...this._config!, entities: ev.detail.value },
|
config,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,6 +517,7 @@
|
|||||||
"min": "min",
|
"min": "min",
|
||||||
"max": "max",
|
"max": "max",
|
||||||
"mean": "mean",
|
"mean": "mean",
|
||||||
|
"state": "state",
|
||||||
"sum": "sum"
|
"sum": "sum"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3969,7 +3970,8 @@
|
|||||||
"mean": "Mean",
|
"mean": "Mean",
|
||||||
"min": "Min",
|
"min": "Min",
|
||||||
"max": "Max",
|
"max": "Max",
|
||||||
"sum": "Sum"
|
"state": "State",
|
||||||
|
"sum": "Sum (change during period)"
|
||||||
},
|
},
|
||||||
"chart_type": "Chart type",
|
"chart_type": "Chart type",
|
||||||
"periods": {
|
"periods": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user