diff --git a/src/common/datetime/calc_date_range.ts b/src/common/datetime/calc_date_range.ts new file mode 100644 index 0000000000..4371512dae --- /dev/null +++ b/src/common/datetime/calc_date_range.ts @@ -0,0 +1,116 @@ +import { + addDays, + subHours, + endOfDay, + endOfMonth, + endOfWeek, + endOfYear, + startOfDay, + startOfMonth, + startOfWeek, + startOfYear, + startOfQuarter, + endOfQuarter, + subDays, + subMonths, +} from "date-fns"; +import type { HomeAssistant } from "../../types"; +import { calcDate } from "./calc_date"; +import { firstWeekdayIndex } from "./first_weekday"; + +export type DateRange = + | "today" + | "yesterday" + | "this_week" + | "this_month" + | "this_quarter" + | "this_year" + | "now-7d" + | "now-30d" + | "now-12m" + | "now-1h" + | "now-12h" + | "now-24h"; + +export const calcDateRange = ( + hass: HomeAssistant, + range: DateRange +): [Date, Date] => { + const today = new Date(); + const weekStartsOn = firstWeekdayIndex(hass.locale); + switch (range) { + case "today": + return [ + calcDate(today, startOfDay, hass.locale, hass.config, { + weekStartsOn, + }), + calcDate(today, endOfDay, hass.locale, hass.config, { + weekStartsOn, + }), + ]; + case "yesterday": + return [ + calcDate(addDays(today, -1), startOfDay, hass.locale, hass.config, { + weekStartsOn, + }), + calcDate(addDays(today, -1), endOfDay, hass.locale, hass.config, { + weekStartsOn, + }), + ]; + case "this_week": + return [ + calcDate(today, startOfWeek, hass.locale, hass.config, { + weekStartsOn, + }), + calcDate(today, endOfWeek, hass.locale, hass.config, { + weekStartsOn, + }), + ]; + case "this_month": + return [ + calcDate(today, startOfMonth, hass.locale, hass.config), + calcDate(today, endOfMonth, hass.locale, hass.config), + ]; + case "this_quarter": + return [ + calcDate(today, startOfQuarter, hass.locale, hass.config), + calcDate(today, endOfQuarter, hass.locale, hass.config), + ]; + case "this_year": + return [ + calcDate(today, startOfYear, hass.locale, hass.config), + calcDate(today, endOfYear, hass.locale, hass.config), + ]; + case "now-7d": + return [ + calcDate(today, subDays, hass.locale, hass.config, 7), + calcDate(today, subDays, hass.locale, hass.config, 1), + ]; + case "now-30d": + return [ + calcDate(today, subDays, hass.locale, hass.config, 30), + calcDate(today, subDays, hass.locale, hass.config, 1), + ]; + case "now-12m": + return [ + calcDate(subMonths(today, 12), startOfMonth, hass.locale, hass.config), + calcDate(subMonths(today, 1), endOfMonth, hass.locale, hass.config), + ]; + case "now-1h": + return [ + calcDate(today, subHours, hass.locale, hass.config, 1), + calcDate(today, subHours, hass.locale, hass.config, 0), + ]; + case "now-12h": + return [ + calcDate(today, subHours, hass.locale, hass.config, 12), + calcDate(today, subHours, hass.locale, hass.config, 0), + ]; + case "now-24h": + return [ + calcDate(today, subHours, hass.locale, hass.config, 24), + calcDate(today, subHours, hass.locale, hass.config, 0), + ]; + } + return [today, today]; +}; diff --git a/src/components/ha-date-range-picker.ts b/src/components/ha-date-range-picker.ts index 40c59a3144..d85f8d809b 100644 --- a/src/components/ha-date-range-picker.ts +++ b/src/components/ha-date-range-picker.ts @@ -3,25 +3,13 @@ import "@material/mwc-list/mwc-list"; import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { mdiCalendar } from "@mdi/js"; -import { - addDays, - subHours, - endOfDay, - endOfMonth, - endOfWeek, - endOfYear, - startOfDay, - startOfMonth, - startOfWeek, - startOfYear, - isThisYear, -} from "date-fns"; +import { isThisYear } from "date-fns"; import { fromZonedTime, toZonedTime } from "date-fns-tz"; import type { PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; -import { calcDate, shiftDateRange } from "../common/datetime/calc_date"; +import { shiftDateRange } from "../common/datetime/calc_date"; import { firstWeekdayIndex } from "../common/datetime/first_weekday"; import { formatShortDateTime, @@ -36,9 +24,28 @@ import "./ha-icon-button"; import "./ha-icon-button-next"; import "./ha-icon-button-prev"; import "./ha-textarea"; +import { calcDateRange } from "../common/datetime/calc_date_range"; +import type { DateRange } from "../common/datetime/calc_date_range"; export type DateRangePickerRanges = Record; +declare global { + interface HASSDomEvents { + "preset-selected": { index: number }; + } +} + +const RANGE_KEYS: DateRange[] = ["today", "yesterday", "this_week"]; +const EXTENDED_RANGE_KEYS: DateRange[] = [ + "this_month", + "this_year", + "now-1h", + "now-12h", + "now-24h", + "now-7d", + "now-30d", +]; + @customElement("ha-date-range-picker") export class HaDateRangePicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -84,194 +91,16 @@ export class HaDateRangePicker extends LitElement { (changedProps.has("hass") && this.hass?.localize !== changedProps.get("hass")?.localize) ) { - const today = new Date(); - const weekStartsOn = firstWeekdayIndex(this.hass.locale); - const weekStart = calcDate( - today, - startOfWeek, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ); - const weekEnd = calcDate( - today, - endOfWeek, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ); + const rangeKeys = this.extendedPresets + ? [...RANGE_KEYS, ...EXTENDED_RANGE_KEYS] + : RANGE_KEYS; - this._ranges = { - [this.hass.localize("ui.components.date-range-picker.ranges.today")]: [ - calcDate(today, startOfDay, this.hass.locale, this.hass.config, { - weekStartsOn, - }), - calcDate(today, endOfDay, this.hass.locale, this.hass.config, { - weekStartsOn, - }), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.yesterday" - )]: [ - calcDate( - addDays(today, -1), - startOfDay, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ), - calcDate( - addDays(today, -1), - endOfDay, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.this_week" - )]: [weekStart, weekEnd], - ...(this.extendedPresets - ? { - [this.hass.localize( - "ui.components.date-range-picker.ranges.this_month" - )]: [ - calcDate( - today, - startOfMonth, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ), - calcDate( - today, - endOfMonth, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.this_year" - )]: [ - calcDate( - today, - startOfYear, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ), - calcDate(today, endOfYear, this.hass.locale, this.hass.config, { - weekStartsOn, - }), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.now-1h" - )]: [ - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 1 - ), - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 0 - ), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.now-12h" - )]: [ - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 12 - ), - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 0 - ), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.now-24h" - )]: [ - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 24 - ), - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 0 - ), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.now-7d" - )]: [ - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 24 * 7 - ), - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 0 - ), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.now-30d" - )]: [ - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 24 * 30 - ), - calcDate( - today, - subHours, - this.hass.locale, - this.hass.config, - 0 - ), - ], - } - : {}), - }; + this._ranges = {}; + rangeKeys.forEach((key) => { + this._ranges![ + this.hass.localize(`ui.components.date-range-picker.ranges.${key}`) + ] = calcDateRange(this.hass, key); + }); } } @@ -410,6 +239,10 @@ export class HaDateRangePicker extends LitElement { const dateRange = Object.values(this.ranges || this._ranges!)[ ev.detail.index ]; + + fireEvent(this, "preset-selected", { + index: ev.detail.index, + }); const dateRangePicker = this._dateRangePicker; dateRangePicker.clickRange(dateRange); dateRangePicker.clickedApply(); diff --git a/src/data/energy.ts b/src/data/energy.ts index de4e014409..7b5ea204da 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -27,6 +27,8 @@ import type { StatisticsUnitConfiguration, } from "./recorder"; import { fetchStatistics, getStatisticMetadata } from "./recorder"; +import { calcDateRange } from "../common/datetime/calc_date_range"; +import type { DateRange } from "../common/datetime/calc_date_range"; const energyCollectionKeys: (string | undefined)[] = []; @@ -665,21 +667,17 @@ export const getEnergyDataCollection = ( collection._active = 0; collection.prefs = options.prefs; + const now = new Date(); const hour = formatTime24h(now, hass.locale, hass.config).split(":")[0]; // Set start to start of today if we have data for today, otherwise yesterday - collection.start = calcDate( - hour === "0" ? addDays(now, -1) : now, - startOfDay, - hass.locale, - hass.config - ); - collection.end = calcDate( - hour === "0" ? addDays(now, -1) : now, - endOfDay, - hass.locale, - hass.config - ); + const preferredPeriod = + (localStorage.getItem(`energy-default-period-${key}`) as DateRange) || + "today"; + const period = + preferredPeriod === "today" && hour === "0" ? "yesterday" : preferredPeriod; + + [collection.start, collection.end] = calcDateRange(hass, period); const scheduleUpdatePeriod = () => { collection._updatePeriodTimeout = window.setTimeout( diff --git a/src/panels/lovelace/components/hui-energy-period-selector.ts b/src/panels/lovelace/components/hui-energy-period-selector.ts index 5aacb90a6d..08f136e088 100644 --- a/src/panels/lovelace/components/hui-energy-period-selector.ts +++ b/src/panels/lovelace/components/hui-energy-period-selector.ts @@ -5,20 +5,13 @@ import { differenceInDays, differenceInMonths, endOfDay, - endOfMonth, - endOfQuarter, endOfToday, endOfWeek, - endOfYear, isFirstDayOfMonth, isLastDayOfMonth, startOfDay, - startOfMonth, - startOfQuarter, startOfWeek, - startOfYear, subDays, - subMonths, } from "date-fns"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { PropertyValues } from "lit"; @@ -50,6 +43,20 @@ import type { EnergyData } from "../../../data/energy"; import { getEnergyDataCollection } from "../../../data/energy"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import type { HomeAssistant } from "../../../types"; +import { calcDateRange } from "../../../common/datetime/calc_date_range"; +import type { DateRange } from "../../../common/datetime/calc_date_range"; + +const RANGE_KEYS: DateRange[] = [ + "today", + "yesterday", + "this_week", + "this_month", + "this_quarter", + "this_year", + "now-7d", + "now-30d", + "now-12m", +]; @customElement("hui-energy-period-selector") export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) { @@ -117,94 +124,13 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) { (changedProps.has("hass") && this.hass?.localize !== changedProps.get("hass")?.localize) ) { - const today = new Date(); - const weekStartsOn = firstWeekdayIndex(this.hass.locale); - // pre defined date ranges - this._ranges = { - [this.hass.localize("ui.components.date-range-picker.ranges.today")]: [ - calcDate(today, startOfDay, this.hass.locale, this.hass.config, { - weekStartsOn, - }), - calcDate(today, endOfDay, this.hass.locale, this.hass.config, { - weekStartsOn, - }), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.yesterday" - )]: [ - calcDate( - calcDate(today, subDays, this.hass.locale, this.hass.config, 1), - startOfDay, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ), - calcDate( - calcDate(today, subDays, this.hass.locale, this.hass.config, 1), - endOfDay, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.this_week" - )]: [ - calcDate(today, startOfWeek, this.hass.locale, this.hass.config, { - weekStartsOn, - }), - calcDate(today, endOfWeek, this.hass.locale, this.hass.config, { - weekStartsOn, - }), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.this_month" - )]: [ - calcDate(today, startOfMonth, this.hass.locale, this.hass.config), - calcDate(today, endOfMonth, this.hass.locale, this.hass.config), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.this_quarter" - )]: [ - calcDate(today, startOfQuarter, this.hass.locale, this.hass.config), - calcDate(today, endOfQuarter, this.hass.locale, this.hass.config), - ], - [this.hass.localize( - "ui.components.date-range-picker.ranges.this_year" - )]: [ - calcDate(today, startOfYear, this.hass.locale, this.hass.config), - calcDate(today, endOfYear, this.hass.locale, this.hass.config), - ], - [this.hass.localize("ui.components.date-range-picker.ranges.now-7d")]: [ - calcDate(today, subDays, this.hass.locale, this.hass.config, 7), - calcDate(today, subDays, this.hass.locale, this.hass.config, 1), - ], - [this.hass.localize("ui.components.date-range-picker.ranges.now-30d")]: - [ - calcDate(today, subDays, this.hass.locale, this.hass.config, 30), - calcDate(today, subDays, this.hass.locale, this.hass.config, 1), - ], - [this.hass.localize("ui.components.date-range-picker.ranges.now-12m")]: - [ - calcDate( - subMonths(today, 12), - startOfMonth, - this.hass.locale, - this.hass.config - ), - calcDate( - subMonths(today, 1), - endOfMonth, - this.hass.locale, - this.hass.config - ), - ], - }; + this._ranges = {}; + RANGE_KEYS.forEach((key) => { + this._ranges[ + this.hass.localize(`ui.components.date-range-picker.ranges.${key}`) + ] = calcDateRange(this.hass, key); + }); } } @@ -272,6 +198,7 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) { .endDate=${this._endDate || new Date()} .ranges=${this._ranges} @value-changed=${this._dateRangeChanged} + @preset-selected=${this._presetSelected} minimal header-position > @@ -393,6 +320,13 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) { this._updateCollectionPeriod(); } + private _presetSelected(ev) { + localStorage.setItem( + `energy-default-period-_${this.collectionKey || "energy"}`, + RANGE_KEYS[ev.detail.index] + ); + } + private _pickNow() { if (!this._startDate) return; @@ -404,44 +338,14 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) { ); const today = new Date(); if (range === "month") { - this._startDate = calcDate( - today, - startOfMonth, - this.hass.locale, - this.hass.config - ); - this._endDate = calcDate( - today, - endOfMonth, - this.hass.locale, - this.hass.config - ); + [this._startDate, this._endDate] = calcDateRange(this.hass, "this_month"); } else if (range === "quarter") { - this._startDate = calcDate( - today, - startOfQuarter, - this.hass.locale, - this.hass.config - ); - this._endDate = calcDate( - today, - endOfQuarter, - this.hass.locale, - this.hass.config + [this._startDate, this._endDate] = calcDateRange( + this.hass, + "this_quarter" ); } else if (range === "year") { - this._startDate = calcDate( - today, - startOfYear, - this.hass.locale, - this.hass.config - ); - this._endDate = calcDate( - today, - endOfYear, - this.hass.locale, - this.hass.config - ); + [this._startDate, this._endDate] = calcDateRange(this.hass, "this_year"); } else { const weekStartsOn = firstWeekdayIndex(this.hass.locale); const weekStart = calcDate( @@ -469,23 +373,9 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) { this._endDate!.getTime() === weekEnd.getTime() ) { // Pick current week - this._startDate = calcDate( - today, - startOfWeek, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } - ); - this._endDate = calcDate( - today, - endOfWeek, - this.hass.locale, - this.hass.config, - { - weekStartsOn, - } + [this._startDate, this._endDate] = calcDateRange( + this.hass, + "this_week" ); } else { // Custom date range