mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-13 20:36:35 +00:00
Get solar forecasts from energy platform (#9794)
* Get solar forecasts from energy platform * Allow picking other forecasts in settings
This commit is contained in:
parent
b15684bcbd
commit
5bd92d04d9
@ -23,7 +23,6 @@ import { mockTranslations } from "./stubs/translations";
|
||||
import { mockEnergy } from "./stubs/energy";
|
||||
import { mockConfig } from "./stubs/config";
|
||||
import { energyEntities } from "./stubs/entities";
|
||||
import { mockForecastSolar } from "./stubs/forecast_solar";
|
||||
|
||||
class HaDemo extends HomeAssistantAppEl {
|
||||
protected async _initializeHass() {
|
||||
@ -52,7 +51,6 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
mockMediaPlayer(hass);
|
||||
mockFrontend(hass);
|
||||
mockEnergy(hass);
|
||||
mockForecastSolar(hass);
|
||||
mockConfig(hass);
|
||||
mockPersistentNotification(hass);
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||
import { EnergySolarForecasts } from "../../../src/data/energy";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||
@ -80,4 +82,53 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||
],
|
||||
}));
|
||||
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
|
||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||
hass.mockWS(
|
||||
"energy/solar_forecast",
|
||||
(): EnergySolarForecasts => ({
|
||||
solar_forecast: {
|
||||
wh_hours: {
|
||||
[`${todayString}T06:00:00`]: 0,
|
||||
[`${todayString}T06:23:00`]: 6,
|
||||
[`${todayString}T06:45:00`]: 39,
|
||||
[`${todayString}T07:00:00`]: 28,
|
||||
[`${todayString}T08:00:00`]: 208,
|
||||
[`${todayString}T09:00:00`]: 352,
|
||||
[`${todayString}T10:00:00`]: 544,
|
||||
[`${todayString}T11:00:00`]: 748,
|
||||
[`${todayString}T12:00:00`]: 1259,
|
||||
[`${todayString}T13:00:00`]: 1361,
|
||||
[`${todayString}T14:00:00`]: 1373,
|
||||
[`${todayString}T15:00:00`]: 1370,
|
||||
[`${todayString}T16:00:00`]: 1186,
|
||||
[`${todayString}T17:00:00`]: 937,
|
||||
[`${todayString}T18:00:00`]: 652,
|
||||
[`${todayString}T19:00:00`]: 370,
|
||||
[`${todayString}T20:00:00`]: 155,
|
||||
[`${todayString}T21:48:00`]: 24,
|
||||
[`${todayString}T22:36:00`]: 0,
|
||||
[`${tomorrowString}T06:01:00`]: 0,
|
||||
[`${tomorrowString}T06:23:00`]: 9,
|
||||
[`${tomorrowString}T06:45:00`]: 47,
|
||||
[`${tomorrowString}T07:00:00`]: 48,
|
||||
[`${tomorrowString}T08:00:00`]: 473,
|
||||
[`${tomorrowString}T09:00:00`]: 827,
|
||||
[`${tomorrowString}T10:00:00`]: 1153,
|
||||
[`${tomorrowString}T11:00:00`]: 1413,
|
||||
[`${tomorrowString}T12:00:00`]: 1590,
|
||||
[`${tomorrowString}T13:00:00`]: 1652,
|
||||
[`${tomorrowString}T14:00:00`]: 1612,
|
||||
[`${tomorrowString}T15:00:00`]: 1438,
|
||||
[`${tomorrowString}T16:00:00`]: 1149,
|
||||
[`${tomorrowString}T17:00:00`]: 830,
|
||||
[`${tomorrowString}T18:00:00`]: 542,
|
||||
[`${tomorrowString}T19:00:00`]: 311,
|
||||
[`${tomorrowString}T20:00:00`]: 140,
|
||||
[`${tomorrowString}T21:47:00`]: 22,
|
||||
[`${tomorrowString}T22:34:00`]: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
@ -1,55 +0,0 @@
|
||||
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||
import { ForecastSolarForecast } from "../../../src/data/forecast_solar";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockForecastSolar = (hass: MockHomeAssistant) => {
|
||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||
hass.mockWS(
|
||||
"forecast_solar/forecasts",
|
||||
(): Record<string, ForecastSolarForecast> => ({
|
||||
solar_forecast: {
|
||||
wh_hours: {
|
||||
[`${todayString}T06:00:00`]: 0,
|
||||
[`${todayString}T06:23:00`]: 6,
|
||||
[`${todayString}T06:45:00`]: 39,
|
||||
[`${todayString}T07:00:00`]: 28,
|
||||
[`${todayString}T08:00:00`]: 208,
|
||||
[`${todayString}T09:00:00`]: 352,
|
||||
[`${todayString}T10:00:00`]: 544,
|
||||
[`${todayString}T11:00:00`]: 748,
|
||||
[`${todayString}T12:00:00`]: 1259,
|
||||
[`${todayString}T13:00:00`]: 1361,
|
||||
[`${todayString}T14:00:00`]: 1373,
|
||||
[`${todayString}T15:00:00`]: 1370,
|
||||
[`${todayString}T16:00:00`]: 1186,
|
||||
[`${todayString}T17:00:00`]: 937,
|
||||
[`${todayString}T18:00:00`]: 652,
|
||||
[`${todayString}T19:00:00`]: 370,
|
||||
[`${todayString}T20:00:00`]: 155,
|
||||
[`${todayString}T21:48:00`]: 24,
|
||||
[`${todayString}T22:36:00`]: 0,
|
||||
[`${tomorrowString}T06:01:00`]: 0,
|
||||
[`${tomorrowString}T06:23:00`]: 9,
|
||||
[`${tomorrowString}T06:45:00`]: 47,
|
||||
[`${tomorrowString}T07:00:00`]: 48,
|
||||
[`${tomorrowString}T08:00:00`]: 473,
|
||||
[`${tomorrowString}T09:00:00`]: 827,
|
||||
[`${tomorrowString}T10:00:00`]: 1153,
|
||||
[`${tomorrowString}T11:00:00`]: 1413,
|
||||
[`${tomorrowString}T12:00:00`]: 1590,
|
||||
[`${tomorrowString}T13:00:00`]: 1652,
|
||||
[`${tomorrowString}T14:00:00`]: 1612,
|
||||
[`${tomorrowString}T15:00:00`]: 1438,
|
||||
[`${tomorrowString}T16:00:00`]: 1149,
|
||||
[`${tomorrowString}T17:00:00`]: 830,
|
||||
[`${tomorrowString}T18:00:00`]: 542,
|
||||
[`${tomorrowString}T19:00:00`]: 311,
|
||||
[`${tomorrowString}T20:00:00`]: 140,
|
||||
[`${tomorrowString}T21:47:00`]: 22,
|
||||
[`${tomorrowString}T22:34:00`]: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
@ -63,6 +63,13 @@ export const emptyGasEnergyPreference = (): GasSourceTypeEnergyPreference => ({
|
||||
number_energy_price: null,
|
||||
});
|
||||
|
||||
interface EnergySolarForecast {
|
||||
wh_hours: Record<string, number>;
|
||||
}
|
||||
export type EnergySolarForecasts = {
|
||||
[config_entry_id: string]: EnergySolarForecast;
|
||||
};
|
||||
|
||||
export interface DeviceConsumptionEnergyPreference {
|
||||
// This is an ever increasing value
|
||||
stat_consumption: string;
|
||||
@ -143,6 +150,7 @@ export interface EnergyPreferences {
|
||||
|
||||
export interface EnergyInfo {
|
||||
cost_sensors: Record<string, string>;
|
||||
solar_forecast_domains: string[];
|
||||
}
|
||||
|
||||
export interface EnergyValidationIssue {
|
||||
@ -440,3 +448,8 @@ export const getEnergyDataCollection = (
|
||||
};
|
||||
return collection;
|
||||
};
|
||||
|
||||
export const getEnergySolarForecasts = (hass: HomeAssistant) =>
|
||||
hass.callWS<EnergySolarForecasts>({
|
||||
type: "energy/solar_forecast",
|
||||
});
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export interface ForecastSolarForecast {
|
||||
wh_hours: Record<string, number>;
|
||||
}
|
||||
|
||||
export const getForecastSolarForecasts = (hass: HomeAssistant) =>
|
||||
hass.callWS<Record<string, ForecastSolarForecast>>({
|
||||
type: "forecast_solar/forecasts",
|
||||
});
|
@ -6,6 +6,7 @@ import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||
import "../../../../components/ha-card";
|
||||
import {
|
||||
EnergyInfo,
|
||||
EnergyPreferences,
|
||||
EnergyPreferencesValidation,
|
||||
EnergyValidationIssue,
|
||||
@ -33,6 +34,9 @@ export class EnergySolarSettings extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public validationResult?: EnergyPreferencesValidation;
|
||||
|
||||
@property({ attribute: false })
|
||||
public info?: EnergyInfo;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const solarSources: SolarSourceTypeEnergyPreference[] = [];
|
||||
const solarValidation: EnergyValidationIssue[][] = [];
|
||||
@ -95,21 +99,29 @@ export class EnergySolarSettings extends LitElement {
|
||||
? computeStateName(entityState)
|
||||
: source.stat_energy_from}</span
|
||||
>
|
||||
<mwc-icon-button @click=${this._editSource}>
|
||||
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
${this.info
|
||||
? html`
|
||||
<mwc-icon-button @click=${this._editSource}>
|
||||
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
`
|
||||
: ""}
|
||||
<mwc-icon-button @click=${this._deleteSource}>
|
||||
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
<div class="row border-bottom">
|
||||
<ha-svg-icon .path=${mdiSolarPower}></ha-svg-icon>
|
||||
<mwc-button @click=${this._addSource}
|
||||
>Add solar production</mwc-button
|
||||
>
|
||||
</div>
|
||||
${this.info
|
||||
? html`
|
||||
<div class="row border-bottom">
|
||||
<ha-svg-icon .path=${mdiSolarPower}></ha-svg-icon>
|
||||
<mwc-button @click=${this._addSource}>
|
||||
Add solar production
|
||||
</mwc-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
@ -117,6 +129,7 @@ export class EnergySolarSettings extends LitElement {
|
||||
|
||||
private _addSource() {
|
||||
showEnergySettingsSolarDialog(this, {
|
||||
info: this.info!,
|
||||
saveCallback: async (source) => {
|
||||
await this._savePreferences({
|
||||
...this.preferences,
|
||||
@ -130,6 +143,7 @@ export class EnergySolarSettings extends LitElement {
|
||||
const origSource: SolarSourceTypeEnergyPreference =
|
||||
ev.currentTarget.closest(".row").source;
|
||||
showEnergySettingsSolarDialog(this, {
|
||||
info: this.info!,
|
||||
source: { ...origSource },
|
||||
saveCallback: async (newSource) => {
|
||||
await this._savePreferences({
|
||||
|
@ -44,8 +44,8 @@ export class DialogEnergySolarSettings
|
||||
public async showDialog(
|
||||
params: EnergySettingsSolarDialogParams
|
||||
): Promise<void> {
|
||||
this._fetchForecastSolarConfigEntries();
|
||||
this._params = params;
|
||||
this._fetchSolarForecastConfigEntries();
|
||||
this._source = params.source
|
||||
? { ...params.source }
|
||||
: (this._source = emptySolarEnergyPreference());
|
||||
@ -118,7 +118,7 @@ export class DialogEnergySolarSettings
|
||||
referrerpolicy="no-referrer"
|
||||
style="height: 24px; margin-right: 16px;"
|
||||
src=${brandsUrl({
|
||||
domain: "forecast_solar",
|
||||
domain: entry.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.selectedTheme?.dark,
|
||||
})}
|
||||
@ -155,9 +155,10 @@ export class DialogEnergySolarSettings
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchForecastSolarConfigEntries() {
|
||||
this._configEntries = (await getConfigEntries(this.hass)).filter(
|
||||
(entry) => entry.domain === "forecast_solar"
|
||||
private async _fetchSolarForecastConfigEntries() {
|
||||
const domains = this._params!.info.solar_forecast_domains;
|
||||
this._configEntries = (await getConfigEntries(this.hass)).filter((entry) =>
|
||||
domains.includes(entry.domain)
|
||||
);
|
||||
}
|
||||
|
||||
@ -192,7 +193,7 @@ export class DialogEnergySolarSettings
|
||||
this._source!.config_entry_solar_forecast = [];
|
||||
}
|
||||
this._source!.config_entry_solar_forecast.push(params.entryId);
|
||||
this._fetchForecastSolarConfigEntries();
|
||||
this._fetchSolarForecastConfigEntries();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import {
|
||||
BatterySourceTypeEnergyPreference,
|
||||
DeviceConsumptionEnergyPreference,
|
||||
EnergyInfo,
|
||||
FlowFromGridSourceEnergyPreference,
|
||||
FlowToGridSourceEnergyPreference,
|
||||
GasSourceTypeEnergyPreference,
|
||||
@ -31,6 +32,7 @@ export interface EnergySettingsGridFlowToDialogParams {
|
||||
}
|
||||
|
||||
export interface EnergySettingsSolarDialogParams {
|
||||
info: EnergyInfo;
|
||||
source?: SolarSourceTypeEnergyPreference;
|
||||
saveCallback: (source: SolarSourceTypeEnergyPreference) => Promise<void>;
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
EnergyPreferences,
|
||||
EnergyPreferencesValidation,
|
||||
getEnergyPreferences,
|
||||
getEnergyPreferenceValidation,
|
||||
EnergyInfo,
|
||||
EnergyPreferences,
|
||||
getEnergyInfo,
|
||||
getEnergyPreferences,
|
||||
} from "../../../data/energy";
|
||||
import "../../../layouts/hass-loading-screen";
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
@ -37,6 +39,8 @@ class HaConfigEnergy extends LitElement {
|
||||
|
||||
@state() private _searchParms = new URLSearchParams(window.location.search);
|
||||
|
||||
@state() private _info?: EnergyInfo;
|
||||
|
||||
@state() private _preferences?: EnergyPreferences;
|
||||
|
||||
@state() private _validationResult?: EnergyPreferencesValidation;
|
||||
@ -90,6 +94,7 @@ class HaConfigEnergy extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.preferences=${this._preferences!}
|
||||
.validationResult=${this._validationResult!}
|
||||
.info=${this._info}
|
||||
@value-changed=${this._prefsChanged}
|
||||
></ha-energy-solar-settings>
|
||||
<ha-energy-battery-settings
|
||||
@ -115,7 +120,10 @@ class HaConfigEnergy extends LitElement {
|
||||
}
|
||||
|
||||
private async _fetchConfig() {
|
||||
this._error = undefined;
|
||||
|
||||
const validationPromise = getEnergyPreferenceValidation(this.hass);
|
||||
const energyInfoPromise = await getEnergyInfo(this.hass);
|
||||
try {
|
||||
this._preferences = await getEnergyPreferences(this.hass);
|
||||
} catch (e) {
|
||||
@ -130,6 +138,7 @@ class HaConfigEnergy extends LitElement {
|
||||
} catch (e) {
|
||||
this._error = e.message;
|
||||
}
|
||||
this._info = await energyInfoPromise;
|
||||
}
|
||||
|
||||
private async _prefsChanged(ev: CustomEvent) {
|
||||
@ -140,6 +149,7 @@ class HaConfigEnergy extends LitElement {
|
||||
} catch (e) {
|
||||
this._error = e.message;
|
||||
}
|
||||
this._info = await getEnergyInfo(this.hass);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
@ -23,14 +23,11 @@ import {
|
||||
import { labDarken } from "../../../../common/color/lab";
|
||||
import {
|
||||
EnergyData,
|
||||
EnergySolarForecasts,
|
||||
getEnergyDataCollection,
|
||||
getEnergySolarForecasts,
|
||||
SolarSourceTypeEnergyPreference,
|
||||
} from "../../../../data/energy";
|
||||
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
||||
import {
|
||||
ForecastSolarForecast,
|
||||
getForecastSolarForecasts,
|
||||
} from "../../../../data/forecast_solar";
|
||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import {
|
||||
@ -218,13 +215,12 @@ export class HuiEnergySolarGraphCard
|
||||
(source) => source.type === "solar"
|
||||
) as SolarSourceTypeEnergyPreference[];
|
||||
|
||||
let forecasts: Record<string, ForecastSolarForecast>;
|
||||
let forecasts: EnergySolarForecasts | undefined;
|
||||
if (
|
||||
isComponentLoaded(this.hass, "forecast_solar") &&
|
||||
solarSources.some((source) => source.config_entry_solar_forecast)
|
||||
solarSources.some((source) => source.config_entry_solar_forecast?.length)
|
||||
) {
|
||||
try {
|
||||
forecasts = await getForecastSolarForecasts(this.hass);
|
||||
forecasts = await getEnergySolarForecasts(this.hass);
|
||||
} catch (_e) {
|
||||
// ignore
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user