mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-22 08:46:35 +00:00
Add self-sufficiency gauge card (#15704)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
cdd29c8bf7
commit
fa75b18a6b
@ -141,6 +141,11 @@ export class EnergyStrategy {
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
view.cards!.push({
|
||||
type: "energy-self-sufficiency-gauge",
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
// Only include if we have a grid
|
||||
|
@ -0,0 +1,257 @@
|
||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||
import { mdiInformation } from "@mdi/js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-gauge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
EnergyData,
|
||||
energySourcesByType,
|
||||
getEnergyDataCollection,
|
||||
} from "../../../../data/energy";
|
||||
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard } from "../../types";
|
||||
import { severityMap } from "../hui-gauge-card";
|
||||
import type { EnergySelfSufficiencyGaugeCardConfig } from "../types";
|
||||
|
||||
@customElement("hui-energy-self-sufficiency-gauge-card")
|
||||
class HuiEnergySelfSufficiencyGaugeCard
|
||||
extends SubscribeMixin(LitElement)
|
||||
implements LovelaceCard
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: EnergySelfSufficiencyGaugeCardConfig;
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass!, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => {
|
||||
this._data = data;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public setConfig(config: EnergySelfSufficiencyGaugeCardConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._config || !this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
if (!this._data) {
|
||||
return html`${this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.loading"
|
||||
)}`;
|
||||
}
|
||||
|
||||
const prefs = this._data.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 hasBattery = types.battery !== undefined;
|
||||
const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0;
|
||||
|
||||
const totalFromGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
||||
) ?? 0;
|
||||
|
||||
let totalSolarProduction: number | null = null;
|
||||
|
||||
if (hasSolarProduction) {
|
||||
totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
}
|
||||
|
||||
let totalBatteryIn: number | null = null;
|
||||
let totalBatteryOut: number | null = null;
|
||||
|
||||
if (hasBattery) {
|
||||
totalBatteryIn =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_to)
|
||||
) || 0;
|
||||
totalBatteryOut =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
}
|
||||
|
||||
let returnedToGrid: number | null = null;
|
||||
|
||||
if (hasReturnToGrid) {
|
||||
returnedToGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
||||
) || 0;
|
||||
}
|
||||
|
||||
let solarConsumption: number | null = null;
|
||||
if (hasSolarProduction) {
|
||||
solarConsumption =
|
||||
(totalSolarProduction || 0) -
|
||||
(returnedToGrid || 0) -
|
||||
(totalBatteryIn || 0);
|
||||
}
|
||||
|
||||
let batteryFromGrid: null | number = null;
|
||||
let batteryToGrid: null | number = null;
|
||||
if (solarConsumption !== null && solarConsumption < 0) {
|
||||
// What we returned to the grid and what went in to the battery is more than produced,
|
||||
// so we have used grid energy to fill the battery
|
||||
// or returned battery energy to the grid
|
||||
if (hasBattery) {
|
||||
batteryFromGrid = solarConsumption * -1;
|
||||
if (batteryFromGrid > totalFromGrid) {
|
||||
batteryToGrid = Math.min(0, batteryFromGrid - totalFromGrid);
|
||||
batteryFromGrid = totalFromGrid;
|
||||
}
|
||||
}
|
||||
solarConsumption = 0;
|
||||
}
|
||||
|
||||
let batteryConsumption: number | null = null;
|
||||
if (hasBattery) {
|
||||
batteryConsumption = (totalBatteryOut || 0) - (batteryToGrid || 0);
|
||||
}
|
||||
|
||||
const gridConsumption = Math.max(0, totalFromGrid - (batteryFromGrid || 0));
|
||||
|
||||
const totalHomeConsumption = Math.max(
|
||||
0,
|
||||
gridConsumption + (solarConsumption || 0) + (batteryConsumption || 0)
|
||||
);
|
||||
|
||||
let value: number | undefined;
|
||||
if (
|
||||
totalFromGrid !== null &&
|
||||
totalHomeConsumption !== null &&
|
||||
totalHomeConsumption > 0
|
||||
) {
|
||||
value = (1 - totalFromGrid / totalHomeConsumption) * 100;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
${value !== undefined
|
||||
? html`
|
||||
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
|
||||
<simple-tooltip animation-delay="0" for="info" position="left">
|
||||
<span>
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.self_sufficiency_gauge.card_indicates_self_sufficiency_quota"
|
||||
)}
|
||||
</span>
|
||||
</simple-tooltip>
|
||||
<ha-gauge
|
||||
min="0"
|
||||
max="100"
|
||||
.value=${value}
|
||||
.locale=${this.hass.locale}
|
||||
label="%"
|
||||
style=${styleMap({
|
||||
"--gauge-color": this._computeSeverity(value),
|
||||
})}
|
||||
></ha-gauge>
|
||||
<div class="name">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.self_sufficiency_gauge.self_sufficiency_quota"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.self_sufficiency_gauge.self_sufficiency_could_not_calc"
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _computeSeverity(numberValue: number): string {
|
||||
if (numberValue > 75) {
|
||||
return severityMap.green;
|
||||
}
|
||||
if (numberValue < 50) {
|
||||
return severityMap.yellow;
|
||||
}
|
||||
return severityMap.normal;
|
||||
}
|
||||
|
||||
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;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.name {
|
||||
text-align: center;
|
||||
line-height: initial;
|
||||
color: var(--primary-text-color);
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
simple-tooltip > span {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
}
|
||||
simple-tooltip {
|
||||
width: 80%;
|
||||
max-width: 250px;
|
||||
top: 8px !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-energy-self-sufficiency-gauge-card": HuiEnergySelfSufficiencyGaugeCard;
|
||||
}
|
||||
}
|
@ -68,10 +68,11 @@ class HuiEnergySolarGaugeCard
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const totalSolarProduction = calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar.map((source) => source.stat_energy_from)
|
||||
);
|
||||
const totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
|
||||
const productionReturnedToGrid = calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
|
@ -159,6 +159,13 @@ export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergySelfSufficiencyGaugeCardConfig
|
||||
extends LovelaceCardConfig {
|
||||
type: "energy-self-sufficiency-gauge";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergyGridGaugeCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-grid-result-gauge";
|
||||
title?: string;
|
||||
|
@ -53,6 +53,8 @@ const LAZY_LOAD_TYPES = {
|
||||
import("../cards/energy/hui-energy-grid-neutrality-gauge-card"),
|
||||
"energy-solar-consumed-gauge": () =>
|
||||
import("../cards/energy/hui-energy-solar-consumed-gauge-card"),
|
||||
"energy-self-sufficiency-gauge": () =>
|
||||
import("../cards/energy/hui-energy-self-sufficiency-gauge-card"),
|
||||
"energy-solar-graph": () =>
|
||||
import("../cards/energy/hui-energy-solar-graph-card"),
|
||||
"energy-sources-table": () =>
|
||||
|
5
src/translations/en.json
Executable file → Normal file
5
src/translations/en.json
Executable file → Normal file
@ -4219,6 +4219,11 @@
|
||||
"not_produced_solar_energy": "You have not produced any solar energy",
|
||||
"self_consumed_solar_could_not_calc": "Self-consumed solar energy couldn't be calculated"
|
||||
},
|
||||
"self_sufficiency_gauge": {
|
||||
"card_indicates_self_sufficiency_quota": "This card indicates how self-sufficient your home is.",
|
||||
"self_sufficiency_quota": "Self-sufficiency quota",
|
||||
"self_sufficiency_could_not_calc": "Self-sufficiency quota couldn't be calculated"
|
||||
},
|
||||
"grid_neutrality_gauge": {
|
||||
"energy_dependency": "This card indicates your net energy usage.",
|
||||
"color_explain": "If the needle is in the purple, you returned more energy to the grid than you consumed from it. If it's in the blue, you consumed more energy from the grid than you returned.",
|
||||
|
Loading…
x
Reference in New Issue
Block a user