Add grid neutrality gauge (#9655)

This commit is contained in:
Bram Kragten 2021-07-30 21:55:58 +02:00 committed by GitHub
parent cfad45b7c2
commit 2cdf78c504
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 208 additions and 5 deletions

View File

@ -13,7 +13,7 @@ const getAngle = (value: number, min: number, max: number) => {
return (percentage * 180) / 100;
};
interface LevelDefinition {
export interface LevelDefinition {
level: number;
stroke: string;
}
@ -26,6 +26,8 @@ export class Gauge extends LitElement {
@property({ type: Number }) public value = 0;
@property({ type: String }) public valueText?: string;
@property() public locale!: FrontendLocaleData;
@property({ type: Boolean }) public needle?: boolean;
@ -131,7 +133,9 @@ export class Gauge extends LitElement {
</svg>
<svg class="text">
<text class="value-text">
${formatNumber(this.value, this.locale)} ${this.label}
${this.valueText || formatNumber(this.value, this.locale)} ${
this.label
}
</text>
</svg>`;
}

View File

@ -1,4 +1,8 @@
import { EnergyPreferences, getEnergyPreferences } from "../../../data/energy";
import {
EnergyPreferences,
getEnergyPreferences,
GridSourceTypeEnergyPreference,
} from "../../../data/energy";
import { LovelaceViewConfig } from "../../../data/lovelace";
import { LovelaceViewStrategy } from "../../lovelace/strategies/get-strategy";
@ -39,9 +43,10 @@ export class EnergyStrategy {
view.type = "sidebar";
const hasGrid = energyPrefs.energy_sources.some(
const hasGrid = energyPrefs.energy_sources.find(
(source) => source.type === "grid"
);
) as GridSourceTypeEnergyPreference;
const hasReturn = hasGrid && hasGrid.flow_to.length;
const hasSolar = energyPrefs.energy_sources.some(
(source) => source.type === "solar"
);
@ -100,6 +105,15 @@ export class EnergyStrategy {
});
}
// Only include if we have a grid source & return.
if (hasReturn) {
view.cards!.push({
type: "energy-grid-neutrality-gauge",
prefs: energyPrefs,
view_layout: { position: "sidebar" },
});
}
// Only include if we have a grid
if (hasGrid) {
view.cards!.push({

View File

@ -0,0 +1,177 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatNumber } from "../../../../common/string/format_number";
import "../../../../components/ha-card";
import "../../../../components/ha-gauge";
import type { LevelDefinition } from "../../../../components/ha-gauge";
import { GridSourceTypeEnergyPreference } from "../../../../data/energy";
import {
calculateStatisticsSumGrowth,
fetchStatistics,
Statistics,
} from "../../../../data/history";
import type { HomeAssistant } from "../../../../types";
import type { LovelaceCard } from "../../types";
import type { EnergyGridGaugeCardConfig } from "../types";
const LEVELS: LevelDefinition[] = [
{ level: -1, stroke: "var(--label-badge-red)" },
{ level: -0.2, stroke: "var(--label-badge-yellow)" },
{ level: 0, stroke: "var(--label-badge-green)" },
];
@customElement("hui-energy-grid-neutrality-gauge-card")
class HuiEnergyGridGaugeCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: EnergyGridGaugeCardConfig;
@state() private _stats?: Statistics;
public getCardSize(): number {
return 4;
}
public setConfig(config: EnergyGridGaugeCardConfig): void {
this._config = config;
}
public willUpdate(changedProps) {
super.willUpdate(changedProps);
if (!this.hasUpdated) {
this._getStatistics();
}
}
protected render(): TemplateResult {
if (!this._config || !this.hass) {
return html``;
}
if (!this._stats) {
return html`Loading...`;
}
const prefs = this._config!.prefs;
const gridSource = prefs.energy_sources.find(
(src) => src.type === "grid"
) as GridSourceTypeEnergyPreference | undefined;
let value: number | undefined;
if (!gridSource) {
return html``;
}
const consumedFromGrid = calculateStatisticsSumGrowth(
this._stats,
gridSource.flow_from.map((flow) => flow.stat_energy_from)
);
const returnedToGrid = calculateStatisticsSumGrowth(
this._stats,
gridSource.flow_to.map((flow) => flow.stat_energy_to)
);
if (consumedFromGrid !== null && returnedToGrid !== null) {
if (returnedToGrid > consumedFromGrid) {
value = 1 - consumedFromGrid / returnedToGrid;
} else if (returnedToGrid < consumedFromGrid) {
value = (1 - returnedToGrid / consumedFromGrid) * -1;
} else {
value = 0;
}
}
return html`
<ha-card>
${value !== undefined
? html`<ha-gauge
min="-1"
max="1"
.value=${value}
.valueText=${formatNumber(
Math.abs(returnedToGrid! - consumedFromGrid!),
this.hass.locale,
{ maximumFractionDigits: 2 }
)}
.locale=${this.hass!.locale}
.levels=${LEVELS}
label="kWh"
needle
></ha-gauge>
<div class="name">
${returnedToGrid! >= consumedFromGrid!
? "Returned to the grid"
: "Consumed from the grid"}
</div>`
: "Grid neutrality could not be calculated"}
</ha-card>
`;
}
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") {
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 get styles(): CSSResultGroup {
return css`
ha-card {
height: 100%;
overflow: hidden;
padding: 16px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
box-sizing: border-box;
}
ha-gauge {
width: 100%;
max-width: 250px;
}
.name {
text-align: center;
line-height: initial;
color: var(--primary-text-color);
width: 100%;
font-size: 15px;
margin-top: 8px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-energy-grid-neutrality-gauge-card": HuiEnergyGridGaugeCard;
}
}

View File

@ -131,6 +131,12 @@ export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
prefs: EnergyPreferences;
}
export interface EnergyGridGaugeCardConfig extends LovelaceCardConfig {
type: "energy-grid-result-gauge";
title?: string;
prefs: EnergyPreferences;
}
export interface EnergyCarbonGaugeCardConfig extends LovelaceCardConfig {
type: "energy-carbon-consumed-gauge";
title?: string;

View File

@ -47,6 +47,8 @@ const LAZY_LOAD_TYPES = {
import("../cards/energy/hui-energy-distribution-card"),
"energy-solar-consumed-gauge": () =>
import("../cards/energy/hui-energy-solar-consumed-gauge-card"),
"energy-grid-neutrality-gauge": () =>
import("../cards/energy/hui-energy-grid-neutrality-gauge-card"),
"energy-carbon-consumed-gauge": () =>
import("../cards/energy/hui-energy-carbon-consumed-gauge-card"),
grid: () => import("../cards/hui-grid-card"),