mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-28 07:17:21 +00:00
Update energy dashboard (#9624)
This commit is contained in:
parent
0c0091375c
commit
73b9b87ef3
@ -272,7 +272,7 @@ export default class HaChartBase extends LitElement {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin-right: 4px;
|
margin-right: 6px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -280,9 +280,10 @@ export default class HaChartBase extends LitElement {
|
|||||||
.chartTooltip .bullet {
|
.chartTooltip .bullet {
|
||||||
align-self: baseline;
|
align-self: baseline;
|
||||||
}
|
}
|
||||||
|
:host([rtl]) .chartLegend .bullet,
|
||||||
:host([rtl]) .chartTooltip .bullet {
|
:host([rtl]) .chartTooltip .bullet {
|
||||||
margin-right: inherit;
|
margin-right: inherit;
|
||||||
margin-left: 4px;
|
margin-left: 6px;
|
||||||
}
|
}
|
||||||
.chartTooltip {
|
.chartTooltip {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
@ -314,6 +315,7 @@ export default class HaChartBase extends LitElement {
|
|||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
.chartTooltip .title {
|
.chartTooltip .title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -85,7 +85,8 @@ export class EnergyStrategy {
|
|||||||
// Only include if we have a grid.
|
// Only include if we have a grid.
|
||||||
if (hasGrid) {
|
if (hasGrid) {
|
||||||
view.cards!.push({
|
view.cards!.push({
|
||||||
type: "energy-usage",
|
title: "Energy distribution",
|
||||||
|
type: "energy-distribution",
|
||||||
prefs: energyPrefs,
|
prefs: energyPrefs,
|
||||||
view_layout: { position: "sidebar" },
|
view_layout: { position: "sidebar" },
|
||||||
});
|
});
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { round } from "../../../common/number/round";
|
import { round } from "../../../../common/number/round";
|
||||||
import { subscribeOne } from "../../../common/util/subscribe-one";
|
import { subscribeOne } from "../../../../common/util/subscribe-one";
|
||||||
import "../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../components/ha-gauge";
|
import "../../../../components/ha-gauge";
|
||||||
import { getConfigEntries } from "../../../data/config_entries";
|
import { getConfigEntries } from "../../../../data/config_entries";
|
||||||
import { energySourcesByType } from "../../../data/energy";
|
import { energySourcesByType } from "../../../../data/energy";
|
||||||
import { subscribeEntityRegistry } from "../../../data/entity_registry";
|
import { subscribeEntityRegistry } from "../../../../data/entity_registry";
|
||||||
import {
|
import {
|
||||||
calculateStatisticsSumGrowth,
|
calculateStatisticsSumGrowth,
|
||||||
fetchStatistics,
|
fetchStatistics,
|
||||||
Statistics,
|
Statistics,
|
||||||
} from "../../../data/history";
|
} from "../../../../data/history";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../../components/hui-warning";
|
||||||
import type { LovelaceCard } from "../types";
|
import type { LovelaceCard } from "../../types";
|
||||||
import { severityMap } from "./hui-gauge-card";
|
import { severityMap } from "../hui-gauge-card";
|
||||||
import type { EnergyCarbonGaugeCardConfig } from "./types";
|
import type { EnergyCarbonGaugeCardConfig } from "../types";
|
||||||
|
|
||||||
@customElement("hui-energy-carbon-consumed-gauge-card")
|
@customElement("hui-energy-carbon-consumed-gauge-card")
|
||||||
class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard {
|
class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard {
|
||||||
@ -103,7 +103,7 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
(totalSolarProduction || 0) -
|
(totalSolarProduction || 0) -
|
||||||
(totalGridReturned || 0);
|
(totalGridReturned || 0);
|
||||||
|
|
||||||
value = round((highCarbonEnergy / totalEnergyConsumed) * 100);
|
value = round((1 - highCarbonEnergy / totalEnergyConsumed) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -116,23 +116,23 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
.locale=${this.hass!.locale}
|
.locale=${this.hass!.locale}
|
||||||
label="%"
|
label="%"
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
"--gauge-color": this._computeSeverity(64),
|
"--gauge-color": this._computeSeverity(value),
|
||||||
})}
|
})}
|
||||||
></ha-gauge>
|
></ha-gauge>
|
||||||
<div class="name">High-carbon energy consumed</div>`
|
<div class="name">Non-fossil energy consumed</div>`
|
||||||
: html`Consumed high-carbon energy couldn't be calculated`}
|
: html`Consumed non-fossil energy couldn't be calculated`}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeSeverity(numberValue: number): string {
|
private _computeSeverity(numberValue: number): string {
|
||||||
if (numberValue > 50) {
|
if (numberValue < 10) {
|
||||||
return severityMap.red;
|
return severityMap.red;
|
||||||
}
|
}
|
||||||
if (numberValue > 30) {
|
if (numberValue < 30) {
|
||||||
return severityMap.yellow;
|
return severityMap.yellow;
|
||||||
}
|
}
|
||||||
if (numberValue < 10) {
|
if (numberValue > 75) {
|
||||||
return severityMap.green;
|
return severityMap.green;
|
||||||
}
|
}
|
||||||
return severityMap.normal;
|
return severityMap.normal;
|
@ -9,23 +9,23 @@ import {
|
|||||||
unsafeCSS,
|
unsafeCSS,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import { round } from "../../../common/number/round";
|
import { round } from "../../../../common/number/round";
|
||||||
import "../../../components/chart/statistics-chart";
|
import "../../../../components/chart/statistics-chart";
|
||||||
import "../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
EnergyInfo,
|
EnergyInfo,
|
||||||
getEnergyInfo,
|
getEnergyInfo,
|
||||||
GridSourceTypeEnergyPreference,
|
GridSourceTypeEnergyPreference,
|
||||||
} from "../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
import {
|
import {
|
||||||
calculateStatisticSumGrowth,
|
calculateStatisticSumGrowth,
|
||||||
fetchStatistics,
|
fetchStatistics,
|
||||||
Statistics,
|
Statistics,
|
||||||
} from "../../../data/history";
|
} from "../../../../data/history";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { EnergyDevicesGraphCardConfig } from "./types";
|
import { EnergyDevicesGraphCardConfig } from "../types";
|
||||||
|
|
||||||
@customElement("hui-energy-costs-table-card")
|
@customElement("hui-energy-costs-table-card")
|
||||||
export class HuiEnergyCostsTableCard
|
export class HuiEnergyCostsTableCard
|
@ -14,18 +14,18 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { getColorByIndex } from "../../../common/color/colors";
|
import { getColorByIndex } from "../../../../common/color/colors";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import "../../../components/chart/ha-chart-base";
|
import "../../../../components/chart/ha-chart-base";
|
||||||
import "../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
calculateStatisticSumGrowth,
|
calculateStatisticSumGrowth,
|
||||||
fetchStatistics,
|
fetchStatistics,
|
||||||
Statistics,
|
Statistics,
|
||||||
} from "../../../data/history";
|
} from "../../../../data/history";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { EnergyDevicesGraphCardConfig } from "./types";
|
import { EnergyDevicesGraphCardConfig } from "../types";
|
||||||
|
|
||||||
@customElement("hui-energy-devices-graph-card")
|
@customElement("hui-energy-devices-graph-card")
|
||||||
export class HuiEnergyDevicesGraphCard
|
export class HuiEnergyDevicesGraphCard
|
505
src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts
Normal file
505
src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
import {
|
||||||
|
mdiArrowLeft,
|
||||||
|
mdiArrowRight,
|
||||||
|
mdiHome,
|
||||||
|
mdiLeaf,
|
||||||
|
mdiSolarPower,
|
||||||
|
mdiTransmissionTower,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import { css, html, LitElement, svg } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { round } from "../../../../common/number/round";
|
||||||
|
import { subscribeOne } from "../../../../common/util/subscribe-one";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import { getConfigEntries } from "../../../../data/config_entries";
|
||||||
|
import { energySourcesByType } from "../../../../data/energy";
|
||||||
|
import { subscribeEntityRegistry } from "../../../../data/entity_registry";
|
||||||
|
import {
|
||||||
|
calculateStatisticsSumGrowth,
|
||||||
|
fetchStatistics,
|
||||||
|
Statistics,
|
||||||
|
} from "../../../../data/history";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { LovelaceCard } from "../../types";
|
||||||
|
import { EnergyDistributionCardConfig } from "../types";
|
||||||
|
|
||||||
|
const CIRCLE_CIRCUMFERENCE = 238.76104;
|
||||||
|
|
||||||
|
@customElement("hui-energy-distribution-card")
|
||||||
|
class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: EnergyDistributionCardConfig;
|
||||||
|
|
||||||
|
@state() private _stats?: Statistics;
|
||||||
|
|
||||||
|
@state() private _co2SignalEntity?: string;
|
||||||
|
|
||||||
|
private _fetching = false;
|
||||||
|
|
||||||
|
public setConfig(config: EnergyDistributionCardConfig): void {
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCardSize(): Promise<number> | number {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public willUpdate(changedProps) {
|
||||||
|
super.willUpdate(changedProps);
|
||||||
|
|
||||||
|
if (!this._fetching && !this._stats) {
|
||||||
|
this._fetching = true;
|
||||||
|
Promise.all([this._getStatistics(), this._fetchCO2SignalEntity()]).then(
|
||||||
|
() => {
|
||||||
|
this._fetching = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._config) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._stats) {
|
||||||
|
return html`Loading…`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefs = this._config!.prefs;
|
||||||
|
const types = energySourcesByType(prefs);
|
||||||
|
|
||||||
|
// The strategy only includes this card if we have a grid.
|
||||||
|
const hasConsumption = true;
|
||||||
|
|
||||||
|
const hasSolarProduction = types.solar !== undefined;
|
||||||
|
const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0;
|
||||||
|
|
||||||
|
const totalGridConsumption =
|
||||||
|
calculateStatisticsSumGrowth(
|
||||||
|
this._stats,
|
||||||
|
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
||||||
|
) ?? 0;
|
||||||
|
|
||||||
|
let totalSolarProduction: number | null = null;
|
||||||
|
|
||||||
|
if (hasSolarProduction) {
|
||||||
|
totalSolarProduction = calculateStatisticsSumGrowth(
|
||||||
|
this._stats,
|
||||||
|
types.solar!.map((source) => source.stat_energy_from)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let productionReturnedToGrid: number | null = null;
|
||||||
|
|
||||||
|
if (hasReturnToGrid) {
|
||||||
|
productionReturnedToGrid = calculateStatisticsSumGrowth(
|
||||||
|
this._stats,
|
||||||
|
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// total consumption = consumption_from_grid + solar_production - return_to_grid
|
||||||
|
|
||||||
|
let co2percentage: number | undefined;
|
||||||
|
|
||||||
|
if (this._co2SignalEntity) {
|
||||||
|
const co2State = this.hass.states[this._co2SignalEntity];
|
||||||
|
if (co2State) {
|
||||||
|
co2percentage = Number(co2State.state);
|
||||||
|
if (isNaN(co2percentage)) {
|
||||||
|
co2percentage = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalConsumption =
|
||||||
|
totalGridConsumption +
|
||||||
|
(totalSolarProduction || 0) -
|
||||||
|
(productionReturnedToGrid || 0);
|
||||||
|
|
||||||
|
let homeSolarCircumference: number | undefined;
|
||||||
|
if (hasSolarProduction) {
|
||||||
|
const homePctSolar =
|
||||||
|
((totalSolarProduction || 0) - (productionReturnedToGrid || 0)) /
|
||||||
|
totalConsumption;
|
||||||
|
homeSolarCircumference = CIRCLE_CIRCUMFERENCE * homePctSolar;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lowCarbonConsumption: number | undefined;
|
||||||
|
|
||||||
|
let homeLowCarbonCircumference: number | undefined;
|
||||||
|
let homeHighCarbonCircumference: number | undefined;
|
||||||
|
if (co2percentage !== undefined) {
|
||||||
|
const gridPctHighCarbon = co2percentage / 100;
|
||||||
|
|
||||||
|
lowCarbonConsumption =
|
||||||
|
totalGridConsumption - totalGridConsumption * gridPctHighCarbon;
|
||||||
|
|
||||||
|
const homePctGridHighCarbon =
|
||||||
|
(gridPctHighCarbon * totalGridConsumption) / totalConsumption;
|
||||||
|
|
||||||
|
homeHighCarbonCircumference =
|
||||||
|
CIRCLE_CIRCUMFERENCE * homePctGridHighCarbon;
|
||||||
|
|
||||||
|
homeLowCarbonCircumference =
|
||||||
|
CIRCLE_CIRCUMFERENCE -
|
||||||
|
(homeSolarCircumference || 0) -
|
||||||
|
homeHighCarbonCircumference;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card .header=${this._config.title}>
|
||||||
|
<div class="card-content">
|
||||||
|
${lowCarbonConsumption !== undefined || hasSolarProduction
|
||||||
|
? html`<div class="row">
|
||||||
|
${lowCarbonConsumption === undefined
|
||||||
|
? html`<div class="spacer"></div>`
|
||||||
|
: html`
|
||||||
|
<div class="circle-container low-carbon">
|
||||||
|
<span class="label">Non-fossil</span>
|
||||||
|
<div class="circle">
|
||||||
|
<ha-svg-icon .path="${mdiLeaf}"></ha-svg-icon>
|
||||||
|
${round(lowCarbonConsumption, 1)} kWh
|
||||||
|
</div>
|
||||||
|
<svg width="80" height="30">
|
||||||
|
<line x1="40" y1="0" x2="40" y2="30"></line>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
${hasSolarProduction
|
||||||
|
? html`<div class="circle-container solar">
|
||||||
|
<span class="label">Solar</span>
|
||||||
|
<div class="circle">
|
||||||
|
<ha-svg-icon .path="${mdiSolarPower}"></ha-svg-icon>
|
||||||
|
${round(totalSolarProduction || 0, 1)} kWh
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
: ""}
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</div>`
|
||||||
|
: ""}
|
||||||
|
<div class="row">
|
||||||
|
<div class="circle-container grid">
|
||||||
|
<div class="circle">
|
||||||
|
<ha-svg-icon .path="${mdiTransmissionTower}"></ha-svg-icon>
|
||||||
|
<span class="consumption">
|
||||||
|
${hasReturnToGrid
|
||||||
|
? html`<ha-svg-icon
|
||||||
|
class="small"
|
||||||
|
.path=${mdiArrowRight}
|
||||||
|
></ha-svg-icon>`
|
||||||
|
: ""}${round(totalGridConsumption, 1)}
|
||||||
|
kWh
|
||||||
|
</span>
|
||||||
|
${productionReturnedToGrid
|
||||||
|
? html`<span class="return">
|
||||||
|
<ha-svg-icon
|
||||||
|
class="small"
|
||||||
|
.path=${mdiArrowLeft}
|
||||||
|
></ha-svg-icon
|
||||||
|
>${round(productionReturnedToGrid, 1)} kWh
|
||||||
|
</span>`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
<span class="label">Grid</span>
|
||||||
|
</div>
|
||||||
|
<div class="circle-container home">
|
||||||
|
<div
|
||||||
|
class="circle ${classMap({
|
||||||
|
border:
|
||||||
|
homeSolarCircumference === undefined &&
|
||||||
|
homeLowCarbonCircumference === undefined,
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path="${mdiHome}"></ha-svg-icon>
|
||||||
|
${round(totalConsumption, 1)} kWh
|
||||||
|
${homeSolarCircumference !== undefined ||
|
||||||
|
homeLowCarbonCircumference !== undefined
|
||||||
|
? html`<svg>
|
||||||
|
${homeSolarCircumference !== undefined
|
||||||
|
? svg`
|
||||||
|
<circle
|
||||||
|
class="solar"
|
||||||
|
cx="40"
|
||||||
|
cy="40"
|
||||||
|
r="38"
|
||||||
|
stroke-dasharray="${homeSolarCircumference} ${
|
||||||
|
CIRCLE_CIRCUMFERENCE - homeSolarCircumference
|
||||||
|
}"
|
||||||
|
shape-rendering="geometricPrecision"
|
||||||
|
stroke-dashoffset="0"
|
||||||
|
/>`
|
||||||
|
: ""}
|
||||||
|
${homeHighCarbonCircumference
|
||||||
|
? svg`
|
||||||
|
<circle
|
||||||
|
class="low-carbon"
|
||||||
|
cx="40"
|
||||||
|
cy="40"
|
||||||
|
r="38"
|
||||||
|
stroke-dasharray="${homeLowCarbonCircumference} ${
|
||||||
|
CIRCLE_CIRCUMFERENCE - homeLowCarbonCircumference!
|
||||||
|
}"
|
||||||
|
stroke-dashoffset="${
|
||||||
|
((homeSolarCircumference || 0) +
|
||||||
|
homeHighCarbonCircumference!) *
|
||||||
|
-1
|
||||||
|
}"
|
||||||
|
shape-rendering="geometricPrecision"
|
||||||
|
/>`
|
||||||
|
: ""}
|
||||||
|
<circle
|
||||||
|
class="grid"
|
||||||
|
cx="40"
|
||||||
|
cy="40"
|
||||||
|
r="38"
|
||||||
|
stroke-dasharray="${homeHighCarbonCircumference ??
|
||||||
|
CIRCLE_CIRCUMFERENCE -
|
||||||
|
homeSolarCircumference!} ${homeHighCarbonCircumference
|
||||||
|
? CIRCLE_CIRCUMFERENCE - homeHighCarbonCircumference
|
||||||
|
: homeSolarCircumference}"
|
||||||
|
stroke-dashoffset="${(homeSolarCircumference || 0) *
|
||||||
|
-1}"
|
||||||
|
shape-rendering="geometricPrecision"
|
||||||
|
/>
|
||||||
|
</svg>`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
<span class="label">Home</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="lines">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
>
|
||||||
|
${productionReturnedToGrid && hasSolarProduction
|
||||||
|
? svg`<path
|
||||||
|
class="return"
|
||||||
|
d="M50,0 v20 c0,40 -10,35 -65,35 h20"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
></path>`
|
||||||
|
: ""}
|
||||||
|
${totalSolarProduction
|
||||||
|
? svg`<path
|
||||||
|
class="solar"
|
||||||
|
d="M50,0 v20 c0,40 10,35 65,35 h20"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
></path>`
|
||||||
|
: ""}
|
||||||
|
${totalGridConsumption
|
||||||
|
? svg`<path
|
||||||
|
class="grid"
|
||||||
|
d="M0,55 H100"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
></path>`
|
||||||
|
: ""}
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchCO2SignalEntity() {
|
||||||
|
const [configEntries, entityRegistryEntries] = await Promise.all([
|
||||||
|
getConfigEntries(this.hass),
|
||||||
|
subscribeOne(this.hass.connection, subscribeEntityRegistry),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const co2ConfigEntry = configEntries.find(
|
||||||
|
(entry) => entry.domain === "co2signal"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!co2ConfigEntry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entry of entityRegistryEntries) {
|
||||||
|
if (entry.config_entry_id !== co2ConfigEntry.entry_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The integration offers 2 entities. We want the % one.
|
||||||
|
const co2State = this.hass.states[entry.entity_id];
|
||||||
|
if (!co2State || co2State.attributes.unit_of_measurement !== "%") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._co2SignalEntity = co2State.entity_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getStatistics(): Promise<void> {
|
||||||
|
const startDate = new Date();
|
||||||
|
startDate.setHours(0, 0, 0, 0);
|
||||||
|
startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint
|
||||||
|
|
||||||
|
const statistics: string[] = [];
|
||||||
|
const prefs = this._config!.prefs;
|
||||||
|
for (const source of prefs.energy_sources) {
|
||||||
|
if (source.type === "solar") {
|
||||||
|
statistics.push(source.stat_energy_from);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grid source
|
||||||
|
for (const flowFrom of source.flow_from) {
|
||||||
|
statistics.push(flowFrom.stat_energy_from);
|
||||||
|
}
|
||||||
|
for (const flowTo of source.flow_to) {
|
||||||
|
statistics.push(flowTo.stat_energy_to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._stats = await fetchStatistics(
|
||||||
|
this.hass!,
|
||||||
|
startDate,
|
||||||
|
undefined,
|
||||||
|
statistics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
--mdc-icon-size: 24px;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.lines {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 146px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.lines svg {
|
||||||
|
width: calc(100% - 160px);
|
||||||
|
height: 100%;
|
||||||
|
max-width: 340px;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.circle-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.circle-container.solar {
|
||||||
|
height: 130px;
|
||||||
|
}
|
||||||
|
.spacer {
|
||||||
|
width: 80px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
.circle {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2px solid;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
ha-svg-icon {
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
ha-svg-icon.small {
|
||||||
|
--mdc-icon-size: 12px;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
line,
|
||||||
|
path {
|
||||||
|
stroke: var(--primary-text-color);
|
||||||
|
stroke-width: 1;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
.circle svg {
|
||||||
|
position: absolute;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: 4px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.circle svg circle {
|
||||||
|
animation: rotate-in 0.2s ease-in;
|
||||||
|
}
|
||||||
|
.low-carbon line {
|
||||||
|
stroke: #0f9d58;
|
||||||
|
}
|
||||||
|
.low-carbon .circle {
|
||||||
|
border-color: #0f9d58;
|
||||||
|
}
|
||||||
|
.low-carbon ha-svg-icon {
|
||||||
|
color: #0f9d58;
|
||||||
|
}
|
||||||
|
.solar .circle {
|
||||||
|
border-color: #ff9800;
|
||||||
|
}
|
||||||
|
path.solar,
|
||||||
|
circle.solar {
|
||||||
|
stroke: #ff9800;
|
||||||
|
}
|
||||||
|
circle.low-carbon {
|
||||||
|
stroke: #0f9d58;
|
||||||
|
}
|
||||||
|
circle.return,
|
||||||
|
path.return {
|
||||||
|
stroke: #673ab7;
|
||||||
|
}
|
||||||
|
.return {
|
||||||
|
color: #673ab7;
|
||||||
|
}
|
||||||
|
.grid .circle {
|
||||||
|
border-color: #126a9a;
|
||||||
|
}
|
||||||
|
.consumption {
|
||||||
|
color: #126a9a;
|
||||||
|
}
|
||||||
|
circle.grid,
|
||||||
|
path.grid {
|
||||||
|
stroke: #126a9a;
|
||||||
|
}
|
||||||
|
.home .circle {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.home .circle.border {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
@keyframes rotate-in {
|
||||||
|
from {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-energy-distribution-card": HuiEnergyDistrubutionCard;
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,19 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { round } from "../../../common/number/round";
|
import { round } from "../../../../common/number/round";
|
||||||
import "../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../components/ha-gauge";
|
import "../../../../components/ha-gauge";
|
||||||
import { energySourcesByType } from "../../../data/energy";
|
import { energySourcesByType } from "../../../../data/energy";
|
||||||
import {
|
import {
|
||||||
calculateStatisticsSumGrowth,
|
calculateStatisticsSumGrowth,
|
||||||
fetchStatistics,
|
fetchStatistics,
|
||||||
Statistics,
|
Statistics,
|
||||||
} from "../../../data/history";
|
} from "../../../../data/history";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import type { LovelaceCard } from "../types";
|
import type { LovelaceCard } from "../../types";
|
||||||
import { severityMap } from "./hui-gauge-card";
|
import { severityMap } from "../hui-gauge-card";
|
||||||
import type { EnergySolarGaugeCardConfig } from "./types";
|
import type { EnergySolarGaugeCardConfig } from "../types";
|
||||||
|
|
||||||
@customElement("hui-energy-solar-consumed-gauge-card")
|
@customElement("hui-energy-solar-consumed-gauge-card")
|
||||||
class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard {
|
class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard {
|
||||||
@ -77,7 +77,7 @@ class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
.locale=${this.hass!.locale}
|
.locale=${this.hass!.locale}
|
||||||
label="%"
|
label="%"
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
"--gauge-color": this._computeSeverity(64),
|
"--gauge-color": this._computeSeverity(value),
|
||||||
})}
|
})}
|
||||||
></ha-gauge>
|
></ha-gauge>
|
||||||
<div class="name">Self consumed solar energy</div>`
|
<div class="name">Self consumed solar energy</div>`
|
||||||
@ -87,9 +87,12 @@ class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _computeSeverity(numberValue: number): string {
|
private _computeSeverity(numberValue: number): string {
|
||||||
if (numberValue > 50) {
|
if (numberValue > 75) {
|
||||||
return severityMap.green;
|
return severityMap.green;
|
||||||
}
|
}
|
||||||
|
if (numberValue < 50) {
|
||||||
|
return severityMap.yellow;
|
||||||
|
}
|
||||||
return severityMap.normal;
|
return severityMap.normal;
|
||||||
}
|
}
|
||||||
|
|
@ -8,31 +8,31 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import "../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
import { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { EnergySolarGraphCardConfig } from "./types";
|
import { EnergySolarGraphCardConfig } from "../types";
|
||||||
import { fetchStatistics, Statistics } from "../../../data/history";
|
import { fetchStatistics, Statistics } from "../../../../data/history";
|
||||||
import {
|
import {
|
||||||
hex2rgb,
|
hex2rgb,
|
||||||
lab2rgb,
|
lab2rgb,
|
||||||
rgb2hex,
|
rgb2hex,
|
||||||
rgb2lab,
|
rgb2lab,
|
||||||
} from "../../../common/color/convert-color";
|
} from "../../../../common/color/convert-color";
|
||||||
import { labDarken } from "../../../common/color/lab";
|
import { labDarken } from "../../../../common/color/lab";
|
||||||
import { SolarSourceTypeEnergyPreference } from "../../../data/energy";
|
import { SolarSourceTypeEnergyPreference } from "../../../../data/energy";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
||||||
import {
|
import {
|
||||||
ForecastSolarForecast,
|
ForecastSolarForecast,
|
||||||
getForecastSolarForecasts,
|
getForecastSolarForecasts,
|
||||||
} from "../../../data/forecast_solar";
|
} from "../../../../data/forecast_solar";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import "../../../components/chart/ha-chart-base";
|
import "../../../../components/chart/ha-chart-base";
|
||||||
import "../../../components/ha-switch";
|
import "../../../../components/ha-switch";
|
||||||
import "../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
|
|
||||||
const SOLAR_COLOR = { border: "#FF9800", background: "#ffcb80" };
|
const SOLAR_COLOR = "#FF9800";
|
||||||
|
|
||||||
@customElement("hui-energy-solar-graph-card")
|
@customElement("hui-energy-solar-graph-card")
|
||||||
export class HuiEnergySolarGraphCard
|
export class HuiEnergySolarGraphCard
|
||||||
@ -123,17 +123,11 @@ export class HuiEnergySolarGraphCard
|
|||||||
"has-header": !!this._config.title,
|
"has-header": !!this._config.title,
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
<ha-formfield label="Show all forecast data"
|
|
||||||
><ha-switch
|
|
||||||
.checked=${this._showAllForecastData}
|
|
||||||
@change=${this._showAllForecastChanged}
|
|
||||||
></ha-switch
|
|
||||||
></ha-formfield>
|
|
||||||
${this._chartData
|
${this._chartData
|
||||||
? html`<ha-chart-base
|
? html`<ha-chart-base
|
||||||
.data=${this._chartData}
|
.data=${this._chartData}
|
||||||
.options=${this._chartOptions}
|
.options=${this._chartOptions}
|
||||||
chart-type="line"
|
chart-type="bar"
|
||||||
></ha-chart-base>`
|
></ha-chart-base>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@ -142,12 +136,18 @@ export class HuiEnergySolarGraphCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createOptions() {
|
private _createOptions() {
|
||||||
|
const startDate = new Date();
|
||||||
|
startDate.setHours(0, 0, 0, 0);
|
||||||
|
const startTime = startDate.getTime();
|
||||||
|
|
||||||
this._chartOptions = {
|
this._chartOptions = {
|
||||||
parsing: false,
|
parsing: false,
|
||||||
animation: false,
|
animation: false,
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
type: "time",
|
type: "time",
|
||||||
|
suggestedMin: startTime,
|
||||||
|
suggestedMax: startTime + 24 * 60 * 60 * 1000,
|
||||||
adapters: {
|
adapters: {
|
||||||
date: {
|
date: {
|
||||||
locale: this.hass.locale,
|
locale: this.hass.locale,
|
||||||
@ -168,9 +168,14 @@ export class HuiEnergySolarGraphCard
|
|||||||
time: {
|
time: {
|
||||||
tooltipFormat: "datetimeseconds",
|
tooltipFormat: "datetimeseconds",
|
||||||
},
|
},
|
||||||
|
offset: true,
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
type: "linear",
|
type: "linear",
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: "kWh",
|
||||||
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
},
|
},
|
||||||
@ -199,9 +204,10 @@ export class HuiEnergySolarGraphCard
|
|||||||
},
|
},
|
||||||
elements: {
|
elements: {
|
||||||
line: {
|
line: {
|
||||||
tension: 0.4,
|
tension: 0.3,
|
||||||
borderWidth: 1.5,
|
borderWidth: 1.5,
|
||||||
},
|
},
|
||||||
|
bar: { borderWidth: 1.5 },
|
||||||
point: {
|
point: {
|
||||||
hitRadius: 5,
|
hitRadius: 5,
|
||||||
},
|
},
|
||||||
@ -252,7 +258,7 @@ export class HuiEnergySolarGraphCard
|
|||||||
) as SolarSourceTypeEnergyPreference[];
|
) as SolarSourceTypeEnergyPreference[];
|
||||||
|
|
||||||
const statisticsData = Object.values(this._data!);
|
const statisticsData = Object.values(this._data!);
|
||||||
const datasets: ChartDataset<"line">[] = [];
|
const datasets: ChartDataset<"bar">[] = [];
|
||||||
let endTime: Date;
|
let endTime: Date;
|
||||||
|
|
||||||
if (statisticsData.length === 0) {
|
if (statisticsData.length === 0) {
|
||||||
@ -272,22 +278,18 @@ export class HuiEnergySolarGraphCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
solarSources.forEach((source, idx) => {
|
solarSources.forEach((source, idx) => {
|
||||||
const data: ChartDataset<"line">[] = [];
|
const data: ChartDataset<"bar" | "line">[] = [];
|
||||||
const entity = this.hass.states[source.stat_energy_from];
|
const entity = this.hass.states[source.stat_energy_from];
|
||||||
|
|
||||||
const borderColor =
|
const borderColor =
|
||||||
idx > 0
|
idx > 0
|
||||||
? rgb2hex(
|
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(SOLAR_COLOR)), idx)))
|
||||||
lab2rgb(labDarken(rgb2lab(hex2rgb(SOLAR_COLOR.border)), idx))
|
: SOLAR_COLOR;
|
||||||
)
|
|
||||||
: SOLAR_COLOR.border;
|
|
||||||
|
|
||||||
data.push({
|
data.push({
|
||||||
label: `Production ${
|
label: `Production ${
|
||||||
entity ? computeStateName(entity) : source.stat_energy_from
|
entity ? computeStateName(entity) : source.stat_energy_from
|
||||||
}`,
|
}`,
|
||||||
fill: true,
|
|
||||||
stepped: false,
|
|
||||||
borderColor: borderColor,
|
borderColor: borderColor,
|
||||||
backgroundColor: borderColor + "7F",
|
backgroundColor: borderColor + "7F",
|
||||||
data: [],
|
data: [],
|
||||||
@ -343,6 +345,7 @@ export class HuiEnergySolarGraphCard
|
|||||||
|
|
||||||
if (forecastsData) {
|
if (forecastsData) {
|
||||||
const forecast: ChartDataset<"line"> = {
|
const forecast: ChartDataset<"line"> = {
|
||||||
|
type: "line",
|
||||||
label: `Forecast ${
|
label: `Forecast ${
|
||||||
entity ? computeStateName(entity) : source.stat_energy_from
|
entity ? computeStateName(entity) : source.stat_energy_from
|
||||||
}`,
|
}`,
|
||||||
@ -382,11 +385,6 @@ export class HuiEnergySolarGraphCard
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showAllForecastChanged(ev) {
|
|
||||||
this._showAllForecastData = ev.target.checked;
|
|
||||||
this._renderChart();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
ha-card {
|
ha-card {
|
@ -1,21 +1,21 @@
|
|||||||
import { mdiCashMultiple, mdiSolarPower } from "@mdi/js";
|
import { mdiCashMultiple, mdiSolarPower } from "@mdi/js";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
energySourcesByType,
|
energySourcesByType,
|
||||||
GridSourceTypeEnergyPreference,
|
GridSourceTypeEnergyPreference,
|
||||||
SolarSourceTypeEnergyPreference,
|
SolarSourceTypeEnergyPreference,
|
||||||
} from "../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
import {
|
import {
|
||||||
calculateStatisticSumGrowth,
|
calculateStatisticSumGrowth,
|
||||||
fetchStatistics,
|
fetchStatistics,
|
||||||
Statistics,
|
Statistics,
|
||||||
} from "../../../data/history";
|
} from "../../../../data/history";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { EnergySummaryCardConfig } from "./types";
|
import { EnergySummaryCardConfig } from "../types";
|
||||||
import "../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
|
|
||||||
const renderSumStatHelper = (
|
const renderSumStatHelper = (
|
||||||
data: Statistics,
|
data: Statistics,
|
@ -8,33 +8,28 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import "../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
import { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { EnergySummaryGraphCardConfig } from "./types";
|
import { EnergySummaryGraphCardConfig } from "../types";
|
||||||
import { fetchStatistics, Statistics } from "../../../data/history";
|
import { fetchStatistics, Statistics } from "../../../../data/history";
|
||||||
import {
|
import {
|
||||||
hex2rgb,
|
hex2rgb,
|
||||||
lab2rgb,
|
lab2rgb,
|
||||||
rgb2hex,
|
rgb2hex,
|
||||||
rgb2lab,
|
rgb2lab,
|
||||||
} from "../../../common/color/convert-color";
|
} from "../../../../common/color/convert-color";
|
||||||
import { labDarken } from "../../../common/color/lab";
|
import { labDarken } from "../../../../common/color/lab";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import "../../../components/chart/ha-chart-base";
|
import "../../../../components/chart/ha-chart-base";
|
||||||
import { round } from "../../../common/number/round";
|
import { round } from "../../../../common/number/round";
|
||||||
|
|
||||||
const NEGATIVE = ["to_grid"];
|
const NEGATIVE = ["to_grid"];
|
||||||
const ORDER = {
|
|
||||||
used_solar: 0,
|
|
||||||
from_grid: 100,
|
|
||||||
to_grid: 200,
|
|
||||||
};
|
|
||||||
const COLORS = {
|
const COLORS = {
|
||||||
to_grid: { border: "#56d256", background: "#87ceab" },
|
to_grid: { border: "#673ab7", background: "#b39bdb" },
|
||||||
from_grid: { border: "#126A9A", background: "#88b5cd" },
|
from_grid: { border: "#126A9A", background: "#8ab5cd" },
|
||||||
used_solar: { border: "#FF9800", background: "#ffcb80" },
|
used_solar: { border: "#FF9800", background: "#fecc8e" },
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("hui-energy-summary-graph-card")
|
@customElement("hui-energy-summary-graph-card")
|
||||||
@ -126,7 +121,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
? html`<ha-chart-base
|
? html`<ha-chart-base
|
||||||
.data=${this._chartData}
|
.data=${this._chartData}
|
||||||
.options=${this._chartOptions}
|
.options=${this._chartOptions}
|
||||||
chartType="line"
|
chart-type="bar"
|
||||||
></ha-chart-base>`
|
></ha-chart-base>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@ -135,12 +130,18 @@ export class HuiEnergySummaryGraphCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createOptions() {
|
private _createOptions() {
|
||||||
|
const startDate = new Date();
|
||||||
|
startDate.setHours(0, 0, 0, 0);
|
||||||
|
const startTime = startDate.getTime();
|
||||||
|
|
||||||
this._chartOptions = {
|
this._chartOptions = {
|
||||||
parsing: false,
|
parsing: false,
|
||||||
animation: false,
|
animation: false,
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
type: "time",
|
type: "time",
|
||||||
|
suggestedMin: startTime,
|
||||||
|
suggestedMax: startTime + 24 * 60 * 60 * 1000,
|
||||||
adapters: {
|
adapters: {
|
||||||
date: {
|
date: {
|
||||||
locale: this.hass.locale,
|
locale: this.hass.locale,
|
||||||
@ -161,10 +162,15 @@ export class HuiEnergySummaryGraphCard
|
|||||||
time: {
|
time: {
|
||||||
tooltipFormat: "datetimeseconds",
|
tooltipFormat: "datetimeseconds",
|
||||||
},
|
},
|
||||||
|
offset: true,
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
stacked: true,
|
stacked: true,
|
||||||
type: "linear",
|
type: "linear",
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: "kWh",
|
||||||
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
callback: (value) => Math.abs(round(value)),
|
callback: (value) => Math.abs(round(value)),
|
||||||
@ -193,9 +199,13 @@ export class HuiEnergySummaryGraphCard
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
`Total consumed: ${totalConsumed.toFixed(2)} kWh`,
|
totalConsumed
|
||||||
`Total returned: ${totalReturned.toFixed(2)} kWh`,
|
? `Total consumed: ${totalConsumed.toFixed(2)} kWh`
|
||||||
];
|
: "",
|
||||||
|
totalReturned
|
||||||
|
? `Total returned: ${totalReturned.toFixed(2)} kWh`
|
||||||
|
: "",
|
||||||
|
].filter(Boolean);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -213,10 +223,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
mode: "nearest",
|
mode: "nearest",
|
||||||
},
|
},
|
||||||
elements: {
|
elements: {
|
||||||
line: {
|
bar: { borderWidth: 1.5 },
|
||||||
tension: 0.4,
|
|
||||||
borderWidth: 1.5,
|
|
||||||
},
|
|
||||||
point: {
|
point: {
|
||||||
hitRadius: 5,
|
hitRadius: 5,
|
||||||
},
|
},
|
||||||
@ -280,7 +287,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
const statisticsData = Object.values(this._data!);
|
const statisticsData = Object.values(this._data!);
|
||||||
const datasets: ChartDataset<"line">[] = [];
|
const datasets: ChartDataset<"bar">[] = [];
|
||||||
let endTime: Date;
|
let endTime: Date;
|
||||||
|
|
||||||
if (statisticsData.length === 0) {
|
if (statisticsData.length === 0) {
|
||||||
@ -370,7 +377,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
const negative = NEGATIVE.includes(type);
|
const negative = NEGATIVE.includes(type);
|
||||||
|
|
||||||
Object.entries(sources).forEach(([statId, source], idx) => {
|
Object.entries(sources).forEach(([statId, source], idx) => {
|
||||||
const data: ChartDataset<"line">[] = [];
|
const data: ChartDataset<"bar">[] = [];
|
||||||
const entity = this.hass.states[statId];
|
const entity = this.hass.states[statId];
|
||||||
const color = COLORS[type];
|
const color = COLORS[type];
|
||||||
|
|
||||||
@ -381,9 +388,6 @@ export class HuiEnergySummaryGraphCard
|
|||||||
: entity
|
: entity
|
||||||
? computeStateName(entity)
|
? computeStateName(entity)
|
||||||
: statId,
|
: statId,
|
||||||
fill: true,
|
|
||||||
stepped: false,
|
|
||||||
order: ORDER[type] + idx,
|
|
||||||
borderColor:
|
borderColor:
|
||||||
idx > 0
|
idx > 0
|
||||||
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(color.border)), idx)))
|
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(color.border)), idx)))
|
||||||
@ -394,7 +398,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
lab2rgb(labDarken(rgb2lab(hex2rgb(color.background)), idx))
|
lab2rgb(labDarken(rgb2lab(hex2rgb(color.background)), idx))
|
||||||
)
|
)
|
||||||
: color.background,
|
: color.background,
|
||||||
stack: negative ? "negative" : "positive",
|
stack: "stack",
|
||||||
data: [],
|
data: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -402,6 +406,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
for (const key of uniqueKeys) {
|
for (const key of uniqueKeys) {
|
||||||
const value = key in source ? Math.round(source[key] * 100) / 100 : 0;
|
const value = key in source ? Math.round(source[key] * 100) / 100 : 0;
|
||||||
const date = new Date(key);
|
const date = new Date(key);
|
||||||
|
// @ts-expect-error
|
||||||
data[0].data.push({
|
data[0].data.push({
|
||||||
x: date.getTime(),
|
x: date.getTime(),
|
||||||
y: value && negative ? -1 * value : value,
|
y: value && negative ? -1 * value : value,
|
@ -1,322 +0,0 @@
|
|||||||
import { mdiHome, mdiLeaf, mdiSolarPower, mdiTransmissionTower } from "@mdi/js";
|
|
||||||
import { css, html, LitElement } from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import { subscribeOne } from "../../../common/util/subscribe-one";
|
|
||||||
import "../../../components/ha-svg-icon";
|
|
||||||
import { getConfigEntries } from "../../../data/config_entries";
|
|
||||||
import { energySourcesByType } from "../../../data/energy";
|
|
||||||
import { subscribeEntityRegistry } from "../../../data/entity_registry";
|
|
||||||
import {
|
|
||||||
calculateStatisticsSumGrowth,
|
|
||||||
fetchStatistics,
|
|
||||||
Statistics,
|
|
||||||
} from "../../../data/history";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
|
||||||
import { LovelaceCard } from "../types";
|
|
||||||
import { EnergySummaryCardConfig } from "./types";
|
|
||||||
import "../../../components/ha-card";
|
|
||||||
import { round } from "../../../common/number/round";
|
|
||||||
|
|
||||||
@customElement("hui-energy-usage-card")
|
|
||||||
class HuiEnergyUsageCard extends LitElement implements LovelaceCard {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@state() private _config?: EnergySummaryCardConfig;
|
|
||||||
|
|
||||||
@state() private _stats?: Statistics;
|
|
||||||
|
|
||||||
@state() private _co2SignalEntity?: string;
|
|
||||||
|
|
||||||
private _fetching = false;
|
|
||||||
|
|
||||||
public setConfig(config: EnergySummaryCardConfig): void {
|
|
||||||
this._config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCardSize(): Promise<number> | number {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
public willUpdate(changedProps) {
|
|
||||||
super.willUpdate(changedProps);
|
|
||||||
|
|
||||||
if (!this._fetching && !this._stats) {
|
|
||||||
this._fetching = true;
|
|
||||||
Promise.all([this._getStatistics(), this._fetchCO2SignalEntity()]).then(
|
|
||||||
() => {
|
|
||||||
this._fetching = false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (!this._config) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._stats) {
|
|
||||||
return html`Loading…`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefs = this._config!.prefs;
|
|
||||||
const types = energySourcesByType(prefs);
|
|
||||||
|
|
||||||
// The strategy only includes this card if we have a grid.
|
|
||||||
const hasConsumption = true;
|
|
||||||
|
|
||||||
const hasSolarProduction = types.solar !== undefined;
|
|
||||||
const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0;
|
|
||||||
|
|
||||||
const totalGridConsumption =
|
|
||||||
calculateStatisticsSumGrowth(
|
|
||||||
this._stats,
|
|
||||||
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
|
||||||
) ?? 0;
|
|
||||||
|
|
||||||
let totalSolarProduction: number | null = null;
|
|
||||||
|
|
||||||
if (hasSolarProduction) {
|
|
||||||
totalSolarProduction = calculateStatisticsSumGrowth(
|
|
||||||
this._stats,
|
|
||||||
types.solar!.map((source) => source.stat_energy_from)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let productionReturnedToGrid: number | null = null;
|
|
||||||
|
|
||||||
if (hasReturnToGrid) {
|
|
||||||
productionReturnedToGrid = calculateStatisticsSumGrowth(
|
|
||||||
this._stats,
|
|
||||||
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// total consumption = consumption_from_grid + solar_production - return_to_grid
|
|
||||||
|
|
||||||
let co2percentage: number | undefined;
|
|
||||||
|
|
||||||
if (this._co2SignalEntity) {
|
|
||||||
const co2State = this.hass.states[this._co2SignalEntity];
|
|
||||||
if (co2State) {
|
|
||||||
co2percentage = Number(co2State.state);
|
|
||||||
if (isNaN(co2percentage)) {
|
|
||||||
co2percentage = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are calculating low carbon consumption based on what we got from the grid
|
|
||||||
// minus what we gave back because what we gave back is low carbon
|
|
||||||
const relativeGridFlow =
|
|
||||||
totalGridConsumption - (productionReturnedToGrid || 0);
|
|
||||||
|
|
||||||
let lowCarbonConsumption: number | undefined;
|
|
||||||
|
|
||||||
if (co2percentage !== undefined) {
|
|
||||||
if (relativeGridFlow > 0) {
|
|
||||||
lowCarbonConsumption = round(relativeGridFlow * (co2percentage / 100));
|
|
||||||
} else {
|
|
||||||
lowCarbonConsumption = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalConsumption =
|
|
||||||
totalGridConsumption +
|
|
||||||
(totalSolarProduction || 0) -
|
|
||||||
(productionReturnedToGrid || 0);
|
|
||||||
|
|
||||||
const gridPctLowCarbon =
|
|
||||||
co2percentage === undefined ? 0 : co2percentage / 100;
|
|
||||||
const gridPctHighCarbon = 1 - gridPctLowCarbon;
|
|
||||||
|
|
||||||
const homePctSolar =
|
|
||||||
((totalSolarProduction || 0) - (productionReturnedToGrid || 0)) /
|
|
||||||
totalConsumption;
|
|
||||||
// When we know the ratio solar-grid, we can adjust the low/high carbon
|
|
||||||
// percentages to reflect that.
|
|
||||||
const homePctGridLowCarbon = gridPctLowCarbon * (1 - homePctSolar);
|
|
||||||
const homePctGridHighCarbon = gridPctHighCarbon * (1 - homePctSolar);
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<ha-card header="Usage">
|
|
||||||
<div class="card-content">
|
|
||||||
<div class="row">
|
|
||||||
${co2percentage === undefined
|
|
||||||
? ""
|
|
||||||
: html`
|
|
||||||
<div class="circle-container">
|
|
||||||
<span class="label">Low-carbon</span>
|
|
||||||
<div class="circle low-carbon">
|
|
||||||
<ha-svg-icon .path="${mdiLeaf}"></ha-svg-icon>
|
|
||||||
${co2percentage}% / ${round(lowCarbonConsumption!)} kWh
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`}
|
|
||||||
<div class="circle-container">
|
|
||||||
<span class="label">Solar</span>
|
|
||||||
<div class="circle solar">
|
|
||||||
<ha-svg-icon .path="${mdiSolarPower}"></ha-svg-icon>
|
|
||||||
${round(totalSolarProduction || 0)} kWh
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="circle-container">
|
|
||||||
<div class="circle grid">
|
|
||||||
<ha-svg-icon .path="${mdiTransmissionTower}"></ha-svg-icon>
|
|
||||||
${round(totalGridConsumption - (productionReturnedToGrid || 0))}
|
|
||||||
kWh
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Grid high carbon: ${round(gridPctHighCarbon * 100, 1)}%
|
|
||||||
</li>
|
|
||||||
<li>Grid low carbon: ${round(gridPctLowCarbon * 100, 1)}%</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<span class="label">Grid</span>
|
|
||||||
</div>
|
|
||||||
<div class="circle-container home">
|
|
||||||
<div class="circle home">
|
|
||||||
<ha-svg-icon .path="${mdiHome}"></ha-svg-icon>
|
|
||||||
${round(totalConsumption)} kWh
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Grid high carbon: ${round(homePctGridHighCarbon * 100)}%
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Grid low carbon: ${round(homePctGridLowCarbon * 100)}%
|
|
||||||
</li>
|
|
||||||
<li>Solar: ${round(homePctSolar * 100)}%</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<span class="label">Home</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _fetchCO2SignalEntity() {
|
|
||||||
const [configEntries, entityRegistryEntries] = await Promise.all([
|
|
||||||
getConfigEntries(this.hass),
|
|
||||||
subscribeOne(this.hass.connection, subscribeEntityRegistry),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const co2ConfigEntry = configEntries.find(
|
|
||||||
(entry) => entry.domain === "co2signal"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!co2ConfigEntry) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const entry of entityRegistryEntries) {
|
|
||||||
if (entry.config_entry_id !== co2ConfigEntry.entry_id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The integration offers 2 entities. We want the % one.
|
|
||||||
const co2State = this.hass.states[entry.entity_id];
|
|
||||||
if (!co2State || co2State.attributes.unit_of_measurement !== "%") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._co2SignalEntity = co2State.entity_id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getStatistics(): Promise<void> {
|
|
||||||
const startDate = new Date();
|
|
||||||
startDate.setHours(0, 0, 0, 0);
|
|
||||||
startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint
|
|
||||||
|
|
||||||
const statistics: string[] = [];
|
|
||||||
const prefs = this._config!.prefs;
|
|
||||||
for (const source of prefs.energy_sources) {
|
|
||||||
if (source.type === "solar") {
|
|
||||||
statistics.push(source.stat_energy_from);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// grid source
|
|
||||||
for (const flowFrom of source.flow_from) {
|
|
||||||
statistics.push(flowFrom.stat_energy_from);
|
|
||||||
}
|
|
||||||
for (const flowTo of source.flow_to) {
|
|
||||||
statistics.push(flowTo.stat_energy_to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._stats = await fetchStatistics(
|
|
||||||
this.hass!,
|
|
||||||
startDate,
|
|
||||||
undefined,
|
|
||||||
statistics
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
--mdc-icon-size: 26px;
|
|
||||||
}
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
.row:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
.circle-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 40px;
|
|
||||||
}
|
|
||||||
.circle {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.circle-container:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
.circle ul {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.low-carbon {
|
|
||||||
border-color: #0da035;
|
|
||||||
}
|
|
||||||
.low-carbon ha-svg-icon {
|
|
||||||
color: #0da035;
|
|
||||||
}
|
|
||||||
.solar {
|
|
||||||
border-color: #ff9800;
|
|
||||||
}
|
|
||||||
.grid {
|
|
||||||
border-color: #134763;
|
|
||||||
}
|
|
||||||
.circle-container.home {
|
|
||||||
margin-left: 120px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"hui-energy-usage-card": HuiEnergyUsageCard;
|
|
||||||
}
|
|
||||||
}
|
|
@ -92,31 +92,42 @@ export interface ButtonCardConfig extends LovelaceCardConfig {
|
|||||||
|
|
||||||
export interface EnergySummaryCardConfig extends LovelaceCardConfig {
|
export interface EnergySummaryCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-summary";
|
type: "energy-summary";
|
||||||
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EnergyDistributionCardConfig extends LovelaceCardConfig {
|
||||||
|
type: "energy-distribution";
|
||||||
|
title?: string;
|
||||||
|
prefs: EnergyPreferences;
|
||||||
|
}
|
||||||
export interface EnergySummaryGraphCardConfig extends LovelaceCardConfig {
|
export interface EnergySummaryGraphCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-summary-graph";
|
type: "energy-summary-graph";
|
||||||
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnergySolarGraphCardConfig extends LovelaceCardConfig {
|
export interface EnergySolarGraphCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-solar-graph";
|
type: "energy-solar-graph";
|
||||||
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnergyDevicesGraphCardConfig extends LovelaceCardConfig {
|
export interface EnergyDevicesGraphCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-devices-graph";
|
type: "energy-devices-graph";
|
||||||
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
|
export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-solar-consumed-gauge";
|
type: "energy-solar-consumed-gauge";
|
||||||
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnergyCarbonGaugeCardConfig extends LovelaceCardConfig {
|
export interface EnergyCarbonGaugeCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-carbon-consumed-gauge";
|
type: "energy-carbon-consumed-gauge";
|
||||||
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,18 +35,21 @@ const LAZY_LOAD_TYPES = {
|
|||||||
"alarm-panel": () => import("../cards/hui-alarm-panel-card"),
|
"alarm-panel": () => import("../cards/hui-alarm-panel-card"),
|
||||||
error: () => import("../cards/hui-error-card"),
|
error: () => import("../cards/hui-error-card"),
|
||||||
"empty-state": () => import("../cards/hui-empty-state-card"),
|
"empty-state": () => import("../cards/hui-empty-state-card"),
|
||||||
"energy-summary": () => import("../cards/hui-energy-summary-card"),
|
"energy-summary": () => import("../cards/energy/hui-energy-summary-card"),
|
||||||
"energy-summary-graph": () =>
|
"energy-summary-graph": () =>
|
||||||
import("../cards/hui-energy-summary-graph-card"),
|
import("../cards/energy/hui-energy-summary-graph-card"),
|
||||||
"energy-solar-graph": () => import("../cards/hui-energy-solar-graph-card"),
|
"energy-solar-graph": () =>
|
||||||
|
import("../cards/energy/hui-energy-solar-graph-card"),
|
||||||
"energy-devices-graph": () =>
|
"energy-devices-graph": () =>
|
||||||
import("../cards/hui-energy-devices-graph-card"),
|
import("../cards/energy/hui-energy-devices-graph-card"),
|
||||||
"energy-costs-table": () => import("../cards/hui-energy-costs-table-card"),
|
"energy-costs-table": () =>
|
||||||
"energy-usage": () => import("../cards/hui-energy-usage-card"),
|
import("../cards/energy/hui-energy-costs-table-card"),
|
||||||
|
"energy-distribution": () =>
|
||||||
|
import("../cards/energy/hui-energy-distribution-card"),
|
||||||
"energy-solar-consumed-gauge": () =>
|
"energy-solar-consumed-gauge": () =>
|
||||||
import("../cards/hui-energy-solar-consumed-gauge-card"),
|
import("../cards/energy/hui-energy-solar-consumed-gauge-card"),
|
||||||
"energy-carbon-consumed-gauge": () =>
|
"energy-carbon-consumed-gauge": () =>
|
||||||
import("../cards/hui-energy-carbon-consumed-gauge-card"),
|
import("../cards/energy/hui-energy-carbon-consumed-gauge-card"),
|
||||||
grid: () => import("../cards/hui-grid-card"),
|
grid: () => import("../cards/hui-grid-card"),
|
||||||
starting: () => import("../cards/hui-starting-card"),
|
starting: () => import("../cards/hui-starting-card"),
|
||||||
"entity-filter": () => import("../cards/hui-entity-filter-card"),
|
"entity-filter": () => import("../cards/hui-entity-filter-card"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user