mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 01:36:49 +00:00
fix and finish statistics card (#9658)
This commit is contained in:
parent
5234e9bce5
commit
cfad45b7c2
@ -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")) {
|
||||
|
@ -38,8 +38,8 @@ class StatisticsChart extends LitElement {
|
||||
@property({ type: Array }) public statTypes: Array<StatisticType> = [
|
||||
"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<number | null> = [];
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -60,10 +60,14 @@ export class Gauge extends LitElement {
|
||||
protected render() {
|
||||
return svg`
|
||||
<svg viewBox="0 0 100 50" class="gauge">
|
||||
<path
|
||||
${
|
||||
!this.needle || !this.levels
|
||||
? svg`<path
|
||||
class="dial"
|
||||
d="M 10 50 A 40 40 0 0 1 90 50"
|
||||
></path>
|
||||
></path>`
|
||||
: ""
|
||||
}
|
||||
|
||||
${
|
||||
this.levels
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
type="number"
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.days_to_show"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._days_to_show}
|
||||
min="1"
|
||||
.configValue=${"days_to_show"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<p>Show stat types:</p>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
type="number"
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.days_to_show"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._days_to_show}
|
||||
min="1"
|
||||
.configValue=${"days_to_show"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-formfield label="Sum">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("sum")}
|
||||
name="sum"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Mean">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("mean")}
|
||||
name="mean"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Min">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("min")}
|
||||
name="min"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Max">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("max")}
|
||||
name="max"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<p>Chart type:</p>
|
||||
<ha-formfield label="Line">
|
||||
<ha-radio
|
||||
.checked=${this._chart_type === "line"}
|
||||
value="line"
|
||||
name="chart_type"
|
||||
@change=${this._chartTypeChanged}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Bar">
|
||||
<ha-radio
|
||||
.checked=${this._chart_type === "bar"}
|
||||
value="bar"
|
||||
name="chart_type"
|
||||
@change=${this._chartTypeChanged}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
<ha-statistics-picker
|
||||
.hass=${this.hass}
|
||||
@ -89,6 +172,31 @@ export class HuiStatisticsGraphCardEditor
|
||||
`;
|
||||
}
|
||||
|
||||
private _chartTypeChanged(ev: CustomEvent) {
|
||||
const input = ev.currentTarget as HaRadio;
|
||||
fireEvent(this, "config-changed", {
|
||||
config: { ...this._config!, chart_type: input.value },
|
||||
});
|
||||
}
|
||||
|
||||
private _statTypesChanged(ev) {
|
||||
const name = ev.currentTarget.name;
|
||||
const checked = ev.currentTarget.checked;
|
||||
if (checked) {
|
||||
fireEvent(this, "config-changed", {
|
||||
config: { ...this._config!, stat_types: [...this._stat_types, name] },
|
||||
});
|
||||
return;
|
||||
}
|
||||
const statTypes = [...this._stat_types];
|
||||
fireEvent(this, "config-changed", {
|
||||
config: {
|
||||
...this._config!,
|
||||
stat_types: statTypes.filter((stat) => stat !== name),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user