mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
Merge pull request #9685 from home-assistant/dev
This commit is contained in:
commit
ba20aef206
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20210801.0",
|
||||
version="20210802.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/frontend",
|
||||
author="The Home Assistant Authors",
|
||||
|
@ -12,6 +12,8 @@ import { ConfigEntry, getConfigEntries } from "./config_entries";
|
||||
import { subscribeEntityRegistry } from "./entity_registry";
|
||||
import { fetchStatistics, Statistics } from "./history";
|
||||
|
||||
const energyCollectionKeys: string[] = [];
|
||||
|
||||
export const emptyFlowFromGridSourceEnergyPreference =
|
||||
(): FlowFromGridSourceEnergyPreference => ({
|
||||
stat_energy_from: "",
|
||||
@ -123,11 +125,7 @@ export const saveEnergyPreferences = async (
|
||||
type: "energy/save_prefs",
|
||||
...prefs,
|
||||
});
|
||||
const energyCollection = getEnergyDataCollection(hass);
|
||||
energyCollection.clearPrefs();
|
||||
if (energyCollection._active) {
|
||||
energyCollection.refresh();
|
||||
}
|
||||
clearEnergyCollectionPreferences(hass);
|
||||
return newPrefs;
|
||||
};
|
||||
|
||||
@ -208,9 +206,23 @@ const getEnergyData = async (
|
||||
// grid source
|
||||
for (const flowFrom of source.flow_from) {
|
||||
statIDs.push(flowFrom.stat_energy_from);
|
||||
if (flowFrom.stat_cost) {
|
||||
statIDs.push(flowFrom.stat_cost);
|
||||
}
|
||||
const costStatId = info.cost_sensors[flowFrom.stat_energy_from];
|
||||
if (costStatId) {
|
||||
statIDs.push(costStatId);
|
||||
}
|
||||
}
|
||||
for (const flowTo of source.flow_to) {
|
||||
statIDs.push(flowTo.stat_energy_to);
|
||||
if (flowTo.stat_compensation) {
|
||||
statIDs.push(flowTo.stat_compensation);
|
||||
}
|
||||
const costStatId = info.cost_sensors[flowTo.stat_energy_to];
|
||||
if (costStatId) {
|
||||
statIDs.push(costStatId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,17 +250,37 @@ export interface EnergyCollection extends Collection<EnergyData> {
|
||||
_active: number;
|
||||
}
|
||||
|
||||
const clearEnergyCollectionPreferences = (hass: HomeAssistant) => {
|
||||
energyCollectionKeys.forEach((key) => {
|
||||
const energyCollection = getEnergyDataCollection(hass, { key });
|
||||
energyCollection.clearPrefs();
|
||||
if (energyCollection._active) {
|
||||
energyCollection.refresh();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getEnergyDataCollection = (
|
||||
hass: HomeAssistant,
|
||||
prefs?: EnergyPreferences
|
||||
options: { prefs?: EnergyPreferences; key?: string } = {}
|
||||
): EnergyCollection => {
|
||||
if ((hass.connection as any)._energy) {
|
||||
return (hass.connection as any)._energy;
|
||||
let key = "_energy";
|
||||
if (options.key) {
|
||||
if (!options.key.startsWith("energy_")) {
|
||||
throw new Error("Key need to start with energy_");
|
||||
}
|
||||
key = `_${options.key}`;
|
||||
}
|
||||
|
||||
if ((hass.connection as any)[key]) {
|
||||
return (hass.connection as any)[key];
|
||||
}
|
||||
|
||||
energyCollectionKeys.push(options.key || "energy");
|
||||
|
||||
const collection = getCollection<EnergyData>(
|
||||
hass.connection,
|
||||
"_energy",
|
||||
key,
|
||||
async () => {
|
||||
if (!collection.prefs) {
|
||||
// This will raise if not found.
|
||||
@ -304,7 +336,7 @@ export const getEnergyDataCollection = (
|
||||
};
|
||||
|
||||
collection._active = 0;
|
||||
collection.prefs = prefs;
|
||||
collection.prefs = options.prefs;
|
||||
const now = new Date();
|
||||
// Set start to start of today if we have data for today, otherwise yesterday
|
||||
collection.start = now.getHours() > 0 ? startOfToday() : startOfYesterday();
|
||||
|
@ -274,6 +274,7 @@ class HassTabsSubpage extends LitElement {
|
||||
flex: 1;
|
||||
max-height: var(--header-height);
|
||||
line-height: 20px;
|
||||
color: var(--sidebar-text-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@ -87,8 +87,8 @@ export class DialogEnergySolarSettings
|
||||
|
||||
<h3>Solar production forecast</h3>
|
||||
<p>
|
||||
We can predict how much energy your solar panels will produce, you can
|
||||
link or setup an integration that will provide this data.
|
||||
Adding solar production forecast information will allow you to quickly
|
||||
see your expected production for today.
|
||||
</p>
|
||||
|
||||
<ha-formfield label="Don't forecast production">
|
||||
|
@ -20,6 +20,7 @@ import "../lovelace/views/hui-view";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { Lovelace } from "../lovelace/types";
|
||||
import { LovelaceConfig } from "../../data/lovelace";
|
||||
import "../lovelace/components/hui-energy-period-selector";
|
||||
|
||||
const LOVELACE_CONFIG: LovelaceConfig = {
|
||||
views: [
|
||||
@ -59,11 +60,21 @@ class PanelEnergy extends LitElement {
|
||||
<ha-app-layout>
|
||||
<app-header fixed slot="header">
|
||||
<app-toolbar>
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
<div main-title>${this.hass.localize("panel.energy")}</div>
|
||||
<div class="nav-title">
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
<div main-title>${this.hass.localize("panel.energy")}</div>
|
||||
</div>
|
||||
${this.narrow
|
||||
? ""
|
||||
: html`
|
||||
<hui-energy-period-selector
|
||||
.hass=${this.hass}
|
||||
collectionKey="energy_dashboard"
|
||||
></hui-energy-period-selector>
|
||||
`}
|
||||
<a href="/config/energy?historyBack=1">
|
||||
<mwc-icon-button>
|
||||
<ha-svg-icon .path=${mdiCog}></ha-svg-icon>
|
||||
@ -113,6 +124,17 @@ class PanelEnergy extends LitElement {
|
||||
mwc-icon-button {
|
||||
color: var(--text-primary-color);
|
||||
}
|
||||
app-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.nav-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
hui-energy-period-selector {
|
||||
width: 300px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {
|
||||
EnergyPreferences,
|
||||
getEnergyDataCollection,
|
||||
getEnergyPreferences,
|
||||
GridSourceTypeEnergyPreference,
|
||||
} from "../../../data/energy";
|
||||
@ -52,17 +51,20 @@ export class EnergyStrategy {
|
||||
(source) => source.type === "solar"
|
||||
);
|
||||
|
||||
getEnergyDataCollection(hass, prefs);
|
||||
|
||||
view.cards!.push({
|
||||
type: "energy-date-selection",
|
||||
});
|
||||
if (info.narrow) {
|
||||
view.cards!.push({
|
||||
type: "energy-date-selection",
|
||||
collection_key: "energy_dashboard",
|
||||
view_layout: { position: "sidebar" },
|
||||
});
|
||||
}
|
||||
|
||||
// Only include if we have a grid source.
|
||||
if (hasGrid) {
|
||||
view.cards!.push({
|
||||
title: "Energy usage",
|
||||
type: "energy-usage-graph",
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
@ -71,6 +73,7 @@ export class EnergyStrategy {
|
||||
view.cards!.push({
|
||||
title: "Solar production",
|
||||
type: "energy-solar-graph",
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
@ -80,6 +83,7 @@ export class EnergyStrategy {
|
||||
title: "Energy distribution",
|
||||
type: "energy-distribution",
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
@ -87,6 +91,7 @@ export class EnergyStrategy {
|
||||
view.cards!.push({
|
||||
title: "Sources",
|
||||
type: "energy-sources-table",
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
@ -95,6 +100,7 @@ export class EnergyStrategy {
|
||||
view.cards!.push({
|
||||
type: "energy-grid-neutrality-gauge",
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
@ -103,6 +109,7 @@ export class EnergyStrategy {
|
||||
view.cards!.push({
|
||||
type: "energy-solar-consumed-gauge",
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
@ -111,6 +118,7 @@ export class EnergyStrategy {
|
||||
view.cards!.push({
|
||||
type: "energy-carbon-consumed-gauge",
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
@ -119,6 +127,7 @@ export class EnergyStrategy {
|
||||
view.cards!.push({
|
||||
title: "Monitor individual devices",
|
||||
type: "energy-devices-graph",
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { mdiInformation } from "@mdi/js";
|
||||
import "@polymer/paper-tooltip";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@ -5,6 +7,7 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import { round } from "../../../../common/number/round";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-gauge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
EnergyData,
|
||||
energySourcesByType,
|
||||
@ -42,7 +45,9 @@ class HuiEnergyCarbonGaugeCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass).subscribe((data) => {
|
||||
getEnergyDataCollection(this.hass, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => {
|
||||
this._data = data;
|
||||
}),
|
||||
];
|
||||
@ -118,8 +123,14 @@ class HuiEnergyCarbonGaugeCard
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card
|
||||
>${value !== undefined
|
||||
<ha-card>
|
||||
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
|
||||
<paper-tooltip animation-delay="0" for="info" position="left">
|
||||
This card represents how much of the energy consumed by your home was
|
||||
generated using non-fossil fuels like solar, wind and nuclear.
|
||||
</paper-tooltip>
|
||||
|
||||
${value !== undefined
|
||||
? html` <ha-gauge
|
||||
min="0"
|
||||
max="100"
|
||||
@ -176,6 +187,18 @@ class HuiEnergyCarbonGaugeCard
|
||||
font-size: 15px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
paper-tooltip {
|
||||
width: 80%;
|
||||
max-width: 250px;
|
||||
margin-top: 10%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +1,19 @@
|
||||
import {
|
||||
startOfWeek,
|
||||
endOfWeek,
|
||||
startOfToday,
|
||||
endOfToday,
|
||||
startOfYesterday,
|
||||
endOfYesterday,
|
||||
addDays,
|
||||
} from "date-fns";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-date-range-picker";
|
||||
import type { DateRangePickerRanges } from "../../../../components/ha-date-range-picker";
|
||||
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCard } from "../../types";
|
||||
import { EnergyDevicesGraphCardConfig } from "../types";
|
||||
import "../../components/hui-energy-period-selector";
|
||||
|
||||
@customElement("hui-energy-date-selection-card")
|
||||
export class HuiEnergyDateSelectionCard
|
||||
extends SubscribeMixin(LitElement)
|
||||
extends LitElement
|
||||
implements LovelaceCard
|
||||
{
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _config?: EnergyDevicesGraphCardConfig;
|
||||
|
||||
@state() private _ranges?: DateRangePickerRanges;
|
||||
|
||||
@state() _startDate?: Date;
|
||||
|
||||
@state() _endDate?: Date;
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass).subscribe((data) =>
|
||||
this._updateDates(data)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public willUpdate() {
|
||||
if (!this.hasUpdated) {
|
||||
const today = new Date();
|
||||
const weekStart = startOfWeek(today);
|
||||
const weekEnd = endOfWeek(today);
|
||||
|
||||
this._ranges = {
|
||||
[this.hass.localize("ui.components.date-range-picker.ranges.today")]: [
|
||||
startOfToday(),
|
||||
endOfToday(),
|
||||
],
|
||||
[this.hass.localize(
|
||||
"ui.components.date-range-picker.ranges.yesterday"
|
||||
)]: [startOfYesterday(), endOfYesterday()],
|
||||
[this.hass.localize(
|
||||
"ui.components.date-range-picker.ranges.this_week"
|
||||
)]: [weekStart, weekEnd],
|
||||
[this.hass.localize(
|
||||
"ui.components.date-range-picker.ranges.last_week"
|
||||
)]: [addDays(weekStart, -7), addDays(weekEnd, -7)],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public getCardSize(): Promise<number> | number {
|
||||
return 1;
|
||||
}
|
||||
@ -76,38 +23,18 @@ export class HuiEnergyDateSelectionCard
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config || !this._startDate) {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-date-range-picker
|
||||
<hui-energy-period-selector
|
||||
.hass=${this.hass}
|
||||
.startDate=${this._startDate}
|
||||
.endDate=${this._endDate!}
|
||||
.ranges=${this._ranges}
|
||||
@change=${this._dateRangeChanged}
|
||||
></ha-date-range-picker>
|
||||
.collectionKey=${this._config.collection_key}
|
||||
></hui-energy-period-selector>
|
||||
`;
|
||||
}
|
||||
|
||||
private _updateDates(energyData: EnergyData): void {
|
||||
this._startDate = energyData.start;
|
||||
this._endDate = energyData.end || endOfToday();
|
||||
}
|
||||
|
||||
private _dateRangeChanged(ev: CustomEvent): void {
|
||||
if (
|
||||
ev.detail.startDate.getTime() === this._startDate!.getTime() &&
|
||||
ev.detail.endDate.getTime() === this._endDate!.getTime()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const energyCollection = getEnergyDataCollection(this.hass);
|
||||
energyCollection.setPeriod(ev.detail.startDate, ev.detail.endDate);
|
||||
energyCollection.refresh();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css``;
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ export class HuiEnergyDevicesGraphCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass).subscribe((data) =>
|
||||
this._getStatistics(data)
|
||||
),
|
||||
getEnergyDataCollection(this.hass, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => this._getStatistics(data)),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import { css, html, LitElement, svg } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import "@material/mwc-button";
|
||||
import { formatNumber } from "../../../../common/string/format_number";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
@ -47,7 +48,9 @@ class HuiEnergyDistrubutionCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass).subscribe((data) => {
|
||||
getEnergyDataCollection(this.hass, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => {
|
||||
this._data = data;
|
||||
}),
|
||||
];
|
||||
@ -399,6 +402,15 @@ class HuiEnergyDistrubutionCard
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
${this._config.linkDashboard
|
||||
? html`
|
||||
<div class="card-actions">
|
||||
<a href="/energy"
|
||||
><mwc-button> Go to the energy dashboard </mwc-button></a
|
||||
>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
@ -554,6 +566,9 @@ class HuiEnergyDistrubutionCard
|
||||
stroke-dasharray: 238.76104;
|
||||
}
|
||||
}
|
||||
.card-actions a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { mdiInformation } from "@mdi/js";
|
||||
import "@polymer/paper-tooltip";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatNumber } from "../../../../common/string/format_number";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-gauge";
|
||||
import type { LevelDefinition } from "../../../../components/ha-gauge";
|
||||
import {
|
||||
@ -35,7 +38,9 @@ class HuiEnergyGridGaugeCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass!).subscribe((data) => {
|
||||
getEnergyDataCollection(this.hass!, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => {
|
||||
this._data = data;
|
||||
}),
|
||||
];
|
||||
@ -91,6 +96,13 @@ class HuiEnergyGridGaugeCard
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
|
||||
<paper-tooltip animation-delay="0" for="info" position="left">
|
||||
This card represents your energy dependency. If it's green, it means
|
||||
you produced more energy than that you consumed from the grid. If it's
|
||||
in the red, it means that you relied on the grid for part of your
|
||||
home's energy consumption.
|
||||
</paper-tooltip>
|
||||
${value !== undefined
|
||||
? html`<ha-gauge
|
||||
min="-1"
|
||||
@ -142,6 +154,18 @@ class HuiEnergyGridGaugeCard
|
||||
font-size: 15px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
paper-tooltip {
|
||||
width: 80%;
|
||||
max-width: 250px;
|
||||
margin-top: 10%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { mdiInformation } from "@mdi/js";
|
||||
import "@polymer/paper-tooltip";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-gauge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
EnergyData,
|
||||
energySourcesByType,
|
||||
@ -29,7 +32,9 @@ class HuiEnergySolarGaugeCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass!).subscribe((data) => {
|
||||
getEnergyDataCollection(this.hass!, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => {
|
||||
this._data = data;
|
||||
}),
|
||||
];
|
||||
@ -81,6 +86,13 @@ class HuiEnergySolarGaugeCard
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
|
||||
<paper-tooltip animation-delay="0" for="info" position="left">
|
||||
This card represents how much of the solar energy was not used by your
|
||||
home and was returned to the grid. If you frequently return a lot, try
|
||||
to conserve this energy by installing a battery or buying an electric
|
||||
car to charge.
|
||||
</paper-tooltip>
|
||||
${value !== undefined
|
||||
? html`<ha-gauge
|
||||
min="0"
|
||||
@ -137,6 +149,18 @@ class HuiEnergySolarGaugeCard
|
||||
font-size: 15px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
paper-tooltip {
|
||||
width: 80%;
|
||||
max-width: 250px;
|
||||
margin-top: 10%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -58,9 +58,9 @@ export class HuiEnergySolarGraphCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass).subscribe((data) =>
|
||||
this._getStatistics(data)
|
||||
),
|
||||
getEnergyDataCollection(this.hass, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => this._getStatistics(data)),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,9 @@ export class HuiEnergySourcesTableCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass).subscribe((data) => {
|
||||
getEnergyDataCollection(this.hass, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => {
|
||||
this._data = data;
|
||||
}),
|
||||
];
|
||||
|
@ -46,9 +46,9 @@ export class HuiEnergyUsageGraphCard
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass).subscribe((data) =>
|
||||
this._getStatistics(data)
|
||||
),
|
||||
getEnergyDataCollection(this.hass, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => this._getStatistics(data)),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -92,45 +92,55 @@ export interface ButtonCardConfig extends LovelaceCardConfig {
|
||||
export interface EnergySummaryCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-summary";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergyDistributionCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-distribution";
|
||||
title?: string;
|
||||
linkDashboard?: boolean;
|
||||
collection_key?: string;
|
||||
}
|
||||
export interface EnergyUsageGraphCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-summary-graph";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergySolarGraphCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-solar-graph";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergyDevicesGraphCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-devices-graph";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergySourcesTableCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-sources-table";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-solar-consumed-gauge";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergyGridGaugeCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-grid-result-gauge";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergyCarbonGaugeCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-carbon-consumed-gauge";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EntityFilterCardConfig extends LovelaceCardConfig {
|
||||
|
@ -7,6 +7,10 @@ import { compare } from "../../../common/string/compare";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||
import {
|
||||
EnergyPreferences,
|
||||
GridSourceTypeEnergyPreference,
|
||||
} from "../../../data/energy";
|
||||
import type { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { LovelaceCardConfig, LovelaceViewConfig } from "../../../data/lovelace";
|
||||
@ -292,7 +296,8 @@ export const generateDefaultViewConfig = (
|
||||
deviceEntries: DeviceRegistryEntry[],
|
||||
entityEntries: EntityRegistryEntry[],
|
||||
entities: HassEntities,
|
||||
localize: LocalizeFunc
|
||||
localize: LocalizeFunc,
|
||||
energyPrefs?: EnergyPreferences
|
||||
): LovelaceViewConfig => {
|
||||
const states = computeDefaultViewStates(entities, entityEntries);
|
||||
const path = "default_view";
|
||||
@ -337,6 +342,21 @@ export const generateDefaultViewConfig = (
|
||||
);
|
||||
});
|
||||
|
||||
if (energyPrefs) {
|
||||
// Distribution card requires the grid to be configured
|
||||
const grid = energyPrefs.energy_sources.find(
|
||||
(source) => source.type === "grid"
|
||||
) as GridSourceTypeEnergyPreference | undefined;
|
||||
|
||||
if (grid && grid.flow_from.length > 0) {
|
||||
areaCards.push({
|
||||
title: "Energy distribution today",
|
||||
type: "energy-distribution",
|
||||
linkDashboard: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
config.cards!.unshift(...areaCards);
|
||||
|
||||
return config;
|
||||
|
100
src/panels/lovelace/components/hui-energy-period-selector.ts
Normal file
100
src/panels/lovelace/components/hui-energy-period-selector.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { mdiChevronLeft, mdiChevronRight } from "@mdi/js";
|
||||
import { endOfToday, addDays, endOfDay, isToday, isYesterday } from "date-fns";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatDate } from "../../../common/datetime/format_date";
|
||||
import { EnergyData, getEnergyDataCollection } from "../../../data/energy";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "@material/mwc-icon-button/mwc-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
|
||||
@customElement("hui-energy-period-selector")
|
||||
export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public collectionKey?: string;
|
||||
|
||||
@state() _startDate?: Date;
|
||||
|
||||
@state() _endDate?: Date;
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass, {
|
||||
key: this.collectionKey,
|
||||
}).subscribe((data) => this._updateDates(data)),
|
||||
];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._startDate) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const isStartToday = isToday(this._startDate);
|
||||
let label;
|
||||
if (isStartToday) {
|
||||
label = "Today";
|
||||
} else if (isYesterday(this._startDate)) {
|
||||
label = "Yesterday";
|
||||
} else {
|
||||
label = formatDate(this._startDate, this.hass.locale);
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="row">
|
||||
<mwc-icon-button label="Previous Day" @click=${this._pickPreviousDay}>
|
||||
<ha-svg-icon .path=${mdiChevronLeft}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<div class="label">${label}</div>
|
||||
<mwc-icon-button label="Next Day" @click=${this._pickNextDay}>
|
||||
<ha-svg-icon .path=${mdiChevronRight}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _pickPreviousDay() {
|
||||
this._setDate(addDays(this._startDate!, -1));
|
||||
}
|
||||
|
||||
private _pickNextDay() {
|
||||
this._setDate(addDays(this._startDate!, +1));
|
||||
}
|
||||
|
||||
private _setDate(startDate: Date) {
|
||||
const endDate = endOfDay(startDate);
|
||||
const energyCollection = getEnergyDataCollection(this.hass, {
|
||||
key: this.collectionKey,
|
||||
});
|
||||
energyCollection.setPeriod(startDate, endDate);
|
||||
energyCollection.refresh();
|
||||
}
|
||||
|
||||
private _updateDates(energyData: EnergyData): void {
|
||||
this._startDate = energyData.start;
|
||||
this._endDate = energyData.end || endOfToday();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-energy-period-selector": HuiEnergyPeriodSelector;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
|
||||
import { subscribeOne } from "../../../common/util/subscribe-one";
|
||||
import { subscribeAreaRegistry } from "../../../data/area_registry";
|
||||
import { subscribeDeviceRegistry } from "../../../data/device_registry";
|
||||
import { getEnergyPreferences } from "../../../data/energy";
|
||||
import { subscribeEntityRegistry } from "../../../data/entity_registry";
|
||||
import { generateDefaultViewConfig } from "../common/generate-lovelace-config";
|
||||
import {
|
||||
@ -37,12 +38,14 @@ export class OriginalStatesStrategy {
|
||||
subscribeEntityRegistry(hass.connection, () => undefined);
|
||||
}
|
||||
|
||||
const [areaEntries, deviceEntries, entityEntries, localize] =
|
||||
const [areaEntries, deviceEntries, entityEntries, localize, energyPrefs] =
|
||||
await Promise.all([
|
||||
subscribeOne(hass.connection, subscribeAreaRegistry),
|
||||
subscribeOne(hass.connection, subscribeDeviceRegistry),
|
||||
subscribeOne(hass.connection, subscribeEntityRegistry),
|
||||
hass.loadBackendTranslation("title"),
|
||||
// It raises if not configured, just swallow that.
|
||||
getEnergyPreferences(hass).catch(() => undefined),
|
||||
]);
|
||||
|
||||
// User can override default view. If they didn't, we will add one
|
||||
@ -52,7 +55,8 @@ export class OriginalStatesStrategy {
|
||||
deviceEntries,
|
||||
entityEntries,
|
||||
hass.states,
|
||||
localize
|
||||
localize,
|
||||
energyPrefs
|
||||
);
|
||||
|
||||
// Add map of geo locations to default view if loaded
|
||||
|
@ -59,12 +59,20 @@ const REDIRECTS: Redirects = {
|
||||
component: "zwave_js",
|
||||
redirect: "/config/zwave_js/dashboard",
|
||||
},
|
||||
config_energy: {
|
||||
component: "energy",
|
||||
redirect: "/config/energy/dashboard",
|
||||
},
|
||||
devices: {
|
||||
redirect: "/config/devices/dashboard",
|
||||
},
|
||||
entities: {
|
||||
redirect: "/config/entities",
|
||||
},
|
||||
energy: {
|
||||
component: "energy",
|
||||
redirect: "/energy",
|
||||
},
|
||||
areas: {
|
||||
redirect: "/config/areas/dashboard",
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user