mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-18 23:06:40 +00:00
Add period selection to energy dashboard (#9756)
This commit is contained in:
parent
3897e3d452
commit
dc50e54afc
@ -1,4 +1,4 @@
|
||||
import { addHours, differenceInHours } from "date-fns";
|
||||
import { addHours, differenceInHours, endOfDay } from "date-fns";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { StatisticValue } from "../../../src/data/history";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
@ -222,6 +222,7 @@ const statisticsFunctions: Record<
|
||||
"sensor.energy_production_tarif_2": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 21 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
@ -237,15 +238,17 @@ const statisticsFunctions: Record<
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
end,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
return [...morning, ...production, ...evening];
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 1);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.solar_production": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 7 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 23 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
@ -261,11 +264,12 @@ const statisticsFunctions: Record<
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
end,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
return [...morning, ...production, ...evening];
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 2);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.grid_fossil_fuel_percentage": (id, start, end) =>
|
||||
generateMeanStatistics(id, start, end, 35, 1.3),
|
||||
|
@ -17,7 +17,7 @@
|
||||
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types",
|
||||
"format": "yarn run format:eslint && yarn run format:prettier",
|
||||
"mocha": "ts-mocha -p test-mocha/tsconfig.test.json \"test-mocha/**/*.ts\"",
|
||||
"test": "yarn run lint && yarn run mocha"
|
||||
"test": "yarn run mocha"
|
||||
},
|
||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -3,33 +3,11 @@ import memoizeOne from "memoize-one";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { toLocaleDateStringSupportsOptions } from "./check_options_support";
|
||||
|
||||
const formatDateMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
);
|
||||
|
||||
export const formatDate = toLocaleDateStringSupportsOptions
|
||||
// Tuesday, August 10
|
||||
export const formatDateWeekday = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "longDate");
|
||||
|
||||
const formatDateShortMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
})
|
||||
);
|
||||
|
||||
export const formatDateShort = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateShortMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "shortDate");
|
||||
|
||||
formatDateWeekdayMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "dddd, MMMM D");
|
||||
const formatDateWeekdayMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
@ -39,7 +17,80 @@ const formatDateWeekdayMem = memoizeOne(
|
||||
})
|
||||
);
|
||||
|
||||
export const formatDateWeekday = toLocaleDateStringSupportsOptions
|
||||
// August 10, 2021
|
||||
export const formatDate = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateWeekdayMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "dddd, MMM D");
|
||||
formatDateMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY");
|
||||
const formatDateMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
);
|
||||
|
||||
// 10/08/2021
|
||||
export const formatDateNumeric = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateNumericMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "M/D/YYYY");
|
||||
const formatDateNumericMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
})
|
||||
);
|
||||
|
||||
// Aug 10
|
||||
export const formatDateShort = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateShortMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMM D");
|
||||
const formatDateShortMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
})
|
||||
);
|
||||
|
||||
// August 2021
|
||||
export const formatDateMonthYear = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMonthYearMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMMM YYYY");
|
||||
const formatDateMonthYearMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})
|
||||
);
|
||||
|
||||
// August
|
||||
export const formatDateMonth = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMonthMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMMM");
|
||||
const formatDateMonthMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
month: "long",
|
||||
})
|
||||
);
|
||||
|
||||
// 2021
|
||||
export const formatDateYear = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateYearMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "YYYY");
|
||||
const formatDateYearMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
})
|
||||
);
|
||||
|
@ -4,6 +4,12 @@ import { FrontendLocaleData } from "../../data/translation";
|
||||
import { toLocaleStringSupportsOptions } from "./check_options_support";
|
||||
import { useAmPm } from "./use_am_pm";
|
||||
|
||||
// August 9, 2021, 8:23 AM
|
||||
export const formatDateTime = toLocaleStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateTimeMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "MMMM D, YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
const formatDateTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
@ -16,12 +22,12 @@ const formatDateTimeMem = memoizeOne(
|
||||
})
|
||||
);
|
||||
|
||||
export const formatDateTime = toLocaleStringSupportsOptions
|
||||
// August 9, 2021, 8:23:15 AM
|
||||
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateTimeMem(locale).format(dateObj)
|
||||
formatDateTimeWithSecondsMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "MMMM D, YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
|
||||
format(dateObj, "MMMM D, YYYY, HH:mm:ss" + useAmPm(locale) ? " A" : "");
|
||||
const formatDateTimeWithSecondsMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
@ -35,8 +41,20 @@ const formatDateTimeWithSecondsMem = memoizeOne(
|
||||
})
|
||||
);
|
||||
|
||||
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
|
||||
// 9/8/2021, 8:23 AM
|
||||
export const formatDateTimeNumeric = toLocaleStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateTimeWithSecondsMem(locale).format(dateObj)
|
||||
formatDateTimeNumericMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "MMMM D, YYYY, HH:mm:ss" + useAmPm(locale) ? " A" : "");
|
||||
format(dateObj, "M/D/YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
const formatDateTimeNumericMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
})
|
||||
);
|
||||
|
@ -4,6 +4,12 @@ import { FrontendLocaleData } from "../../data/translation";
|
||||
import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
|
||||
import { useAmPm } from "./use_am_pm";
|
||||
|
||||
// 9:15 PM || 21:15
|
||||
export const formatTime = toLocaleTimeStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "shortTime" + useAmPm(locale) ? " A" : "");
|
||||
const formatTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
@ -13,12 +19,12 @@ const formatTimeMem = memoizeOne(
|
||||
})
|
||||
);
|
||||
|
||||
export const formatTime = toLocaleTimeStringSupportsOptions
|
||||
// 9:15:24 PM || 21:15:24
|
||||
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeMem(locale).format(dateObj)
|
||||
formatTimeWithSecondsMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "shortTime" + useAmPm(locale) ? " A" : "");
|
||||
|
||||
format(dateObj, "mediumTime" + useAmPm(locale) ? " A" : "");
|
||||
const formatTimeWithSecondsMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
@ -29,12 +35,12 @@ const formatTimeWithSecondsMem = memoizeOne(
|
||||
})
|
||||
);
|
||||
|
||||
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
|
||||
// Tuesday 7:00 PM || Tuesday 19:00
|
||||
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeWithSecondsMem(locale).format(dateObj)
|
||||
formatTimeWeekdayMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "mediumTime" + useAmPm(locale) ? " A" : "");
|
||||
|
||||
format(dateObj, "dddd, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
const formatTimeWeekdayMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
@ -44,9 +50,3 @@ const formatTimeWeekdayMem = memoizeOne(
|
||||
hour12: useAmPm(locale),
|
||||
})
|
||||
);
|
||||
|
||||
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeWeekdayMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "dddd, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
|
@ -1,6 +1,7 @@
|
||||
import memoizeOne from "memoize-one";
|
||||
import { FrontendLocaleData, TimeFormat } from "../../data/translation";
|
||||
|
||||
export const useAmPm = (locale: FrontendLocaleData): boolean => {
|
||||
export const useAmPm = memoizeOne((locale: FrontendLocaleData): boolean => {
|
||||
if (
|
||||
locale.time_format === TimeFormat.language ||
|
||||
locale.time_format === TimeFormat.system
|
||||
@ -12,4 +13,4 @@ export const useAmPm = (locale: FrontendLocaleData): boolean => {
|
||||
}
|
||||
|
||||
return locale.time_format === TimeFormat.am_pm;
|
||||
};
|
||||
});
|
||||
|
@ -35,7 +35,14 @@ import {
|
||||
endOfQuarter,
|
||||
endOfYear,
|
||||
} from "date-fns";
|
||||
import { formatDate, formatDateShort } from "../../common/datetime/format_date";
|
||||
import {
|
||||
formatDate,
|
||||
formatDateMonth,
|
||||
formatDateMonthYear,
|
||||
formatDateShort,
|
||||
formatDateWeekday,
|
||||
formatDateYear,
|
||||
} from "../../common/datetime/format_date";
|
||||
import {
|
||||
formatDateTime,
|
||||
formatDateTimeWithSeconds,
|
||||
@ -53,8 +60,11 @@ const FORMATS = {
|
||||
minute: "minute",
|
||||
hour: "hour",
|
||||
day: "day",
|
||||
date: "date",
|
||||
weekday: "weekday",
|
||||
week: "week",
|
||||
month: "month",
|
||||
monthyear: "monthyear",
|
||||
quarter: "quarter",
|
||||
year: "year",
|
||||
};
|
||||
@ -81,16 +91,22 @@ _adapters._date.override({
|
||||
return formatTime(new Date(time), this.options.locale);
|
||||
case "hour":
|
||||
return formatTime(new Date(time), this.options.locale);
|
||||
case "weekday":
|
||||
return formatDateWeekday(new Date(time), this.options.locale);
|
||||
case "date":
|
||||
return formatDate(new Date(time), this.options.locale);
|
||||
case "day":
|
||||
return formatDateShort(new Date(time), this.options.locale);
|
||||
case "week":
|
||||
return formatDate(new Date(time), this.options.locale);
|
||||
case "month":
|
||||
return formatDate(new Date(time), this.options.locale);
|
||||
return formatDateMonth(new Date(time), this.options.locale);
|
||||
case "monthyear":
|
||||
return formatDateMonthYear(new Date(time), this.options.locale);
|
||||
case "quarter":
|
||||
return formatDate(new Date(time), this.options.locale);
|
||||
case "year":
|
||||
return formatDate(new Date(time), this.options.locale);
|
||||
return formatDateYear(new Date(time), this.options.locale);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public fullWidth = false;
|
||||
|
||||
@property({ type: Boolean }) public dense = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div>
|
||||
@ -34,6 +36,8 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
? `${100 / this.buttons.length}%`
|
||||
: "initial",
|
||||
})}
|
||||
outlined
|
||||
.dense=${this.dense}
|
||||
.value=${button.value}
|
||||
?active=${this.active === button.value}
|
||||
@click=${this._handleClick}
|
||||
@ -56,10 +60,16 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
--mdc-icon-button-size: var(--button-toggle-size, 36px);
|
||||
--mdc-icon-size: var(--button-toggle-icon-size, 20px);
|
||||
}
|
||||
mwc-icon-button,
|
||||
mwc-button {
|
||||
--mdc-shape-small: 0;
|
||||
--mdc-button-outline-width: 1px 0 1px 1px;
|
||||
}
|
||||
mwc-icon-button {
|
||||
border: 1px solid var(--primary-color);
|
||||
border-right-width: 0px;
|
||||
}
|
||||
mwc-icon-button,
|
||||
mwc-button {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -82,16 +92,19 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
}
|
||||
mwc-icon-button:first-child,
|
||||
mwc-button:first-child {
|
||||
--mdc-shape-small: 4px 0 0 4px;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
mwc-icon-button:last-child,
|
||||
mwc-button:last-child {
|
||||
border-radius: 0 4px 4px 0;
|
||||
border-right-width: 1px;
|
||||
--mdc-shape-small: 0 4px 4px 0;
|
||||
--mdc-button-outline-width: 1px;
|
||||
}
|
||||
mwc-icon-button:only-child,
|
||||
mwc-button:only-child {
|
||||
border-radius: 4px;
|
||||
--mdc-shape-small: 4px;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
`;
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { useAmPm } from "../../common/datetime/use_am_pm";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { TimeSelector } from "../../data/selector";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../paper-time-input";
|
||||
|
||||
@ -20,12 +18,8 @@ export class HaTimeSelector extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
private _useAmPmMem = memoizeOne((locale: FrontendLocaleData): boolean =>
|
||||
useAmPm(locale)
|
||||
);
|
||||
|
||||
protected render() {
|
||||
const useAMPM = this._useAmPmMem(this.hass.locale);
|
||||
const useAMPM = useAmPm(this.hass.locale);
|
||||
|
||||
const parts = this.value?.split(":") || [];
|
||||
const hours = parts[0];
|
||||
@ -50,7 +44,7 @@ export class HaTimeSelector extends LitElement {
|
||||
|
||||
private _timeChanged(ev) {
|
||||
let value = ev.target.value;
|
||||
const useAMPM = this._useAmPmMem(this.hass.locale);
|
||||
const useAMPM = useAmPm(this.hass.locale);
|
||||
let hours = Number(ev.target.hour || 0);
|
||||
if (value && useAMPM) {
|
||||
if (ev.target.amPm === "PM") {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { addDays, addMonths, startOfDay, startOfMonth } from "date-fns";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||
@ -406,3 +407,90 @@ export const calculateStatisticsSumGrowthWithPercentage = (
|
||||
|
||||
return sum;
|
||||
};
|
||||
|
||||
export const reduceSumStatisticsByDay = (
|
||||
values: StatisticValue[]
|
||||
): StatisticValue[] => {
|
||||
if (!values?.length) {
|
||||
return [];
|
||||
}
|
||||
const result: StatisticValue[] = [];
|
||||
if (
|
||||
values.length > 1 &&
|
||||
new Date(values[0].start).getDate() === new Date(values[1].start).getDate()
|
||||
) {
|
||||
// add init value if the first value isn't end of previous period
|
||||
result.push({
|
||||
...values[0]!,
|
||||
start: startOfMonth(addDays(new Date(values[0].start), -1)).toISOString(),
|
||||
});
|
||||
}
|
||||
let lastValue: StatisticValue;
|
||||
let prevDate: number | undefined;
|
||||
for (const value of values) {
|
||||
const date = new Date(value.start).getDate();
|
||||
if (prevDate === undefined) {
|
||||
prevDate = date;
|
||||
}
|
||||
if (prevDate !== date) {
|
||||
// Last value of the day
|
||||
result.push({
|
||||
...lastValue!,
|
||||
start: startOfDay(new Date(lastValue!.start)).toISOString(),
|
||||
});
|
||||
prevDate = date;
|
||||
}
|
||||
lastValue = value;
|
||||
}
|
||||
// Add final value
|
||||
result.push({
|
||||
...lastValue!,
|
||||
start: startOfDay(new Date(lastValue!.start)).toISOString(),
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export const reduceSumStatisticsByMonth = (
|
||||
values: StatisticValue[]
|
||||
): StatisticValue[] => {
|
||||
if (!values?.length) {
|
||||
return [];
|
||||
}
|
||||
const result: StatisticValue[] = [];
|
||||
if (
|
||||
values.length > 1 &&
|
||||
new Date(values[0].start).getMonth() ===
|
||||
new Date(values[1].start).getMonth()
|
||||
) {
|
||||
// add init value if the first value isn't end of previous period
|
||||
result.push({
|
||||
...values[0]!,
|
||||
start: startOfMonth(
|
||||
addMonths(new Date(values[0].start), -1)
|
||||
).toISOString(),
|
||||
});
|
||||
}
|
||||
let lastValue: StatisticValue;
|
||||
let prevMonth: number | undefined;
|
||||
for (const value of values) {
|
||||
const month = new Date(value.start).getMonth();
|
||||
if (prevMonth === undefined) {
|
||||
prevMonth = month;
|
||||
}
|
||||
if (prevMonth !== month) {
|
||||
// Last value of the day
|
||||
result.push({
|
||||
...lastValue!,
|
||||
start: startOfMonth(new Date(lastValue!.start)).toISOString(),
|
||||
});
|
||||
prevMonth = month;
|
||||
}
|
||||
lastValue = value;
|
||||
}
|
||||
// Add final value
|
||||
result.push({
|
||||
...lastValue!,
|
||||
start: startOfMonth(new Date(lastValue!.start)).toISOString(),
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
@ -10,7 +10,13 @@ import {
|
||||
ChartOptions,
|
||||
ScatterDataPoint,
|
||||
} from "chart.js";
|
||||
import { endOfToday, isToday, startOfToday } from "date-fns";
|
||||
import {
|
||||
addHours,
|
||||
differenceInDays,
|
||||
endOfToday,
|
||||
isToday,
|
||||
startOfToday,
|
||||
} from "date-fns";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCard } from "../../types";
|
||||
import { EnergySolarGraphCardConfig } from "../types";
|
||||
@ -39,6 +45,10 @@ import {
|
||||
} from "../../../../common/string/format_number";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { FrontendLocaleData } from "../../../../data/translation";
|
||||
import {
|
||||
reduceSumStatisticsByMonth,
|
||||
reduceSumStatisticsByDay,
|
||||
} from "../../../../data/history";
|
||||
|
||||
@customElement("hui-energy-solar-graph-card")
|
||||
export class HuiEnergySolarGraphCard
|
||||
@ -110,84 +120,108 @@ export class HuiEnergySolarGraphCard
|
||||
}
|
||||
|
||||
private _createOptions = memoizeOne(
|
||||
(start: Date, end: Date, locale: FrontendLocaleData): ChartOptions => ({
|
||||
parsing: false,
|
||||
animation: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: start.getTime(),
|
||||
suggestedMax: end.getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale: locale,
|
||||
(start: Date, end: Date, locale: FrontendLocaleData): ChartOptions => {
|
||||
const dayDifference = differenceInDays(end, start);
|
||||
return {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: (dayDifference > 2
|
||||
? addHours(start, -11)
|
||||
: start
|
||||
).getTime(),
|
||||
suggestedMax: (dayDifference > 2
|
||||
? addHours(end, -11)
|
||||
: end
|
||||
).getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale: locale,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
major: {
|
||||
enabled: true,
|
||||
},
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat:
|
||||
dayDifference > 35
|
||||
? "monthyear"
|
||||
: dayDifference > 7
|
||||
? "date"
|
||||
: dayDifference > 2
|
||||
? "weekday"
|
||||
: dayDifference > 0
|
||||
? "datetime"
|
||||
: "hour",
|
||||
minUnit:
|
||||
dayDifference > 35
|
||||
? "month"
|
||||
: dayDifference > 2
|
||||
? "day"
|
||||
: "hour",
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
y: {
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: "kWh",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
major: {
|
||||
enabled: true,
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: "nearest",
|
||||
callbacks: {
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
locale
|
||||
)} kWh`,
|
||||
},
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat: "datetime",
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
y: {
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: "kWh",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
callbacks: {
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
locale
|
||||
)} kWh`,
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0.3,
|
||||
borderWidth: 1.5,
|
||||
},
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0.3,
|
||||
borderWidth: 1.5,
|
||||
},
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
})
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
private async _getStatistics(energyData: EnergyData): Promise<void> {
|
||||
@ -229,6 +263,11 @@ export class HuiEnergySolarGraphCard
|
||||
.getPropertyValue("--energy-solar-color")
|
||||
.trim();
|
||||
|
||||
const dayDifference = differenceInDays(
|
||||
energyData.end || new Date(),
|
||||
energyData.start
|
||||
);
|
||||
|
||||
solarSources.forEach((source, idx) => {
|
||||
const data: ChartDataset<"bar" | "line">[] = [];
|
||||
const entity = this.hass.states[source.stat_energy_from];
|
||||
@ -244,9 +283,20 @@ export class HuiEnergySolarGraphCard
|
||||
const solarProductionData: ScatterDataPoint[] = [];
|
||||
|
||||
// Process solar production data.
|
||||
if (energyData.stats[source.stat_energy_from]) {
|
||||
for (const point of energyData.stats[source.stat_energy_from]) {
|
||||
if (!point.sum) {
|
||||
if (source.stat_energy_from in energyData.stats) {
|
||||
const stats =
|
||||
dayDifference > 35
|
||||
? reduceSumStatisticsByMonth(
|
||||
energyData.stats[source.stat_energy_from]
|
||||
)
|
||||
: dayDifference > 2
|
||||
? reduceSumStatisticsByDay(
|
||||
energyData.stats[source.stat_energy_from]
|
||||
)
|
||||
: energyData.stats[source.stat_energy_from];
|
||||
|
||||
for (const point of stats) {
|
||||
if (point.sum === null) {
|
||||
continue;
|
||||
}
|
||||
if (prevValue === null) {
|
||||
@ -294,7 +344,14 @@ export class HuiEnergySolarGraphCard
|
||||
) {
|
||||
return;
|
||||
}
|
||||
dateObj.setMinutes(0, 0, 0);
|
||||
if (dayDifference > 35) {
|
||||
dateObj.setDate(1);
|
||||
}
|
||||
if (dayDifference > 2) {
|
||||
dateObj.setHours(0, 0, 0, 0);
|
||||
} else {
|
||||
dateObj.setMinutes(0, 0, 0);
|
||||
}
|
||||
const time = dateObj.getTime();
|
||||
if (time in forecastsData) {
|
||||
forecastsData[time] += value;
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||
import { startOfToday, endOfToday, isToday } from "date-fns";
|
||||
import {
|
||||
startOfToday,
|
||||
endOfToday,
|
||||
isToday,
|
||||
differenceInDays,
|
||||
addHours,
|
||||
} 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";
|
||||
@ -21,6 +27,10 @@ import {
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
|
||||
import {
|
||||
reduceSumStatisticsByDay,
|
||||
reduceSumStatisticsByMonth,
|
||||
} from "../../../../data/history";
|
||||
import { FrontendLocaleData } from "../../../../data/translation";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
@ -97,106 +107,136 @@ export class HuiEnergyUsageGraphCard
|
||||
}
|
||||
|
||||
private _createOptions = memoizeOne(
|
||||
(start: Date, end: Date, locale: FrontendLocaleData): ChartOptions => ({
|
||||
parsing: false,
|
||||
animation: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: start.getTime(),
|
||||
suggestedMax: end.getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale: locale,
|
||||
(start: Date, end: Date, locale: FrontendLocaleData): ChartOptions => {
|
||||
const dayDifference = differenceInDays(end, start);
|
||||
return {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: (dayDifference > 2
|
||||
? addHours(start, -11)
|
||||
: start
|
||||
).getTime(),
|
||||
suggestedMax: (dayDifference > 2
|
||||
? addHours(end, -11)
|
||||
: end
|
||||
).getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale: locale,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
major: {
|
||||
enabled: true,
|
||||
},
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat:
|
||||
dayDifference > 35
|
||||
? "monthyear"
|
||||
: dayDifference > 7
|
||||
? "date"
|
||||
: dayDifference > 2
|
||||
? "weekday"
|
||||
: dayDifference > 0
|
||||
? "datetime"
|
||||
: "hour",
|
||||
minUnit:
|
||||
dayDifference > 35
|
||||
? "month"
|
||||
: dayDifference > 2
|
||||
? "day"
|
||||
: "hour",
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: "kWh",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: (value) => formatNumber(Math.abs(value), locale),
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
major: {
|
||||
enabled: true,
|
||||
},
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat: "datetime",
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: "kWh",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: (value) => formatNumber(Math.abs(value), locale),
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: "x",
|
||||
intersect: true,
|
||||
position: "nearest",
|
||||
filter: (val) => val.formattedValue !== "0",
|
||||
callbacks: {
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
Math.abs(context.parsed.y),
|
||||
locale
|
||||
)} kWh`,
|
||||
footer: (contexts) => {
|
||||
let totalConsumed = 0;
|
||||
let totalReturned = 0;
|
||||
for (const context of contexts) {
|
||||
const value = (context.dataset.data[context.dataIndex] as any)
|
||||
.y;
|
||||
if (value > 0) {
|
||||
totalConsumed += value;
|
||||
} else {
|
||||
totalReturned += Math.abs(value);
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: "x",
|
||||
intersect: true,
|
||||
position: "nearest",
|
||||
filter: (val) => val.formattedValue !== "0",
|
||||
callbacks: {
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
Math.abs(context.parsed.y),
|
||||
locale
|
||||
)} kWh`,
|
||||
footer: (contexts) => {
|
||||
let totalConsumed = 0;
|
||||
let totalReturned = 0;
|
||||
for (const context of contexts) {
|
||||
const value = (context.dataset.data[context.dataIndex] as any)
|
||||
.y;
|
||||
if (value > 0) {
|
||||
totalConsumed += value;
|
||||
} else {
|
||||
totalReturned += Math.abs(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [
|
||||
totalConsumed
|
||||
? `Total consumed: ${formatNumber(totalConsumed, locale)} kWh`
|
||||
: "",
|
||||
totalReturned
|
||||
? `Total returned: ${formatNumber(totalReturned, locale)} kWh`
|
||||
: "",
|
||||
].filter(Boolean);
|
||||
return [
|
||||
totalConsumed
|
||||
? `Total consumed: ${formatNumber(
|
||||
totalConsumed,
|
||||
locale
|
||||
)} kWh`
|
||||
: "",
|
||||
totalReturned
|
||||
? `Total returned: ${formatNumber(
|
||||
totalReturned,
|
||||
locale
|
||||
)} kWh`
|
||||
: "",
|
||||
].filter(Boolean);
|
||||
},
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
elements: {
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
},
|
||||
elements: {
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
})
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
private async _getStatistics(energyData: EnergyData): Promise<void> {
|
||||
@ -233,7 +273,13 @@ export class HuiEnergyUsageGraphCard
|
||||
}
|
||||
}
|
||||
|
||||
const dayDifference = differenceInDays(
|
||||
energyData.end || new Date(),
|
||||
energyData.start
|
||||
);
|
||||
|
||||
const statisticsData = Object.values(energyData.stats);
|
||||
|
||||
const datasets: ChartDataset<"bar">[] = [];
|
||||
let endTime: Date;
|
||||
|
||||
@ -287,14 +333,20 @@ export class HuiEnergyUsageGraphCard
|
||||
const totalStats: { [start: string]: number } = {};
|
||||
const sets: { [statId: string]: { [start: string]: number } } = {};
|
||||
statIds!.forEach((id) => {
|
||||
const stats = energyData.stats[id];
|
||||
const stats =
|
||||
dayDifference > 35
|
||||
? reduceSumStatisticsByMonth(energyData.stats[id])
|
||||
: dayDifference > 2
|
||||
? reduceSumStatisticsByDay(energyData.stats[id])
|
||||
: energyData.stats[id];
|
||||
if (!stats) {
|
||||
return;
|
||||
}
|
||||
|
||||
const set = {};
|
||||
let prevValue: number;
|
||||
stats.forEach((stat) => {
|
||||
if (!stat.sum) {
|
||||
if (stat.sum === null) {
|
||||
return;
|
||||
}
|
||||
if (!prevValue) {
|
||||
|
@ -1,15 +1,45 @@
|
||||
import { mdiChevronLeft, mdiChevronRight } from "@mdi/js";
|
||||
import { endOfToday, addDays, endOfDay, isToday, startOfToday } from "date-fns";
|
||||
import {
|
||||
endOfToday,
|
||||
addDays,
|
||||
endOfDay,
|
||||
startOfToday,
|
||||
endOfWeek,
|
||||
endOfMonth,
|
||||
startOfDay,
|
||||
startOfWeek,
|
||||
startOfMonth,
|
||||
addMonths,
|
||||
addWeeks,
|
||||
startOfYear,
|
||||
addYears,
|
||||
endOfYear,
|
||||
isWithinInterval,
|
||||
differenceInDays,
|
||||
} 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 {
|
||||
formatDate,
|
||||
formatDateMonthYear,
|
||||
formatDateShort,
|
||||
formatDateYear,
|
||||
} from "../../../common/datetime/format_date";
|
||||
import { EnergyData, getEnergyDataCollection } from "../../../data/energy";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { HomeAssistant, ToggleButton } from "../../../types";
|
||||
import "@material/mwc-icon-button/mwc-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "../../../components/ha-button-toggle-group";
|
||||
|
||||
const viewButtons: ToggleButton[] = [
|
||||
{ label: "Day", value: "day" },
|
||||
{ label: "Week", value: "week" },
|
||||
{ label: "Month", value: "month" },
|
||||
{ label: "Year", value: "year" },
|
||||
];
|
||||
|
||||
@customElement("hui-energy-period-selector")
|
||||
export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
@ -21,6 +51,8 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() _endDate?: Date;
|
||||
|
||||
@state() private _period?: "day" | "week" | "month" | "year";
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass, {
|
||||
@ -37,41 +69,110 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
return html`
|
||||
<div class="row">
|
||||
<div class="label">
|
||||
${formatDate(this._startDate, this.hass.locale)}
|
||||
${this._period === "day"
|
||||
? formatDate(this._startDate, this.hass.locale)
|
||||
: this._period === "month"
|
||||
? formatDateMonthYear(this._startDate, this.hass.locale)
|
||||
: this._period === "year"
|
||||
? formatDateYear(this._startDate, this.hass.locale)
|
||||
: `${formatDateShort(
|
||||
this._startDate,
|
||||
this.hass.locale
|
||||
)} - ${formatDateShort(
|
||||
this._endDate || new Date(),
|
||||
this.hass.locale
|
||||
)}`}
|
||||
</div>
|
||||
|
||||
<mwc-icon-button label="Previous Day" @click=${this._pickPreviousDay}>
|
||||
<mwc-icon-button label="Previous" @click=${this._pickPrevious}>
|
||||
<ha-svg-icon .path=${mdiChevronLeft}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-icon-button label="Next Day" @click=${this._pickNextDay}>
|
||||
<mwc-icon-button label="Next" @click=${this._pickNext}>
|
||||
<ha-svg-icon .path=${mdiChevronRight}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
|
||||
<mwc-button
|
||||
<mwc-button dense outlined @click=${this._pickToday}>
|
||||
Today
|
||||
</mwc-button>
|
||||
<ha-button-toggle-group
|
||||
.buttons=${viewButtons}
|
||||
.active=${this._period}
|
||||
dense
|
||||
outlined
|
||||
.disabled=${isToday(this._startDate)}
|
||||
@click=${this._pickToday}
|
||||
>Today</mwc-button
|
||||
>
|
||||
@value-changed=${this._handleView}
|
||||
></ha-button-toggle-group>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleView(ev: CustomEvent): void {
|
||||
this._period = ev.detail.value;
|
||||
const today = startOfToday();
|
||||
const start =
|
||||
!this._startDate ||
|
||||
isWithinInterval(today, {
|
||||
start: this._startDate,
|
||||
end: this._endDate || endOfToday(),
|
||||
})
|
||||
? today
|
||||
: this._startDate;
|
||||
|
||||
this._setDate(
|
||||
this._period === "day"
|
||||
? startOfDay(start)
|
||||
: this._period === "week"
|
||||
? startOfWeek(start, { weekStartsOn: 1 })
|
||||
: this._period === "month"
|
||||
? startOfMonth(start)
|
||||
: startOfYear(start)
|
||||
);
|
||||
}
|
||||
|
||||
private _pickToday() {
|
||||
this._setDate(startOfToday());
|
||||
this._setDate(
|
||||
this._period === "day"
|
||||
? startOfToday()
|
||||
: this._period === "week"
|
||||
? startOfWeek(new Date(), { weekStartsOn: 1 })
|
||||
: this._period === "month"
|
||||
? startOfMonth(new Date())
|
||||
: startOfYear(new Date())
|
||||
);
|
||||
}
|
||||
|
||||
private _pickPreviousDay() {
|
||||
this._setDate(addDays(this._startDate!, -1));
|
||||
private _pickPrevious() {
|
||||
const newStart =
|
||||
this._period === "day"
|
||||
? addDays(this._startDate!, -1)
|
||||
: this._period === "week"
|
||||
? addWeeks(this._startDate!, -1)
|
||||
: this._period === "month"
|
||||
? addMonths(this._startDate!, -1)
|
||||
: addYears(this._startDate!, -1);
|
||||
this._setDate(newStart);
|
||||
}
|
||||
|
||||
private _pickNextDay() {
|
||||
this._setDate(addDays(this._startDate!, +1));
|
||||
private _pickNext() {
|
||||
const newStart =
|
||||
this._period === "day"
|
||||
? addDays(this._startDate!, 1)
|
||||
: this._period === "week"
|
||||
? addWeeks(this._startDate!, 1)
|
||||
: this._period === "month"
|
||||
? addMonths(this._startDate!, 1)
|
||||
: addYears(this._startDate!, 1);
|
||||
this._setDate(newStart);
|
||||
}
|
||||
|
||||
private _setDate(startDate: Date) {
|
||||
const endDate = endOfDay(startDate);
|
||||
const endDate =
|
||||
this._period === "day"
|
||||
? endOfDay(startDate)
|
||||
: this._period === "week"
|
||||
? endOfWeek(startDate, { weekStartsOn: 1 })
|
||||
: this._period === "month"
|
||||
? endOfMonth(startDate)
|
||||
: endOfYear(startDate);
|
||||
|
||||
const energyCollection = getEnergyDataCollection(this.hass, {
|
||||
key: this.collectionKey,
|
||||
});
|
||||
@ -82,6 +183,17 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
private _updateDates(energyData: EnergyData): void {
|
||||
this._startDate = energyData.start;
|
||||
this._endDate = energyData.end || endOfToday();
|
||||
const dayDifference = differenceInDays(this._endDate, this._startDate);
|
||||
this._period =
|
||||
dayDifference < 1
|
||||
? "day"
|
||||
: dayDifference === 6
|
||||
? "week"
|
||||
: dayDifference > 26 && dayDifference < 31 // 28, 29, 30 or 31 days in a month
|
||||
? "month"
|
||||
: dayDifference === 364 || dayDifference === 365 // Leap year
|
||||
? "year"
|
||||
: undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
@ -96,16 +208,20 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
:host {
|
||||
--mdc-button-outline-color: currentColor;
|
||||
--primary-color: currentColor;
|
||||
--mdc-theme-primary: currentColor;
|
||||
--mdc-button-disabled-outline-color: var(--disabled-text-color);
|
||||
--mdc-button-disabled-ink-color: var(--disabled-text-color);
|
||||
--mdc-icon-button-ripple-opacity: 0.2;
|
||||
}
|
||||
mwc-icon-button {
|
||||
--mdc-icon-button-size: 28px;
|
||||
}
|
||||
mwc-button {
|
||||
mwc-button,
|
||||
ha-button-toggle-group {
|
||||
padding-left: 8px;
|
||||
--mdc-theme-primary: currentColor;
|
||||
--mdc-button-outline-color: currentColor;
|
||||
|
||||
--mdc-button-disabled-outline-color: var(--disabled-text-color);
|
||||
--mdc-button-disabled-ink-color: var(--disabled-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ export type FullCalendarView =
|
||||
|
||||
export interface ToggleButton {
|
||||
label: string;
|
||||
iconPath: string;
|
||||
iconPath?: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user