mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-25 05:06:18 +00:00
Add power configuration to Energy dashboard (#27373)
Co-authored-by: Norbert Rittel <norbert@rittel.de> Co-authored-by: Aidan Timson <aidan@timmo.dev>
This commit is contained in:
@@ -21,7 +21,6 @@ import "../ha-combo-box-item";
|
||||
import "../ha-generic-picker";
|
||||
import type { HaGenericPicker } from "../ha-generic-picker";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-input-helper-text";
|
||||
import type {
|
||||
PickerComboBoxItem,
|
||||
PickerComboBoxSearchFn,
|
||||
@@ -475,6 +474,7 @@ export class HaStatisticPicker extends LitElement {
|
||||
.hideClearIcon=${this.hideClearIcon}
|
||||
.searchFn=${this._searchFn}
|
||||
.valueRenderer=${this._valueRenderer}
|
||||
.helper=${this.helper}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</ha-generic-picker>
|
||||
|
||||
@@ -102,6 +102,7 @@ export type EnergySolarForecasts = Record<string, EnergySolarForecast>;
|
||||
export interface DeviceConsumptionEnergyPreference {
|
||||
// This is an ever increasing value
|
||||
stat_consumption: string;
|
||||
stat_rate?: string;
|
||||
name?: string;
|
||||
included_in_stat?: string;
|
||||
}
|
||||
@@ -130,11 +131,17 @@ export interface FlowToGridSourceEnergyPreference {
|
||||
number_energy_price: number | null;
|
||||
}
|
||||
|
||||
export interface GridPowerSourceEnergyPreference {
|
||||
// W meter
|
||||
stat_rate: string;
|
||||
}
|
||||
|
||||
export interface GridSourceTypeEnergyPreference {
|
||||
type: "grid";
|
||||
|
||||
flow_from: FlowFromGridSourceEnergyPreference[];
|
||||
flow_to: FlowToGridSourceEnergyPreference[];
|
||||
power?: GridPowerSourceEnergyPreference[];
|
||||
|
||||
cost_adjustment_day: number;
|
||||
}
|
||||
@@ -143,6 +150,7 @@ export interface SolarSourceTypeEnergyPreference {
|
||||
type: "solar";
|
||||
|
||||
stat_energy_from: string;
|
||||
stat_rate?: string;
|
||||
config_entry_solar_forecast: string[] | null;
|
||||
}
|
||||
|
||||
@@ -150,6 +158,7 @@ export interface BatterySourceTypeEnergyPreference {
|
||||
type: "battery";
|
||||
stat_energy_from: string;
|
||||
stat_energy_to: string;
|
||||
stat_rate?: string;
|
||||
}
|
||||
export interface GasSourceTypeEnergyPreference {
|
||||
type: "gas";
|
||||
|
||||
@@ -26,6 +26,7 @@ import type {
|
||||
EnergySource,
|
||||
FlowFromGridSourceEnergyPreference,
|
||||
FlowToGridSourceEnergyPreference,
|
||||
GridPowerSourceEnergyPreference,
|
||||
GridSourceTypeEnergyPreference,
|
||||
} from "../../../../data/energy";
|
||||
import {
|
||||
@@ -47,6 +48,7 @@ import { documentationUrl } from "../../../../util/documentation-url";
|
||||
import {
|
||||
showEnergySettingsGridFlowFromDialog,
|
||||
showEnergySettingsGridFlowToDialog,
|
||||
showEnergySettingsGridPowerDialog,
|
||||
} from "../dialogs/show-dialogs-energy";
|
||||
import "./ha-energy-validation-result";
|
||||
import { energyCardStyles } from "./styles";
|
||||
@@ -226,6 +228,58 @@ export class EnergyGridSettings extends LitElement {
|
||||
>
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
${this.hass.localize("ui.panel.config.energy.grid.grid_power")}
|
||||
</h3>
|
||||
${gridSource.power?.map((power) => {
|
||||
const entityState = this.hass.states[power.stat_rate];
|
||||
return html`
|
||||
<div class="row" .source=${power}>
|
||||
${entityState?.attributes.icon
|
||||
? html`<ha-icon
|
||||
.icon=${entityState.attributes.icon}
|
||||
></ha-icon>`
|
||||
: html`<ha-svg-icon
|
||||
.path=${mdiTransmissionTower}
|
||||
></ha-svg-icon>`}
|
||||
<span class="content"
|
||||
>${getStatisticLabel(
|
||||
this.hass,
|
||||
power.stat_rate,
|
||||
this.statsMetadata?.[power.stat_rate]
|
||||
)}</span
|
||||
>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.grid.edit_power"
|
||||
)}
|
||||
@click=${this._editPowerSource}
|
||||
.path=${mdiPencil}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.grid.delete_power"
|
||||
)}
|
||||
@click=${this._deletePowerSource}
|
||||
.path=${mdiDelete}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
<div class="row border-bottom">
|
||||
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
|
||||
<ha-button
|
||||
@click=${this._addPowerSource}
|
||||
appearance="filled"
|
||||
size="small"
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.energy.grid.add_power"
|
||||
)}</ha-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.energy.grid.grid_carbon_footprint"
|
||||
@@ -499,6 +553,97 @@ export class EnergyGridSettings extends LitElement {
|
||||
await this._savePreferences(cleanedPreferences);
|
||||
}
|
||||
|
||||
private _addPowerSource() {
|
||||
const gridSource = this.preferences.energy_sources.find(
|
||||
(src) => src.type === "grid"
|
||||
) as GridSourceTypeEnergyPreference | undefined;
|
||||
showEnergySettingsGridPowerDialog(this, {
|
||||
grid_source: gridSource,
|
||||
saveCallback: async (power) => {
|
||||
let preferences: EnergyPreferences;
|
||||
if (!gridSource) {
|
||||
preferences = {
|
||||
...this.preferences,
|
||||
energy_sources: [
|
||||
...this.preferences.energy_sources,
|
||||
{
|
||||
...emptyGridSourceEnergyPreference(),
|
||||
power: [power],
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
preferences = {
|
||||
...this.preferences,
|
||||
energy_sources: this.preferences.energy_sources.map((src) =>
|
||||
src.type === "grid"
|
||||
? { ...src, power: [...(gridSource.power || []), power] }
|
||||
: src
|
||||
),
|
||||
};
|
||||
}
|
||||
await this._savePreferences(preferences);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _editPowerSource(ev) {
|
||||
const origSource: GridPowerSourceEnergyPreference =
|
||||
ev.currentTarget.closest(".row").source;
|
||||
const gridSource = this.preferences.energy_sources.find(
|
||||
(src) => src.type === "grid"
|
||||
) as GridSourceTypeEnergyPreference | undefined;
|
||||
showEnergySettingsGridPowerDialog(this, {
|
||||
source: { ...origSource },
|
||||
grid_source: gridSource,
|
||||
saveCallback: async (source) => {
|
||||
const power =
|
||||
energySourcesByType(this.preferences).grid![0].power || [];
|
||||
|
||||
const preferences: EnergyPreferences = {
|
||||
...this.preferences,
|
||||
energy_sources: this.preferences.energy_sources.map((src) =>
|
||||
src.type === "grid"
|
||||
? {
|
||||
...src,
|
||||
power: power.map((p) => (p === origSource ? source : p)),
|
||||
}
|
||||
: src
|
||||
),
|
||||
};
|
||||
await this._savePreferences(preferences);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async _deletePowerSource(ev) {
|
||||
const sourceToDelete: GridPowerSourceEnergyPreference =
|
||||
ev.currentTarget.closest(".row").source;
|
||||
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.energy.delete_source"),
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const power =
|
||||
energySourcesByType(this.preferences).grid![0].power?.filter(
|
||||
(p) => p !== sourceToDelete
|
||||
) || [];
|
||||
|
||||
const preferences: EnergyPreferences = {
|
||||
...this.preferences,
|
||||
energy_sources: this.preferences.energy_sources.map((source) =>
|
||||
source.type === "grid" ? { ...source, power } : source
|
||||
),
|
||||
};
|
||||
|
||||
const cleanedPreferences = this._removeEmptySources(preferences);
|
||||
await this._savePreferences(cleanedPreferences);
|
||||
}
|
||||
|
||||
private _removeEmptySources(preferences: EnergyPreferences) {
|
||||
// Check if grid sources became an empty type and remove if so
|
||||
preferences.energy_sources = preferences.energy_sources.reduce<
|
||||
@@ -507,7 +652,8 @@ export class EnergyGridSettings extends LitElement {
|
||||
if (
|
||||
source.type !== "grid" ||
|
||||
source.flow_from.length > 0 ||
|
||||
source.flow_to.length > 0
|
||||
source.flow_to.length > 0 ||
|
||||
(source.power && source.power.length > 0)
|
||||
) {
|
||||
acc.push(source);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import type { HomeAssistant } from "../../../../types";
|
||||
import type { EnergySettingsBatteryDialogParams } from "./show-dialogs-energy";
|
||||
|
||||
const energyUnitClasses = ["energy"];
|
||||
const powerUnitClasses = ["power"];
|
||||
|
||||
@customElement("dialog-energy-battery-settings")
|
||||
export class DialogEnergyBatterySettings
|
||||
@@ -32,10 +33,14 @@ export class DialogEnergyBatterySettings
|
||||
|
||||
@state() private _energy_units?: string[];
|
||||
|
||||
@state() private _power_units?: string[];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
private _excludeList?: string[];
|
||||
|
||||
private _excludeListPower?: string[];
|
||||
|
||||
public async showDialog(
|
||||
params: EnergySettingsBatteryDialogParams
|
||||
): Promise<void> {
|
||||
@@ -46,6 +51,9 @@ export class DialogEnergyBatterySettings
|
||||
this._energy_units = (
|
||||
await getSensorDeviceClassConvertibleUnits(this.hass, "energy")
|
||||
).units;
|
||||
this._power_units = (
|
||||
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
|
||||
).units;
|
||||
const allSources: string[] = [];
|
||||
this._params.battery_sources.forEach((entry) => {
|
||||
allSources.push(entry.stat_energy_from);
|
||||
@@ -56,6 +64,9 @@ export class DialogEnergyBatterySettings
|
||||
id !== this._source?.stat_energy_from &&
|
||||
id !== this._source?.stat_energy_to
|
||||
);
|
||||
this._excludeListPower = this._params.battery_sources
|
||||
.map((entry) => entry.stat_rate)
|
||||
.filter((id) => id && id !== this._source?.stat_rate) as string[];
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
@@ -72,8 +83,6 @@ export class DialogEnergyBatterySettings
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const pickableUnit = this._energy_units?.join(", ") || "";
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@@ -85,12 +94,6 @@ export class DialogEnergyBatterySettings
|
||||
@closed=${this.closeDialog}
|
||||
>
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.energy.battery.dialog.entity_para",
|
||||
{ unit: pickableUnit }
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
@@ -105,6 +108,10 @@ export class DialogEnergyBatterySettings
|
||||
this._source.stat_energy_from,
|
||||
]}
|
||||
@value-changed=${this._statisticToChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.battery.dialog.energy_helper_into",
|
||||
{ unit: this._energy_units?.join(", ") || "" }
|
||||
)}
|
||||
dialogInitialFocus
|
||||
></ha-statistic-picker>
|
||||
|
||||
@@ -121,6 +128,25 @@ export class DialogEnergyBatterySettings
|
||||
this._source.stat_energy_to,
|
||||
]}
|
||||
@value-changed=${this._statisticFromChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.battery.dialog.energy_helper_out",
|
||||
{ unit: this._energy_units?.join(", ") || "" }
|
||||
)}
|
||||
></ha-statistic-picker>
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
.includeUnitClass=${powerUnitClasses}
|
||||
.value=${this._source.stat_rate}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.battery.dialog.power"
|
||||
)}
|
||||
.excludeStatistics=${this._excludeListPower}
|
||||
@value-changed=${this._powerChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.battery.dialog.power_helper",
|
||||
{ unit: this._power_units?.join(", ") || "" }
|
||||
)}
|
||||
></ha-statistic-picker>
|
||||
|
||||
<ha-button
|
||||
@@ -150,6 +176,10 @@ export class DialogEnergyBatterySettings
|
||||
this._source = { ...this._source!, stat_energy_from: ev.detail.value };
|
||||
}
|
||||
|
||||
private _powerChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._source = { ...this._source!, stat_rate: ev.detail.value };
|
||||
}
|
||||
|
||||
private async _save() {
|
||||
try {
|
||||
await this._params!.saveCallback(this._source!);
|
||||
@@ -168,7 +198,11 @@ export class DialogEnergyBatterySettings
|
||||
--mdc-dialog-max-width: 430px;
|
||||
}
|
||||
ha-statistic-picker {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-bottom: var(--ha-space-4);
|
||||
}
|
||||
ha-statistic-picker:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -21,6 +21,7 @@ import type { HomeAssistant } from "../../../../types";
|
||||
import type { EnergySettingsDeviceDialogParams } from "./show-dialogs-energy";
|
||||
|
||||
const energyUnitClasses = ["energy"];
|
||||
const powerUnitClasses = ["power"];
|
||||
|
||||
@customElement("dialog-energy-device-settings")
|
||||
export class DialogEnergyDeviceSettings
|
||||
@@ -35,10 +36,14 @@ export class DialogEnergyDeviceSettings
|
||||
|
||||
@state() private _energy_units?: string[];
|
||||
|
||||
@state() private _power_units?: string[];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
private _excludeList?: string[];
|
||||
|
||||
private _excludeListPower?: string[];
|
||||
|
||||
private _possibleParents: DeviceConsumptionEnergyPreference[] = [];
|
||||
|
||||
public async showDialog(
|
||||
@@ -50,9 +55,15 @@ export class DialogEnergyDeviceSettings
|
||||
this._energy_units = (
|
||||
await getSensorDeviceClassConvertibleUnits(this.hass, "energy")
|
||||
).units;
|
||||
this._power_units = (
|
||||
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
|
||||
).units;
|
||||
this._excludeList = this._params.device_consumptions
|
||||
.map((entry) => entry.stat_consumption)
|
||||
.filter((id) => id !== this._device?.stat_consumption);
|
||||
this._excludeListPower = this._params.device_consumptions
|
||||
.map((entry) => entry.stat_rate)
|
||||
.filter((id) => id && id !== this._device?.stat_rate) as string[];
|
||||
}
|
||||
|
||||
private _computePossibleParents() {
|
||||
@@ -93,8 +104,6 @@ export class DialogEnergyDeviceSettings
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const pickableUnit = this._energy_units?.join(", ") || "";
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@@ -108,12 +117,6 @@ export class DialogEnergyDeviceSettings
|
||||
@closed=${this.closeDialog}
|
||||
>
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption.dialog.selected_stat_intro",
|
||||
{ unit: pickableUnit }
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
@@ -125,9 +128,28 @@ export class DialogEnergyDeviceSettings
|
||||
)}
|
||||
.excludeStatistics=${this._excludeList}
|
||||
@value-changed=${this._statisticChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption.dialog.selected_stat_intro",
|
||||
{ unit: this._energy_units?.join(", ") || "" }
|
||||
)}
|
||||
dialogInitialFocus
|
||||
></ha-statistic-picker>
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
.includeUnitClass=${powerUnitClasses}
|
||||
.value=${this._device?.stat_rate}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption.dialog.device_consumption_power"
|
||||
)}
|
||||
.excludeStatistics=${this._excludeListPower}
|
||||
@value-changed=${this._powerStatisticChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption.dialog.selected_stat_intro",
|
||||
{ unit: this._power_units?.join(", ") || "" }
|
||||
)}
|
||||
></ha-statistic-picker>
|
||||
|
||||
<ha-textfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption.dialog.display_name"
|
||||
@@ -210,6 +232,20 @@ export class DialogEnergyDeviceSettings
|
||||
this._computePossibleParents();
|
||||
}
|
||||
|
||||
private _powerStatisticChanged(ev: CustomEvent<{ value: string }>) {
|
||||
if (!this._device) {
|
||||
return;
|
||||
}
|
||||
const newDevice = {
|
||||
...this._device,
|
||||
stat_rate: ev.detail.value,
|
||||
} as DeviceConsumptionEnergyPreference;
|
||||
if (!newDevice.stat_rate) {
|
||||
delete newDevice.stat_rate;
|
||||
}
|
||||
this._device = newDevice;
|
||||
}
|
||||
|
||||
private _nameChanged(ev) {
|
||||
const newDevice = {
|
||||
...this._device!,
|
||||
@@ -245,15 +281,19 @@ export class DialogEnergyDeviceSettings
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-statistic-picker {
|
||||
display: block;
|
||||
margin-bottom: var(--ha-space-2);
|
||||
}
|
||||
ha-statistic-picker {
|
||||
width: 100%;
|
||||
}
|
||||
ha-select {
|
||||
margin-top: 16px;
|
||||
margin-top: var(--ha-space-4);
|
||||
width: 100%;
|
||||
}
|
||||
ha-textfield {
|
||||
margin-top: 16px;
|
||||
margin-top: var(--ha-space-4);
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -104,8 +104,6 @@ export class DialogEnergyGridFlowSettings
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const pickableUnit = this._energy_units?.join(", ") || "";
|
||||
|
||||
const unitPriceFixed = `${this.hass.config.currency}/kWh`;
|
||||
|
||||
const externalSource =
|
||||
@@ -135,19 +133,11 @@ export class DialogEnergyGridFlowSettings
|
||||
@closed=${this.closeDialog}
|
||||
>
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
<div>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.paragraph`
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.entity_para`,
|
||||
{ unit: pickableUnit }
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.paragraph`
|
||||
)}
|
||||
</p>
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
@@ -163,6 +153,10 @@ export class DialogEnergyGridFlowSettings
|
||||
)}
|
||||
.excludeStatistics=${this._excludeList}
|
||||
@value-changed=${this._statisticChanged}
|
||||
.helper=${this.hass.localize(
|
||||
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.entity_para`,
|
||||
{ unit: this._energy_units?.join(", ") || "" }
|
||||
)}
|
||||
dialogInitialFocus
|
||||
></ha-statistic-picker>
|
||||
|
||||
@@ -361,6 +355,10 @@ export class DialogEnergyGridFlowSettings
|
||||
ha-dialog {
|
||||
--mdc-dialog-max-width: 430px;
|
||||
}
|
||||
ha-statistic-picker {
|
||||
display: block;
|
||||
margin: var(--ha-space-4) 0;
|
||||
}
|
||||
ha-formfield {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
import { mdiTransmissionTower } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/entity/ha-statistic-picker";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-button";
|
||||
import type { GridPowerSourceEnergyPreference } from "../../../../data/energy";
|
||||
import { energyStatisticHelpUrl } from "../../../../data/energy";
|
||||
import { getSensorDeviceClassConvertibleUnits } from "../../../../data/sensor";
|
||||
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { EnergySettingsGridPowerDialogParams } from "./show-dialogs-energy";
|
||||
|
||||
const powerUnitClasses = ["power"];
|
||||
|
||||
@customElement("dialog-energy-grid-power-settings")
|
||||
export class DialogEnergyGridPowerSettings
|
||||
extends LitElement
|
||||
implements HassDialog<EnergySettingsGridPowerDialogParams>
|
||||
{
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _params?: EnergySettingsGridPowerDialogParams;
|
||||
|
||||
@state() private _source?: GridPowerSourceEnergyPreference;
|
||||
|
||||
@state() private _power_units?: string[];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
private _excludeListPower?: string[];
|
||||
|
||||
public async showDialog(
|
||||
params: EnergySettingsGridPowerDialogParams
|
||||
): Promise<void> {
|
||||
this._params = params;
|
||||
this._source = params.source ? { ...params.source } : { stat_rate: "" };
|
||||
|
||||
const initialSourceIdPower = this._source.stat_rate;
|
||||
|
||||
this._power_units = (
|
||||
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
|
||||
).units;
|
||||
|
||||
this._excludeListPower = [
|
||||
...(this._params.grid_source?.power?.map((entry) => entry.stat_rate) ||
|
||||
[]),
|
||||
].filter((id) => id && id !== initialSourceIdPower) as string[];
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._params = undefined;
|
||||
this._source = undefined;
|
||||
this._error = undefined;
|
||||
this._excludeListPower = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
return true;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._params || !this._source) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
.heading=${html`<ha-svg-icon
|
||||
.path=${mdiTransmissionTower}
|
||||
style="--mdc-icon-size: 32px;"
|
||||
></ha-svg-icon
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.energy.grid.power_dialog.header"
|
||||
)}`}
|
||||
@closed=${this.closeDialog}
|
||||
>
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
.helpMissingEntityUrl=${energyStatisticHelpUrl}
|
||||
.includeUnitClass=${powerUnitClasses}
|
||||
.value=${this._source.stat_rate}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.grid.power_dialog.power_stat"
|
||||
)}
|
||||
.excludeStatistics=${this._excludeListPower}
|
||||
@value-changed=${this._powerStatisticChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.grid.power_dialog.power_helper",
|
||||
{ unit: this._power_units?.join(", ") || "" }
|
||||
)}
|
||||
dialogInitialFocus
|
||||
></ha-statistic-picker>
|
||||
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
slot="primaryAction"
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._save}
|
||||
.disabled=${!this._source.stat_rate}
|
||||
slot="primaryAction"
|
||||
>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _powerStatisticChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._source = {
|
||||
...this._source!,
|
||||
stat_rate: ev.detail.value,
|
||||
};
|
||||
}
|
||||
|
||||
private async _save() {
|
||||
try {
|
||||
await this._params!.saveCallback(this._source!);
|
||||
this.closeDialog();
|
||||
} catch (err: any) {
|
||||
this._error = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--mdc-dialog-max-width: 430px;
|
||||
}
|
||||
ha-statistic-picker {
|
||||
display: block;
|
||||
margin: var(--ha-space-4) 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-energy-grid-power-settings": DialogEnergyGridPowerSettings;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import { brandsUrl } from "../../../../util/brands-url";
|
||||
import type { EnergySettingsSolarDialogParams } from "./show-dialogs-energy";
|
||||
|
||||
const energyUnitClasses = ["energy"];
|
||||
const powerUnitClasses = ["power"];
|
||||
|
||||
@customElement("dialog-energy-solar-settings")
|
||||
export class DialogEnergySolarSettings
|
||||
@@ -46,10 +47,14 @@ export class DialogEnergySolarSettings
|
||||
|
||||
@state() private _energy_units?: string[];
|
||||
|
||||
@state() private _power_units?: string[];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
private _excludeList?: string[];
|
||||
|
||||
private _excludeListPower?: string[];
|
||||
|
||||
public async showDialog(
|
||||
params: EnergySettingsSolarDialogParams
|
||||
): Promise<void> {
|
||||
@@ -62,9 +67,15 @@ export class DialogEnergySolarSettings
|
||||
this._energy_units = (
|
||||
await getSensorDeviceClassConvertibleUnits(this.hass, "energy")
|
||||
).units;
|
||||
this._power_units = (
|
||||
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
|
||||
).units;
|
||||
this._excludeList = this._params.solar_sources
|
||||
.map((entry) => entry.stat_energy_from)
|
||||
.filter((id) => id !== this._source?.stat_energy_from);
|
||||
this._excludeListPower = this._params.solar_sources
|
||||
.map((entry) => entry.stat_rate)
|
||||
.filter((id) => id && id !== this._source?.stat_rate) as string[];
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
@@ -81,8 +92,6 @@ export class DialogEnergySolarSettings
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const pickableUnit = this._energy_units?.join(", ") || "";
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@@ -94,12 +103,6 @@ export class DialogEnergySolarSettings
|
||||
@closed=${this.closeDialog}
|
||||
>
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.energy.solar.dialog.entity_para",
|
||||
{ unit: pickableUnit }
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
@@ -111,9 +114,28 @@ export class DialogEnergySolarSettings
|
||||
)}
|
||||
.excludeStatistics=${this._excludeList}
|
||||
@value-changed=${this._statisticChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.solar.dialog.entity_para",
|
||||
{ unit: this._energy_units?.join(", ") || "" }
|
||||
)}
|
||||
dialogInitialFocus
|
||||
></ha-statistic-picker>
|
||||
|
||||
<ha-statistic-picker
|
||||
.hass=${this.hass}
|
||||
.includeUnitClass=${powerUnitClasses}
|
||||
.value=${this._source.stat_rate}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.energy.solar.dialog.solar_production_power"
|
||||
)}
|
||||
.excludeStatistics=${this._excludeListPower}
|
||||
@value-changed=${this._powerStatisticChanged}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.energy.solar.dialog.entity_para",
|
||||
{ unit: this._power_units?.join(", ") || "" }
|
||||
)}
|
||||
></ha-statistic-picker>
|
||||
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.energy.solar.dialog.solar_production_forecast"
|
||||
@@ -267,6 +289,10 @@ export class DialogEnergySolarSettings
|
||||
this._source = { ...this._source!, stat_energy_from: ev.detail.value };
|
||||
}
|
||||
|
||||
private _powerStatisticChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._source = { ...this._source!, stat_rate: ev.detail.value };
|
||||
}
|
||||
|
||||
private async _save() {
|
||||
try {
|
||||
if (!this._forecast) {
|
||||
@@ -287,6 +313,10 @@ export class DialogEnergySolarSettings
|
||||
ha-dialog {
|
||||
--mdc-dialog-max-width: 430px;
|
||||
}
|
||||
ha-statistic-picker {
|
||||
display: block;
|
||||
margin-bottom: var(--ha-space-4);
|
||||
}
|
||||
img {
|
||||
height: 24px;
|
||||
margin-right: 16px;
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
FlowFromGridSourceEnergyPreference,
|
||||
FlowToGridSourceEnergyPreference,
|
||||
GasSourceTypeEnergyPreference,
|
||||
GridPowerSourceEnergyPreference,
|
||||
GridSourceTypeEnergyPreference,
|
||||
SolarSourceTypeEnergyPreference,
|
||||
WaterSourceTypeEnergyPreference,
|
||||
@@ -41,6 +42,12 @@ export interface EnergySettingsGridFlowToDialogParams {
|
||||
saveCallback: (source: FlowToGridSourceEnergyPreference) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface EnergySettingsGridPowerDialogParams {
|
||||
source?: GridPowerSourceEnergyPreference;
|
||||
grid_source?: GridSourceTypeEnergyPreference;
|
||||
saveCallback: (source: GridPowerSourceEnergyPreference) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface EnergySettingsSolarDialogParams {
|
||||
info: EnergyInfo;
|
||||
source?: SolarSourceTypeEnergyPreference;
|
||||
@@ -152,3 +159,14 @@ export const showEnergySettingsGridFlowToDialog = (
|
||||
dialogParams: { ...dialogParams, direction: "to" },
|
||||
});
|
||||
};
|
||||
|
||||
export const showEnergySettingsGridPowerDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: EnergySettingsGridPowerDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-energy-grid-power-settings",
|
||||
dialogImport: () => import("./dialog-energy-grid-power-settings"),
|
||||
dialogParams: dialogParams,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3077,6 +3077,15 @@
|
||||
"grid_carbon_footprint": "Grid carbon footprint",
|
||||
"remove_co2_signal": "Remove Electricity Maps integration",
|
||||
"add_co2_signal": "Add Electricity Maps integration",
|
||||
"grid_power": "Grid power",
|
||||
"add_power": "Add power sensor",
|
||||
"edit_power": "Edit power sensor",
|
||||
"delete_power": "Delete power sensor",
|
||||
"power_dialog": {
|
||||
"header": "Configure grid power",
|
||||
"power_stat": "Power sensor",
|
||||
"power_helper": "Pick a sensor which measures grid power in either of {unit}. Positive values indicate importing electricity from the grid, negative values indicate exporting electricity to the grid."
|
||||
},
|
||||
"flow_dialog": {
|
||||
"cost_entity_helper": "Any sensor with a unit of `{currency}/(valid energy unit)` (e.g. `{currency}/Wh` or `{currency}/kWh`) may be used and will be automatically converted.",
|
||||
"from": {
|
||||
@@ -3124,6 +3133,7 @@
|
||||
"header": "Configure solar panels",
|
||||
"entity_para": "Pick a sensor which measures solar energy production in either of {unit}.",
|
||||
"solar_production_energy": "Solar production energy",
|
||||
"solar_production_power": "Solar production power",
|
||||
"solar_production_forecast": "Solar production forecast",
|
||||
"solar_production_forecast_description": "Adding solar production forecast information will allow you to quickly see your expected production for today.",
|
||||
"dont_forecast_production": "Don't forecast production",
|
||||
@@ -3141,9 +3151,12 @@
|
||||
"add_battery_system": "Add battery system",
|
||||
"dialog": {
|
||||
"header": "Configure battery system",
|
||||
"entity_para": "Pick sensors which measure energy going into and coming out of the battery in either of {unit}.",
|
||||
"energy_into_battery": "Energy going into the battery",
|
||||
"energy_out_of_battery": "Energy coming out of the battery"
|
||||
"energy_helper_into": "Pick a sensor that measures the electricity flowing into the battery in either of {unit}.",
|
||||
"energy_helper_out": "Pick a sensor that measures the electricity flowing out of the battery in either of {unit}.",
|
||||
"energy_into_battery": "Energy charged into the battery",
|
||||
"energy_out_of_battery": "Energy discharged from the battery",
|
||||
"power": "Battery power",
|
||||
"power_helper": "Pick a sensor which measures the electricity flowing into and out of the battery in either of {unit}. Positive values indicate discharging the battery, negative values indicate charging the battery."
|
||||
}
|
||||
},
|
||||
"gas": {
|
||||
@@ -3205,7 +3218,8 @@
|
||||
"header": "Add a device",
|
||||
"display_name": "Display name",
|
||||
"device_consumption_energy": "Device energy consumption",
|
||||
"selected_stat_intro": "Select the energy sensor that measures the device's energy usage in either of {unit}.",
|
||||
"device_consumption_power": "Device power consumption",
|
||||
"selected_stat_intro": "Select the sensor that measures the device's electricity usage in either of {unit}.",
|
||||
"included_in_device": "Upstream device",
|
||||
"included_in_device_helper": "If this device is already counted by another device (such as a smart switch measured by a smart breaker), selecting the upstream device prevents duplicate energy tracking.",
|
||||
"no_upstream_devices": "No eligible upstream devices"
|
||||
|
||||
Reference in New Issue
Block a user