mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-30 04:36:36 +00:00
Add net energy support for grid
This commit is contained in:
parent
03dc3e52b7
commit
9978bec259
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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}>
|
||||
|
@ -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"
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user