From ce730e2127bad8fbb15a46006ccb032eb28dcf7d Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Fri, 13 Aug 2021 11:10:27 -0700
Subject: [PATCH 01/10] Add basic validation UI
---
src/data/energy.ts | 20 +++++++++++
.../components/ha-energy-battery-settings.ts | 31 +++++++++++++++--
.../components/ha-energy-device-settings.ts | 15 +++++++++
.../components/ha-energy-gas-settings.ts | 31 +++++++++++++++--
.../components/ha-energy-grid-settings.ts | 33 ++++++++++++++++---
.../components/ha-energy-solar-settings.ts | 32 ++++++++++++++++--
.../ha-energy-validation-message.ts | 15 +++++++++
src/panels/config/energy/ha-config-energy.ts | 26 ++++++++++++++-
8 files changed, 189 insertions(+), 14 deletions(-)
create mode 100644 src/panels/config/energy/components/ha-energy-validation-message.ts
diff --git a/src/data/energy.ts b/src/data/energy.ts
index 8e801ced29c5..a4484c024cf0 100644
--- a/src/data/energy.ts
+++ b/src/data/energy.ts
@@ -144,11 +144,31 @@ export interface EnergyInfo {
cost_sensors: Record;
}
+export interface EnergyValidationMessage {
+ message: string;
+ link: string | null;
+}
+
+export interface EnergyValidationResult {
+ errors: EnergyValidationMessage[];
+ warnings: EnergyValidationMessage[];
+}
+
+export interface EnergyPreferencesValidation extends EnergyValidationResult {
+ energy_sources: EnergyValidationResult[];
+ device_consumption: EnergyValidationResult[];
+}
+
export const getEnergyInfo = (hass: HomeAssistant) =>
hass.callWS({
type: "energy/info",
});
+export const getEnergyPreferenceValidation = (hass: HomeAssistant) =>
+ hass.callWS({
+ type: "energy/validate",
+ });
+
export const getEnergyPreferences = (hass: HomeAssistant) =>
hass.callWS({
type: "energy/get_prefs",
diff --git a/src/panels/config/energy/components/ha-energy-battery-settings.ts b/src/panels/config/energy/components/ha-energy-battery-settings.ts
index 9caa3b8e604a..675a6efa68b6 100644
--- a/src/panels/config/energy/components/ha-energy-battery-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-battery-settings.ts
@@ -10,7 +10,8 @@ import "../../../../components/ha-settings-row";
import {
BatterySourceTypeEnergyPreference,
EnergyPreferences,
- energySourcesByType,
+ EnergyPreferencesValidation,
+ EnergyValidationResult,
saveEnergyPreferences,
} from "../../../../data/energy";
import {
@@ -21,6 +22,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsBatteryDialog } from "../dialogs/show-dialogs-energy";
+import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-battery-settings")
@@ -30,10 +32,23 @@ export class EnergyBatterySettings extends LitElement {
@property({ attribute: false })
public preferences!: EnergyPreferences;
+ @property({ attribute: false })
+ public validationResult?: EnergyPreferencesValidation;
+
protected render(): TemplateResult {
- const types = energySourcesByType(this.preferences);
+ const batterySources: BatterySourceTypeEnergyPreference[] = [];
+ const batteryValidation: EnergyValidationResult[] = [];
- const batterySources = types.battery || [];
+ 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`
@@ -54,6 +69,16 @@ export class EnergyBatterySettings extends LitElement {
)}
+ ${batteryValidation.map(
+ (result) => html`
+ ${result.errors.map((msg) =>
+ renderEnergyValidationMessage("error", msg)
+ )}
+ ${result.warnings.map((msg) =>
+ renderEnergyValidationMessage("warning", msg)
+ )}
+ `
+ )}
Battery systems
${batterySources.map((source) => {
const fromEntityState = this.hass.states[source.stat_energy_from];
diff --git a/src/panels/config/energy/components/ha-energy-device-settings.ts b/src/panels/config/energy/components/ha-energy-device-settings.ts
index 561973a0e014..caa786d3c14f 100644
--- a/src/panels/config/energy/components/ha-energy-device-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-device-settings.ts
@@ -9,6 +9,7 @@ import "../../../../components/ha-card";
import {
DeviceConsumptionEnergyPreference,
EnergyPreferences,
+ EnergyPreferencesValidation,
saveEnergyPreferences,
} from "../../../../data/energy";
import {
@@ -19,6 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsDeviceDialog } from "../dialogs/show-dialogs-energy";
+import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-device-settings")
@@ -28,6 +30,9 @@ export class EnergyDeviceSettings extends LitElement {
@property({ attribute: false })
public preferences!: EnergyPreferences;
+ @property({ attribute: false })
+ public validationResult?: EnergyPreferencesValidation;
+
protected render(): TemplateResult {
return html`
@@ -55,6 +60,16 @@ export class EnergyDeviceSettings extends LitElement {
)}
+ ${this.validationResult?.device_consumption.map(
+ (result) => html`
+ ${result.errors.map((msg) =>
+ renderEnergyValidationMessage("error", msg)
+ )}
+ ${result.warnings.map((msg) =>
+ renderEnergyValidationMessage("warning", msg)
+ )}
+ `
+ )}
Devices
${this.preferences.device_consumption.map((device) => {
const entityState = this.hass.states[device.stat_consumption];
diff --git a/src/panels/config/energy/components/ha-energy-gas-settings.ts b/src/panels/config/energy/components/ha-energy-gas-settings.ts
index 00fc47e1dfe0..34e390e8c661 100644
--- a/src/panels/config/energy/components/ha-energy-gas-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-gas-settings.ts
@@ -7,9 +7,10 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
import "../../../../components/ha-card";
import {
EnergyPreferences,
- energySourcesByType,
saveEnergyPreferences,
GasSourceTypeEnergyPreference,
+ EnergyPreferencesValidation,
+ EnergyValidationResult,
} from "../../../../data/energy";
import {
showConfirmationDialog,
@@ -19,6 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsGasDialog } from "../dialogs/show-dialogs-energy";
+import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-gas-settings")
@@ -28,10 +30,23 @@ export class EnergyGasSettings extends LitElement {
@property({ attribute: false })
public preferences!: EnergyPreferences;
+ @property({ attribute: false })
+ public validationResult?: EnergyPreferencesValidation;
+
protected render(): TemplateResult {
- const types = energySourcesByType(this.preferences);
+ const gasSources: GasSourceTypeEnergyPreference[] = [];
+ const gasValidation: EnergyValidationResult[] = [];
- const gasSources = types.gas || [];
+ 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`
@@ -50,6 +65,16 @@ export class EnergyGasSettings extends LitElement {
>${this.hass.localize("ui.panel.config.energy.gas.learn_more")}
+ ${gasValidation.map(
+ (result) => html`
+ ${result.errors.map((msg) =>
+ renderEnergyValidationMessage("error", msg)
+ )}
+ ${result.warnings.map((msg) =>
+ renderEnergyValidationMessage("warning", msg)
+ )}
+ `
+ )}
Gas consumption
${gasSources.map((source) => {
const entityState = this.hass.states[source.stat_energy_from];
diff --git a/src/panels/config/energy/components/ha-energy-grid-settings.ts b/src/panels/config/energy/components/ha-energy-grid-settings.ts
index 5bf861f9d4ca..db27ce6cb31f 100644
--- a/src/panels/config/energy/components/ha-energy-grid-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-grid-settings.ts
@@ -19,7 +19,9 @@ import {
import {
emptyGridSourceEnergyPreference,
EnergyPreferences,
+ EnergyPreferencesValidation,
energySourcesByType,
+ EnergyValidationResult,
FlowFromGridSourceEnergyPreference,
FlowToGridSourceEnergyPreference,
GridSourceTypeEnergyPreference,
@@ -38,6 +40,7 @@ import {
showEnergySettingsGridFlowFromDialog,
showEnergySettingsGridFlowToDialog,
} from "../dialogs/show-dialogs-energy";
+import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-grid-settings")
@@ -47,6 +50,9 @@ export class EnergyGridSettings extends LitElement {
@property({ attribute: false })
public preferences!: EnergyPreferences;
+ @property({ attribute: false })
+ public validationResult?: EnergyPreferencesValidation;
+
@state() private _configEntries?: ConfigEntry[];
protected firstUpdated() {
@@ -54,11 +60,23 @@ export class EnergyGridSettings extends LitElement {
}
protected render(): TemplateResult {
- const types = energySourcesByType(this.preferences);
+ const gridIdx = this.preferences.energy_sources.findIndex(
+ (source) => source.type === "grid"
+ );
- const gridSource = types.grid
- ? types.grid[0]
- : emptyGridSourceEnergyPreference();
+ let gridSource: GridSourceTypeEnergyPreference;
+ let gridValidation: EnergyValidationResult | undefined;
+
+ 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`
@@ -82,6 +100,13 @@ export class EnergyGridSettings extends LitElement {
)}
+ ${gridValidation?.errors.map((msg) =>
+ renderEnergyValidationMessage("error", msg)
+ )}
+ ${gridValidation?.warnings.map((msg) =>
+ renderEnergyValidationMessage("warning", msg)
+ )}
+
Grid consumption
${gridSource.flow_from.map((flow) => {
const entityState = this.hass.states[flow.stat_energy_from];
diff --git a/src/panels/config/energy/components/ha-energy-solar-settings.ts b/src/panels/config/energy/components/ha-energy-solar-settings.ts
index 109c61d36ef1..5a0fd7b60e12 100644
--- a/src/panels/config/energy/components/ha-energy-solar-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-solar-settings.ts
@@ -7,7 +7,8 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
import "../../../../components/ha-card";
import {
EnergyPreferences,
- energySourcesByType,
+ EnergyPreferencesValidation,
+ EnergyValidationResult,
saveEnergyPreferences,
SolarSourceTypeEnergyPreference,
} from "../../../../data/energy";
@@ -19,6 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsSolarDialog } from "../dialogs/show-dialogs-energy";
+import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-solar-settings")
@@ -28,10 +30,23 @@ export class EnergySolarSettings extends LitElement {
@property({ attribute: false })
public preferences!: EnergyPreferences;
+ @property({ attribute: false })
+ public validationResult?: EnergyPreferencesValidation;
+
protected render(): TemplateResult {
- const types = energySourcesByType(this.preferences);
+ const solarSources: SolarSourceTypeEnergyPreference[] = [];
+ const solarValidation: EnergyValidationResult[] = [];
- const solarSources = types.solar || [];
+ 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`
@@ -55,6 +70,17 @@ export class EnergySolarSettings extends LitElement {
)}
+ ${solarValidation.map(
+ (result) => html`
+ ${result.errors.map((msg) =>
+ renderEnergyValidationMessage("error", msg)
+ )}
+ ${result.warnings.map((msg) =>
+ renderEnergyValidationMessage("warning", msg)
+ )}
+ `
+ )}
+
Solar production
${solarSources.map((source) => {
const entityState = this.hass.states[source.stat_energy_from];
diff --git a/src/panels/config/energy/components/ha-energy-validation-message.ts b/src/panels/config/energy/components/ha-energy-validation-message.ts
new file mode 100644
index 000000000000..01b2fc6e9be9
--- /dev/null
+++ b/src/panels/config/energy/components/ha-energy-validation-message.ts
@@ -0,0 +1,15 @@
+import { html } from "lit";
+import { EnergyValidationMessage } from "../../../../data/energy";
+
+export const renderEnergyValidationMessage = (
+ type: "error" | "warning",
+ message: EnergyValidationMessage
+) =>
+ html`
+ [${type}]
+ ${message.message}${!message.link
+ ? ""
+ : html` Learn more`}
+
`;
diff --git a/src/panels/config/energy/ha-config-energy.ts b/src/panels/config/energy/ha-config-energy.ts
index 96a98e125146..ba4086a23d93 100644
--- a/src/panels/config/energy/ha-config-energy.ts
+++ b/src/panels/config/energy/ha-config-energy.ts
@@ -1,7 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
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-tabs-subpage";
import { haStyle } from "../../../resources/styles";
@@ -12,6 +17,7 @@ import "./components/ha-energy-grid-settings";
import "./components/ha-energy-solar-settings";
import "./components/ha-energy-battery-settings";
import "./components/ha-energy-gas-settings";
+import { renderEnergyValidationMessage } from "./components/ha-energy-validation-message";
const INITIAL_CONFIG: EnergyPreferences = {
energy_sources: [],
@@ -34,6 +40,8 @@ class HaConfigEnergy extends LitElement {
@state() private _preferences?: EnergyPreferences;
+ @state() private _validationResult?: EnergyPreferencesValidation;
+
@state() private _error?: string;
protected firstUpdated() {
@@ -70,22 +78,31 @@ class HaConfigEnergy extends LitElement {
After setting up a new device, it can take up to 2 hours for new
data to arrive in your energy dashboard.
+ ${this._validationResult?.errors.map((msg) =>
+ renderEnergyValidationMessage("error", msg)
+ )}
+ ${this._validationResult?.warnings.map((msg) =>
+ renderEnergyValidationMessage("warning", msg)
+ )}
@@ -104,6 +122,7 @@ class HaConfigEnergy extends LitElement {
}
private async _fetchConfig() {
+ const validationPromise = getEnergyPreferenceValidation(this.hass);
try {
this._preferences = await getEnergyPreferences(this.hass);
} catch (e) {
@@ -113,6 +132,11 @@ class HaConfigEnergy extends LitElement {
this._error = e.message;
}
}
+ try {
+ this._validationResult = await validationPromise;
+ } catch (e) {
+ this._error = e.message;
+ }
}
private _prefsChanged(ev: CustomEvent) {
From 70ba7030bc5c3752f5a9bc424d655b1090459b5b Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Fri, 13 Aug 2021 11:25:36 -0700
Subject: [PATCH 02/10] Also refresh validation results when prefs change
---
src/panels/config/energy/ha-config-energy.ts | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/panels/config/energy/ha-config-energy.ts b/src/panels/config/energy/ha-config-energy.ts
index ba4086a23d93..1dd2b42e4a8b 100644
--- a/src/panels/config/energy/ha-config-energy.ts
+++ b/src/panels/config/energy/ha-config-energy.ts
@@ -139,8 +139,14 @@ class HaConfigEnergy extends LitElement {
}
}
- private _prefsChanged(ev: CustomEvent) {
+ private async _prefsChanged(ev: CustomEvent) {
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 {
From c8ee2e7722514e882f0e21b9d3299e51f360b152 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 17 Aug 2021 15:25:55 -0700
Subject: [PATCH 03/10] Update look
---
src/common/util/group-by.ts | 14 ++
src/data/energy.ts | 18 +--
.../components/ha-energy-battery-settings.ts | 22 +--
.../components/ha-energy-device-settings.ts | 17 ++-
.../components/ha-energy-gas-settings.ts | 21 ++-
.../components/ha-energy-grid-settings.ts | 20 +--
.../components/ha-energy-solar-settings.ts | 21 ++-
.../ha-energy-validation-message.ts | 126 +++++++++++++++---
src/panels/config/energy/ha-config-energy.ts | 7 -
src/translations/en.json | 11 +-
10 files changed, 192 insertions(+), 85 deletions(-)
create mode 100644 src/common/util/group-by.ts
diff --git a/src/common/util/group-by.ts b/src/common/util/group-by.ts
new file mode 100644
index 000000000000..4bf63e459e6b
--- /dev/null
+++ b/src/common/util/group-by.ts
@@ -0,0 +1,14 @@
+export const groupBy = (
+ list: T[],
+ keySelector: (item: T) => string
+): { [key: string]: T[] } => {
+ const result = {};
+ for (const item of list) {
+ const key = keySelector(item);
+ if (!result[key]) {
+ result[key] = [];
+ }
+ result[key].push(item);
+ }
+ return result;
+};
diff --git a/src/data/energy.ts b/src/data/energy.ts
index a4484c024cf0..f5316bdda045 100644
--- a/src/data/energy.ts
+++ b/src/data/energy.ts
@@ -144,19 +144,15 @@ export interface EnergyInfo {
cost_sensors: Record;
}
-export interface EnergyValidationMessage {
- message: string;
- link: string | null;
+export interface EnergyValidationIssue {
+ type: string;
+ identifier: string;
+ value?: unknown;
}
-export interface EnergyValidationResult {
- errors: EnergyValidationMessage[];
- warnings: EnergyValidationMessage[];
-}
-
-export interface EnergyPreferencesValidation extends EnergyValidationResult {
- energy_sources: EnergyValidationResult[];
- device_consumption: EnergyValidationResult[];
+export interface EnergyPreferencesValidation {
+ energy_sources: EnergyValidationIssue[][];
+ device_consumption: EnergyValidationIssue[][];
}
export const getEnergyInfo = (hass: HomeAssistant) =>
diff --git a/src/panels/config/energy/components/ha-energy-battery-settings.ts b/src/panels/config/energy/components/ha-energy-battery-settings.ts
index 675a6efa68b6..22c1fa6cfb3b 100644
--- a/src/panels/config/energy/components/ha-energy-battery-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-battery-settings.ts
@@ -11,7 +11,7 @@ import {
BatterySourceTypeEnergyPreference,
EnergyPreferences,
EnergyPreferencesValidation,
- EnergyValidationResult,
+ EnergyValidationIssue,
saveEnergyPreferences,
} from "../../../../data/energy";
import {
@@ -22,7 +22,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsBatteryDialog } from "../dialogs/show-dialogs-energy";
-import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
+import "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-battery-settings")
@@ -37,7 +37,7 @@ export class EnergyBatterySettings extends LitElement {
protected render(): TemplateResult {
const batterySources: BatterySourceTypeEnergyPreference[] = [];
- const batteryValidation: EnergyValidationResult[] = [];
+ const batteryValidation: EnergyValidationIssue[][] = [];
this.preferences.energy_sources.forEach((source, idx) => {
if (source.type !== "battery") {
@@ -70,15 +70,15 @@ export class EnergyBatterySettings extends LitElement {
>
${batteryValidation.map(
- (result) => html`
- ${result.errors.map((msg) =>
- renderEnergyValidationMessage("error", msg)
- )}
- ${result.warnings.map((msg) =>
- renderEnergyValidationMessage("warning", msg)
- )}
- `
+ (result) =>
+ html`
+
+ `
)}
+
Battery systems
${batterySources.map((source) => {
const fromEntityState = this.hass.states[source.stat_energy_from];
diff --git a/src/panels/config/energy/components/ha-energy-device-settings.ts b/src/panels/config/energy/components/ha-energy-device-settings.ts
index caa786d3c14f..06a22fcd4f43 100644
--- a/src/panels/config/energy/components/ha-energy-device-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-device-settings.ts
@@ -20,7 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsDeviceDialog } from "../dialogs/show-dialogs-energy";
-import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
+import "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-device-settings")
@@ -61,14 +61,13 @@ export class EnergyDeviceSettings extends LitElement {
>
${this.validationResult?.device_consumption.map(
- (result) => html`
- ${result.errors.map((msg) =>
- renderEnergyValidationMessage("error", msg)
- )}
- ${result.warnings.map((msg) =>
- renderEnergyValidationMessage("warning", msg)
- )}
- `
+ (result) =>
+ html`
+
+ `
)}
Devices
${this.preferences.device_consumption.map((device) => {
diff --git a/src/panels/config/energy/components/ha-energy-gas-settings.ts b/src/panels/config/energy/components/ha-energy-gas-settings.ts
index 34e390e8c661..2a80cbb4de4f 100644
--- a/src/panels/config/energy/components/ha-energy-gas-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-gas-settings.ts
@@ -10,7 +10,7 @@ import {
saveEnergyPreferences,
GasSourceTypeEnergyPreference,
EnergyPreferencesValidation,
- EnergyValidationResult,
+ EnergyValidationIssue,
} from "../../../../data/energy";
import {
showConfirmationDialog,
@@ -20,7 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsGasDialog } from "../dialogs/show-dialogs-energy";
-import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
+import "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-gas-settings")
@@ -35,7 +35,7 @@ export class EnergyGasSettings extends LitElement {
protected render(): TemplateResult {
const gasSources: GasSourceTypeEnergyPreference[] = [];
- const gasValidation: EnergyValidationResult[] = [];
+ const gasValidation: EnergyValidationIssue[][] = [];
this.preferences.energy_sources.forEach((source, idx) => {
if (source.type !== "gas") {
@@ -66,14 +66,13 @@ export class EnergyGasSettings extends LitElement {
>
${gasValidation.map(
- (result) => html`
- ${result.errors.map((msg) =>
- renderEnergyValidationMessage("error", msg)
- )}
- ${result.warnings.map((msg) =>
- renderEnergyValidationMessage("warning", msg)
- )}
- `
+ (result) =>
+ html`
+
+ `
)}
Gas consumption
${gasSources.map((source) => {
diff --git a/src/panels/config/energy/components/ha-energy-grid-settings.ts b/src/panels/config/energy/components/ha-energy-grid-settings.ts
index db27ce6cb31f..f26e8dbe4d44 100644
--- a/src/panels/config/energy/components/ha-energy-grid-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-grid-settings.ts
@@ -21,7 +21,7 @@ import {
EnergyPreferences,
EnergyPreferencesValidation,
energySourcesByType,
- EnergyValidationResult,
+ EnergyValidationIssue,
FlowFromGridSourceEnergyPreference,
FlowToGridSourceEnergyPreference,
GridSourceTypeEnergyPreference,
@@ -40,7 +40,7 @@ import {
showEnergySettingsGridFlowFromDialog,
showEnergySettingsGridFlowToDialog,
} from "../dialogs/show-dialogs-energy";
-import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
+import "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-grid-settings")
@@ -65,7 +65,7 @@ export class EnergyGridSettings extends LitElement {
);
let gridSource: GridSourceTypeEnergyPreference;
- let gridValidation: EnergyValidationResult | undefined;
+ let gridValidation: EnergyValidationIssue[] | undefined;
if (gridIdx === -1) {
gridSource = emptyGridSourceEnergyPreference();
@@ -100,12 +100,14 @@ export class EnergyGridSettings extends LitElement {
)}
- ${gridValidation?.errors.map((msg) =>
- renderEnergyValidationMessage("error", msg)
- )}
- ${gridValidation?.warnings.map((msg) =>
- renderEnergyValidationMessage("warning", msg)
- )}
+ ${gridValidation
+ ? html`
+
+ `
+ : ""}
Grid consumption
${gridSource.flow_from.map((flow) => {
diff --git a/src/panels/config/energy/components/ha-energy-solar-settings.ts b/src/panels/config/energy/components/ha-energy-solar-settings.ts
index 5a0fd7b60e12..43b1f5506334 100644
--- a/src/panels/config/energy/components/ha-energy-solar-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-solar-settings.ts
@@ -8,7 +8,7 @@ import "../../../../components/ha-card";
import {
EnergyPreferences,
EnergyPreferencesValidation,
- EnergyValidationResult,
+ EnergyValidationIssue,
saveEnergyPreferences,
SolarSourceTypeEnergyPreference,
} from "../../../../data/energy";
@@ -20,7 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsSolarDialog } from "../dialogs/show-dialogs-energy";
-import { renderEnergyValidationMessage } from "./ha-energy-validation-message";
+import "./ha-energy-validation-message";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-solar-settings")
@@ -35,7 +35,7 @@ export class EnergySolarSettings extends LitElement {
protected render(): TemplateResult {
const solarSources: SolarSourceTypeEnergyPreference[] = [];
- const solarValidation: EnergyValidationResult[] = [];
+ const solarValidation: EnergyValidationIssue[][] = [];
this.preferences.energy_sources.forEach((source, idx) => {
if (source.type !== "solar") {
@@ -71,14 +71,13 @@ export class EnergySolarSettings extends LitElement {
>
${solarValidation.map(
- (result) => html`
- ${result.errors.map((msg) =>
- renderEnergyValidationMessage("error", msg)
- )}
- ${result.warnings.map((msg) =>
- renderEnergyValidationMessage("warning", msg)
- )}
- `
+ (result) =>
+ html`
+
+ `
)}
Solar production
diff --git a/src/panels/config/energy/components/ha-energy-validation-message.ts b/src/panels/config/energy/components/ha-energy-validation-message.ts
index 01b2fc6e9be9..41278514eaff 100644
--- a/src/panels/config/energy/components/ha-energy-validation-message.ts
+++ b/src/panels/config/energy/components/ha-energy-validation-message.ts
@@ -1,15 +1,111 @@
-import { html } from "lit";
-import { EnergyValidationMessage } from "../../../../data/energy";
-
-export const renderEnergyValidationMessage = (
- type: "error" | "warning",
- message: EnergyValidationMessage
-) =>
- html`
- [${type}]
- ${message.message}${!message.link
- ? ""
- : html` Learn more`}
-
`;
+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`
+
+
+
+
+
+
+ ${this.hass.localize(
+ `ui.panel.config.energy.validation.issues.${issueType}.title`
+ ) || issueType}
+
+
+ ${this.hass.localize(
+ `ui.panel.config.energy.validation.issues.${issueType}.description`
+ )}
+ ${issueType === "entity_not_tracked" || true
+ ? html`
+
${this.hass.localize(
+ "ui.panel.config.common.learn_more"
+ )}
+ `
+ : ""}
+
+
+ ${gIssues.map(
+ (issue) =>
+ html`-
+ ${issue.identifier}${issue.value
+ ? html` (${issue.value})`
+ : ""}
+
`
+ )}
+
+
+
+ `
+ );
+ }
+
+ static styles = css`
+ .issue-type {
+ position: relative;
+ padding: 4px;
+ display: flex;
+ }
+ .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: "";
+ }
+ .icon {
+ margin: 4px 8px;
+ width: 24px;
+ }
+ ha-svg-icon {
+ color: var(--warning-color);
+ }
+ .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;
+ }
+}
diff --git a/src/panels/config/energy/ha-config-energy.ts b/src/panels/config/energy/ha-config-energy.ts
index 1dd2b42e4a8b..bd1efaa2ab79 100644
--- a/src/panels/config/energy/ha-config-energy.ts
+++ b/src/panels/config/energy/ha-config-energy.ts
@@ -17,7 +17,6 @@ import "./components/ha-energy-grid-settings";
import "./components/ha-energy-solar-settings";
import "./components/ha-energy-battery-settings";
import "./components/ha-energy-gas-settings";
-import { renderEnergyValidationMessage } from "./components/ha-energy-validation-message";
const INITIAL_CONFIG: EnergyPreferences = {
energy_sources: [],
@@ -78,12 +77,6 @@ class HaConfigEnergy extends LitElement {
After setting up a new device, it can take up to 2 hours for new
data to arrive in your energy dashboard.
- ${this._validationResult?.errors.map((msg) =>
- renderEnergyValidationMessage("error", msg)
- )}
- ${this._validationResult?.warnings.map((msg) =>
- renderEnergyValidationMessage("warning", msg)
- )}
diff --git a/src/translations/en.json b/src/translations/en.json
index 4dbc1ce1e006..c983284df0ce 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -934,7 +934,8 @@
"common": {
"editor": {
"confirm_unsaved": "You have unsaved changes. Are you sure you want to leave?"
- }
+ },
+ "learn_more": "Learn more"
},
"areas": {
"caption": "Areas",
@@ -1079,6 +1080,14 @@
"dialog": {
"selected_stat_intro": "Select the entity that represents the device energy usage."
}
+ },
+ "validation": {
+ "issues": {
+ "entity_not_defined": {
+ "title": "Entity not defined",
+ "description": "One of the configured entities is not currently defined in Home Assistant."
+ }
+ }
}
},
"helpers": {
From 4a349fc1072f2a30e4b70a3c9eb9f7f8c0ab3908 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 17 Aug 2021 15:27:05 -0700
Subject: [PATCH 04/10] Remove || true
---
.../config/energy/components/ha-energy-validation-message.ts | 2 +-
src/translations/en.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/panels/config/energy/components/ha-energy-validation-message.ts b/src/panels/config/energy/components/ha-energy-validation-message.ts
index 41278514eaff..004986c250f8 100644
--- a/src/panels/config/energy/components/ha-energy-validation-message.ts
+++ b/src/panels/config/energy/components/ha-energy-validation-message.ts
@@ -37,7 +37,7 @@ class EnergyValidationMessage extends LitElement {
${this.hass.localize(
`ui.panel.config.energy.validation.issues.${issueType}.description`
)}
- ${issueType === "entity_not_tracked" || true
+ ${issueType === "entity_not_tracked"
? html`
Date: Tue, 17 Aug 2021 15:34:49 -0700
Subject: [PATCH 05/10] Add missing errors
---
.../ha-energy-validation-message.ts | 9 +++++----
src/translations/en.json | 20 +++++++++++++++++++
2 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/panels/config/energy/components/ha-energy-validation-message.ts b/src/panels/config/energy/components/ha-energy-validation-message.ts
index 004986c250f8..8dcd10276058 100644
--- a/src/panels/config/energy/components/ha-energy-validation-message.ts
+++ b/src/panels/config/energy/components/ha-energy-validation-message.ts
@@ -39,14 +39,14 @@ class EnergyValidationMessage extends LitElement {
)}
${issueType === "entity_not_tracked"
? html`
- ${this.hass.localize(
"ui.panel.config.common.learn_more"
)}
+ >)
`
: ""}
@@ -86,10 +86,11 @@ class EnergyValidationMessage extends LitElement {
.icon {
margin: 4px 8px;
width: 24px;
- }
- ha-svg-icon {
color: var(--warning-color);
}
+ .content {
+ padding-right: 4px;
+ }
.title {
font-weight: bold;
margin-top: 5px;
diff --git a/src/translations/en.json b/src/translations/en.json
index 60a2efb2e007..7cadc8c765a9 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1086,6 +1086,26 @@
"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": {
+ "title": "Unexpected unit of measurement",
+ "description": "The following entities do not have expected units of measurement kWh or Wh:"
}
}
}
From 024ecc85b4683a153aad4b268dd4a24472c8758f Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 17 Aug 2021 16:11:20 -0700
Subject: [PATCH 06/10] Validate state class
---
src/translations/en.json | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/translations/en.json b/src/translations/en.json
index 7cadc8c765a9..97ca82fb7f1e 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1103,9 +1103,17 @@
"title": "Entity has a negative state",
"description": "The following entities have a negative state while a positive state is expected:"
},
- "entity_unexpected_unit": {
+ "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": {
+ "title": "Unexpected state class",
+ "description": "The following entities do not have expeted state class \"total_increasing\""
}
}
}
From 98c582f00e541b1973ce5e1c75092bc5986bd62a Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 17 Aug 2021 20:57:45 -0700
Subject: [PATCH 07/10] Rename file
---
.../config/energy/components/ha-energy-battery-settings.ts | 2 +-
.../config/energy/components/ha-energy-device-settings.ts | 2 +-
src/panels/config/energy/components/ha-energy-gas-settings.ts | 2 +-
src/panels/config/energy/components/ha-energy-grid-settings.ts | 2 +-
src/panels/config/energy/components/ha-energy-solar-settings.ts | 2 +-
...rgy-validation-message.ts => ha-energy-validation-result.ts} | 0
src/translations/en.json | 2 +-
7 files changed, 6 insertions(+), 6 deletions(-)
rename src/panels/config/energy/components/{ha-energy-validation-message.ts => ha-energy-validation-result.ts} (100%)
diff --git a/src/panels/config/energy/components/ha-energy-battery-settings.ts b/src/panels/config/energy/components/ha-energy-battery-settings.ts
index 22c1fa6cfb3b..d1360fd61d5b 100644
--- a/src/panels/config/energy/components/ha-energy-battery-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-battery-settings.ts
@@ -22,7 +22,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsBatteryDialog } from "../dialogs/show-dialogs-energy";
-import "./ha-energy-validation-message";
+import "./ha-energy-validation-result";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-battery-settings")
diff --git a/src/panels/config/energy/components/ha-energy-device-settings.ts b/src/panels/config/energy/components/ha-energy-device-settings.ts
index 06a22fcd4f43..c99b36849d0f 100644
--- a/src/panels/config/energy/components/ha-energy-device-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-device-settings.ts
@@ -20,7 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsDeviceDialog } from "../dialogs/show-dialogs-energy";
-import "./ha-energy-validation-message";
+import "./ha-energy-validation-result";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-device-settings")
diff --git a/src/panels/config/energy/components/ha-energy-gas-settings.ts b/src/panels/config/energy/components/ha-energy-gas-settings.ts
index 2a80cbb4de4f..4b57b0ee3de7 100644
--- a/src/panels/config/energy/components/ha-energy-gas-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-gas-settings.ts
@@ -20,7 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsGasDialog } from "../dialogs/show-dialogs-energy";
-import "./ha-energy-validation-message";
+import "./ha-energy-validation-result";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-gas-settings")
diff --git a/src/panels/config/energy/components/ha-energy-grid-settings.ts b/src/panels/config/energy/components/ha-energy-grid-settings.ts
index f26e8dbe4d44..6f59fe6a187c 100644
--- a/src/panels/config/energy/components/ha-energy-grid-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-grid-settings.ts
@@ -40,7 +40,7 @@ import {
showEnergySettingsGridFlowFromDialog,
showEnergySettingsGridFlowToDialog,
} from "../dialogs/show-dialogs-energy";
-import "./ha-energy-validation-message";
+import "./ha-energy-validation-result";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-grid-settings")
diff --git a/src/panels/config/energy/components/ha-energy-solar-settings.ts b/src/panels/config/energy/components/ha-energy-solar-settings.ts
index 43b1f5506334..d58550e8d8f1 100644
--- a/src/panels/config/energy/components/ha-energy-solar-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-solar-settings.ts
@@ -20,7 +20,7 @@ import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { documentationUrl } from "../../../../util/documentation-url";
import { showEnergySettingsSolarDialog } from "../dialogs/show-dialogs-energy";
-import "./ha-energy-validation-message";
+import "./ha-energy-validation-result";
import { energyCardStyles } from "./styles";
@customElement("ha-energy-solar-settings")
diff --git a/src/panels/config/energy/components/ha-energy-validation-message.ts b/src/panels/config/energy/components/ha-energy-validation-result.ts
similarity index 100%
rename from src/panels/config/energy/components/ha-energy-validation-message.ts
rename to src/panels/config/energy/components/ha-energy-validation-result.ts
diff --git a/src/translations/en.json b/src/translations/en.json
index 97ca82fb7f1e..a7910856ea71 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1111,7 +1111,7 @@
"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": {
+ "entity_unexpected_state_class_total_increasing": {
"title": "Unexpected state class",
"description": "The following entities do not have expeted state class \"total_increasing\""
}
From 5e2e4b806831c438308f171f03696f67cd7cd41d Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 17 Aug 2021 21:18:11 -0700
Subject: [PATCH 08/10] Simplify energySourcesByType
---
src/common/util/group-by.ts | 7 ++++---
src/data/energy.ts | 14 +++-----------
2 files changed, 7 insertions(+), 14 deletions(-)
diff --git a/src/common/util/group-by.ts b/src/common/util/group-by.ts
index 4bf63e459e6b..0f3f76dfff8c 100644
--- a/src/common/util/group-by.ts
+++ b/src/common/util/group-by.ts
@@ -5,10 +5,11 @@ export const groupBy = (
const result = {};
for (const item of list) {
const key = keySelector(item);
- if (!result[key]) {
- result[key] = [];
+ if (key in result) {
+ result[key].push(item);
+ } else {
+ result[key] = [item];
}
- result[key].push(item);
}
return result;
};
diff --git a/src/data/energy.ts b/src/data/energy.ts
index f5316bdda045..799bb284f7ff 100644
--- a/src/data/energy.ts
+++ b/src/data/energy.ts
@@ -6,6 +6,7 @@ import {
startOfYesterday,
} from "date-fns";
import { Collection, getCollection } from "home-assistant-js-websocket";
+import { groupBy } from "../common/util/group-by";
import { subscribeOne } from "../common/util/subscribe-one";
import { HomeAssistant } from "../types";
import { ConfigEntry, getConfigEntries } from "./config_entries";
@@ -189,17 +190,8 @@ interface EnergySourceByType {
gas?: GasSourceTypeEnergyPreference[];
}
-export const energySourcesByType = (prefs: EnergyPreferences) => {
- const types: 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 const energySourcesByType = (prefs: EnergyPreferences) =>
+ groupBy(prefs.energy_sources, (item) => item.type) as EnergySourceByType;
export interface EnergyData {
start: Date;
From 8daa55f38e4228d0e34d7de4515c5e71f71f3056 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 17 Aug 2021 21:28:29 -0700
Subject: [PATCH 09/10] Update src/translations/en.json
---
src/translations/en.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/translations/en.json b/src/translations/en.json
index a7910856ea71..a06485886e31 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1113,7 +1113,7 @@
},
"entity_unexpected_state_class_total_increasing": {
"title": "Unexpected state class",
- "description": "The following entities do not have expeted state class \"total_increasing\""
+ "description": "The following entities do not have expected state class \"total_increasing\""
}
}
}
From 8c53b46401d4349b9f04bac04d36a8fdd2b3e0e2 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen
Date: Tue, 17 Aug 2021 23:20:51 -0700
Subject: [PATCH 10/10] Update ha-energy-validation-result.ts
---
.../config/energy/components/ha-energy-validation-result.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/panels/config/energy/components/ha-energy-validation-result.ts b/src/panels/config/energy/components/ha-energy-validation-result.ts
index 8dcd10276058..344b6d85b321 100644
--- a/src/panels/config/energy/components/ha-energy-validation-result.ts
+++ b/src/panels/config/energy/components/ha-energy-validation-result.ts
@@ -71,6 +71,7 @@ class EnergyValidationMessage extends LitElement {
position: relative;
padding: 4px;
display: flex;
+ margin: 4px 0;
}
.issue-type::before {
position: absolute;
@@ -82,6 +83,7 @@ class EnergyValidationMessage extends LitElement {
opacity: 0.12;
pointer-events: none;
content: "";
+ border-radius: 4px;
}
.icon {
margin: 4px 8px;