mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 01:06:35 +00:00
Energy dashboard tweaks and fixes (#9643)
* Energy dashboard tweaks and fixes * Make headers smaller * Change button styling in onboarding * Disable add when no stat choosen * Oops * Update hui-energy-carbon-consumed-gauge-card.ts * Update hui-energy-distribution-card.ts
This commit is contained in:
parent
6e7af18494
commit
1531e99528
@ -1,5 +1,22 @@
|
|||||||
import { FrontendLocaleData, NumberFormat } from "../../data/translation";
|
import { FrontendLocaleData, NumberFormat } from "../../data/translation";
|
||||||
|
|
||||||
|
export const numberFormatToLocale = (
|
||||||
|
localeOptions: FrontendLocaleData
|
||||||
|
): string | string[] | undefined => {
|
||||||
|
switch (localeOptions.number_format) {
|
||||||
|
case NumberFormat.comma_decimal:
|
||||||
|
return ["en-US", "en"]; // Use United States with fallback to English formatting 1,234,567.89
|
||||||
|
case NumberFormat.decimal_comma:
|
||||||
|
return ["de", "es", "it"]; // Use German with fallback to Spanish then Italian formatting 1.234.567,89
|
||||||
|
case NumberFormat.space_comma:
|
||||||
|
return ["fr", "sv", "cs"]; // Use French with fallback to Swedish and Czech formatting 1 234 567,89
|
||||||
|
case NumberFormat.system:
|
||||||
|
return undefined;
|
||||||
|
default:
|
||||||
|
return localeOptions.language;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a number based on the user's preference with thousands separator(s) and decimal character for better legibility.
|
* Formats a number based on the user's preference with thousands separator(s) and decimal character for better legibility.
|
||||||
*
|
*
|
||||||
@ -9,27 +26,12 @@ import { FrontendLocaleData, NumberFormat } from "../../data/translation";
|
|||||||
*/
|
*/
|
||||||
export const formatNumber = (
|
export const formatNumber = (
|
||||||
num: string | number,
|
num: string | number,
|
||||||
locale?: FrontendLocaleData,
|
localeOptions?: FrontendLocaleData,
|
||||||
options?: Intl.NumberFormatOptions
|
options?: Intl.NumberFormatOptions
|
||||||
): string => {
|
): string => {
|
||||||
let format: string | string[] | undefined;
|
const locale = localeOptions
|
||||||
|
? numberFormatToLocale(localeOptions)
|
||||||
switch (locale?.number_format) {
|
: undefined;
|
||||||
case NumberFormat.comma_decimal:
|
|
||||||
format = ["en-US", "en"]; // Use United States with fallback to English formatting 1,234,567.89
|
|
||||||
break;
|
|
||||||
case NumberFormat.decimal_comma:
|
|
||||||
format = ["de", "es", "it"]; // Use German with fallback to Spanish then Italian formatting 1.234.567,89
|
|
||||||
break;
|
|
||||||
case NumberFormat.space_comma:
|
|
||||||
format = ["fr", "sv", "cs"]; // Use French with fallback to Swedish and Czech formatting 1 234 567,89
|
|
||||||
break;
|
|
||||||
case NumberFormat.system:
|
|
||||||
format = undefined;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format = locale?.language;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Polyfill for Number.isNaN, which is more reliable than the global isNaN()
|
// Polyfill for Number.isNaN, which is more reliable than the global isNaN()
|
||||||
Number.isNaN =
|
Number.isNaN =
|
||||||
@ -39,13 +41,13 @@ export const formatNumber = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
localeOptions?.number_format !== NumberFormat.none &&
|
||||||
!Number.isNaN(Number(num)) &&
|
!Number.isNaN(Number(num)) &&
|
||||||
Intl &&
|
Intl
|
||||||
locale?.number_format !== NumberFormat.none
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return new Intl.NumberFormat(
|
return new Intl.NumberFormat(
|
||||||
format,
|
locale,
|
||||||
getDefaultFormatOptions(num, options)
|
getDefaultFormatOptions(num, options)
|
||||||
).format(Number(num));
|
).format(Number(num));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -152,7 +152,17 @@ export default class HaChartBase extends LitElement {
|
|||||||
.querySelector("canvas")!
|
.querySelector("canvas")!
|
||||||
.getContext("2d")!;
|
.getContext("2d")!;
|
||||||
|
|
||||||
this.chart = new (await import("../../resources/chartjs")).Chart(ctx, {
|
const ChartConstructor = (await import("../../resources/chartjs")).Chart;
|
||||||
|
|
||||||
|
const computedStyles = getComputedStyle(this);
|
||||||
|
|
||||||
|
ChartConstructor.defaults.borderColor =
|
||||||
|
computedStyles.getPropertyValue("--divider-color");
|
||||||
|
ChartConstructor.defaults.color = computedStyles.getPropertyValue(
|
||||||
|
"--secondary-text-color"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.chart = new ChartConstructor(ctx, {
|
||||||
type: this.chartType,
|
type: this.chartType,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
options: this._createOptions(),
|
options: this._createOptions(),
|
||||||
|
@ -109,6 +109,8 @@ class StateHistoryChartLine extends LitElement {
|
|||||||
hitRadius: 5,
|
hitRadius: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// @ts-expect-error
|
||||||
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (changedProps.has("data")) {
|
if (changedProps.has("data")) {
|
||||||
|
@ -5,6 +5,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { getColorByIndex } from "../../common/color/colors";
|
import { getColorByIndex } from "../../common/color/colors";
|
||||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
|
import { numberFormatToLocale } from "../../common/string/format_number";
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
import { TimelineEntity } from "../../data/history";
|
import { TimelineEntity } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
@ -186,6 +187,8 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
propagate: true,
|
propagate: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// @ts-expect-error
|
||||||
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (changedProps.has("data")) {
|
if (changedProps.has("data")) {
|
||||||
|
@ -16,6 +16,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { getColorByIndex } from "../../common/color/colors";
|
import { getColorByIndex } from "../../common/color/colors";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
|
import { numberFormatToLocale } from "../../common/string/format_number";
|
||||||
import {
|
import {
|
||||||
Statistics,
|
Statistics,
|
||||||
statisticsHaveType,
|
statisticsHaveType,
|
||||||
@ -119,7 +120,7 @@ class StatisticsChart extends LitElement {
|
|||||||
: {},
|
: {},
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
tooltipFormat: "datetimeseconds",
|
tooltipFormat: "datetime",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
@ -157,6 +158,8 @@ class StatisticsChart extends LitElement {
|
|||||||
hitRadius: 5,
|
hitRadius: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// @ts-expect-error
|
||||||
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,15 @@ export class DialogEnergyGridFlowSettings
|
|||||||
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
||||||
${this.hass.localize("ui.common.cancel")}
|
${this.hass.localize("ui.common.cancel")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<mwc-button @click=${this._save} slot="primaryAction">
|
<mwc-button
|
||||||
|
@click=${this._save}
|
||||||
|
.disabled=${!this._source[
|
||||||
|
this._params!.direction === "from"
|
||||||
|
? "stat_energy_from"
|
||||||
|
: "stat_energy_to"
|
||||||
|
]}
|
||||||
|
slot="primaryAction"
|
||||||
|
>
|
||||||
${this.hass.localize("ui.common.save")}
|
${this.hass.localize("ui.common.save")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
@ -231,32 +239,42 @@ export class DialogEnergyGridFlowSettings
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _numberPriceChanged(ev: CustomEvent) {
|
private _numberPriceChanged(ev: CustomEvent) {
|
||||||
this._source!.number_energy_price = Number(ev.detail.value);
|
|
||||||
this._source!.entity_energy_price = null;
|
|
||||||
this._costStat = null;
|
this._costStat = null;
|
||||||
|
this._source = {
|
||||||
|
...this._source!,
|
||||||
|
number_energy_price: Number(ev.detail.value),
|
||||||
|
entity_energy_price: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _priceStatChanged(ev: CustomEvent) {
|
private _priceStatChanged(ev: CustomEvent) {
|
||||||
this._costStat = ev.detail.value;
|
this._costStat = ev.detail.value;
|
||||||
this._source!.entity_energy_price = null;
|
this._source = {
|
||||||
this._source!.number_energy_price = null;
|
...this._source!,
|
||||||
|
entity_energy_price: null,
|
||||||
|
number_energy_price: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _priceEntityChanged(ev: CustomEvent) {
|
private _priceEntityChanged(ev: CustomEvent) {
|
||||||
this._source!.entity_energy_price = ev.detail.value;
|
|
||||||
this._source!.number_energy_price = null;
|
|
||||||
this._costStat = null;
|
this._costStat = null;
|
||||||
|
this._source = {
|
||||||
|
...this._source!,
|
||||||
|
entity_energy_price: ev.detail.value,
|
||||||
|
number_energy_price: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _statisticChanged(ev: CustomEvent<{ value: string }>) {
|
private _statisticChanged(ev: CustomEvent<{ value: string }>) {
|
||||||
this._source![
|
this._source = {
|
||||||
this._params!.direction === "from" ? "stat_energy_from" : "stat_energy_to"
|
...this._source!,
|
||||||
] = ev.detail.value;
|
[this._params!.direction === "from"
|
||||||
this._source![
|
? "stat_energy_from"
|
||||||
this._params!.direction === "from"
|
: "stat_energy_to"]: ev.detail.value,
|
||||||
|
[this._params!.direction === "from"
|
||||||
? "entity_energy_from"
|
? "entity_energy_from"
|
||||||
: "entity_energy_to"
|
: "entity_energy_to"]: ev.detail.value,
|
||||||
] = ev.detail.value;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _save() {
|
private async _save() {
|
||||||
|
@ -140,7 +140,11 @@ export class DialogEnergySolarSettings
|
|||||||
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
||||||
${this.hass.localize("ui.common.cancel")}
|
${this.hass.localize("ui.common.cancel")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<mwc-button @click=${this._save} slot="primaryAction">
|
<mwc-button
|
||||||
|
@click=${this._save}
|
||||||
|
.disabled=${!this._source.stat_energy_from}
|
||||||
|
slot="primaryAction"
|
||||||
|
>
|
||||||
${this.hass.localize("ui.common.save")}
|
${this.hass.localize("ui.common.save")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
@ -191,7 +195,7 @@ export class DialogEnergySolarSettings
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _statisticChanged(ev: CustomEvent<{ value: string }>) {
|
private _statisticChanged(ev: CustomEvent<{ value: string }>) {
|
||||||
this._source!.stat_energy_from = ev.detail.value;
|
this._source = { ...this._source!, stat_energy_from: ev.detail.value };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _save() {
|
private async _save() {
|
||||||
|
@ -61,15 +61,15 @@ export class EnergySetupWizard extends LitElement implements LovelaceCard {
|
|||||||
></ha-energy-device-settings>`}
|
></ha-energy-device-settings>`}
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
${this._step > 0
|
${this._step > 0
|
||||||
? html`<mwc-button @click=${this._back}
|
? html`<mwc-button outlined @click=${this._back}
|
||||||
>${this.hass.localize("ui.panel.energy.setup.back")}</mwc-button
|
>${this.hass.localize("ui.panel.energy.setup.back")}</mwc-button
|
||||||
>`
|
>`
|
||||||
: html`<div></div>`}
|
: html`<div></div>`}
|
||||||
${this._step < 2
|
${this._step < 2
|
||||||
? html`<mwc-button outlined @click=${this._next}
|
? html`<mwc-button unelevated @click=${this._next}
|
||||||
>${this.hass.localize("ui.panel.energy.setup.next")}</mwc-button
|
>${this.hass.localize("ui.panel.energy.setup.next")}</mwc-button
|
||||||
>`
|
>`
|
||||||
: html`<mwc-button raised @click=${this._setupDone}>
|
: html`<mwc-button unelevated @click=${this._setupDone}>
|
||||||
${this.hass.localize("ui.panel.energy.setup.done")}
|
${this.hass.localize("ui.panel.energy.setup.done")}
|
||||||
</mwc-button>`}
|
</mwc-button>`}
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,7 +50,7 @@ export class EnergyStrategy {
|
|||||||
if (hasGrid) {
|
if (hasGrid) {
|
||||||
view.cards!.push({
|
view.cards!.push({
|
||||||
title: "Energy usage",
|
title: "Energy usage",
|
||||||
type: "energy-summary-graph",
|
type: "energy-usage-graph",
|
||||||
prefs: energyPrefs,
|
prefs: energyPrefs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -64,11 +64,10 @@ export class EnergyStrategy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only include if we have a grid.
|
if (hasGrid || hasSolar) {
|
||||||
if (hasGrid) {
|
|
||||||
view.cards!.push({
|
view.cards!.push({
|
||||||
title: "Costs",
|
title: "Sources",
|
||||||
type: "energy-costs-table",
|
type: "energy-sources-table",
|
||||||
prefs: energyPrefs,
|
prefs: energyPrefs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
(totalSolarProduction || 0) -
|
(totalSolarProduction || 0) -
|
||||||
(totalGridReturned || 0);
|
(totalGridReturned || 0);
|
||||||
|
|
||||||
value = round((1 - highCarbonEnergy / totalEnergyConsumed) * 100);
|
value = round((highCarbonEnergy / totalEnergyConsumed) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
@ -1,272 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
import dataTableStyles from "@material/data-table/dist/mdc.data-table.min.css";
|
|
||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
TemplateResult,
|
|
||||||
unsafeCSS,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
|
||||||
import { round } from "../../../../common/number/round";
|
|
||||||
import { formatNumber } from "../../../../common/string/format_number";
|
|
||||||
import "../../../../components/chart/statistics-chart";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
import {
|
|
||||||
EnergyInfo,
|
|
||||||
getEnergyInfo,
|
|
||||||
GridSourceTypeEnergyPreference,
|
|
||||||
} from "../../../../data/energy";
|
|
||||||
import {
|
|
||||||
calculateStatisticSumGrowth,
|
|
||||||
fetchStatistics,
|
|
||||||
Statistics,
|
|
||||||
} from "../../../../data/history";
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
|
||||||
import { LovelaceCard } from "../../types";
|
|
||||||
import { EnergyDevicesGraphCardConfig } from "../types";
|
|
||||||
|
|
||||||
@customElement("hui-energy-costs-table-card")
|
|
||||||
export class HuiEnergyCostsTableCard
|
|
||||||
extends LitElement
|
|
||||||
implements LovelaceCard
|
|
||||||
{
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@state() private _config?: EnergyDevicesGraphCardConfig;
|
|
||||||
|
|
||||||
@state() private _stats?: Statistics;
|
|
||||||
|
|
||||||
@state() private _energyInfo?: EnergyInfo;
|
|
||||||
|
|
||||||
public getCardSize(): Promise<number> | number {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setConfig(config: EnergyDevicesGraphCardConfig): void {
|
|
||||||
this._config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public willUpdate() {
|
|
||||||
if (!this.hasUpdated) {
|
|
||||||
this._getEnergyInfo().then(() => this._getStatistics());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
if (!this.hass || !this._config) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._stats) {
|
|
||||||
return html`Loading...`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const source = this._config.prefs.energy_sources?.find(
|
|
||||||
(src) => src.type === "grid"
|
|
||||||
) as GridSourceTypeEnergyPreference | undefined;
|
|
||||||
|
|
||||||
if (!source) {
|
|
||||||
return html`No grid source found.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let totalEnergy = 0;
|
|
||||||
let totalCost = 0;
|
|
||||||
|
|
||||||
return html` <ha-card .header="${this._config.title}">
|
|
||||||
<div class="mdc-data-table">
|
|
||||||
<div class="mdc-data-table__table-container">
|
|
||||||
<table class="mdc-data-table__table" aria-label="Dessert calories">
|
|
||||||
<thead>
|
|
||||||
<tr class="mdc-data-table__header-row">
|
|
||||||
<th
|
|
||||||
class="mdc-data-table__header-cell"
|
|
||||||
role="columnheader"
|
|
||||||
scope="col"
|
|
||||||
>
|
|
||||||
Grid source
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
class="mdc-data-table__header-cell mdc-data-table__header-cell--numeric"
|
|
||||||
role="columnheader"
|
|
||||||
scope="col"
|
|
||||||
>
|
|
||||||
Energy
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
class="mdc-data-table__header-cell mdc-data-table__header-cell--numeric"
|
|
||||||
role="columnheader"
|
|
||||||
scope="col"
|
|
||||||
>
|
|
||||||
Cost
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="mdc-data-table__content">
|
|
||||||
${source.flow_from.map((flow) => {
|
|
||||||
const entity = this.hass.states[flow.stat_energy_from];
|
|
||||||
const energy =
|
|
||||||
calculateStatisticSumGrowth(
|
|
||||||
this._stats![flow.stat_energy_from]
|
|
||||||
) || 0;
|
|
||||||
totalEnergy += energy;
|
|
||||||
const cost_stat =
|
|
||||||
flow.stat_cost ||
|
|
||||||
this._energyInfo!.cost_sensors[flow.stat_energy_from];
|
|
||||||
const cost =
|
|
||||||
(cost_stat &&
|
|
||||||
calculateStatisticSumGrowth(this._stats![cost_stat])) ||
|
|
||||||
0;
|
|
||||||
totalCost += cost;
|
|
||||||
return html`<tr class="mdc-data-table__row">
|
|
||||||
<th class="mdc-data-table__cell" scope="row">
|
|
||||||
${entity ? computeStateName(entity) : flow.stat_energy_from}
|
|
||||||
</th>
|
|
||||||
<td
|
|
||||||
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
|
||||||
>
|
|
||||||
${round(energy)} kWh
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
|
||||||
>
|
|
||||||
${formatNumber(cost, this.hass.locale, {
|
|
||||||
style: "currency",
|
|
||||||
currency: this.hass.config.currency!,
|
|
||||||
})}
|
|
||||||
</td>
|
|
||||||
</tr>`;
|
|
||||||
})}
|
|
||||||
${source.flow_to.map((flow) => {
|
|
||||||
const entity = this.hass.states[flow.stat_energy_to];
|
|
||||||
const energy =
|
|
||||||
(calculateStatisticSumGrowth(
|
|
||||||
this._stats![flow.stat_energy_to]
|
|
||||||
) || 0) * -1;
|
|
||||||
totalEnergy += energy;
|
|
||||||
const cost_stat =
|
|
||||||
flow.stat_compensation ||
|
|
||||||
this._energyInfo!.cost_sensors[flow.stat_energy_to];
|
|
||||||
const cost =
|
|
||||||
((cost_stat &&
|
|
||||||
calculateStatisticSumGrowth(this._stats![cost_stat])) ||
|
|
||||||
0) * -1;
|
|
||||||
totalCost += cost;
|
|
||||||
return html`<tr class="mdc-data-table__row">
|
|
||||||
<th class="mdc-data-table__cell" scope="row">
|
|
||||||
${entity ? computeStateName(entity) : flow.stat_energy_to}
|
|
||||||
</th>
|
|
||||||
<td
|
|
||||||
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
|
||||||
>
|
|
||||||
${round(energy)} kWh
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
|
||||||
>
|
|
||||||
${formatNumber(cost, this.hass.locale, {
|
|
||||||
style: "currency",
|
|
||||||
currency: this.hass.config.currency!,
|
|
||||||
})}
|
|
||||||
</td>
|
|
||||||
</tr>`;
|
|
||||||
})}
|
|
||||||
<tr class="mdc-data-table__row total">
|
|
||||||
<th class="mdc-data-table__cell" scope="row">Total</th>
|
|
||||||
<td class="mdc-data-table__cell mdc-data-table__cell--numeric">
|
|
||||||
${round(totalEnergy)} kWh
|
|
||||||
</td>
|
|
||||||
<td class="mdc-data-table__cell mdc-data-table__cell--numeric">
|
|
||||||
${formatNumber(totalCost, this.hass.locale, {
|
|
||||||
style: "currency",
|
|
||||||
currency: this.hass.config.currency!,
|
|
||||||
})}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ha-card>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getEnergyInfo() {
|
|
||||||
this._energyInfo = await getEnergyInfo(this.hass);
|
|
||||||
}
|
|
||||||
|
|
||||||
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[] = Object.values(this._energyInfo!.cost_sensors);
|
|
||||||
const prefs = this._config!.prefs;
|
|
||||||
for (const source of prefs.energy_sources) {
|
|
||||||
if (source.type === "solar") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// grid source
|
|
||||||
for (const flowFrom of source.flow_from) {
|
|
||||||
statistics.push(flowFrom.stat_energy_from);
|
|
||||||
if (flowFrom.stat_cost) {
|
|
||||||
statistics.push(flowFrom.stat_cost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const flowTo of source.flow_to) {
|
|
||||||
statistics.push(flowTo.stat_energy_to);
|
|
||||||
if (flowTo.stat_compensation) {
|
|
||||||
statistics.push(flowTo.stat_compensation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._stats = await fetchStatistics(
|
|
||||||
this.hass!,
|
|
||||||
startDate,
|
|
||||||
undefined,
|
|
||||||
statistics
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
${unsafeCSS(dataTableStyles)}
|
|
||||||
.mdc-data-table {
|
|
||||||
width: 100%;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
.mdc-data-table__header-cell,
|
|
||||||
.mdc-data-table__cell {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
border-bottom-color: var(--divider-color);
|
|
||||||
}
|
|
||||||
.mdc-data-table__row:not(.mdc-data-table__row--selected):hover {
|
|
||||||
background-color: rgba(var(--rgb-primary-text-color), 0.04);
|
|
||||||
}
|
|
||||||
.total {
|
|
||||||
--mdc-typography-body2-font-weight: 500;
|
|
||||||
}
|
|
||||||
.total .mdc-data-table__cell {
|
|
||||||
border-top: 1px solid var(--divider-color);
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.has-header {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"hui-energy-costs-table-card": HuiEnergyCostsTableCard;
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,6 +16,10 @@ 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 {
|
||||||
|
formatNumber,
|
||||||
|
numberFormatToLocale,
|
||||||
|
} from "../../../../common/string/format_number";
|
||||||
import "../../../../components/chart/ha-chart-base";
|
import "../../../../components/chart/ha-chart-base";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
@ -106,7 +110,10 @@ export class HuiEnergyDevicesGraphCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card .header="${this._config.title}">
|
<ha-card>
|
||||||
|
${this._config.title
|
||||||
|
? html`<h1 class="card-header">${this._config.title}</h1>`
|
||||||
|
: ""}
|
||||||
<div
|
<div
|
||||||
class="content ${classMap({
|
class="content ${classMap({
|
||||||
"has-header": !!this._config.title,
|
"has-header": !!this._config.title,
|
||||||
@ -144,12 +151,15 @@ export class HuiEnergyDevicesGraphCard
|
|||||||
mode: "nearest",
|
mode: "nearest",
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (context) =>
|
label: (context) =>
|
||||||
`${context.dataset.label}: ${
|
`${context.dataset.label}: ${formatNumber(
|
||||||
Math.round(context.parsed.x * 100) / 100
|
context.parsed.x,
|
||||||
} kWh`,
|
this.hass.locale
|
||||||
|
)} kWh`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// @ts-expect-error
|
||||||
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +246,9 @@ export class HuiEnergyDevicesGraphCard
|
|||||||
ha-card {
|
ha-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.card-header {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { css, html, LitElement, svg } 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 { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { round } from "../../../../common/number/round";
|
import { formatNumber } from "../../../../common/string/format_number";
|
||||||
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-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
@ -140,13 +140,9 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (highCarbonConsumption !== null) {
|
if (highCarbonConsumption !== null) {
|
||||||
const gridPctHighCarbon = highCarbonConsumption / totalConsumption;
|
lowCarbonConsumption = totalGridConsumption - highCarbonConsumption;
|
||||||
|
|
||||||
lowCarbonConsumption =
|
const homePctGridHighCarbon = highCarbonConsumption / totalConsumption;
|
||||||
totalGridConsumption - totalGridConsumption * gridPctHighCarbon;
|
|
||||||
|
|
||||||
const homePctGridHighCarbon =
|
|
||||||
(gridPctHighCarbon * totalGridConsumption) / totalConsumption;
|
|
||||||
|
|
||||||
homeHighCarbonCircumference =
|
homeHighCarbonCircumference =
|
||||||
CIRCLE_CIRCUMFERENCE * homePctGridHighCarbon;
|
CIRCLE_CIRCUMFERENCE * homePctGridHighCarbon;
|
||||||
@ -158,6 +154,15 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
homeSolarCircumference = CIRCLE_CIRCUMFERENCE * 0.1;
|
||||||
|
|
||||||
|
homeHighCarbonCircumference = CIRCLE_CIRCUMFERENCE * 0.8;
|
||||||
|
|
||||||
|
homeLowCarbonCircumference =
|
||||||
|
CIRCLE_CIRCUMFERENCE -
|
||||||
|
(homeSolarCircumference || 0) -
|
||||||
|
homeHighCarbonCircumference;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card .header=${this._config.title}>
|
<ha-card .header=${this._config.title}>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -165,8 +170,7 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
? html`<div class="row">
|
? html`<div class="row">
|
||||||
${lowCarbonConsumption === undefined
|
${lowCarbonConsumption === undefined
|
||||||
? html`<div class="spacer"></div>`
|
? html`<div class="spacer"></div>`
|
||||||
: html`
|
: html`<div class="circle-container low-carbon">
|
||||||
<div class="circle-container low-carbon">
|
|
||||||
<span class="label">Non-fossil</span>
|
<span class="label">Non-fossil</span>
|
||||||
<a
|
<a
|
||||||
class="circle"
|
class="circle"
|
||||||
@ -175,19 +179,30 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
rel="noopener no referrer"
|
rel="noopener no referrer"
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path="${mdiLeaf}"></ha-svg-icon>
|
<ha-svg-icon .path="${mdiLeaf}"></ha-svg-icon>
|
||||||
${round(lowCarbonConsumption, 1)} kWh
|
${lowCarbonConsumption
|
||||||
|
? formatNumber(
|
||||||
|
lowCarbonConsumption,
|
||||||
|
this.hass.locale,
|
||||||
|
{ maximumFractionDigits: 1 }
|
||||||
|
)
|
||||||
|
: "-"}
|
||||||
|
kWh
|
||||||
</a>
|
</a>
|
||||||
<svg width="80" height="30">
|
<svg width="80" height="30">
|
||||||
<line x1="40" y1="0" x2="40" y2="30"></line>
|
<line x1="40" y1="0" x2="40" y2="30"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>`}
|
||||||
`}
|
|
||||||
${hasSolarProduction
|
${hasSolarProduction
|
||||||
? html`<div class="circle-container solar">
|
? html`<div class="circle-container solar">
|
||||||
<span class="label">Solar</span>
|
<span class="label">Solar</span>
|
||||||
<div class="circle">
|
<div class="circle">
|
||||||
<ha-svg-icon .path="${mdiSolarPower}"></ha-svg-icon>
|
<ha-svg-icon .path="${mdiSolarPower}"></ha-svg-icon>
|
||||||
${round(totalSolarProduction || 0, 1)} kWh
|
${formatNumber(
|
||||||
|
totalSolarProduction || 0,
|
||||||
|
this.hass.locale,
|
||||||
|
{ maximumFractionDigits: 1 }
|
||||||
|
)}
|
||||||
|
kWh
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
@ -204,7 +219,11 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
class="small"
|
class="small"
|
||||||
.path=${mdiArrowRight}
|
.path=${mdiArrowRight}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: ""}${round(totalGridConsumption, 1)}
|
: ""}${formatNumber(
|
||||||
|
totalGridConsumption,
|
||||||
|
this.hass.locale,
|
||||||
|
{ maximumFractionDigits: 1 }
|
||||||
|
)}
|
||||||
kWh
|
kWh
|
||||||
</span>
|
</span>
|
||||||
${productionReturnedToGrid !== null
|
${productionReturnedToGrid !== null
|
||||||
@ -213,7 +232,12 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
class="small"
|
class="small"
|
||||||
.path=${mdiArrowLeft}
|
.path=${mdiArrowLeft}
|
||||||
></ha-svg-icon
|
></ha-svg-icon
|
||||||
>${round(productionReturnedToGrid, 1)} kWh
|
>${formatNumber(
|
||||||
|
productionReturnedToGrid,
|
||||||
|
this.hass.locale,
|
||||||
|
{ maximumFractionDigits: 1 }
|
||||||
|
)}
|
||||||
|
kWh
|
||||||
</span>`
|
</span>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@ -228,13 +252,15 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path="${mdiHome}"></ha-svg-icon>
|
<ha-svg-icon .path="${mdiHome}"></ha-svg-icon>
|
||||||
${round(totalConsumption, 1)} kWh
|
${formatNumber(totalConsumption, this.hass.locale, {
|
||||||
|
maximumFractionDigits: 1,
|
||||||
|
})}
|
||||||
|
kWh
|
||||||
${homeSolarCircumference !== undefined ||
|
${homeSolarCircumference !== undefined ||
|
||||||
homeLowCarbonCircumference !== undefined
|
homeLowCarbonCircumference !== undefined
|
||||||
? html`<svg>
|
? html`<svg>
|
||||||
${homeSolarCircumference !== undefined
|
${homeSolarCircumference !== undefined
|
||||||
? svg`
|
? svg`<circle
|
||||||
<circle
|
|
||||||
class="solar"
|
class="solar"
|
||||||
cx="40"
|
cx="40"
|
||||||
cy="40"
|
cy="40"
|
||||||
@ -243,23 +269,24 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
CIRCLE_CIRCUMFERENCE - homeSolarCircumference
|
CIRCLE_CIRCUMFERENCE - homeSolarCircumference
|
||||||
}"
|
}"
|
||||||
shape-rendering="geometricPrecision"
|
shape-rendering="geometricPrecision"
|
||||||
stroke-dashoffset="0"
|
stroke-dashoffset="-${
|
||||||
|
CIRCLE_CIRCUMFERENCE - homeSolarCircumference
|
||||||
|
}"
|
||||||
/>`
|
/>`
|
||||||
: ""}
|
: ""}
|
||||||
${homeHighCarbonCircumference
|
${homeLowCarbonCircumference
|
||||||
? svg`
|
? svg`<circle
|
||||||
<circle
|
|
||||||
class="low-carbon"
|
class="low-carbon"
|
||||||
cx="40"
|
cx="40"
|
||||||
cy="40"
|
cy="40"
|
||||||
r="38"
|
r="38"
|
||||||
stroke-dasharray="${homeLowCarbonCircumference} ${
|
stroke-dasharray="${homeLowCarbonCircumference} ${
|
||||||
CIRCLE_CIRCUMFERENCE - homeLowCarbonCircumference!
|
CIRCLE_CIRCUMFERENCE - homeLowCarbonCircumference
|
||||||
}"
|
}"
|
||||||
stroke-dashoffset="${
|
stroke-dashoffset="-${
|
||||||
((homeSolarCircumference || 0) +
|
CIRCLE_CIRCUMFERENCE -
|
||||||
homeHighCarbonCircumference!) *
|
homeLowCarbonCircumference -
|
||||||
-1
|
(homeSolarCircumference || 0)
|
||||||
}"
|
}"
|
||||||
shape-rendering="geometricPrecision"
|
shape-rendering="geometricPrecision"
|
||||||
/>`
|
/>`
|
||||||
@ -271,11 +298,11 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
r="38"
|
r="38"
|
||||||
stroke-dasharray="${homeHighCarbonCircumference ??
|
stroke-dasharray="${homeHighCarbonCircumference ??
|
||||||
CIRCLE_CIRCUMFERENCE -
|
CIRCLE_CIRCUMFERENCE -
|
||||||
homeSolarCircumference!} ${homeHighCarbonCircumference
|
homeSolarCircumference!} ${homeHighCarbonCircumference !==
|
||||||
|
undefined
|
||||||
? CIRCLE_CIRCUMFERENCE - homeHighCarbonCircumference
|
? CIRCLE_CIRCUMFERENCE - homeHighCarbonCircumference
|
||||||
: homeSolarCircumference}"
|
: homeSolarCircumference}"
|
||||||
stroke-dashoffset="${(homeSolarCircumference || 0) *
|
stroke-dashoffset="0"
|
||||||
-1}"
|
|
||||||
shape-rendering="geometricPrecision"
|
shape-rendering="geometricPrecision"
|
||||||
/>
|
/>
|
||||||
</svg>`
|
</svg>`
|
||||||
@ -315,7 +342,11 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
vector-effect="non-scaling-stroke"
|
vector-effect="non-scaling-stroke"
|
||||||
></path>
|
></path>
|
||||||
${productionReturnedToGrid && hasSolarProduction
|
${productionReturnedToGrid && hasSolarProduction
|
||||||
? svg`<circle r="1" class="return" vector-effect="non-scaling-stroke">
|
? svg`<circle
|
||||||
|
r="1"
|
||||||
|
class="return"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
>
|
||||||
<animateMotion
|
<animateMotion
|
||||||
dur="${
|
dur="${
|
||||||
6 -
|
6 -
|
||||||
@ -332,8 +363,11 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
</circle>`
|
</circle>`
|
||||||
: ""}
|
: ""}
|
||||||
${totalSolarProduction
|
${totalSolarProduction
|
||||||
? svg`
|
? svg`<circle
|
||||||
<circle r="1" class="solar" vector-effect="non-scaling-stroke">
|
r="1"
|
||||||
|
class="solar"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
>
|
||||||
<animateMotion
|
<animateMotion
|
||||||
dur="${
|
dur="${
|
||||||
6 -
|
6 -
|
||||||
@ -351,7 +385,11 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
</circle>`
|
</circle>`
|
||||||
: ""}
|
: ""}
|
||||||
${totalGridConsumption
|
${totalGridConsumption
|
||||||
? svg`<circle r="1" class="grid" vector-effect="non-scaling-stroke">
|
? svg`<circle
|
||||||
|
r="1"
|
||||||
|
class="grid"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
>
|
||||||
<animateMotion
|
<animateMotion
|
||||||
dur="${
|
dur="${
|
||||||
6 -
|
6 -
|
||||||
@ -471,12 +509,15 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.circle-container.low-carbon {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
.circle-container.solar {
|
.circle-container.solar {
|
||||||
|
margin-left: 4px;
|
||||||
height: 130px;
|
height: 130px;
|
||||||
}
|
}
|
||||||
.spacer {
|
.spacer {
|
||||||
width: 80px;
|
width: 84px;
|
||||||
height: 30px;
|
|
||||||
}
|
}
|
||||||
.circle {
|
.circle {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
@ -519,67 +560,70 @@ class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.low-carbon line {
|
.low-carbon line {
|
||||||
stroke: #0f9d58;
|
stroke: var(--energy-non-fossil-color);
|
||||||
}
|
}
|
||||||
.low-carbon .circle {
|
.low-carbon .circle {
|
||||||
border-color: #0f9d58;
|
border-color: var(--energy-non-fossil-color);
|
||||||
}
|
}
|
||||||
.low-carbon ha-svg-icon {
|
.low-carbon ha-svg-icon {
|
||||||
color: #0f9d58;
|
color: var(--energy-non-fossil-color);
|
||||||
|
}
|
||||||
|
circle.low-carbon {
|
||||||
|
stroke: var(--energy-non-fossil-color);
|
||||||
|
fill: var(--energy-non-fossil-color);
|
||||||
}
|
}
|
||||||
.solar .circle {
|
.solar .circle {
|
||||||
border-color: #ff9800;
|
border-color: var(--energy-solar-color);
|
||||||
}
|
}
|
||||||
circle.solar,
|
circle.solar,
|
||||||
path.solar {
|
path.solar {
|
||||||
stroke: #ff9800;
|
stroke: var(--energy-solar-color);
|
||||||
}
|
}
|
||||||
circle.solar {
|
circle.solar {
|
||||||
stroke-width: 4;
|
stroke-width: 4;
|
||||||
fill: #ff9800;
|
fill: var(--energy-solar-color);
|
||||||
}
|
|
||||||
circle.low-carbon {
|
|
||||||
stroke: #0f9d58;
|
|
||||||
fill: #0f9d58;
|
|
||||||
}
|
}
|
||||||
path.return,
|
path.return,
|
||||||
circle.return {
|
circle.return {
|
||||||
stroke: #673ab7;
|
stroke: var(--energy-grid-return-color);
|
||||||
}
|
}
|
||||||
circle.return {
|
circle.return {
|
||||||
stroke-width: 4;
|
stroke-width: 4;
|
||||||
fill: #673ab7;
|
fill: var(--energy-grid-return-color);
|
||||||
}
|
}
|
||||||
.return {
|
.return {
|
||||||
color: #673ab7;
|
color: var(--energy-grid-return-color);
|
||||||
}
|
}
|
||||||
.grid .circle {
|
.grid .circle {
|
||||||
border-color: #126a9a;
|
border-color: var(--energy-grid-consumption-color);
|
||||||
}
|
}
|
||||||
.consumption {
|
.consumption {
|
||||||
color: #126a9a;
|
color: var(--energy-grid-consumption-color);
|
||||||
}
|
}
|
||||||
circle.grid,
|
circle.grid,
|
||||||
path.grid {
|
path.grid {
|
||||||
stroke: #126a9a;
|
stroke: var(--energy-grid-consumption-color);
|
||||||
}
|
}
|
||||||
circle.grid {
|
circle.grid {
|
||||||
stroke-width: 4;
|
stroke-width: 4;
|
||||||
fill: #126a9a;
|
fill: var(--energy-grid-consumption-color);
|
||||||
}
|
}
|
||||||
.home .circle {
|
.home .circle {
|
||||||
border: none;
|
border-width: 0;
|
||||||
}
|
|
||||||
.home .circle.border {
|
|
||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
.home .circle.border {
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
.circle svg circle {
|
.circle svg circle {
|
||||||
animation: rotate-in 0.2s ease-in;
|
animation: rotate-in 0.6s ease-in;
|
||||||
|
transition: stroke-dashoffset 0.4s, stroke-dasharray 0.4s;
|
||||||
fill: none;
|
fill: none;
|
||||||
}
|
}
|
||||||
@keyframes rotate-in {
|
@keyframes rotate-in {
|
||||||
from {
|
from {
|
||||||
stroke-dashoffset: 0;
|
stroke-dashoffset: 238.76104;
|
||||||
|
stroke-dasharray: 238.76104;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
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 "../../../../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";
|
||||||
@ -63,10 +62,14 @@ class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
let value: number | undefined;
|
let value: number | undefined;
|
||||||
|
|
||||||
if (productionReturnedToGrid !== null && totalSolarProduction !== null) {
|
if (productionReturnedToGrid !== null && totalSolarProduction) {
|
||||||
const cosumedSolar = totalSolarProduction - productionReturnedToGrid;
|
const cosumedSolar = Math.min(
|
||||||
value = round((cosumedSolar / totalSolarProduction) * 100);
|
0,
|
||||||
|
totalSolarProduction - productionReturnedToGrid
|
||||||
|
);
|
||||||
|
value = (cosumedSolar / totalSolarProduction) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
${value !== undefined
|
${value !== undefined
|
||||||
@ -81,7 +84,9 @@ class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
})}
|
})}
|
||||||
></ha-gauge>
|
></ha-gauge>
|
||||||
<div class="name">Self consumed solar energy</div>`
|
<div class="name">Self consumed solar energy</div>`
|
||||||
: html`Self consumed solar energy couldn't be calculated`}
|
: totalSolarProduction === 0
|
||||||
|
? "You have not produced any solar energy"
|
||||||
|
: "Self consumed solar energy couldn't be calculated"}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,10 @@ 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";
|
||||||
|
import {
|
||||||
const SOLAR_COLOR = "#FF9800";
|
formatNumber,
|
||||||
|
numberFormatToLocale,
|
||||||
|
} from "../../../../common/string/format_number";
|
||||||
|
|
||||||
@customElement("hui-energy-solar-graph-card")
|
@customElement("hui-energy-solar-graph-card")
|
||||||
export class HuiEnergySolarGraphCard
|
export class HuiEnergySolarGraphCard
|
||||||
@ -119,7 +121,10 @@ export class HuiEnergySolarGraphCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card .header="${this._config.title}">
|
<ha-card>
|
||||||
|
${this._config.title
|
||||||
|
? html`<h1 class="card-header">${this._config.title}</h1>`
|
||||||
|
: ""}
|
||||||
<div
|
<div
|
||||||
class="content ${classMap({
|
class="content ${classMap({
|
||||||
"has-header": !!this._config.title,
|
"has-header": !!this._config.title,
|
||||||
@ -166,7 +171,7 @@ export class HuiEnergySolarGraphCard
|
|||||||
: {},
|
: {},
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
tooltipFormat: "datetimeseconds",
|
tooltipFormat: "datetime",
|
||||||
},
|
},
|
||||||
offset: true,
|
offset: true,
|
||||||
},
|
},
|
||||||
@ -186,7 +191,10 @@ export class HuiEnergySolarGraphCard
|
|||||||
mode: "nearest",
|
mode: "nearest",
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (context) =>
|
label: (context) =>
|
||||||
`${context.dataset.label}: ${context.parsed.y} kWh`,
|
`${context.dataset.label}: ${formatNumber(
|
||||||
|
context.parsed.y,
|
||||||
|
this.hass.locale
|
||||||
|
)} kWh`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
filler: {
|
filler: {
|
||||||
@ -212,6 +220,8 @@ export class HuiEnergySolarGraphCard
|
|||||||
hitRadius: 5,
|
hitRadius: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// @ts-expect-error
|
||||||
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,6 +229,7 @@ export class HuiEnergySolarGraphCard
|
|||||||
if (this._fetching) {
|
if (this._fetching) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
startDate.setHours(0, 0, 0, 0);
|
startDate.setHours(0, 0, 0, 0);
|
||||||
startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint
|
startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint
|
||||||
@ -273,20 +284,25 @@ export class HuiEnergySolarGraphCard
|
|||||||
endTime = new Date();
|
endTime = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const computedStyles = getComputedStyle(this);
|
||||||
|
const solarColor = computedStyles
|
||||||
|
.getPropertyValue("--energy-solar-color")
|
||||||
|
.trim();
|
||||||
|
|
||||||
solarSources.forEach((source, idx) => {
|
solarSources.forEach((source, idx) => {
|
||||||
const data: ChartDataset<"bar" | "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(lab2rgb(labDarken(rgb2lab(hex2rgb(SOLAR_COLOR)), idx)))
|
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx)))
|
||||||
: SOLAR_COLOR;
|
: solarColor;
|
||||||
|
|
||||||
data.push({
|
data.push({
|
||||||
label: `Production ${
|
label: `Production ${
|
||||||
entity ? computeStateName(entity) : source.stat_energy_from
|
entity ? computeStateName(entity) : source.stat_energy_from
|
||||||
}`,
|
}`,
|
||||||
borderColor: borderColor,
|
borderColor,
|
||||||
backgroundColor: borderColor + "7F",
|
backgroundColor: borderColor + "7F",
|
||||||
data: [],
|
data: [],
|
||||||
});
|
});
|
||||||
@ -307,7 +323,7 @@ export class HuiEnergySolarGraphCard
|
|||||||
if (prevStart === point.start) {
|
if (prevStart === point.start) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const value = Math.round((point.sum - prevValue) * 100) / 100;
|
const value = point.sum - prevValue;
|
||||||
const date = new Date(point.start);
|
const date = new Date(point.start);
|
||||||
data[0].data.push({
|
data[0].data.push({
|
||||||
x: date.getTime(),
|
x: date.getTime(),
|
||||||
@ -347,7 +363,9 @@ export class HuiEnergySolarGraphCard
|
|||||||
}`,
|
}`,
|
||||||
fill: false,
|
fill: false,
|
||||||
stepped: false,
|
stepped: false,
|
||||||
borderColor: "#000",
|
borderColor: computedStyles.getPropertyValue(
|
||||||
|
"--primary-text-color"
|
||||||
|
),
|
||||||
borderDash: [7, 5],
|
borderDash: [7, 5],
|
||||||
pointRadius: 0,
|
pointRadius: 0,
|
||||||
data: [],
|
data: [],
|
||||||
@ -386,6 +404,9 @@ export class HuiEnergySolarGraphCard
|
|||||||
ha-card {
|
ha-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.card-header {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,426 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import dataTableStyles from "@material/data-table/dist/mdc.data-table.min.css";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
unsafeCSS,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { styleMap } from "lit/directives/style-map";
|
||||||
|
import {
|
||||||
|
rgb2hex,
|
||||||
|
lab2rgb,
|
||||||
|
rgb2lab,
|
||||||
|
hex2rgb,
|
||||||
|
} from "../../../../common/color/convert-color";
|
||||||
|
import { labDarken } from "../../../../common/color/lab";
|
||||||
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
|
import { formatNumber } from "../../../../common/string/format_number";
|
||||||
|
import "../../../../components/chart/statistics-chart";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import {
|
||||||
|
EnergyInfo,
|
||||||
|
energySourcesByType,
|
||||||
|
getEnergyInfo,
|
||||||
|
} from "../../../../data/energy";
|
||||||
|
import {
|
||||||
|
calculateStatisticSumGrowth,
|
||||||
|
fetchStatistics,
|
||||||
|
Statistics,
|
||||||
|
} from "../../../../data/history";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { LovelaceCard } from "../../types";
|
||||||
|
import { EnergySourcesTableCardConfig } from "../types";
|
||||||
|
|
||||||
|
@customElement("hui-energy-sources-table-card")
|
||||||
|
export class HuiEnergySourcesTableCard
|
||||||
|
extends LitElement
|
||||||
|
implements LovelaceCard
|
||||||
|
{
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: EnergySourcesTableCardConfig;
|
||||||
|
|
||||||
|
@state() private _stats?: Statistics;
|
||||||
|
|
||||||
|
@state() private _energyInfo?: EnergyInfo;
|
||||||
|
|
||||||
|
public getCardSize(): Promise<number> | number {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setConfig(config: EnergySourcesTableCardConfig): void {
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public willUpdate() {
|
||||||
|
if (!this.hasUpdated) {
|
||||||
|
this._getEnergyInfo().then(() => this._getStatistics());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass || !this._config) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._stats) {
|
||||||
|
return html`Loading...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalGrid = 0;
|
||||||
|
let totalSolar = 0;
|
||||||
|
let totalCost = 0;
|
||||||
|
|
||||||
|
const types = energySourcesByType(this._config.prefs);
|
||||||
|
|
||||||
|
const computedStyles = getComputedStyle(this);
|
||||||
|
const solarColor = computedStyles
|
||||||
|
.getPropertyValue("--energy-solar-color")
|
||||||
|
.trim();
|
||||||
|
const returnColor = computedStyles
|
||||||
|
.getPropertyValue("--energy-grid-return-color")
|
||||||
|
.trim();
|
||||||
|
const consumptionColor = computedStyles
|
||||||
|
.getPropertyValue("--energy-grid-consumption-color")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const showCosts =
|
||||||
|
types.grid?.[0].flow_from.some(
|
||||||
|
(flow) =>
|
||||||
|
flow.stat_cost || flow.entity_energy_price || flow.number_energy_price
|
||||||
|
) ||
|
||||||
|
types.grid?.[0].flow_to.some(
|
||||||
|
(flow) =>
|
||||||
|
flow.stat_compensation ||
|
||||||
|
flow.entity_energy_price ||
|
||||||
|
flow.number_energy_price
|
||||||
|
);
|
||||||
|
|
||||||
|
return html` <ha-card>
|
||||||
|
${this._config.title
|
||||||
|
? html`<h1 class="card-header">${this._config.title}</h1>`
|
||||||
|
: ""}
|
||||||
|
<div class="mdc-data-table">
|
||||||
|
<div class="mdc-data-table__table-container">
|
||||||
|
<table class="mdc-data-table__table" aria-label="Dessert calories">
|
||||||
|
<thead>
|
||||||
|
<tr class="mdc-data-table__header-row">
|
||||||
|
<th class="mdc-data-table__header-cell"></th>
|
||||||
|
<th
|
||||||
|
class="mdc-data-table__header-cell"
|
||||||
|
role="columnheader"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
Source
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="mdc-data-table__header-cell mdc-data-table__header-cell--numeric"
|
||||||
|
role="columnheader"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
Energy
|
||||||
|
</th>
|
||||||
|
${showCosts
|
||||||
|
? html` <th
|
||||||
|
class="mdc-data-table__header-cell mdc-data-table__header-cell--numeric"
|
||||||
|
role="columnheader"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
Cost
|
||||||
|
</th>`
|
||||||
|
: ""}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="mdc-data-table__content">
|
||||||
|
${types.solar?.map((source, idx) => {
|
||||||
|
const entity = this.hass.states[source.stat_energy_from];
|
||||||
|
const energy =
|
||||||
|
calculateStatisticSumGrowth(
|
||||||
|
this._stats![source.stat_energy_from]
|
||||||
|
) || 0;
|
||||||
|
totalSolar += energy;
|
||||||
|
const color =
|
||||||
|
idx > 0
|
||||||
|
? rgb2hex(
|
||||||
|
lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx))
|
||||||
|
)
|
||||||
|
: solarColor;
|
||||||
|
return html`<tr class="mdc-data-table__row">
|
||||||
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
|
<div
|
||||||
|
class="bullet"
|
||||||
|
style=${styleMap({
|
||||||
|
borderColor: color,
|
||||||
|
backgroundColor: color + "7F",
|
||||||
|
})}
|
||||||
|
></div>
|
||||||
|
</td>
|
||||||
|
<th class="mdc-data-table__cell" scope="row">
|
||||||
|
${entity
|
||||||
|
? computeStateName(entity)
|
||||||
|
: source.stat_energy_from}
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${formatNumber(energy, this.hass.locale)} kWh
|
||||||
|
</td>
|
||||||
|
${showCosts
|
||||||
|
? html`<td class="mdc-data-table__cell"></td>`
|
||||||
|
: ""}
|
||||||
|
</tr>`;
|
||||||
|
})}
|
||||||
|
${types.solar
|
||||||
|
? html`<tr class="mdc-data-table__row total">
|
||||||
|
<td class="mdc-data-table__cell"></td>
|
||||||
|
<th class="mdc-data-table__cell" scope="row">
|
||||||
|
Solar total
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${formatNumber(totalSolar, this.hass.locale)} kWh
|
||||||
|
</td>
|
||||||
|
${showCosts
|
||||||
|
? html`<td class="mdc-data-table__cell"></td>`
|
||||||
|
: ""}
|
||||||
|
</tr>`
|
||||||
|
: ""}
|
||||||
|
${types.grid?.map(
|
||||||
|
(source) => html`${source.flow_from.map((flow, idx) => {
|
||||||
|
const entity = this.hass.states[flow.stat_energy_from];
|
||||||
|
const energy =
|
||||||
|
calculateStatisticSumGrowth(
|
||||||
|
this._stats![flow.stat_energy_from]
|
||||||
|
) || 0;
|
||||||
|
totalGrid += energy;
|
||||||
|
const cost_stat =
|
||||||
|
flow.stat_cost ||
|
||||||
|
this._energyInfo!.cost_sensors[flow.stat_energy_from];
|
||||||
|
const cost = cost_stat
|
||||||
|
? calculateStatisticSumGrowth(this._stats![cost_stat])
|
||||||
|
: null;
|
||||||
|
if (cost !== null) {
|
||||||
|
totalCost += cost;
|
||||||
|
}
|
||||||
|
const color =
|
||||||
|
idx > 0
|
||||||
|
? rgb2hex(
|
||||||
|
lab2rgb(
|
||||||
|
labDarken(rgb2lab(hex2rgb(consumptionColor)), idx)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: consumptionColor;
|
||||||
|
return html`<tr class="mdc-data-table__row">
|
||||||
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
|
<div
|
||||||
|
class="bullet"
|
||||||
|
style=${styleMap({
|
||||||
|
borderColor: color,
|
||||||
|
backgroundColor: color + "7F",
|
||||||
|
})}
|
||||||
|
></div>
|
||||||
|
</td>
|
||||||
|
<th class="mdc-data-table__cell" scope="row">
|
||||||
|
${entity
|
||||||
|
? computeStateName(entity)
|
||||||
|
: flow.stat_energy_from}
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${formatNumber(energy, this.hass.locale)} kWh
|
||||||
|
</td>
|
||||||
|
${showCosts
|
||||||
|
? html` <td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${cost !== null
|
||||||
|
? formatNumber(cost, this.hass.locale, {
|
||||||
|
style: "currency",
|
||||||
|
currency: this.hass.config.currency!,
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
</td>`
|
||||||
|
: ""}
|
||||||
|
</tr>`;
|
||||||
|
})}
|
||||||
|
${source.flow_to.map((flow, idx) => {
|
||||||
|
const entity = this.hass.states[flow.stat_energy_to];
|
||||||
|
const energy =
|
||||||
|
(calculateStatisticSumGrowth(
|
||||||
|
this._stats![flow.stat_energy_to]
|
||||||
|
) || 0) * -1;
|
||||||
|
totalGrid += energy;
|
||||||
|
const cost_stat =
|
||||||
|
flow.stat_compensation ||
|
||||||
|
this._energyInfo!.cost_sensors[flow.stat_energy_to];
|
||||||
|
const cost = cost_stat
|
||||||
|
? calculateStatisticSumGrowth(this._stats![cost_stat])
|
||||||
|
: null;
|
||||||
|
if (cost !== null) {
|
||||||
|
totalCost += cost;
|
||||||
|
}
|
||||||
|
const color =
|
||||||
|
idx > 0
|
||||||
|
? rgb2hex(
|
||||||
|
lab2rgb(labDarken(rgb2lab(hex2rgb(returnColor)), idx))
|
||||||
|
)
|
||||||
|
: returnColor;
|
||||||
|
return html`<tr class="mdc-data-table__row">
|
||||||
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
|
<div
|
||||||
|
class="bullet"
|
||||||
|
style=${styleMap({
|
||||||
|
borderColor: color,
|
||||||
|
backgroundColor: color + "7F",
|
||||||
|
})}
|
||||||
|
></div>
|
||||||
|
</td>
|
||||||
|
<th class="mdc-data-table__cell" scope="row">
|
||||||
|
${entity ? computeStateName(entity) : flow.stat_energy_to}
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${formatNumber(energy, this.hass.locale)} kWh
|
||||||
|
</td>
|
||||||
|
${showCosts
|
||||||
|
? html` <td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${cost !== null
|
||||||
|
? formatNumber(cost, this.hass.locale, {
|
||||||
|
style: "currency",
|
||||||
|
currency: this.hass.config.currency!,
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
</td>`
|
||||||
|
: ""}
|
||||||
|
</tr>`;
|
||||||
|
})}`
|
||||||
|
)}
|
||||||
|
${types.grid
|
||||||
|
? html` <tr class="mdc-data-table__row total">
|
||||||
|
<td class="mdc-data-table__cell"></td>
|
||||||
|
<th class="mdc-data-table__cell" scope="row">Grid total</th>
|
||||||
|
<td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${formatNumber(totalGrid, this.hass.locale)} kWh
|
||||||
|
</td>
|
||||||
|
${showCosts
|
||||||
|
? html`<td
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--numeric"
|
||||||
|
>
|
||||||
|
${formatNumber(totalCost, this.hass.locale, {
|
||||||
|
style: "currency",
|
||||||
|
currency: this.hass.config.currency!,
|
||||||
|
})}
|
||||||
|
</td>`
|
||||||
|
: ""}
|
||||||
|
</tr>`
|
||||||
|
: ""}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ha-card>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getEnergyInfo() {
|
||||||
|
this._energyInfo = await getEnergyInfo(this.hass);
|
||||||
|
}
|
||||||
|
|
||||||
|
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[] = Object.values(this._energyInfo!.cost_sensors);
|
||||||
|
const prefs = this._config!.prefs;
|
||||||
|
for (const source of prefs.energy_sources) {
|
||||||
|
if (source.type === "solar") {
|
||||||
|
statistics.push(source.stat_energy_from);
|
||||||
|
} else {
|
||||||
|
// grid source
|
||||||
|
for (const flowFrom of source.flow_from) {
|
||||||
|
statistics.push(flowFrom.stat_energy_from);
|
||||||
|
if (flowFrom.stat_cost) {
|
||||||
|
statistics.push(flowFrom.stat_cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const flowTo of source.flow_to) {
|
||||||
|
statistics.push(flowTo.stat_energy_to);
|
||||||
|
if (flowTo.stat_compensation) {
|
||||||
|
statistics.push(flowTo.stat_compensation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._stats = await fetchStatistics(
|
||||||
|
this.hass!,
|
||||||
|
startDate,
|
||||||
|
undefined,
|
||||||
|
statistics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
${unsafeCSS(dataTableStyles)}
|
||||||
|
.mdc-data-table {
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.mdc-data-table__header-cell,
|
||||||
|
.mdc-data-table__cell {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
|
.mdc-data-table__row:not(.mdc-data-table__row--selected):hover {
|
||||||
|
background-color: rgba(var(--rgb-primary-text-color), 0.04);
|
||||||
|
}
|
||||||
|
.total {
|
||||||
|
--mdc-typography-body2-font-weight: 500;
|
||||||
|
}
|
||||||
|
.total .mdc-data-table__cell {
|
||||||
|
border-top: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.card-header {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.has-header {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
.cell-bullet {
|
||||||
|
width: 32px;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.bullet {
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 16px;
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-energy-sources-table-card": HuiEnergySourcesTableCard;
|
||||||
|
}
|
||||||
|
}
|
@ -9,39 +9,34 @@ 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 { styleMap } from "lit/directives/style-map";
|
|
||||||
import {
|
import {
|
||||||
hex2rgb,
|
hex2rgb,
|
||||||
lab2rgb,
|
lab2rgb,
|
||||||
rgb2hex,
|
rgb2hex,
|
||||||
rgb2lab,
|
rgb2lab,
|
||||||
} from "../../../../common/color/convert-color";
|
} from "../../../../common/color/convert-color";
|
||||||
|
import { hexBlend } from "../../../../common/color/hex";
|
||||||
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 { round } from "../../../../common/number/round";
|
import {
|
||||||
import { formatNumber } from "../../../../common/string/format_number";
|
formatNumber,
|
||||||
|
numberFormatToLocale,
|
||||||
|
} from "../../../../common/string/format_number";
|
||||||
import "../../../../components/chart/ha-chart-base";
|
import "../../../../components/chart/ha-chart-base";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import { fetchStatistics, Statistics } from "../../../../data/history";
|
import { fetchStatistics, Statistics } from "../../../../data/history";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCard } from "../../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { EnergySummaryGraphCardConfig } from "../types";
|
import { EnergyUsageGraphCardConfig } from "../types";
|
||||||
|
|
||||||
const NEGATIVE = ["to_grid"];
|
@customElement("hui-energy-usage-graph-card")
|
||||||
const COLORS = {
|
export class HuiEnergyUsageGraphCard
|
||||||
to_grid: { border: "#673ab7", background: "#b39bdb" },
|
|
||||||
from_grid: { border: "#126A9A", background: "#8ab5cd" },
|
|
||||||
used_solar: { border: "#FF9800", background: "#fecc8e" },
|
|
||||||
};
|
|
||||||
|
|
||||||
@customElement("hui-energy-summary-graph-card")
|
|
||||||
export class HuiEnergySummaryGraphCard
|
|
||||||
extends LitElement
|
extends LitElement
|
||||||
implements LovelaceCard
|
implements LovelaceCard
|
||||||
{
|
{
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _config?: EnergySummaryGraphCardConfig;
|
@state() private _config?: EnergyUsageGraphCardConfig;
|
||||||
|
|
||||||
@state() private _data?: Statistics;
|
@state() private _data?: Statistics;
|
||||||
|
|
||||||
@ -81,7 +76,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: EnergySummaryGraphCardConfig): void {
|
public setConfig(config: EnergyUsageGraphCardConfig): void {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +90,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
const oldConfig = changedProps.get("_config") as
|
const oldConfig = changedProps.get("_config") as
|
||||||
| EnergySummaryGraphCardConfig
|
| EnergyUsageGraphCardConfig
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
if (oldConfig !== this._config) {
|
if (oldConfig !== this._config) {
|
||||||
@ -116,42 +111,14 @@ export class HuiEnergySummaryGraphCard
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<h1 class="card-header">${this._config.title}</h1>
|
${this._config.title
|
||||||
|
? html`<h1 class="card-header">${this._config.title}</h1>`
|
||||||
|
: ""}
|
||||||
<div
|
<div
|
||||||
class="content ${classMap({
|
class="content ${classMap({
|
||||||
"has-header": !!this._config.title,
|
"has-header": !!this._config.title,
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
<div class="chartLegend">
|
|
||||||
<ul>
|
|
||||||
${this._chartData.datasets.map(
|
|
||||||
(dataset) => html`<li>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="bullet"
|
|
||||||
style=${styleMap({
|
|
||||||
backgroundColor: dataset.backgroundColor as string,
|
|
||||||
borderColor: dataset.borderColor as string,
|
|
||||||
})}
|
|
||||||
></div>
|
|
||||||
<span class="label">${dataset.label}</span>
|
|
||||||
</div>
|
|
||||||
<span class="value"
|
|
||||||
>${formatNumber(
|
|
||||||
Math.abs(
|
|
||||||
dataset.data.reduce(
|
|
||||||
(total, point) => total + (point as any).y,
|
|
||||||
0
|
|
||||||
) as number
|
|
||||||
),
|
|
||||||
this.hass.locale
|
|
||||||
)}
|
|
||||||
kWh</span
|
|
||||||
>
|
|
||||||
</li>`
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<ha-chart-base
|
<ha-chart-base
|
||||||
.data=${this._chartData}
|
.data=${this._chartData}
|
||||||
.options=${this._chartOptions}
|
.options=${this._chartOptions}
|
||||||
@ -193,7 +160,7 @@ export class HuiEnergySummaryGraphCard
|
|||||||
: {},
|
: {},
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
tooltipFormat: "datetimeseconds",
|
tooltipFormat: "datetime",
|
||||||
},
|
},
|
||||||
offset: true,
|
offset: true,
|
||||||
},
|
},
|
||||||
@ -206,7 +173,8 @@ export class HuiEnergySummaryGraphCard
|
|||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
callback: (value) => Math.abs(round(value)),
|
callback: (value) =>
|
||||||
|
formatNumber(Math.abs(value), this.hass.locale),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -218,7 +186,10 @@ export class HuiEnergySummaryGraphCard
|
|||||||
filter: (val) => val.formattedValue !== "0",
|
filter: (val) => val.formattedValue !== "0",
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (context) =>
|
label: (context) =>
|
||||||
`${context.dataset.label}: ${Math.abs(context.parsed.y)} kWh`,
|
`${context.dataset.label}: ${formatNumber(
|
||||||
|
Math.abs(context.parsed.y),
|
||||||
|
this.hass.locale
|
||||||
|
)} kWh`,
|
||||||
footer: (contexts) => {
|
footer: (contexts) => {
|
||||||
let totalConsumed = 0;
|
let totalConsumed = 0;
|
||||||
let totalReturned = 0;
|
let totalReturned = 0;
|
||||||
@ -233,10 +204,16 @@ export class HuiEnergySummaryGraphCard
|
|||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
totalConsumed
|
totalConsumed
|
||||||
? `Total consumed: ${totalConsumed.toFixed(2)} kWh`
|
? `Total consumed: ${formatNumber(
|
||||||
|
totalConsumed,
|
||||||
|
this.hass.locale
|
||||||
|
)} kWh`
|
||||||
: "",
|
: "",
|
||||||
totalReturned
|
totalReturned
|
||||||
? `Total returned: ${totalReturned.toFixed(2)} kWh`
|
? `Total returned: ${formatNumber(
|
||||||
|
totalReturned,
|
||||||
|
this.hass.locale
|
||||||
|
)} kWh`
|
||||||
: "",
|
: "",
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
},
|
},
|
||||||
@ -261,6 +238,8 @@ export class HuiEnergySummaryGraphCard
|
|||||||
hitRadius: 5,
|
hitRadius: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// @ts-expect-error
|
||||||
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +323,23 @@ export class HuiEnergySummaryGraphCard
|
|||||||
} = {};
|
} = {};
|
||||||
const summedData: { [key: string]: { [start: string]: number } } = {};
|
const summedData: { [key: string]: { [start: string]: number } } = {};
|
||||||
|
|
||||||
|
const computedStyles = getComputedStyle(this);
|
||||||
|
const colors = {
|
||||||
|
to_grid: computedStyles
|
||||||
|
.getPropertyValue("--energy-grid-return-color")
|
||||||
|
.trim(),
|
||||||
|
from_grid: computedStyles
|
||||||
|
.getPropertyValue("--energy-grid-consumption-color")
|
||||||
|
.trim(),
|
||||||
|
used_solar: computedStyles
|
||||||
|
.getPropertyValue("--energy-solar-color")
|
||||||
|
.trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const backgroundColor = computedStyles
|
||||||
|
.getPropertyValue("--card-background-color")
|
||||||
|
.trim();
|
||||||
|
|
||||||
Object.entries(statistics).forEach(([key, statIds]) => {
|
Object.entries(statistics).forEach(([key, statIds]) => {
|
||||||
const sum = ["solar", "to_grid"].includes(key);
|
const sum = ["solar", "to_grid"].includes(key);
|
||||||
const add = key !== "solar";
|
const add = key !== "solar";
|
||||||
@ -407,12 +403,13 @@ export class HuiEnergySummaryGraphCard
|
|||||||
const uniqueKeys = Array.from(new Set(allKeys));
|
const uniqueKeys = Array.from(new Set(allKeys));
|
||||||
|
|
||||||
Object.entries(combinedData).forEach(([type, sources]) => {
|
Object.entries(combinedData).forEach(([type, sources]) => {
|
||||||
const negative = NEGATIVE.includes(type);
|
|
||||||
|
|
||||||
Object.entries(sources).forEach(([statId, source], idx) => {
|
Object.entries(sources).forEach(([statId, source], idx) => {
|
||||||
const data: ChartDataset<"bar">[] = [];
|
const data: ChartDataset<"bar">[] = [];
|
||||||
const entity = this.hass.states[statId];
|
const entity = this.hass.states[statId];
|
||||||
const color = COLORS[type];
|
const borderColor =
|
||||||
|
idx > 0
|
||||||
|
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(colors[type])), idx)))
|
||||||
|
: colors[type];
|
||||||
|
|
||||||
data.push({
|
data.push({
|
||||||
label:
|
label:
|
||||||
@ -421,28 +418,20 @@ export class HuiEnergySummaryGraphCard
|
|||||||
: entity
|
: entity
|
||||||
? computeStateName(entity)
|
? computeStateName(entity)
|
||||||
: statId,
|
: statId,
|
||||||
borderColor:
|
borderColor,
|
||||||
idx > 0
|
backgroundColor: hexBlend(borderColor, backgroundColor, 50),
|
||||||
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(color.border)), idx)))
|
|
||||||
: color.border,
|
|
||||||
backgroundColor:
|
|
||||||
idx > 0
|
|
||||||
? rgb2hex(
|
|
||||||
lab2rgb(labDarken(rgb2lab(hex2rgb(color.background)), idx))
|
|
||||||
)
|
|
||||||
: color.background,
|
|
||||||
stack: "stack",
|
stack: "stack",
|
||||||
data: [],
|
data: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process chart data.
|
// Process chart data.
|
||||||
for (const key of uniqueKeys) {
|
for (const key of uniqueKeys) {
|
||||||
const value = key in source ? Math.round(source[key] * 100) / 100 : 0;
|
const value = source[key] || 0;
|
||||||
const date = new Date(key);
|
const date = new Date(key);
|
||||||
// @ts-expect-error
|
// @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 && type === "to_grid" ? -1 * value : value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,43 +459,12 @@ export class HuiEnergySummaryGraphCard
|
|||||||
.has-header {
|
.has-header {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
.chartLegend ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
.chartLegend li {
|
|
||||||
padding: 2px 8px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
.chartLegend li > div {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.chartLegend .bullet {
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
display: inline-block;
|
|
||||||
height: 16px;
|
|
||||||
margin-right: 6px;
|
|
||||||
width: 32px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-energy-summary-graph-card": HuiEnergySummaryGraphCard;
|
"hui-energy-usage-graph-card": HuiEnergyUsageGraphCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -101,7 +101,7 @@ export interface EnergyDistributionCardConfig extends LovelaceCardConfig {
|
|||||||
title?: string;
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
export interface EnergySummaryGraphCardConfig extends LovelaceCardConfig {
|
export interface EnergyUsageGraphCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-summary-graph";
|
type: "energy-summary-graph";
|
||||||
title?: string;
|
title?: string;
|
||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
@ -119,6 +119,12 @@ export interface EnergyDevicesGraphCardConfig extends LovelaceCardConfig {
|
|||||||
prefs: EnergyPreferences;
|
prefs: EnergyPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EnergySourcesTableCardConfig extends LovelaceCardConfig {
|
||||||
|
type: "energy-sources-table";
|
||||||
|
title?: string;
|
||||||
|
prefs: EnergyPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
|
export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
|
||||||
type: "energy-solar-consumed-gauge";
|
type: "energy-solar-consumed-gauge";
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -35,14 +35,14 @@ 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-graph": () =>
|
"energy-usage-graph": () =>
|
||||||
import("../cards/energy/hui-energy-summary-graph-card"),
|
import("../cards/energy/hui-energy-usage-graph-card"),
|
||||||
"energy-solar-graph": () =>
|
"energy-solar-graph": () =>
|
||||||
import("../cards/energy/hui-energy-solar-graph-card"),
|
import("../cards/energy/hui-energy-solar-graph-card"),
|
||||||
"energy-devices-graph": () =>
|
"energy-devices-graph": () =>
|
||||||
import("../cards/energy/hui-energy-devices-graph-card"),
|
import("../cards/energy/hui-energy-devices-graph-card"),
|
||||||
"energy-costs-table": () =>
|
"energy-sources-table": () =>
|
||||||
import("../cards/energy/hui-energy-costs-table-card"),
|
import("../cards/energy/hui-energy-sources-table-card"),
|
||||||
"energy-distribution": () =>
|
"energy-distribution": () =>
|
||||||
import("../cards/energy/hui-energy-distribution-card"),
|
import("../cards/energy/hui-energy-distribution-card"),
|
||||||
"energy-solar-consumed-gauge": () =>
|
"energy-solar-consumed-gauge": () =>
|
||||||
|
@ -82,6 +82,14 @@ documentContainer.innerHTML = `<custom-style>
|
|||||||
--state-climate-dry-color: #efbd07;
|
--state-climate-dry-color: #efbd07;
|
||||||
--state-climate-idle-color: #8a8a8a;
|
--state-climate-idle-color: #8a8a8a;
|
||||||
|
|
||||||
|
/* energy */
|
||||||
|
--energy-grid-consumption-color: #126a9a;
|
||||||
|
--energy-grid-return-color: #673ab7;
|
||||||
|
--energy-solar-color: #ff9800;
|
||||||
|
--energy-non-fossil-color: #0f9d58;
|
||||||
|
|
||||||
|
--rgb-energy-solar-color: 255, 152, 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Paper-styles color.html dependency is stripped on build.
|
Paper-styles color.html dependency is stripped on build.
|
||||||
When a default paper-style color is used, it needs to be copied
|
When a default paper-style color is used, it needs to be copied
|
||||||
|
@ -31,6 +31,7 @@ export const darkStyles = {
|
|||||||
"codemirror-property": "#C792EA",
|
"codemirror-property": "#C792EA",
|
||||||
"codemirror-qualifier": "#DECB6B",
|
"codemirror-qualifier": "#DECB6B",
|
||||||
"codemirror-type": "#DECB6B",
|
"codemirror-type": "#DECB6B",
|
||||||
|
"energy-grid-return-color": "#b39bdb",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const derivedStyles = {
|
export const derivedStyles = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user