Add net energy support for grid

This commit is contained in:
Bram Kragten 2021-09-28 19:55:51 +02:00
parent 03dc3e52b7
commit 9978bec259
No known key found for this signature in database
GPG Key ID: FBE2DFDB363EF55B
10 changed files with 444 additions and 63 deletions

View File

@ -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<string, string>;
cost_sensors: Record<string, Record<string, string>>;
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<EnergySolarForecasts>({
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);
};

View File

@ -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

View File

@ -3,6 +3,7 @@ import {
mdiDelete,
mdiHomeExportOutline,
mdiHomeImportOutline,
mdiHomePlusOutline,
mdiPencil,
mdiTransmissionTower,
} from "@mdi/js";
@ -173,6 +174,41 @@ export class EnergyGridSettings extends LitElement {
<mwc-button @click=${this._addToSource}>Add return</mwc-button>
</div>
<p>
If your meter goes down when your return to the grid, add a net
source.
</p>
<h3>Combined consumption and return</h3>
${gridSource.flow_net?.map((flow) => {
const entityState = this.hass.states[flow.stat_energy_net];
return html`
<div class="row" .source=${flow}>
${entityState?.attributes.icon
? html`<ha-icon
.icon=${entityState.attributes.icon}
></ha-icon>`
: html`<ha-svg-icon
.path=${mdiHomePlusOutline}
></ha-svg-icon>`}
<span class="content"
>${entityState
? computeStateName(entityState)
: flow.stat_energy_net}</span
>
<mwc-icon-button @click=${this._editToSource}>
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
</mwc-icon-button>
<mwc-icon-button @click=${this._deleteToSource}>
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button>
</div>
`;
})}
<div class="row border-bottom">
<ha-svg-icon .path=${mdiHomePlusOutline}></ha-svg-icon>
<mwc-button @click=${this._addToSource}>Add net</mwc-button>
</div>
<h3>Grid carbon footprint</h3>
${this._configEntries?.map(
(entry) => html`<div class="row" .entry=${entry}>

View File

@ -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"
);

View File

@ -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;

View File

@ -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)
);

View File

@ -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) {

View File

@ -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;

View File

@ -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` <ha-card>
return html`<ha-card>
${this._config.title
? html`<h1 class="card-header">${this._config.title}</h1>`
: ""}
@ -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
</td>`
: ""}
</tr>`;
})}
${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`<tr class="mdc-data-table__row">
<td class="mdc-data-table__cell cell-bullet">
<div
class="bullet"
style=${styleMap({
borderColor: colorFrom,
backgroundColor: colorFrom + "7F",
})}
></div>
</td>
<th class="mdc-data-table__cell" scope="row">
${entity
? computeStateName(entity)
: flow.stat_energy_net}
</th>
<td
class="mdc-data-table__cell mdc-data-table__cell--numeric"
>
${formatNumber(energy_from, this.hass.locale)} kWh
</td>
${showCosts
? html` <td
class="mdc-data-table__cell mdc-data-table__cell--numeric"
>
${costIncrease !== null
? formatNumber(costIncrease, this.hass.locale, {
style: "currency",
currency: this.hass.config.currency!,
})
: ""}
</td>`
: ""}
</tr>
<tr class="mdc-data-table__row">
<td class="mdc-data-table__cell cell-bullet">
<div
class="bullet"
style=${styleMap({
borderColor: colorTo,
backgroundColor: colorTo + "7F",
})}
></div>
</td>
<th class="mdc-data-table__cell" scope="row">
${entity
? computeStateName(entity)
: flow.stat_energy_net}
</th>
<td
class="mdc-data-table__cell mdc-data-table__cell--numeric"
>
${formatNumber(energy_to, this.hass.locale)} kWh
</td>
${showCosts
? html` <td
class="mdc-data-table__cell mdc-data-table__cell--numeric"
>
${costDecrease !== null
? formatNumber(costDecrease, this.hass.locale, {
style: "currency",
currency: this.hass.config.currency!,
})
: ""}
</td>`
: ""}
</tr>`;
})}`
)}
${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

View File

@ -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)) {