mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-14 12:56:37 +00:00
Add energy validation UI (#9802)
* Add basic validation UI * Also refresh validation results when prefs change * Update look * Remove || true * Add missing errors * Validate state class * Rename file * Simplify energySourcesByType * Update src/translations/en.json * Update ha-energy-validation-result.ts
This commit is contained in:
parent
9e3d339ec5
commit
b802a410b9
15
src/common/util/group-by.ts
Normal file
15
src/common/util/group-by.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export const groupBy = <T>(
|
||||||
|
list: T[],
|
||||||
|
keySelector: (item: T) => string
|
||||||
|
): { [key: string]: T[] } => {
|
||||||
|
const result = {};
|
||||||
|
for (const item of list) {
|
||||||
|
const key = keySelector(item);
|
||||||
|
if (key in result) {
|
||||||
|
result[key].push(item);
|
||||||
|
} else {
|
||||||
|
result[key] = [item];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
@ -6,6 +6,7 @@ import {
|
|||||||
startOfYesterday,
|
startOfYesterday,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { Collection, getCollection } from "home-assistant-js-websocket";
|
import { Collection, getCollection } from "home-assistant-js-websocket";
|
||||||
|
import { groupBy } from "../common/util/group-by";
|
||||||
import { subscribeOne } from "../common/util/subscribe-one";
|
import { subscribeOne } from "../common/util/subscribe-one";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { ConfigEntry, getConfigEntries } from "./config_entries";
|
import { ConfigEntry, getConfigEntries } from "./config_entries";
|
||||||
@ -144,11 +145,27 @@ export interface EnergyInfo {
|
|||||||
cost_sensors: Record<string, string>;
|
cost_sensors: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EnergyValidationIssue {
|
||||||
|
type: string;
|
||||||
|
identifier: string;
|
||||||
|
value?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnergyPreferencesValidation {
|
||||||
|
energy_sources: EnergyValidationIssue[][];
|
||||||
|
device_consumption: EnergyValidationIssue[][];
|
||||||
|
}
|
||||||
|
|
||||||
export const getEnergyInfo = (hass: HomeAssistant) =>
|
export const getEnergyInfo = (hass: HomeAssistant) =>
|
||||||
hass.callWS<EnergyInfo>({
|
hass.callWS<EnergyInfo>({
|
||||||
type: "energy/info",
|
type: "energy/info",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getEnergyPreferenceValidation = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<EnergyPreferencesValidation>({
|
||||||
|
type: "energy/validate",
|
||||||
|
});
|
||||||
|
|
||||||
export const getEnergyPreferences = (hass: HomeAssistant) =>
|
export const getEnergyPreferences = (hass: HomeAssistant) =>
|
||||||
hass.callWS<EnergyPreferences>({
|
hass.callWS<EnergyPreferences>({
|
||||||
type: "energy/get_prefs",
|
type: "energy/get_prefs",
|
||||||
@ -173,17 +190,8 @@ interface EnergySourceByType {
|
|||||||
gas?: GasSourceTypeEnergyPreference[];
|
gas?: GasSourceTypeEnergyPreference[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const energySourcesByType = (prefs: EnergyPreferences) => {
|
export const energySourcesByType = (prefs: EnergyPreferences) =>
|
||||||
const types: EnergySourceByType = {};
|
groupBy(prefs.energy_sources, (item) => item.type) as EnergySourceByType;
|
||||||
for (const source of prefs.energy_sources) {
|
|
||||||
if (source.type in types) {
|
|
||||||
types[source.type]!.push(source as any);
|
|
||||||
} else {
|
|
||||||
types[source.type] = [source as any];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface EnergyData {
|
export interface EnergyData {
|
||||||
start: Date;
|
start: Date;
|
||||||
|
@ -10,7 +10,8 @@ import "../../../../components/ha-settings-row";
|
|||||||
import {
|
import {
|
||||||
BatterySourceTypeEnergyPreference,
|
BatterySourceTypeEnergyPreference,
|
||||||
EnergyPreferences,
|
EnergyPreferences,
|
||||||
energySourcesByType,
|
EnergyPreferencesValidation,
|
||||||
|
EnergyValidationIssue,
|
||||||
saveEnergyPreferences,
|
saveEnergyPreferences,
|
||||||
} from "../../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
import {
|
import {
|
||||||
@ -21,6 +22,7 @@ import { haStyle } from "../../../../resources/styles";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { documentationUrl } from "../../../../util/documentation-url";
|
import { documentationUrl } from "../../../../util/documentation-url";
|
||||||
import { showEnergySettingsBatteryDialog } from "../dialogs/show-dialogs-energy";
|
import { showEnergySettingsBatteryDialog } from "../dialogs/show-dialogs-energy";
|
||||||
|
import "./ha-energy-validation-result";
|
||||||
import { energyCardStyles } from "./styles";
|
import { energyCardStyles } from "./styles";
|
||||||
|
|
||||||
@customElement("ha-energy-battery-settings")
|
@customElement("ha-energy-battery-settings")
|
||||||
@ -30,10 +32,23 @@ export class EnergyBatterySettings extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public preferences!: EnergyPreferences;
|
public preferences!: EnergyPreferences;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
@property({ attribute: false })
|
||||||
const types = energySourcesByType(this.preferences);
|
public validationResult?: EnergyPreferencesValidation;
|
||||||
|
|
||||||
const batterySources = types.battery || [];
|
protected render(): TemplateResult {
|
||||||
|
const batterySources: BatterySourceTypeEnergyPreference[] = [];
|
||||||
|
const batteryValidation: EnergyValidationIssue[][] = [];
|
||||||
|
|
||||||
|
this.preferences.energy_sources.forEach((source, idx) => {
|
||||||
|
if (source.type !== "battery") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
batterySources.push(source);
|
||||||
|
|
||||||
|
if (this.validationResult) {
|
||||||
|
batteryValidation.push(this.validationResult.energy_sources[idx]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@ -54,6 +69,16 @@ export class EnergyBatterySettings extends LitElement {
|
|||||||
)}</a
|
)}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
${batteryValidation.map(
|
||||||
|
(result) =>
|
||||||
|
html`
|
||||||
|
<ha-energy-validation-result
|
||||||
|
.hass=${this.hass}
|
||||||
|
.issues=${result}
|
||||||
|
></ha-energy-validation-result>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
|
||||||
<h3>Battery systems</h3>
|
<h3>Battery systems</h3>
|
||||||
${batterySources.map((source) => {
|
${batterySources.map((source) => {
|
||||||
const fromEntityState = this.hass.states[source.stat_energy_from];
|
const fromEntityState = this.hass.states[source.stat_energy_from];
|
||||||
|
@ -9,6 +9,7 @@ import "../../../../components/ha-card";
|
|||||||
import {
|
import {
|
||||||
DeviceConsumptionEnergyPreference,
|
DeviceConsumptionEnergyPreference,
|
||||||
EnergyPreferences,
|
EnergyPreferences,
|
||||||
|
EnergyPreferencesValidation,
|
||||||
saveEnergyPreferences,
|
saveEnergyPreferences,
|
||||||
} from "../../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
import {
|
import {
|
||||||
@ -19,6 +20,7 @@ import { haStyle } from "../../../../resources/styles";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { documentationUrl } from "../../../../util/documentation-url";
|
import { documentationUrl } from "../../../../util/documentation-url";
|
||||||
import { showEnergySettingsDeviceDialog } from "../dialogs/show-dialogs-energy";
|
import { showEnergySettingsDeviceDialog } from "../dialogs/show-dialogs-energy";
|
||||||
|
import "./ha-energy-validation-result";
|
||||||
import { energyCardStyles } from "./styles";
|
import { energyCardStyles } from "./styles";
|
||||||
|
|
||||||
@customElement("ha-energy-device-settings")
|
@customElement("ha-energy-device-settings")
|
||||||
@ -28,6 +30,9 @@ export class EnergyDeviceSettings extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public preferences!: EnergyPreferences;
|
public preferences!: EnergyPreferences;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public validationResult?: EnergyPreferencesValidation;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@ -55,6 +60,15 @@ export class EnergyDeviceSettings extends LitElement {
|
|||||||
)}</a
|
)}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
${this.validationResult?.device_consumption.map(
|
||||||
|
(result) =>
|
||||||
|
html`
|
||||||
|
<ha-energy-validation-result
|
||||||
|
.hass=${this.hass}
|
||||||
|
.issues=${result}
|
||||||
|
></ha-energy-validation-result>
|
||||||
|
`
|
||||||
|
)}
|
||||||
<h3>Devices</h3>
|
<h3>Devices</h3>
|
||||||
${this.preferences.device_consumption.map((device) => {
|
${this.preferences.device_consumption.map((device) => {
|
||||||
const entityState = this.hass.states[device.stat_consumption];
|
const entityState = this.hass.states[device.stat_consumption];
|
||||||
|
@ -7,9 +7,10 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
|
|||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
EnergyPreferences,
|
EnergyPreferences,
|
||||||
energySourcesByType,
|
|
||||||
saveEnergyPreferences,
|
saveEnergyPreferences,
|
||||||
GasSourceTypeEnergyPreference,
|
GasSourceTypeEnergyPreference,
|
||||||
|
EnergyPreferencesValidation,
|
||||||
|
EnergyValidationIssue,
|
||||||
} from "../../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
import {
|
import {
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@ -19,6 +20,7 @@ import { haStyle } from "../../../../resources/styles";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { documentationUrl } from "../../../../util/documentation-url";
|
import { documentationUrl } from "../../../../util/documentation-url";
|
||||||
import { showEnergySettingsGasDialog } from "../dialogs/show-dialogs-energy";
|
import { showEnergySettingsGasDialog } from "../dialogs/show-dialogs-energy";
|
||||||
|
import "./ha-energy-validation-result";
|
||||||
import { energyCardStyles } from "./styles";
|
import { energyCardStyles } from "./styles";
|
||||||
|
|
||||||
@customElement("ha-energy-gas-settings")
|
@customElement("ha-energy-gas-settings")
|
||||||
@ -28,10 +30,23 @@ export class EnergyGasSettings extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public preferences!: EnergyPreferences;
|
public preferences!: EnergyPreferences;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
@property({ attribute: false })
|
||||||
const types = energySourcesByType(this.preferences);
|
public validationResult?: EnergyPreferencesValidation;
|
||||||
|
|
||||||
const gasSources = types.gas || [];
|
protected render(): TemplateResult {
|
||||||
|
const gasSources: GasSourceTypeEnergyPreference[] = [];
|
||||||
|
const gasValidation: EnergyValidationIssue[][] = [];
|
||||||
|
|
||||||
|
this.preferences.energy_sources.forEach((source, idx) => {
|
||||||
|
if (source.type !== "gas") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gasSources.push(source);
|
||||||
|
|
||||||
|
if (this.validationResult) {
|
||||||
|
gasValidation.push(this.validationResult.energy_sources[idx]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@ -50,6 +65,15 @@ export class EnergyGasSettings extends LitElement {
|
|||||||
>${this.hass.localize("ui.panel.config.energy.gas.learn_more")}</a
|
>${this.hass.localize("ui.panel.config.energy.gas.learn_more")}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
${gasValidation.map(
|
||||||
|
(result) =>
|
||||||
|
html`
|
||||||
|
<ha-energy-validation-result
|
||||||
|
.hass=${this.hass}
|
||||||
|
.issues=${result}
|
||||||
|
></ha-energy-validation-result>
|
||||||
|
`
|
||||||
|
)}
|
||||||
<h3>Gas consumption</h3>
|
<h3>Gas consumption</h3>
|
||||||
${gasSources.map((source) => {
|
${gasSources.map((source) => {
|
||||||
const entityState = this.hass.states[source.stat_energy_from];
|
const entityState = this.hass.states[source.stat_energy_from];
|
||||||
|
@ -19,7 +19,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
emptyGridSourceEnergyPreference,
|
emptyGridSourceEnergyPreference,
|
||||||
EnergyPreferences,
|
EnergyPreferences,
|
||||||
|
EnergyPreferencesValidation,
|
||||||
energySourcesByType,
|
energySourcesByType,
|
||||||
|
EnergyValidationIssue,
|
||||||
FlowFromGridSourceEnergyPreference,
|
FlowFromGridSourceEnergyPreference,
|
||||||
FlowToGridSourceEnergyPreference,
|
FlowToGridSourceEnergyPreference,
|
||||||
GridSourceTypeEnergyPreference,
|
GridSourceTypeEnergyPreference,
|
||||||
@ -38,6 +40,7 @@ import {
|
|||||||
showEnergySettingsGridFlowFromDialog,
|
showEnergySettingsGridFlowFromDialog,
|
||||||
showEnergySettingsGridFlowToDialog,
|
showEnergySettingsGridFlowToDialog,
|
||||||
} from "../dialogs/show-dialogs-energy";
|
} from "../dialogs/show-dialogs-energy";
|
||||||
|
import "./ha-energy-validation-result";
|
||||||
import { energyCardStyles } from "./styles";
|
import { energyCardStyles } from "./styles";
|
||||||
|
|
||||||
@customElement("ha-energy-grid-settings")
|
@customElement("ha-energy-grid-settings")
|
||||||
@ -47,6 +50,9 @@ export class EnergyGridSettings extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public preferences!: EnergyPreferences;
|
public preferences!: EnergyPreferences;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public validationResult?: EnergyPreferencesValidation;
|
||||||
|
|
||||||
@state() private _configEntries?: ConfigEntry[];
|
@state() private _configEntries?: ConfigEntry[];
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
@ -54,11 +60,23 @@ export class EnergyGridSettings extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const types = energySourcesByType(this.preferences);
|
const gridIdx = this.preferences.energy_sources.findIndex(
|
||||||
|
(source) => source.type === "grid"
|
||||||
|
);
|
||||||
|
|
||||||
const gridSource = types.grid
|
let gridSource: GridSourceTypeEnergyPreference;
|
||||||
? types.grid[0]
|
let gridValidation: EnergyValidationIssue[] | undefined;
|
||||||
: emptyGridSourceEnergyPreference();
|
|
||||||
|
if (gridIdx === -1) {
|
||||||
|
gridSource = emptyGridSourceEnergyPreference();
|
||||||
|
} else {
|
||||||
|
gridSource = this.preferences.energy_sources[
|
||||||
|
gridIdx
|
||||||
|
] as GridSourceTypeEnergyPreference;
|
||||||
|
if (this.validationResult) {
|
||||||
|
gridValidation = this.validationResult.energy_sources[gridIdx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@ -82,6 +100,15 @@ export class EnergyGridSettings extends LitElement {
|
|||||||
)}</a
|
)}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
${gridValidation
|
||||||
|
? html`
|
||||||
|
<ha-energy-validation-result
|
||||||
|
.hass=${this.hass}
|
||||||
|
.issues=${gridValidation}
|
||||||
|
></ha-energy-validation-result>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
|
||||||
<h3>Grid consumption</h3>
|
<h3>Grid consumption</h3>
|
||||||
${gridSource.flow_from.map((flow) => {
|
${gridSource.flow_from.map((flow) => {
|
||||||
const entityState = this.hass.states[flow.stat_energy_from];
|
const entityState = this.hass.states[flow.stat_energy_from];
|
||||||
|
@ -7,7 +7,8 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
|
|||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
EnergyPreferences,
|
EnergyPreferences,
|
||||||
energySourcesByType,
|
EnergyPreferencesValidation,
|
||||||
|
EnergyValidationIssue,
|
||||||
saveEnergyPreferences,
|
saveEnergyPreferences,
|
||||||
SolarSourceTypeEnergyPreference,
|
SolarSourceTypeEnergyPreference,
|
||||||
} from "../../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
@ -19,6 +20,7 @@ import { haStyle } from "../../../../resources/styles";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { documentationUrl } from "../../../../util/documentation-url";
|
import { documentationUrl } from "../../../../util/documentation-url";
|
||||||
import { showEnergySettingsSolarDialog } from "../dialogs/show-dialogs-energy";
|
import { showEnergySettingsSolarDialog } from "../dialogs/show-dialogs-energy";
|
||||||
|
import "./ha-energy-validation-result";
|
||||||
import { energyCardStyles } from "./styles";
|
import { energyCardStyles } from "./styles";
|
||||||
|
|
||||||
@customElement("ha-energy-solar-settings")
|
@customElement("ha-energy-solar-settings")
|
||||||
@ -28,10 +30,23 @@ export class EnergySolarSettings extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public preferences!: EnergyPreferences;
|
public preferences!: EnergyPreferences;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
@property({ attribute: false })
|
||||||
const types = energySourcesByType(this.preferences);
|
public validationResult?: EnergyPreferencesValidation;
|
||||||
|
|
||||||
const solarSources = types.solar || [];
|
protected render(): TemplateResult {
|
||||||
|
const solarSources: SolarSourceTypeEnergyPreference[] = [];
|
||||||
|
const solarValidation: EnergyValidationIssue[][] = [];
|
||||||
|
|
||||||
|
this.preferences.energy_sources.forEach((source, idx) => {
|
||||||
|
if (source.type !== "solar") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
solarSources.push(source);
|
||||||
|
|
||||||
|
if (this.validationResult) {
|
||||||
|
solarValidation.push(this.validationResult.energy_sources[idx]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@ -55,6 +70,16 @@ export class EnergySolarSettings extends LitElement {
|
|||||||
)}</a
|
)}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
${solarValidation.map(
|
||||||
|
(result) =>
|
||||||
|
html`
|
||||||
|
<ha-energy-validation-result
|
||||||
|
.hass=${this.hass}
|
||||||
|
.issues=${result}
|
||||||
|
></ha-energy-validation-result>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
|
||||||
<h3>Solar production</h3>
|
<h3>Solar production</h3>
|
||||||
${solarSources.map((source) => {
|
${solarSources.map((source) => {
|
||||||
const entityState = this.hass.states[source.stat_energy_from];
|
const entityState = this.hass.states[source.stat_energy_from];
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
import { mdiAlertOutline } from "@mdi/js";
|
||||||
|
import { css, html, LitElement } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { groupBy } from "../../../../common/util/group-by";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import { EnergyValidationIssue } from "../../../../data/energy";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
@customElement("ha-energy-validation-result")
|
||||||
|
class EnergyValidationMessage extends LitElement {
|
||||||
|
@property({ attribute: false })
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
public issues!: EnergyValidationIssue[];
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
if (this.issues.length === 0) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
const grouped = groupBy(this.issues, (issue) => issue.type);
|
||||||
|
|
||||||
|
return Object.entries(grouped).map(
|
||||||
|
([issueType, gIssues]) => html`
|
||||||
|
<div class="issue-type">
|
||||||
|
<div class="icon">
|
||||||
|
<ha-svg-icon .path=${mdiAlertOutline}></ha-svg-icon>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="title">
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.energy.validation.issues.${issueType}.title`
|
||||||
|
) || issueType}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.energy.validation.issues.${issueType}.description`
|
||||||
|
)}
|
||||||
|
${issueType === "entity_not_tracked"
|
||||||
|
? html`
|
||||||
|
(<a
|
||||||
|
href="https://www.home-assistant.io/integrations/recorder#configure-filter"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.common.learn_more"
|
||||||
|
)}</a
|
||||||
|
>)
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
${gIssues.map(
|
||||||
|
(issue) =>
|
||||||
|
html`<li>
|
||||||
|
${issue.identifier}${issue.value
|
||||||
|
? html` (${issue.value})`
|
||||||
|
: ""}
|
||||||
|
</li>`
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.issue-type {
|
||||||
|
position: relative;
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
.issue-type::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--warning-color);
|
||||||
|
opacity: 0.12;
|
||||||
|
pointer-events: none;
|
||||||
|
content: "";
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
margin: 4px 8px;
|
||||||
|
width: 24px;
|
||||||
|
color: var(--warning-color);
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding-left: 24px;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-energy-validation-result": EnergyValidationMessage;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,12 @@
|
|||||||
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 "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { EnergyPreferences, getEnergyPreferences } from "../../../data/energy";
|
import {
|
||||||
|
EnergyPreferences,
|
||||||
|
EnergyPreferencesValidation,
|
||||||
|
getEnergyPreferences,
|
||||||
|
getEnergyPreferenceValidation,
|
||||||
|
} from "../../../data/energy";
|
||||||
import "../../../layouts/hass-loading-screen";
|
import "../../../layouts/hass-loading-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
@ -34,6 +39,8 @@ class HaConfigEnergy extends LitElement {
|
|||||||
|
|
||||||
@state() private _preferences?: EnergyPreferences;
|
@state() private _preferences?: EnergyPreferences;
|
||||||
|
|
||||||
|
@state() private _validationResult?: EnergyPreferencesValidation;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
@ -76,16 +83,19 @@ class HaConfigEnergy extends LitElement {
|
|||||||
<ha-energy-grid-settings
|
<ha-energy-grid-settings
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.preferences=${this._preferences!}
|
.preferences=${this._preferences!}
|
||||||
|
.validationResult=${this._validationResult!}
|
||||||
@value-changed=${this._prefsChanged}
|
@value-changed=${this._prefsChanged}
|
||||||
></ha-energy-grid-settings>
|
></ha-energy-grid-settings>
|
||||||
<ha-energy-solar-settings
|
<ha-energy-solar-settings
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.preferences=${this._preferences!}
|
.preferences=${this._preferences!}
|
||||||
|
.validationResult=${this._validationResult!}
|
||||||
@value-changed=${this._prefsChanged}
|
@value-changed=${this._prefsChanged}
|
||||||
></ha-energy-solar-settings>
|
></ha-energy-solar-settings>
|
||||||
<ha-energy-battery-settings
|
<ha-energy-battery-settings
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.preferences=${this._preferences!}
|
.preferences=${this._preferences!}
|
||||||
|
.validationResult=${this._validationResult!}
|
||||||
@value-changed=${this._prefsChanged}
|
@value-changed=${this._prefsChanged}
|
||||||
></ha-energy-battery-settings>
|
></ha-energy-battery-settings>
|
||||||
<ha-energy-gas-settings
|
<ha-energy-gas-settings
|
||||||
@ -96,6 +106,7 @@ class HaConfigEnergy extends LitElement {
|
|||||||
<ha-energy-device-settings
|
<ha-energy-device-settings
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.preferences=${this._preferences!}
|
.preferences=${this._preferences!}
|
||||||
|
.validationResult=${this._validationResult!}
|
||||||
@value-changed=${this._prefsChanged}
|
@value-changed=${this._prefsChanged}
|
||||||
></ha-energy-device-settings>
|
></ha-energy-device-settings>
|
||||||
</div>
|
</div>
|
||||||
@ -104,6 +115,7 @@ class HaConfigEnergy extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchConfig() {
|
private async _fetchConfig() {
|
||||||
|
const validationPromise = getEnergyPreferenceValidation(this.hass);
|
||||||
try {
|
try {
|
||||||
this._preferences = await getEnergyPreferences(this.hass);
|
this._preferences = await getEnergyPreferences(this.hass);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -113,10 +125,21 @@ class HaConfigEnergy extends LitElement {
|
|||||||
this._error = e.message;
|
this._error = e.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
this._validationResult = await validationPromise;
|
||||||
|
} catch (e) {
|
||||||
|
this._error = e.message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _prefsChanged(ev: CustomEvent) {
|
private async _prefsChanged(ev: CustomEvent) {
|
||||||
this._preferences = ev.detail.value;
|
this._preferences = ev.detail.value;
|
||||||
|
this._validationResult = undefined;
|
||||||
|
try {
|
||||||
|
this._validationResult = await getEnergyPreferenceValidation(this.hass);
|
||||||
|
} catch (e) {
|
||||||
|
this._error = e.message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@ -934,7 +934,8 @@
|
|||||||
"common": {
|
"common": {
|
||||||
"editor": {
|
"editor": {
|
||||||
"confirm_unsaved": "You have unsaved changes. Are you sure you want to leave?"
|
"confirm_unsaved": "You have unsaved changes. Are you sure you want to leave?"
|
||||||
}
|
},
|
||||||
|
"learn_more": "Learn more"
|
||||||
},
|
},
|
||||||
"areas": {
|
"areas": {
|
||||||
"caption": "Areas",
|
"caption": "Areas",
|
||||||
@ -1079,6 +1080,42 @@
|
|||||||
"dialog": {
|
"dialog": {
|
||||||
"selected_stat_intro": "Select the entity that represents the device energy usage."
|
"selected_stat_intro": "Select the entity that represents the device energy usage."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"issues": {
|
||||||
|
"entity_not_defined": {
|
||||||
|
"title": "Entity not defined",
|
||||||
|
"description": "Check the integration or your configuration that provides:"
|
||||||
|
},
|
||||||
|
"recorder_untracked": {
|
||||||
|
"title": "Entity not tracked",
|
||||||
|
"description": "The recorder has been configured to exclude these configured entities:"
|
||||||
|
},
|
||||||
|
"entity_unavailable": {
|
||||||
|
"title": "Entity unavailable",
|
||||||
|
"description": "The state of these configured entities are currently not available:"
|
||||||
|
},
|
||||||
|
"entity_state_non_numeric": {
|
||||||
|
"title": "Entity has non-numeric state",
|
||||||
|
"description": "The following entities have a state that cannot be parsed as a number:"
|
||||||
|
},
|
||||||
|
"entity_negative_state": {
|
||||||
|
"title": "Entity has a negative state",
|
||||||
|
"description": "The following entities have a negative state while a positive state is expected:"
|
||||||
|
},
|
||||||
|
"entity_unexpected_unit_energy": {
|
||||||
|
"title": "Unexpected unit of measurement",
|
||||||
|
"description": "The following entities do not have expected units of measurement kWh or Wh:"
|
||||||
|
},
|
||||||
|
"entity_unexpected_unit_price": {
|
||||||
|
"title": "Unexpected unit of measurement",
|
||||||
|
"description": "The following entities do not have expected units of measurement that ends with /kWh or /Wh:"
|
||||||
|
},
|
||||||
|
"entity_unexpected_state_class_total_increasing": {
|
||||||
|
"title": "Unexpected state class",
|
||||||
|
"description": "The following entities do not have expected state class \"total_increasing\""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"helpers": {
|
"helpers": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user