mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Share energy sum calculation across all cards (#25184)
This commit is contained in:
parent
8901c1fb31
commit
6464c2b602
@ -782,12 +782,19 @@ export const getEnergyWaterUnit = (hass: HomeAssistant): string =>
|
||||
export const energyStatisticHelpUrl =
|
||||
"/docs/energy/faq/#troubleshooting-missing-entities";
|
||||
|
||||
interface EnergySumData {
|
||||
export interface EnergySumData {
|
||||
to_grid?: Record<number, number>;
|
||||
from_grid?: Record<number, number>;
|
||||
to_battery?: Record<number, number>;
|
||||
from_battery?: Record<number, number>;
|
||||
solar?: Record<number, number>;
|
||||
total: {
|
||||
to_grid?: number;
|
||||
from_grid?: number;
|
||||
to_battery?: number;
|
||||
from_battery?: number;
|
||||
solar?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface EnergyConsumptionData {
|
||||
@ -860,29 +867,30 @@ const getSummedDataPartial = (
|
||||
}
|
||||
}
|
||||
|
||||
const summedData: EnergySumData = {};
|
||||
const summedData: EnergySumData = { total: {} };
|
||||
Object.entries(statIds).forEach(([key, subStatIds]) => {
|
||||
const totalStats: Record<number, number> = {};
|
||||
const sets: Record<string, Record<number, number>> = {};
|
||||
let sum = 0;
|
||||
subStatIds!.forEach((id) => {
|
||||
const stats = compare ? data.statsCompare[id] : data.stats[id];
|
||||
if (!stats) {
|
||||
return;
|
||||
}
|
||||
|
||||
const set = {};
|
||||
stats.forEach((stat) => {
|
||||
if (stat.change === null || stat.change === undefined) {
|
||||
return;
|
||||
}
|
||||
const val = stat.change;
|
||||
// Get total of solar and to grid to calculate the solar energy used
|
||||
sum += val;
|
||||
totalStats[stat.start] =
|
||||
stat.start in totalStats ? totalStats[stat.start] + val : val;
|
||||
});
|
||||
sets[id] = set;
|
||||
});
|
||||
summedData[key] = totalStats;
|
||||
summedData.total[key] = sum;
|
||||
});
|
||||
|
||||
return summedData;
|
||||
|
@ -11,10 +11,9 @@ import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import type { EnergyData } from "../../../../data/energy";
|
||||
import {
|
||||
energySourcesByType,
|
||||
getEnergyDataCollection,
|
||||
getSummedData,
|
||||
} from "../../../../data/energy";
|
||||
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { createEntityNotFoundWarning } from "../../components/hui-warning";
|
||||
@ -92,14 +91,9 @@ class HuiEnergyCarbonGaugeCard
|
||||
</hui-warning>`;
|
||||
}
|
||||
|
||||
const prefs = this._data.prefs;
|
||||
const types = energySourcesByType(prefs);
|
||||
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
||||
|
||||
const totalGridConsumption =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
||||
) ?? 0;
|
||||
const totalGridConsumption = summedData.total.from_grid ?? 0;
|
||||
|
||||
let value: number | undefined;
|
||||
|
||||
@ -111,18 +105,9 @@ class HuiEnergyCarbonGaugeCard
|
||||
)
|
||||
: 0;
|
||||
|
||||
const totalSolarProduction = types.solar
|
||||
? calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar.map((source) => source.stat_energy_from)
|
||||
) || 0
|
||||
: 0;
|
||||
const totalSolarProduction = summedData.total.solar ?? 0;
|
||||
|
||||
const totalGridReturned =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
||||
) || 0;
|
||||
const totalGridReturned = summedData.total.to_grid ?? 0;
|
||||
|
||||
const totalEnergyConsumed =
|
||||
totalGridConsumption +
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
getEnergyGasUnit,
|
||||
getEnergyWaterUnit,
|
||||
formatConsumptionShort,
|
||||
getSummedData,
|
||||
} from "../../../../data/energy";
|
||||
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
@ -109,11 +110,9 @@ class HuiEnergyDistrubutionCard
|
||||
const hasWater = types.water !== undefined;
|
||||
const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0;
|
||||
|
||||
const totalFromGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
||||
) ?? 0;
|
||||
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
||||
|
||||
const totalFromGrid = summedData.total.from_grid ?? 0;
|
||||
|
||||
let waterUsage: number | null = null;
|
||||
if (hasWater) {
|
||||
@ -136,37 +135,21 @@ class HuiEnergyDistrubutionCard
|
||||
let totalSolarProduction: number | null = null;
|
||||
|
||||
if (hasSolarProduction) {
|
||||
totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
totalSolarProduction = summedData.total.solar ?? 0;
|
||||
}
|
||||
|
||||
let totalBatteryIn: number | null = null;
|
||||
let totalBatteryOut: number | null = null;
|
||||
|
||||
if (hasBattery) {
|
||||
totalBatteryIn =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_to)
|
||||
) || 0;
|
||||
totalBatteryOut =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
totalBatteryIn = summedData.total.to_battery ?? 0;
|
||||
totalBatteryOut = summedData.total.from_battery ?? 0;
|
||||
}
|
||||
|
||||
let returnedToGrid: number | null = null;
|
||||
|
||||
if (hasReturnToGrid) {
|
||||
returnedToGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
||||
) || 0;
|
||||
returnedToGrid = summedData.total.to_grid ?? 0;
|
||||
}
|
||||
|
||||
let solarConsumption: number | null = null;
|
||||
|
@ -9,12 +9,11 @@ import "../../../../components/ha-gauge";
|
||||
import type { LevelDefinition } from "../../../../components/ha-gauge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import type {
|
||||
EnergyData,
|
||||
GridSourceTypeEnergyPreference,
|
||||
import type { EnergyData } from "../../../../data/energy";
|
||||
import {
|
||||
getEnergyDataCollection,
|
||||
getSummedData,
|
||||
} from "../../../../data/energy";
|
||||
import { getEnergyDataCollection } from "../../../../data/energy";
|
||||
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard } from "../../types";
|
||||
@ -75,27 +74,17 @@ class HuiEnergyGridGaugeCard
|
||||
"ui.panel.lovelace.cards.energy.loading"
|
||||
)}`;
|
||||
}
|
||||
|
||||
const prefs = this._data.prefs;
|
||||
const gridSource = prefs.energy_sources.find(
|
||||
(src) => src.type === "grid"
|
||||
) as GridSourceTypeEnergyPreference | undefined;
|
||||
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
||||
|
||||
let value: number | undefined;
|
||||
|
||||
if (!gridSource) {
|
||||
if (!("from_grid" in summedData.total)) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const consumedFromGrid = calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
gridSource.flow_from.map((flow) => flow.stat_energy_from)
|
||||
);
|
||||
const consumedFromGrid = summedData.total.from_grid ?? 0;
|
||||
|
||||
const returnedToGrid = calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
gridSource.flow_to.map((flow) => flow.stat_energy_to)
|
||||
);
|
||||
const returnedToGrid = summedData.total.to_grid ?? 0;
|
||||
|
||||
if (consumedFromGrid !== null && returnedToGrid !== null) {
|
||||
if (returnedToGrid > consumedFromGrid) {
|
||||
|
@ -8,9 +8,9 @@ import type { EnergyData } from "../../../../data/energy";
|
||||
import {
|
||||
energySourcesByType,
|
||||
getEnergyDataCollection,
|
||||
getSummedData,
|
||||
} from "../../../../data/energy";
|
||||
import {
|
||||
calculateStatisticsSumGrowth,
|
||||
calculateStatisticSumGrowth,
|
||||
getStatisticLabel,
|
||||
} from "../../../../data/recorder";
|
||||
@ -80,6 +80,7 @@ class HuiEnergySankeyCard
|
||||
|
||||
const prefs = this._data.prefs;
|
||||
const types = energySourcesByType(prefs);
|
||||
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
||||
|
||||
const computedStyle = getComputedStyle(this);
|
||||
|
||||
@ -98,11 +99,7 @@ class HuiEnergySankeyCard
|
||||
nodes.push(homeNode);
|
||||
|
||||
if (types.grid) {
|
||||
const totalFromGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
||||
) ?? 0;
|
||||
const totalFromGrid = summedData.total.from_grid ?? 0;
|
||||
|
||||
nodes.push({
|
||||
id: "grid",
|
||||
@ -125,11 +122,7 @@ class HuiEnergySankeyCard
|
||||
|
||||
// Add solar if available
|
||||
if (types.solar) {
|
||||
const totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
const totalSolarProduction = summedData.total.solar ?? 0;
|
||||
|
||||
nodes.push({
|
||||
id: "solar",
|
||||
@ -155,16 +148,8 @@ class HuiEnergySankeyCard
|
||||
|
||||
if (types.battery) {
|
||||
// Add battery source
|
||||
const totalBatteryOut =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
const totalBatteryIn =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery.map((source) => source.stat_energy_to)
|
||||
) || 0;
|
||||
const totalBatteryOut = summedData.total.from_battery ?? 0;
|
||||
const totalBatteryIn = summedData.total.to_battery ?? 0;
|
||||
const netBattery = totalBatteryOut - totalBatteryIn;
|
||||
const netBatteryOut = Math.max(netBattery, 0);
|
||||
const netBatteryIn = Math.max(-netBattery, 0);
|
||||
@ -209,11 +194,7 @@ class HuiEnergySankeyCard
|
||||
|
||||
// Add grid return if available
|
||||
if (types.grid && types.grid[0].flow_to) {
|
||||
const totalToGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid[0].flow_to.map((flow) => flow.stat_energy_to)
|
||||
) ?? 0;
|
||||
const totalToGrid = summedData.total.to_grid ?? 0;
|
||||
|
||||
nodes.push({
|
||||
id: "grid_return",
|
||||
|
@ -10,10 +10,9 @@ import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import type { EnergyData } from "../../../../data/energy";
|
||||
import {
|
||||
energySourcesByType,
|
||||
getEnergyDataCollection,
|
||||
getSummedData,
|
||||
} from "../../../../data/energy";
|
||||
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard } from "../../types";
|
||||
@ -75,56 +74,35 @@ class HuiEnergySelfSufficiencyGaugeCard
|
||||
)}`;
|
||||
}
|
||||
|
||||
const prefs = this._data.prefs;
|
||||
const types = energySourcesByType(prefs);
|
||||
|
||||
// The strategy only includes this card if we have a grid.
|
||||
const hasConsumption = true;
|
||||
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
||||
|
||||
const hasSolarProduction = types.solar !== undefined;
|
||||
const hasBattery = types.battery !== undefined;
|
||||
const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0;
|
||||
const hasSolarProduction = summedData.solar !== undefined;
|
||||
const hasBattery =
|
||||
summedData.to_battery !== undefined ||
|
||||
summedData.from_battery !== undefined;
|
||||
const hasReturnToGrid = summedData.to_grid !== undefined;
|
||||
|
||||
const totalFromGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
||||
) ?? 0;
|
||||
const totalFromGrid = summedData.total.from_grid ?? 0;
|
||||
|
||||
let totalSolarProduction: number | null = null;
|
||||
|
||||
if (hasSolarProduction) {
|
||||
totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
totalSolarProduction = summedData.total.solar ?? 0;
|
||||
}
|
||||
|
||||
let totalBatteryIn: number | null = null;
|
||||
let totalBatteryOut: number | null = null;
|
||||
|
||||
if (hasBattery) {
|
||||
totalBatteryIn =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_to)
|
||||
) || 0;
|
||||
totalBatteryOut =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
totalBatteryIn = summedData.total.to_battery ?? 0;
|
||||
totalBatteryOut = summedData.total.from_battery ?? 0;
|
||||
}
|
||||
|
||||
let returnedToGrid: number | null = null;
|
||||
|
||||
if (hasReturnToGrid) {
|
||||
returnedToGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
||||
) || 0;
|
||||
returnedToGrid = summedData.total.to_grid ?? 0;
|
||||
}
|
||||
|
||||
let solarConsumption: number | null = null;
|
||||
|
@ -9,10 +9,9 @@ import "../../../../components/ha-gauge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type { EnergyData } from "../../../../data/energy";
|
||||
import {
|
||||
energySourcesByType,
|
||||
getEnergyDataCollection,
|
||||
getSummedData,
|
||||
} from "../../../../data/energy";
|
||||
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard } from "../../types";
|
||||
@ -74,23 +73,14 @@ class HuiEnergySolarGaugeCard
|
||||
)}`;
|
||||
}
|
||||
|
||||
const prefs = this._data.prefs;
|
||||
const types = energySourcesByType(prefs);
|
||||
|
||||
if (!types.solar) {
|
||||
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
||||
if (!("solar" in summedData.total)) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
const totalSolarProduction = summedData.total.solar;
|
||||
|
||||
const productionReturnedToGrid = calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
||||
);
|
||||
const productionReturnedToGrid = summedData.total.to_grid ?? null;
|
||||
|
||||
let value: number | undefined;
|
||||
|
||||
|
@ -14,8 +14,11 @@ import { getEnergyColor } from "./common/color";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import type { EnergyData } from "../../../../data/energy";
|
||||
import { getEnergyDataCollection } from "../../../../data/energy";
|
||||
import type { EnergyData, EnergySumData } from "../../../../data/energy";
|
||||
import {
|
||||
getEnergyDataCollection,
|
||||
getSummedData,
|
||||
} from "../../../../data/energy";
|
||||
import type { Statistics, StatisticsMetaData } from "../../../../data/recorder";
|
||||
import { getStatisticLabel } from "../../../../data/recorder";
|
||||
import type { FrontendLocaleData } from "../../../../data/translation";
|
||||
@ -279,11 +282,14 @@ export class HuiEnergyUsageGraphCard
|
||||
this._compareStart = energyData.startCompare;
|
||||
this._compareEnd = energyData.endCompare;
|
||||
|
||||
const { summedData, compareSummedData } = getSummedData(energyData);
|
||||
|
||||
if (energyData.statsCompare) {
|
||||
datasets.push(
|
||||
...this._processDataSet(
|
||||
energyData.statsCompare,
|
||||
energyData.statsMetadata,
|
||||
compareSummedData!,
|
||||
statIds,
|
||||
colorIndices,
|
||||
computedStyles,
|
||||
@ -308,6 +314,7 @@ export class HuiEnergyUsageGraphCard
|
||||
...this._processDataSet(
|
||||
energyData.stats,
|
||||
energyData.statsMetadata,
|
||||
summedData,
|
||||
statIds,
|
||||
colorIndices,
|
||||
computedStyles,
|
||||
@ -325,6 +332,7 @@ export class HuiEnergyUsageGraphCard
|
||||
private _processDataSet(
|
||||
statistics: Statistics,
|
||||
statisticsMetaData: Record<string, StatisticsMetaData>,
|
||||
summedData: EnergySumData,
|
||||
statIdsByCat: {
|
||||
to_grid?: string[] | undefined;
|
||||
from_grid?: string[] | undefined;
|
||||
@ -352,24 +360,11 @@ export class HuiEnergyUsageGraphCard
|
||||
used_battery?: Record<string, Record<number, number>>;
|
||||
} = {};
|
||||
|
||||
const summedData: {
|
||||
to_grid?: Record<number, number>;
|
||||
from_grid?: Record<number, number>;
|
||||
to_battery?: Record<number, number>;
|
||||
from_battery?: Record<number, number>;
|
||||
solar?: Record<number, number>;
|
||||
} = {};
|
||||
|
||||
Object.entries(statIdsByCat).forEach(([key, statIds]) => {
|
||||
const sum = [
|
||||
"solar",
|
||||
"to_grid",
|
||||
"from_grid",
|
||||
"to_battery",
|
||||
"from_battery",
|
||||
].includes(key);
|
||||
const add = !["solar", "from_battery"].includes(key);
|
||||
const totalStats: Record<number, number> = {};
|
||||
if (!add) {
|
||||
return;
|
||||
}
|
||||
const sets: Record<string, Record<number, number>> = {};
|
||||
statIds!.forEach((id) => {
|
||||
const stats = statistics[id];
|
||||
@ -383,23 +378,13 @@ export class HuiEnergyUsageGraphCard
|
||||
return;
|
||||
}
|
||||
const val = stat.change;
|
||||
// Get total of solar and to grid to calculate the solar energy used
|
||||
if (sum) {
|
||||
totalStats[stat.start] =
|
||||
stat.start in totalStats ? totalStats[stat.start] + val : val;
|
||||
}
|
||||
if (add && !(stat.start in set)) {
|
||||
if (!(stat.start in set)) {
|
||||
set[stat.start] = val;
|
||||
}
|
||||
});
|
||||
sets[id] = set;
|
||||
});
|
||||
if (sum) {
|
||||
summedData[key] = totalStats;
|
||||
}
|
||||
if (add) {
|
||||
combinedData[key] = sets;
|
||||
}
|
||||
});
|
||||
|
||||
const grid_to_battery = {};
|
||||
|
Loading…
x
Reference in New Issue
Block a user