From 9978bec259519086dc205bc810888bc54f878928 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 28 Sep 2021 19:55:51 +0200 Subject: [PATCH] Add net energy support for grid --- src/data/energy.ts | 103 +++++++++++- src/data/history.ts | 28 +++- .../components/ha-energy-grid-settings.ts | 36 ++++ .../energy/strategies/energy-strategy.ts | 3 +- .../hui-energy-carbon-consumed-gauge-card.ts | 5 +- .../energy/hui-energy-distribution-card.ts | 23 +-- .../hui-energy-grid-neutrality-gauge-card.ts | 13 +- .../hui-energy-solar-consumed-gauge-card.ts | 5 +- .../energy/hui-energy-sources-table-card.ts | 155 +++++++++++++++++- .../energy/hui-energy-usage-graph-card.ts | 136 ++++++++++++--- 10 files changed, 444 insertions(+), 63 deletions(-) diff --git a/src/data/energy.ts b/src/data/energy.ts index 6e1cb28796..f9da3110b6 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -11,7 +11,13 @@ import { subscribeOne } from "../common/util/subscribe-one"; import { HomeAssistant } from "../types"; import { ConfigEntry, getConfigEntries } from "./config_entries"; import { subscribeEntityRegistry } from "./entity_registry"; -import { fetchStatistics, Statistics } from "./history"; +import { + calculateStatisticsSumDecreaseGrowth, + calculateStatisticsSumGrowth, + calculateStatisticsSumIncreaseGrowth, + fetchStatistics, + Statistics, +} from "./history"; const energyCollectionKeys: (string | undefined)[] = []; @@ -38,6 +44,7 @@ export const emptyGridSourceEnergyPreference = type: "grid", flow_from: [], flow_to: [], + flow_net: [], cost_adjustment_day: 0, }); @@ -78,12 +85,12 @@ export interface DeviceConsumptionEnergyPreference { export interface FlowFromGridSourceEnergyPreference { // kWh meter stat_energy_from: string; + entity_energy_from: string | null; // $ meter stat_cost: string | null; // Can be used to generate costs if stat_cost omitted - entity_energy_from: string | null; entity_energy_price: string | null; number_energy_price: number | null; } @@ -91,21 +98,39 @@ export interface FlowFromGridSourceEnergyPreference { export interface FlowToGridSourceEnergyPreference { // kWh meter stat_energy_to: string; + entity_energy_to: string | null; // $ meter stat_compensation: string | null; // Can be used to generate costs if stat_cost omitted - entity_energy_to: string | null; entity_energy_price: string | null; number_energy_price: number | null; } +export interface FlowNetGridSourceEnergyPreference { + // kWh meter + stat_energy_net: string; + entity_energy_net: string | null; + + // $ meter to + stat_cost: string | null; + + // Can be used to generate to costs if stat_cost omitted + entity_energy_price_to: string | null; + number_energy_price_to: number | null; + + // Can be used to generate from costs if stat_cost omitted + entity_energy_price_from: string | null; + number_energy_price_from: number | null; +} + export interface GridSourceTypeEnergyPreference { type: "grid"; flow_from: FlowFromGridSourceEnergyPreference[]; flow_to: FlowToGridSourceEnergyPreference[]; + flow_net?: FlowNetGridSourceEnergyPreference[]; cost_adjustment_day: number; } @@ -149,7 +174,7 @@ export interface EnergyPreferences { } export interface EnergyInfo { - cost_sensors: Record; + cost_sensors: Record>; solar_forecast_domains: string[]; } @@ -263,7 +288,7 @@ const getEnergyData = async ( if (source.stat_cost) { statIDs.push(source.stat_cost); } - const costStatId = info.cost_sensors[source.stat_energy_from]; + const costStatId = info.cost_sensors[source.stat_energy_from].none; if (costStatId) { statIDs.push(costStatId); } @@ -282,7 +307,7 @@ const getEnergyData = async ( if (flowFrom.stat_cost) { statIDs.push(flowFrom.stat_cost); } - const costStatId = info.cost_sensors[flowFrom.stat_energy_from]; + const costStatId = info.cost_sensors[flowFrom.stat_energy_from].none; if (costStatId) { statIDs.push(costStatId); } @@ -292,11 +317,29 @@ const getEnergyData = async ( if (flowTo.stat_compensation) { statIDs.push(flowTo.stat_compensation); } - const costStatId = info.cost_sensors[flowTo.stat_energy_to]; + const costStatId = info.cost_sensors[flowTo.stat_energy_to].none; if (costStatId) { statIDs.push(costStatId); } } + if (source.flow_net) { + for (const flowTo of source.flow_net) { + statIDs.push(flowTo.stat_energy_net); + if (flowTo.stat_cost) { + statIDs.push(flowTo.stat_cost); + } + const costStatIdInc = + info.cost_sensors[flowTo.stat_energy_net].increase; + if (costStatIdInc) { + statIDs.push(costStatIdInc); + } + const costStatIdDec = + info.cost_sensors[flowTo.stat_energy_net].decrease; + if (costStatIdDec) { + statIDs.push(costStatIdDec); + } + } + } } const stats = await fetchStatistics(hass!, addHours(start, -1), end, statIDs); // Subtract 1 hour from start to get starting point data @@ -453,3 +496,49 @@ export const getEnergySolarForecasts = (hass: HomeAssistant) => hass.callWS({ type: "energy/solar_forecast", }); + +export const getTotalGridConsumption = ( + stats: Statistics, + gridSource: GridSourceTypeEnergyPreference +) => { + const consumedFromGrid = calculateStatisticsSumGrowth( + stats, + gridSource.flow_from.map((flow) => flow.stat_energy_from) + ); + + const consumedFromGridNetto = gridSource.flow_net + ? calculateStatisticsSumIncreaseGrowth( + stats, + gridSource.flow_net.map((flow) => flow.stat_energy_net) + ) ?? 0 + : null; + + if (consumedFromGrid === null && consumedFromGridNetto === null) { + return null; + } + + return (consumedFromGrid || 0) + (consumedFromGridNetto || 0); +}; + +export const getTotalGridReturn = ( + stats: Statistics, + gridSource: GridSourceTypeEnergyPreference +) => { + const returnedToGrid = calculateStatisticsSumGrowth( + stats, + gridSource.flow_to.map((flow) => flow.stat_energy_to) + ); + + const returnedToGridNetto = gridSource.flow_net + ? calculateStatisticsSumDecreaseGrowth( + stats, + gridSource.flow_net.map((flow) => flow.stat_energy_net) + ) ?? 0 + : null; + + if (returnedToGrid === null && returnedToGridNetto === null) { + return null; + } + + return (returnedToGrid || 0) + (returnedToGridNetto || 0); +}; diff --git a/src/data/history.ts b/src/data/history.ts index 4e55ab711d..9f3b33e645 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -68,6 +68,8 @@ export interface StatisticValue { mean: number | null; min: number | null; sum: number | null; + sum_decrease?: number; + sum_increase?: number; state: number | null; } @@ -306,17 +308,18 @@ export const fetchStatistics = ( }); export const calculateStatisticSumGrowth = ( - values: StatisticValue[] + values: StatisticValue[], + sumKey: "sum" | "sum_increase" | "sum_decrease" = "sum" ): number | null => { if (!values || values.length < 2) { return null; } - const endSum = values[values.length - 1].sum; - if (endSum === null) { + const endSum = values[values.length - 1][sumKey]; + if (endSum === null || endSum === undefined) { return null; } - const startSum = values[0].sum; - if (startSum === null) { + const startSum = values[0][sumKey]; + if (startSum === null || startSum === undefined) { return endSum; } return endSum - startSum; @@ -324,7 +327,8 @@ export const calculateStatisticSumGrowth = ( export const calculateStatisticsSumGrowth = ( data: Statistics, - stats: string[] + stats: string[], + sumKey: "sum" | "sum_increase" | "sum_decrease" = "sum" ): number | null => { let totalGrowth: number | null = null; @@ -332,7 +336,7 @@ export const calculateStatisticsSumGrowth = ( if (!(stat in data)) { continue; } - const statGrowth = calculateStatisticSumGrowth(data[stat]); + const statGrowth = calculateStatisticSumGrowth(data[stat], sumKey); if (statGrowth === null) { continue; @@ -347,6 +351,16 @@ export const calculateStatisticsSumGrowth = ( return totalGrowth; }; +export const calculateStatisticsSumIncreaseGrowth = ( + data: Statistics, + stats: string[] +): number | null => calculateStatisticsSumGrowth(data, stats, "sum_increase"); + +export const calculateStatisticsSumDecreaseGrowth = ( + data: Statistics, + stats: string[] +): number | null => calculateStatisticsSumGrowth(data, stats, "sum_decrease"); + export const statisticsHaveType = ( stats: StatisticValue[], type: StatisticType diff --git a/src/panels/config/energy/components/ha-energy-grid-settings.ts b/src/panels/config/energy/components/ha-energy-grid-settings.ts index 6f59fe6a18..b857ad41f9 100644 --- a/src/panels/config/energy/components/ha-energy-grid-settings.ts +++ b/src/panels/config/energy/components/ha-energy-grid-settings.ts @@ -3,6 +3,7 @@ import { mdiDelete, mdiHomeExportOutline, mdiHomeImportOutline, + mdiHomePlusOutline, mdiPencil, mdiTransmissionTower, } from "@mdi/js"; @@ -173,6 +174,41 @@ export class EnergyGridSettings extends LitElement { Add return +

+ If your meter goes down when your return to the grid, add a net + source. +

+

Combined consumption and return

+ ${gridSource.flow_net?.map((flow) => { + const entityState = this.hass.states[flow.stat_energy_net]; + return html` +
+ ${entityState?.attributes.icon + ? html`` + : html``} + ${entityState + ? computeStateName(entityState) + : flow.stat_energy_net} + + + + + + +
+ `; + })} +
+ + Add net +
+

Grid carbon footprint

${this._configEntries?.map( (entry) => html`
diff --git a/src/panels/energy/strategies/energy-strategy.ts b/src/panels/energy/strategies/energy-strategy.ts index 60485da740..e15db35309 100644 --- a/src/panels/energy/strategies/energy-strategy.ts +++ b/src/panels/energy/strategies/energy-strategy.ts @@ -46,7 +46,8 @@ export class EnergyStrategy { const hasGrid = prefs.energy_sources.find( (source) => source.type === "grid" ) as GridSourceTypeEnergyPreference; - const hasReturn = hasGrid && hasGrid.flow_to.length; + const hasReturn = + hasGrid && (hasGrid.flow_to.length || hasGrid.flow_net?.length); const hasSolar = prefs.energy_sources.some( (source) => source.type === "solar" ); diff --git a/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts index a64d0864e6..7398fdee23 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts @@ -12,6 +12,7 @@ import { EnergyData, energySourcesByType, getEnergyDataCollection, + getTotalGridConsumption, } from "../../../../data/energy"; import { calculateStatisticsSumGrowth, @@ -77,9 +78,9 @@ class HuiEnergyCarbonGaugeCard const prefs = this._data.prefs; const types = energySourcesByType(prefs); - const totalGridConsumption = calculateStatisticsSumGrowth( + const totalGridConsumption = getTotalGridConsumption( this._data.stats, - types.grid![0].flow_from.map((flow) => flow.stat_energy_from) + types.grid![0] ); let value: number | undefined; diff --git a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts index 6990690623..37cc9818c9 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts @@ -1,3 +1,4 @@ +import "@material/mwc-button"; import { mdiArrowDown, mdiArrowLeft, @@ -14,7 +15,6 @@ import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, html, LitElement, svg } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import "@material/mwc-button"; import { formatNumber } from "../../../../common/string/format_number"; import "../../../../components/ha-card"; import "../../../../components/ha-svg-icon"; @@ -22,6 +22,8 @@ import { EnergyData, energySourcesByType, getEnergyDataCollection, + getTotalGridConsumption, + getTotalGridReturn, } from "../../../../data/energy"; import { calculateStatisticsSumGrowth, @@ -81,13 +83,12 @@ class HuiEnergyDistrubutionCard const hasSolarProduction = types.solar !== undefined; const hasBattery = types.battery !== undefined; const hasGas = types.gas !== undefined; - const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0; + const hasReturnToGrid = + hasConsumption && + (types.grid![0].flow_to.length || types.grid![0].flow_net?.length); const totalFromGrid = - calculateStatisticsSumGrowth( - this._data.stats, - types.grid![0].flow_from.map((flow) => flow.stat_energy_from) - ) ?? 0; + getTotalGridConsumption(this._data.stats, types.grid![0]) ?? 0; let gasUsage: number | null = null; if (hasGas) { @@ -128,10 +129,7 @@ class HuiEnergyDistrubutionCard if (hasReturnToGrid) { returnedToGrid = - calculateStatisticsSumGrowth( - this._data.stats, - types.grid![0].flow_to.map((flow) => flow.stat_energy_to) - ) || 0; + getTotalGridReturn(this._data.stats, types.grid![0]) ?? 0; } let solarConsumption: number | null = null; @@ -217,6 +215,11 @@ class HuiEnergyDistrubutionCard .grid![0].flow_from.map( (flow) => this._data!.stats[flow.stat_energy_from] ) + .concat( + types.grid![0].flow_net?.map( + (flow) => this._data!.stats[flow.stat_energy_net] + ) || [] + ) .filter(Boolean) ); diff --git a/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts index a2df7e1b83..3bb9a29ff7 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts @@ -11,9 +11,11 @@ import type { LevelDefinition } from "../../../../components/ha-gauge"; import { EnergyData, getEnergyDataCollection, + getTotalGridConsumption, + getTotalGridReturn, GridSourceTypeEnergyPreference, } from "../../../../data/energy"; -import { calculateStatisticsSumGrowth } from "../../../../data/history"; + import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; import type { HomeAssistant } from "../../../../types"; import type { LovelaceCard } from "../../types"; @@ -75,15 +77,12 @@ class HuiEnergyGridGaugeCard return html``; } - const consumedFromGrid = calculateStatisticsSumGrowth( + const consumedFromGrid = getTotalGridConsumption( this._data.stats, - gridSource.flow_from.map((flow) => flow.stat_energy_from) + gridSource ); - const returnedToGrid = calculateStatisticsSumGrowth( - this._data.stats, - gridSource.flow_to.map((flow) => flow.stat_energy_to) - ); + const returnedToGrid = getTotalGridReturn(this._data.stats, gridSource); if (consumedFromGrid !== null && returnedToGrid !== null) { if (returnedToGrid > consumedFromGrid) { diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts index 6f54631658..1cb344e8e3 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts @@ -11,6 +11,7 @@ import { EnergyData, energySourcesByType, getEnergyDataCollection, + getTotalGridReturn, } from "../../../../data/energy"; import { calculateStatisticsSumGrowth } from "../../../../data/history"; import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; @@ -69,9 +70,9 @@ class HuiEnergySolarGaugeCard types.solar.map((source) => source.stat_energy_from) ); - const productionReturnedToGrid = calculateStatisticsSumGrowth( + const productionReturnedToGrid = getTotalGridReturn( this._data.stats, - types.grid![0].flow_to.map((flow) => flow.stat_energy_to) + types.grid![0] ); let value: number | undefined; diff --git a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts index 46b4adf9c8..2bc0e858ed 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts @@ -111,12 +111,20 @@ export class HuiEnergySourcesTableCard flow.entity_energy_price || flow.number_energy_price ) || + types.grid?.[0].flow_net?.some( + (flow) => + flow.stat_cost || + flow.entity_energy_price_from || + flow.number_energy_price_from || + flow.entity_energy_price_to || + flow.number_energy_price_to + ) || types.gas?.some( (flow) => flow.stat_cost || flow.entity_energy_price || flow.number_energy_price ); - return html` + return html` ${this._config.title ? html`

${this._config.title}

` : ""} @@ -309,7 +317,7 @@ export class HuiEnergySourcesTableCard totalGrid += energy; const cost_stat = flow.stat_cost || - this._data!.info.cost_sensors[flow.stat_energy_from]; + this._data!.info.cost_sensors[flow.stat_energy_from].none; const cost = cost_stat ? calculateStatisticSumGrowth( this._data!.stats[cost_stat] @@ -369,7 +377,7 @@ export class HuiEnergySourcesTableCard totalGrid += energy; const cost_stat = flow.stat_compensation || - this._data!.info.cost_sensors[flow.stat_energy_to]; + this._data!.info.cost_sensors[flow.stat_energy_to].none; const cost = cost_stat ? (calculateStatisticSumGrowth( this._data!.stats[cost_stat] @@ -415,6 +423,145 @@ export class HuiEnergySourcesTableCard ` : ""} `; + })} + ${source.flow_net?.map((flow, idx) => { + const entity = this.hass.states[flow.stat_energy_net]; + const energy_from = + calculateStatisticSumGrowth( + this._data!.stats[flow.stat_energy_net], + "sum_increase" + ) || 0; + const energy_to = + (calculateStatisticSumGrowth( + this._data!.stats[flow.stat_energy_net], + "sum_decrease" + ) || 0) * -1; + totalGrid += energy_from + energy_to; + let costIncrease: number | null = null; + let costDecrease: number | null = null; + if (flow.stat_cost) { + costIncrease = + calculateStatisticSumGrowth( + this._data!.stats[flow.stat_cost], + "sum_increase" + ) || 0; + costDecrease = + (calculateStatisticSumGrowth( + this._data!.stats[flow.stat_cost], + "sum_decrease" + ) || 0) * 1; + } else { + const cost_stat_increase = + this._data!.info.cost_sensors[flow.stat_energy_net] + .increase; + const cost_stat_decrease = + this._data!.info.cost_sensors[flow.stat_energy_net] + .decrease; + costIncrease = cost_stat_increase + ? calculateStatisticSumGrowth( + this._data!.stats[cost_stat_increase] + ) || 0 + : null; + costDecrease = cost_stat_decrease + ? (calculateStatisticSumGrowth( + this._data!.stats[cost_stat_decrease] + ) || 0) * -1 + : null; + } + if (costIncrease !== null) { + totalGridCost += costIncrease; + } + if (costDecrease !== null) { + totalGridCost += costDecrease; + } + const colorFrom = + idx > 0 + ? rgb2hex( + lab2rgb( + labDarken( + rgb2lab(hex2rgb(consumptionColor)), + source.flow_from.length + idx + ) + ) + ) + : returnColor; + const colorTo = + idx > 0 + ? rgb2hex( + lab2rgb( + labDarken( + rgb2lab(hex2rgb(returnColor)), + source.flow_to.length + idx + ) + ) + ) + : returnColor; + return html` + +
+ + + ${entity + ? computeStateName(entity) + : flow.stat_energy_net} + + + ${formatNumber(energy_from, this.hass.locale)} kWh + + ${showCosts + ? html` + ${costIncrease !== null + ? formatNumber(costIncrease, this.hass.locale, { + style: "currency", + currency: this.hass.config.currency!, + }) + : ""} + ` + : ""} + + + +
+ + + ${entity + ? computeStateName(entity) + : flow.stat_energy_net} + + + ${formatNumber(energy_to, this.hass.locale)} kWh + + ${showCosts + ? html` + ${costDecrease !== null + ? formatNumber(costDecrease, this.hass.locale, { + style: "currency", + currency: this.hass.config.currency!, + }) + : ""} + ` + : ""} + `; })}` )} ${types.grid @@ -447,7 +594,7 @@ export class HuiEnergySourcesTableCard totalGas += energy; const cost_stat = source.stat_cost || - this._data!.info.cost_sensors[source.stat_energy_from]; + this._data!.info.cost_sensors[source.stat_energy_from].none; const cost = cost_stat ? calculateStatisticSumGrowth(this._data!.stats[cost_stat]) || 0 diff --git a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts index bc807a2e04..a9327a311e 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts @@ -231,6 +231,7 @@ export class HuiEnergyUsageGraphCard const statistics: { to_grid?: string[]; from_grid?: string[]; + net_grid?: string[]; solar?: string[]; to_battery?: string[]; from_battery?: string[]; @@ -276,6 +277,15 @@ export class HuiEnergyUsageGraphCard statistics.to_grid = [flowTo.stat_energy_to]; } } + if (source.flow_net) { + for (const flowNet of source.flow_net) { + if (statistics.net_grid) { + statistics.net_grid.push(flowNet.stat_energy_net); + } else { + statistics.net_grid = [flowNet.stat_energy_net]; + } + } + } } const dayDifference = differenceInDays( @@ -325,6 +335,8 @@ export class HuiEnergyUsageGraphCard to_battery?: { [start: string]: number }; from_battery?: { [start: string]: number }; solar?: { [start: string]: number }; + net_grid_increased?: { [start: string]: number }; + net_grid_decreased?: { [start: string]: number }; } = {}; const computedStyles = getComputedStyle(this); @@ -366,9 +378,15 @@ export class HuiEnergyUsageGraphCard "to_battery", "from_battery", ].includes(key); - const add = !["solar", "from_battery"].includes(key); + const add = !["solar", "from_battery", "net_grid"].includes(key); const totalStats: { [start: string]: number } = {}; + const totalStatsInc: { [start: string]: number } = {}; + const totalStatsDec: { [start: string]: number } = {}; const sets: { [statId: string]: { [start: string]: number } } = {}; + const setsNet: { + setInc: { [statId: string]: { [start: string]: number } }; + setDec: { [statId: string]: { [start: string]: number } }; + } = { setInc: {}, setDec: {} }; statIds!.forEach((id) => { const stats = dayDifference > 35 @@ -380,39 +398,111 @@ export class HuiEnergyUsageGraphCard return; } - const set = {}; - let prevValue: number; - stats.forEach((stat) => { - if (stat.sum === null) { - return; - } - if (prevValue === undefined) { + if (key === "net_grid") { + const setInc = {}; + const setDec = {}; + let prevValueInc: number; + let prevValueDec: number; + stats.forEach((stat) => { + if ( + stat.sum_decrease === null || + stat.sum_decrease === undefined || + stat.sum_increase === null || + stat.sum_increase === undefined + ) { + return; + } + if (prevValueInc === undefined) { + prevValueInc = stat.sum_increase; + prevValueDec = stat.sum_decrease; + return; + } + const valIncrease = stat.sum_increase - prevValueInc; + const valDecrease = stat.sum_decrease - prevValueDec; + totalStatsInc[stat.start] = + stat.start in totalStatsInc + ? totalStatsInc[stat.start] + valIncrease + : valIncrease; + totalStatsDec[stat.start] = + stat.start in totalStatsDec + ? totalStatsDec[stat.start] + valDecrease + : valDecrease; + if (!(stat.start in setInc)) { + setInc[stat.start] = valIncrease; + setDec[stat.start] = valDecrease; + } + prevValueInc = stat.sum_increase; + prevValueDec = stat.sum_decrease; + }); + setsNet.setInc[id] = setInc; + setsNet.setDec[id] = setDec; + } else { + const set = {}; + let prevValue: number; + stats.forEach((stat) => { + if (stat.sum === null) { + return; + } + if (prevValue === undefined) { + prevValue = stat.sum; + return; + } + const val = stat.sum - prevValue; + if (sum) { + totalStats[stat.start] = + stat.start in totalStats ? totalStats[stat.start] + val : val; + } + if (add && !(stat.start in set)) { + set[stat.start] = val; + } prevValue = stat.sum; - return; - } - const val = stat.sum - prevValue; - // 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)) { - set[stat.start] = val; - } - prevValue = stat.sum; - }); - sets[id] = set; + }); + sets[id] = set; + } }); + if (key === "net_grid") { + combinedData.from_grid = { + ...combinedData.from_grid, + ...setsNet.setInc, + }; + combinedData.to_grid = { ...combinedData.to_grid, ...setsNet.setDec }; + summedData.net_grid_increased = totalStatsInc; + summedData.net_grid_decreased = totalStatsDec; + return; + } if (sum) { summedData[key] = totalStats; } if (add) { - combinedData[key] = sets; + combinedData[key] = { ...combinedData[key], ...sets }; } }); + if (summedData.net_grid_increased && summedData.net_grid_decreased) { + if (!summedData.to_grid && !summedData.from_grid) { + summedData.to_grid = summedData.net_grid_increased; + summedData.from_grid = summedData.net_grid_decreased; + } else { + if (!summedData.to_grid) { + summedData.to_grid = {}; + } + if (!summedData.from_grid) { + summedData.from_grid = {}; + } + for (const start of Object.keys(summedData.net_grid_increased)) { + summedData.to_grid[start] = + (summedData.to_grid[start] || 0) + + summedData.net_grid_increased[start]; + summedData.from_grid[start] = + (summedData.from_grid[start] || 0) + + summedData.net_grid_decreased[start]; + } + } + } + const grid_to_battery = {}; const battery_to_grid = {}; + if ((summedData.to_grid || summedData.to_battery) && summedData.solar) { const used_solar = {}; for (const start of Object.keys(summedData.solar)) {