Allow to show times in the UI in the timezone of the server (#16799)

This commit is contained in:
Bram Kragten 2023-06-13 12:12:13 +02:00 committed by GitHub
parent f7722a270f
commit 780de42e4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
120 changed files with 1169 additions and 442 deletions

View File

@ -10,7 +10,9 @@ import {
TimeFormat, TimeFormat,
DateFormat, DateFormat,
FirstWeekday, FirstWeekday,
TimeZone,
} from "../../../../src/data/translation"; } from "../../../../src/data/translation";
import "@material/mwc-list/mwc-list";
@customElement("demo-date-time-date") @customElement("demo-date-time-date")
export class DemoDateTimeDate extends LitElement { export class DemoDateTimeDate extends LitElement {
@ -22,6 +24,7 @@ export class DemoDateTimeDate extends LitElement {
number_format: NumberFormat.language, number_format: NumberFormat.language,
time_format: TimeFormat.language, time_format: TimeFormat.language,
date_format: DateFormat.language, date_format: DateFormat.language,
time_zone: TimeZone.local,
first_weekday: FirstWeekday.language, first_weekday: FirstWeekday.language,
}; };
const date = new Date(); const date = new Date();
@ -41,32 +44,48 @@ export class DemoDateTimeDate extends LitElement {
<div class="container"> <div class="container">
<div>${value.nativeName}</div> <div>${value.nativeName}</div>
<div class="center"> <div class="center">
${formatDateNumeric(date, { ${formatDateNumeric(
...defaultLocale, date,
language: key, {
date_format: DateFormat.language, ...defaultLocale,
})} language: key,
date_format: DateFormat.language,
},
this.hass.config
)}
</div> </div>
<div class="center"> <div class="center">
${formatDateNumeric(date, { ${formatDateNumeric(
...defaultLocale, date,
language: key, {
date_format: DateFormat.DMY, ...defaultLocale,
})} language: key,
date_format: DateFormat.DMY,
},
this.hass.config
)}
</div> </div>
<div class="center"> <div class="center">
${formatDateNumeric(date, { ${formatDateNumeric(
...defaultLocale, date,
language: key, {
date_format: DateFormat.MDY, ...defaultLocale,
})} language: key,
date_format: DateFormat.MDY,
},
this.hass.config
)}
</div> </div>
<div class="center"> <div class="center">
${formatDateNumeric(date, { ${formatDateNumeric(
...defaultLocale, date,
language: key, {
date_format: DateFormat.YMD, ...defaultLocale,
})} language: key,
date_format: DateFormat.YMD,
},
this.hass.config
)}
</div> </div>
</div> </div>
` `

View File

@ -354,6 +354,7 @@ export class DemoEntityState extends LitElement {
hass.localize, hass.localize,
entry.stateObj, entry.stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities hass.entities
)}`, )}`,
}, },

View File

@ -143,7 +143,11 @@ export class SupervisorBackupContent extends LitElement {
: this._localize("partial_backup")} : this._localize("partial_backup")}
(${Math.ceil(this.backup.size * 10) / 10 + " MB"})<br /> (${Math.ceil(this.backup.size * 10) / 10 + " MB"})<br />
${this.hass ${this.hass
? formatDateTime(new Date(this.backup.date), this.hass.locale) ? formatDateTime(
new Date(this.backup.date),
this.hass.locale,
this.hass.config
)
: this.backup.date} : this.backup.date}
</div>` </div>`
: html`<paper-input : html`<paper-input
@ -336,7 +340,9 @@ export class SupervisorBackupContent extends LitElement {
const data: any = {}; const data: any = {};
if (!this.backup) { if (!this.backup) {
data.name = this.backupName || formatDate(new Date(), this.hass.locale); data.name =
this.backupName ||
formatDate(new Date(), this.hass.locale, this.hass.config);
} }
if (this.backupHasPassword) { if (this.backupHasPassword) {

View File

@ -1,4 +1,5 @@
import { isSameDay, isSameYear } from "date-fns"; import { isSameDay, isSameYear } from "date-fns";
import { HassConfig } from "home-assistant-js-websocket";
import { FrontendLocaleData } from "../../data/translation"; import { FrontendLocaleData } from "../../data/translation";
import { import {
formatShortDateTime, formatShortDateTime,
@ -9,15 +10,16 @@ import { formatTime } from "./format_time";
export const absoluteTime = ( export const absoluteTime = (
from: Date, from: Date,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
to?: Date to?: Date
): string => { ): string => {
const _to = to ?? new Date(); const _to = to ?? new Date();
if (isSameDay(from, _to)) { if (isSameDay(from, _to)) {
return formatTime(from, locale); return formatTime(from, locale, config);
} }
if (isSameYear(from, _to)) { if (isSameYear(from, _to)) {
return formatShortDateTime(from, locale); return formatShortDateTime(from, locale, config);
} }
return formatShortDateTimeWithYear(from, locale); return formatShortDateTimeWithYear(from, locale, config);
}; };

View File

@ -0,0 +1,25 @@
import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
import { HassConfig } from "home-assistant-js-websocket";
import { FrontendLocaleData, TimeZone } from "../../data/translation";
const calcZonedDate = (
date: Date,
tz: string,
fn: (date: Date, options?: any) => Date,
options?
) => {
const inputZoned = utcToZonedTime(date, tz);
const fnZoned = fn(inputZoned, options);
return zonedTimeToUtc(fnZoned, tz);
};
export const calcDate = (
date: Date,
fn: (date: Date, options?: any) => Date,
locale: FrontendLocaleData,
config: HassConfig,
options?
) =>
locale.time_zone === TimeZone.server
? calcZonedDate(date, config.time_zone, fn, options)
: fn(date, options);

View File

@ -1,3 +1,4 @@
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { FrontendLocaleData, DateFormat } from "../../data/translation"; import { FrontendLocaleData, DateFormat } from "../../data/translation";
import "../../resources/intl-polyfill"; import "../../resources/intl-polyfill";
@ -5,37 +6,44 @@ import "../../resources/intl-polyfill";
// Tuesday, August 10 // Tuesday, August 10
export const formatDateWeekdayDay = ( export const formatDateWeekdayDay = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
) => formatDateWeekdayDayMem(locale).format(dateObj); config: HassConfig
) => formatDateWeekdayDayMem(locale, config.time_zone).format(dateObj);
const formatDateWeekdayDayMem = memoizeOne( const formatDateWeekdayDayMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
weekday: "long", weekday: "long",
month: "long", month: "long",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );
// August 10, 2021 // August 10, 2021
export const formatDate = (dateObj: Date, locale: FrontendLocaleData) => export const formatDate = (
formatDateMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateMem(locale, config.time_zone).format(dateObj);
const formatDateMem = memoizeOne( const formatDateMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
year: "numeric", year: "numeric",
month: "long", month: "long",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );
// 10/08/2021 // 10/08/2021
export const formatDateNumeric = ( export const formatDateNumeric = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
config: HassConfig
) => { ) => {
const formatter = formatDateNumericMem(locale); const formatter = formatDateNumericMem(locale, config.time_zone);
if ( if (
locale.date_format === DateFormat.language || locale.date_format === DateFormat.language ||
@ -67,83 +75,120 @@ export const formatDateNumeric = (
return formats[locale.date_format]; return formats[locale.date_format];
}; };
const formatDateNumericMem = memoizeOne((locale: FrontendLocaleData) => { const formatDateNumericMem = memoizeOne(
const localeString = (locale: FrontendLocaleData, serverTimeZone: string) => {
locale.date_format === DateFormat.system ? undefined : locale.language; const localeString =
locale.date_format === DateFormat.system ? undefined : locale.language;
if (
locale.date_format === DateFormat.language ||
locale.date_format === DateFormat.system
) {
return new Intl.DateTimeFormat(localeString, {
year: "numeric",
month: "numeric",
day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
});
}
if (
locale.date_format === DateFormat.language ||
locale.date_format === DateFormat.system
) {
return new Intl.DateTimeFormat(localeString, { return new Intl.DateTimeFormat(localeString, {
year: "numeric", year: "numeric",
month: "numeric", month: "numeric",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}); });
} }
);
return new Intl.DateTimeFormat(localeString, {
year: "numeric",
month: "numeric",
day: "numeric",
});
});
// Aug 10 // Aug 10
export const formatDateShort = (dateObj: Date, locale: FrontendLocaleData) => export const formatDateShort = (
formatDateShortMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateShortMem(locale, config.time_zone).format(dateObj);
const formatDateShortMem = memoizeOne( const formatDateShortMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
day: "numeric", day: "numeric",
month: "short", month: "short",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );
// August 2021 // August 2021
export const formatDateMonthYear = ( export const formatDateMonthYear = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
) => formatDateMonthYearMem(locale).format(dateObj); config: HassConfig
) => formatDateMonthYearMem(locale, config.time_zone).format(dateObj);
const formatDateMonthYearMem = memoizeOne( const formatDateMonthYearMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
month: "long", month: "long",
year: "numeric", year: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );
// August // August
export const formatDateMonth = (dateObj: Date, locale: FrontendLocaleData) => export const formatDateMonth = (
formatDateMonthMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateMonthMem(locale, config.time_zone).format(dateObj);
const formatDateMonthMem = memoizeOne( const formatDateMonthMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
month: "long", month: "long",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );
// 2021 // 2021
export const formatDateYear = (dateObj: Date, locale: FrontendLocaleData) => export const formatDateYear = (
formatDateYearMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateYearMem(locale, config.time_zone).format(dateObj);
const formatDateYearMem = memoizeOne( const formatDateYearMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
year: "numeric", year: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );
// Monday // Monday
export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) => export const formatDateWeekday = (
formatDateWeekdayMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateWeekdayMem(locale, config.time_zone).format(dateObj);
const formatDateWeekdayMem = memoizeOne( const formatDateWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
weekday: "long", weekday: "long",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
})
);
// Mon
export const formatDateWeekdayShort = (
dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateWeekdayShortMem(locale, config.time_zone).format(dateObj);
const formatDateWeekdayShortMem = memoizeOne(
(locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "short",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );

View File

@ -1,16 +1,20 @@
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation"; import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill"; import "../../resources/intl-polyfill";
import { useAmPm } from "./use_am_pm";
import { formatDateNumeric } from "./format_date"; import { formatDateNumeric } from "./format_date";
import { formatTime } from "./format_time"; import { formatTime } from "./format_time";
import { useAmPm } from "./use_am_pm";
// August 9, 2021, 8:23 AM // August 9, 2021, 8:23 AM
export const formatDateTime = (dateObj: Date, locale: FrontendLocaleData) => export const formatDateTime = (
formatDateTimeMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateTimeMem(locale, config.time_zone).format(dateObj);
const formatDateTimeMem = memoizeOne( const formatDateTimeMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat( new Intl.DateTimeFormat(
locale.language === "en" && !useAmPm(locale) locale.language === "en" && !useAmPm(locale)
? "en-u-hc-h23" ? "en-u-hc-h23"
@ -22,6 +26,7 @@ const formatDateTimeMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: useAmPm(locale), hour12: useAmPm(locale),
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
} }
) )
); );
@ -29,11 +34,12 @@ const formatDateTimeMem = memoizeOne(
// Aug 9, 2021, 8:23 AM // Aug 9, 2021, 8:23 AM
export const formatShortDateTimeWithYear = ( export const formatShortDateTimeWithYear = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
) => formatShortDateTimeWithYearMem(locale).format(dateObj); config: HassConfig
) => formatShortDateTimeWithYearMem(locale, config.time_zone).format(dateObj);
const formatShortDateTimeWithYearMem = memoizeOne( const formatShortDateTimeWithYearMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat( new Intl.DateTimeFormat(
locale.language === "en" && !useAmPm(locale) locale.language === "en" && !useAmPm(locale)
? "en-u-hc-h23" ? "en-u-hc-h23"
@ -45,6 +51,7 @@ const formatShortDateTimeWithYearMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: useAmPm(locale), hour12: useAmPm(locale),
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
} }
) )
); );
@ -52,11 +59,12 @@ const formatShortDateTimeWithYearMem = memoizeOne(
// Aug 9, 8:23 AM // Aug 9, 8:23 AM
export const formatShortDateTime = ( export const formatShortDateTime = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
) => formatShortDateTimeMem(locale).format(dateObj); config: HassConfig
) => formatShortDateTimeMem(locale, config.time_zone).format(dateObj);
const formatShortDateTimeMem = memoizeOne( const formatShortDateTimeMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat( new Intl.DateTimeFormat(
locale.language === "en" && !useAmPm(locale) locale.language === "en" && !useAmPm(locale)
? "en-u-hc-h23" ? "en-u-hc-h23"
@ -67,6 +75,7 @@ const formatShortDateTimeMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: useAmPm(locale), hour12: useAmPm(locale),
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
} }
) )
); );
@ -74,11 +83,12 @@ const formatShortDateTimeMem = memoizeOne(
// August 9, 2021, 8:23:15 AM // August 9, 2021, 8:23:15 AM
export const formatDateTimeWithSeconds = ( export const formatDateTimeWithSeconds = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
) => formatDateTimeWithSecondsMem(locale).format(dateObj); config: HassConfig
) => formatDateTimeWithSecondsMem(locale, config.time_zone).format(dateObj);
const formatDateTimeWithSecondsMem = memoizeOne( const formatDateTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat( new Intl.DateTimeFormat(
locale.language === "en" && !useAmPm(locale) locale.language === "en" && !useAmPm(locale)
? "en-u-hc-h23" ? "en-u-hc-h23"
@ -91,6 +101,7 @@ const formatDateTimeWithSecondsMem = memoizeOne(
minute: "2-digit", minute: "2-digit",
second: "2-digit", second: "2-digit",
hour12: useAmPm(locale), hour12: useAmPm(locale),
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
} }
) )
); );
@ -98,5 +109,11 @@ const formatDateTimeWithSecondsMem = memoizeOne(
// 9/8/2021, 8:23 AM // 9/8/2021, 8:23 AM
export const formatDateTimeNumeric = ( export const formatDateTimeNumeric = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
) => `${formatDateNumeric(dateObj, locale)}, ${formatTime(dateObj, locale)}`; config: HassConfig
) =>
`${formatDateNumeric(dateObj, locale, config)}, ${formatTime(
dateObj,
locale,
config
)}`;

View File

@ -1,14 +1,18 @@
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation"; import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill"; import "../../resources/intl-polyfill";
import { useAmPm } from "./use_am_pm"; import { useAmPm } from "./use_am_pm";
// 9:15 PM || 21:15 // 9:15 PM || 21:15
export const formatTime = (dateObj: Date, locale: FrontendLocaleData) => export const formatTime = (
formatTimeMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatTimeMem(locale, config.time_zone).format(dateObj);
const formatTimeMem = memoizeOne( const formatTimeMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat( new Intl.DateTimeFormat(
locale.language === "en" && !useAmPm(locale) locale.language === "en" && !useAmPm(locale)
? "en-u-hc-h23" ? "en-u-hc-h23"
@ -17,6 +21,7 @@ const formatTimeMem = memoizeOne(
hour: "numeric", hour: "numeric",
minute: "2-digit", minute: "2-digit",
hour12: useAmPm(locale), hour12: useAmPm(locale),
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
} }
) )
); );
@ -24,11 +29,12 @@ const formatTimeMem = memoizeOne(
// 9:15:24 PM || 21:15:24 // 9:15:24 PM || 21:15:24
export const formatTimeWithSeconds = ( export const formatTimeWithSeconds = (
dateObj: Date, dateObj: Date,
locale: FrontendLocaleData locale: FrontendLocaleData,
) => formatTimeWithSecondsMem(locale).format(dateObj); config: HassConfig
) => formatTimeWithSecondsMem(locale, config.time_zone).format(dateObj);
const formatTimeWithSecondsMem = memoizeOne( const formatTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat( new Intl.DateTimeFormat(
locale.language === "en" && !useAmPm(locale) locale.language === "en" && !useAmPm(locale)
? "en-u-hc-h23" ? "en-u-hc-h23"
@ -38,16 +44,20 @@ const formatTimeWithSecondsMem = memoizeOne(
minute: "2-digit", minute: "2-digit",
second: "2-digit", second: "2-digit",
hour12: useAmPm(locale), hour12: useAmPm(locale),
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
} }
) )
); );
// Tuesday 7:00 PM || Tuesday 19:00 // Tuesday 7:00 PM || Tuesday 19:00
export const formatTimeWeekday = (dateObj: Date, locale: FrontendLocaleData) => export const formatTimeWeekday = (
formatTimeWeekdayMem(locale).format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatTimeWeekdayMem(locale, config.time_zone).format(dateObj);
const formatTimeWeekdayMem = memoizeOne( const formatTimeWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat( new Intl.DateTimeFormat(
locale.language === "en" && !useAmPm(locale) locale.language === "en" && !useAmPm(locale)
? "en-u-hc-h23" ? "en-u-hc-h23"
@ -57,20 +67,25 @@ const formatTimeWeekdayMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: useAmPm(locale), hour12: useAmPm(locale),
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
} }
) )
); );
// 21:15 // 21:15
export const formatTime24h = (dateObj: Date) => export const formatTime24h = (
formatTime24hMem().format(dateObj); dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatTime24hMem(locale, config.time_zone).format(dateObj);
const formatTime24hMem = memoizeOne( const formatTime24hMem = memoizeOne(
() => (locale: FrontendLocaleData, serverTimeZone: string) =>
// en-GB to fix Chrome 24:59 to 0:59 https://stackoverflow.com/a/60898146 // en-GB to fix Chrome 24:59 to 0:59 https://stackoverflow.com/a/60898146
new Intl.DateTimeFormat("en-GB", { new Intl.DateTimeFormat("en-GB", {
hour: "numeric", hour: "numeric",
minute: "2-digit", minute: "2-digit",
hour12: false, hour12: false,
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
}) })
); );

View File

@ -1,4 +1,4 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassConfig, HassEntity } from "home-assistant-js-websocket";
import { html, TemplateResult } from "lit"; import { html, TemplateResult } from "lit";
import { until } from "lit/directives/until"; import { until } from "lit/directives/until";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry"; import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
@ -20,6 +20,7 @@ export const computeAttributeValueDisplay = (
localize: LocalizeFunc, localize: LocalizeFunc,
stateObj: HassEntity, stateObj: HassEntity,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
entities: HomeAssistant["entities"], entities: HomeAssistant["entities"],
attribute: string, attribute: string,
value?: any value?: any
@ -59,14 +60,14 @@ export const computeAttributeValueDisplay = (
if (isTimestamp(attributeValue)) { if (isTimestamp(attributeValue)) {
const date = new Date(attributeValue); const date = new Date(attributeValue);
if (checkValidDate(date)) { if (checkValidDate(date)) {
return formatDateTimeWithSeconds(date, locale); return formatDateTimeWithSeconds(date, locale, config);
} }
} }
// Value was not a timestamp, so only do date formatting // Value was not a timestamp, so only do date formatting
const date = new Date(attributeValue); const date = new Date(attributeValue);
if (checkValidDate(date)) { if (checkValidDate(date)) {
return formatDate(date, locale); return formatDate(date, locale, config);
} }
} }
} }
@ -92,6 +93,7 @@ export const computeAttributeValueDisplay = (
localize, localize,
stateObj, stateObj,
locale, locale,
config,
entities, entities,
attribute, attribute,
item item

View File

@ -1,7 +1,7 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassConfig, HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry"; import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { FrontendLocaleData } from "../../data/translation"; import { FrontendLocaleData, TimeZone } from "../../data/translation";
import { import {
updateIsInstallingFromAttributes, updateIsInstallingFromAttributes,
UPDATE_SUPPORT_PROGRESS, UPDATE_SUPPORT_PROGRESS,
@ -28,12 +28,14 @@ export const computeStateDisplaySingleEntity = (
localize: LocalizeFunc, localize: LocalizeFunc,
stateObj: HassEntity, stateObj: HassEntity,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
entity: EntityRegistryDisplayEntry | undefined, entity: EntityRegistryDisplayEntry | undefined,
state?: string state?: string
): string => ): string =>
computeStateDisplayFromEntityAttributes( computeStateDisplayFromEntityAttributes(
localize, localize,
locale, locale,
config,
entity, entity,
stateObj.entity_id, stateObj.entity_id,
stateObj.attributes, stateObj.attributes,
@ -44,6 +46,7 @@ export const computeStateDisplay = (
localize: LocalizeFunc, localize: LocalizeFunc,
stateObj: HassEntity, stateObj: HassEntity,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
entities: HomeAssistant["entities"], entities: HomeAssistant["entities"],
state?: string state?: string
): string => { ): string => {
@ -54,6 +57,7 @@ export const computeStateDisplay = (
return computeStateDisplayFromEntityAttributes( return computeStateDisplayFromEntityAttributes(
localize, localize,
locale, locale,
config,
entity, entity,
stateObj.entity_id, stateObj.entity_id,
stateObj.attributes, stateObj.attributes,
@ -64,6 +68,7 @@ export const computeStateDisplay = (
export const computeStateDisplayFromEntityAttributes = ( export const computeStateDisplayFromEntityAttributes = (
localize: LocalizeFunc, localize: LocalizeFunc,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
entity: EntityRegistryDisplayEntry | undefined, entity: EntityRegistryDisplayEntry | undefined,
entityId: string, entityId: string,
attributes: any, attributes: any,
@ -119,29 +124,40 @@ export const computeStateDisplayFromEntityAttributes = (
if (domain === "datetime") { if (domain === "datetime") {
const time = new Date(state); const time = new Date(state);
return formatDateTime(time, locale); return formatDateTime(time, locale, config);
} }
if (["date", "input_datetime", "time"].includes(domain)) { if (["date", "input_datetime", "time"].includes(domain)) {
// If trying to display an explicit state, need to parse the explicit state to `Date` then format. // If trying to display an explicit state, need to parse the explicit state to `Date` then format.
// Attributes aren't available, we have to use `state`. // Attributes aren't available, we have to use `state`.
// These are timezone agnostic, so we should NOT use the system timezone here.
try { try {
const components = state.split(" "); const components = state.split(" ");
if (components.length === 2) { if (components.length === 2) {
// Date and time. // Date and time.
return formatDateTime(new Date(components.join("T")), locale); return formatDateTime(
new Date(components.join("T")),
{ ...locale, time_zone: TimeZone.local },
config
);
} }
if (components.length === 1) { if (components.length === 1) {
if (state.includes("-")) { if (state.includes("-")) {
// Date only. // Date only.
return formatDate(new Date(`${state}T00:00`), locale); return formatDate(
new Date(`${state}T00:00`),
{ ...locale, time_zone: TimeZone.local },
config
);
} }
if (state.includes(":")) { if (state.includes(":")) {
// Time only. // Time only.
const now = new Date(); const now = new Date();
return formatTime( return formatTime(
new Date(`${now.toISOString().split("T")[0]}T${state}`), new Date(`${now.toISOString().split("T")[0]}T${state}`),
locale { ...locale, time_zone: TimeZone.local },
config
); );
} }
} }
@ -179,7 +195,7 @@ export const computeStateDisplayFromEntityAttributes = (
(domain === "sensor" && attributes.device_class === "timestamp") (domain === "sensor" && attributes.device_class === "timestamp")
) { ) {
try { try {
return formatDateTime(new Date(state), locale); return formatDateTime(new Date(state), locale, config);
} catch (_err) { } catch (_err) {
return state; return state;
} }

View File

@ -1,10 +1,12 @@
import { addDays, startOfWeek } from "date-fns"; import { addDays, startOfWeek } from "date-fns";
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation"; import { FrontendLocaleData } from "../../data/translation";
import { formatDateWeekday } from "../datetime/format_date"; import { formatDateWeekday } from "../datetime/format_date";
export const dayNames = memoizeOne((locale: FrontendLocaleData): string[] => export const dayNames = memoizeOne(
Array.from({ length: 7 }, (_, d) => (locale: FrontendLocaleData, config: HassConfig): string[] =>
formatDateWeekday(addDays(startOfWeek(new Date()), d), locale) Array.from({ length: 7 }, (_, d) =>
) formatDateWeekday(addDays(startOfWeek(new Date()), d), locale, config)
)
); );

View File

@ -1,10 +1,12 @@
import { addMonths, startOfYear } from "date-fns"; import { addMonths, startOfYear } from "date-fns";
import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation"; import { FrontendLocaleData } from "../../data/translation";
import { formatDateMonth } from "../datetime/format_date"; import { formatDateMonth } from "../datetime/format_date";
export const monthNames = memoizeOne((locale: FrontendLocaleData): string[] => export const monthNames = memoizeOne(
Array.from({ length: 12 }, (_, m) => (locale: FrontendLocaleData, config: HassConfig): string[] =>
formatDateMonth(addMonths(startOfYear(new Date()), m), locale) Array.from({ length: 12 }, (_, m) =>
) formatDateMonth(addMonths(startOfYear(new Date()), m), locale, config)
)
); );

View File

@ -80,33 +80,89 @@ _adapters._date.override({
format: function (time, fmt: keyof typeof FORMATS) { format: function (time, fmt: keyof typeof FORMATS) {
switch (fmt) { switch (fmt) {
case "datetime": case "datetime":
return formatDateTime(new Date(time), this.options.locale); return formatDateTime(
new Date(time),
this.options.locale,
this.options.config
);
case "datetimeseconds": case "datetimeseconds":
return formatDateTimeWithSeconds(new Date(time), this.options.locale); return formatDateTimeWithSeconds(
new Date(time),
this.options.locale,
this.options.config
);
case "millisecond": case "millisecond":
return formatTimeWithSeconds(new Date(time), this.options.locale); return formatTimeWithSeconds(
new Date(time),
this.options.locale,
this.options.config
);
case "second": case "second":
return formatTimeWithSeconds(new Date(time), this.options.locale); return formatTimeWithSeconds(
new Date(time),
this.options.locale,
this.options.config
);
case "minute": case "minute":
return formatTime(new Date(time), this.options.locale); return formatTime(
new Date(time),
this.options.locale,
this.options.config
);
case "hour": case "hour":
return formatTime(new Date(time), this.options.locale); return formatTime(
new Date(time),
this.options.locale,
this.options.config
);
case "weekday": case "weekday":
return formatDateWeekdayDay(new Date(time), this.options.locale); return formatDateWeekdayDay(
new Date(time),
this.options.locale,
this.options.config
);
case "date": case "date":
return formatDate(new Date(time), this.options.locale); return formatDate(
new Date(time),
this.options.locale,
this.options.config
);
case "day": case "day":
return formatDateShort(new Date(time), this.options.locale); return formatDateShort(
new Date(time),
this.options.locale,
this.options.config
);
case "week": case "week":
return formatDate(new Date(time), this.options.locale); return formatDate(
new Date(time),
this.options.locale,
this.options.config
);
case "month": case "month":
return formatDateMonth(new Date(time), this.options.locale); return formatDateMonth(
new Date(time),
this.options.locale,
this.options.config
);
case "monthyear": case "monthyear":
return formatDateMonthYear(new Date(time), this.options.locale); return formatDateMonthYear(
new Date(time),
this.options.locale,
this.options.config
);
case "quarter": case "quarter":
return formatDate(new Date(time), this.options.locale); return formatDate(
new Date(time),
this.options.locale,
this.options.config
);
case "year": case "year":
return formatDateYear(new Date(time), this.options.locale); return formatDateYear(
new Date(time),
this.options.locale,
this.options.config
);
default: default:
return ""; return "";
} }

View File

@ -71,6 +71,7 @@ class StateHistoryChartLine extends LitElement {
adapters: { adapters: {
date: { date: {
locale: this.hass.locale, locale: this.hass.locale,
config: this.hass.config,
}, },
}, },
suggestedMax: this.endTime, suggestedMax: this.endTime,

View File

@ -98,6 +98,7 @@ export class StateHistoryChartTimeline extends LitElement {
adapters: { adapters: {
date: { date: {
locale: this.hass.locale, locale: this.hass.locale,
config: this.hass.config,
}, },
}, },
suggestedMin: this.startTime, suggestedMin: this.startTime,
@ -181,8 +182,16 @@ export class StateHistoryChartTimeline extends LitElement {
return [ return [
d.label || "", d.label || "",
formatDateTimeWithSeconds(d.start, this.hass.locale), formatDateTimeWithSeconds(
formatDateTimeWithSeconds(d.end, this.hass.locale), d.start,
this.hass.locale,
this.hass.config
),
formatDateTimeWithSeconds(
d.end,
this.hass.locale,
this.hass.config
),
formattedDuration, formattedDuration,
]; ];
}, },

View File

@ -146,6 +146,7 @@ class StatisticsChart extends LitElement {
adapters: { adapters: {
date: { date: {
locale: this.hass.locale, locale: this.hass.locale,
config: this.hass.config,
}, },
}, },
ticks: { ticks: {

View File

@ -62,6 +62,7 @@ class HaEntityStatePicker extends LitElement {
this.hass.localize, this.hass.localize,
state, state,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
key key
) )
@ -69,6 +70,7 @@ class HaEntityStatePicker extends LitElement {
this.hass.localize, this.hass.localize,
state, state,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
this.attribute, this.attribute,
key key

View File

@ -192,6 +192,7 @@ export class HaStateLabelBadge extends LitElement {
this.hass!.localize, this.hass!.localize,
entityState, entityState,
this.hass!.locale, this.hass!.locale,
this.hass!.config,
this.hass!.entities this.hass!.entities
); );
} }

View File

@ -64,7 +64,11 @@ class HaAbsoluteTime extends ReactiveElement {
if (!this.datetime) { if (!this.datetime) {
this.innerHTML = this.hass.localize("ui.components.absolute_time.never"); this.innerHTML = this.hass.localize("ui.components.absolute_time.never");
} else { } else {
this.innerHTML = absoluteTime(new Date(this.datetime), this.hass.locale); this.innerHTML = absoluteTime(
new Date(this.datetime),
this.hass.locale,
this.hass.config
);
} }
} }
} }

View File

@ -62,6 +62,7 @@ class HaAttributes extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj!, this.stateObj!,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
attribute attribute
)} )}

View File

@ -28,6 +28,7 @@ class HaClimateState extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj, this.stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"preset_mode" "preset_mode"
)}` )}`
@ -136,6 +137,7 @@ class HaClimateState extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj, this.stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
); );
@ -144,6 +146,7 @@ class HaClimateState extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj, this.stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"hvac_action" "hvac_action"
)} (${stateString})` )} (${stateString})`

View File

@ -1,9 +1,11 @@
import { mdiCalendar } from "@mdi/js"; import { mdiCalendar } from "@mdi/js";
import { HassConfig } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement } from "lit"; import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { formatDateNumeric } from "../common/datetime/format_date";
import { firstWeekdayIndex } from "../common/datetime/first_weekday"; import { firstWeekdayIndex } from "../common/datetime/first_weekday";
import { formatDateNumeric } from "../common/datetime/format_date";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { TimeZone } from "../data/translation";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import "./ha-svg-icon"; import "./ha-svg-icon";
import "./ha-textfield"; import "./ha-textfield";
@ -59,7 +61,11 @@ export class HaDateInput extends LitElement {
.value=${this.value .value=${this.value
? formatDateNumeric( ? formatDateNumeric(
new Date(`${this.value.split("T")[0]}T00:00:00`), new Date(`${this.value.split("T")[0]}T00:00:00`),
this.locale {
...this.locale,
time_zone: TimeZone.local,
},
{} as HassConfig
) )
: ""} : ""}
.required=${this.required} .required=${this.required}

View File

@ -3,6 +3,13 @@ import "@material/mwc-list/mwc-list";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiCalendar } from "@mdi/js"; import { mdiCalendar } from "@mdi/js";
import {
addDays,
endOfDay,
endOfWeek,
startOfDay,
startOfWeek,
} from "date-fns";
import { import {
css, css,
CSSResultGroup, CSSResultGroup,
@ -12,10 +19,11 @@ import {
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { formatDateTime } from "../common/datetime/format_date_time"; import { calcDate } from "../common/datetime/calc_date";
import { formatDate } from "../common/datetime/format_date";
import { useAmPm } from "../common/datetime/use_am_pm";
import { firstWeekdayIndex } from "../common/datetime/first_weekday"; import { firstWeekdayIndex } from "../common/datetime/first_weekday";
import { formatDate } from "../common/datetime/format_date";
import { formatDateTime } from "../common/datetime/format_date_time";
import { useAmPm } from "../common/datetime/use_am_pm";
import { computeRTLDirection } from "../common/util/compute_rtl"; import { computeRTLDirection } from "../common/util/compute_rtl";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import "./date-range-picker"; import "./date-range-picker";
@ -34,7 +42,7 @@ export class HaDateRangePicker extends LitElement {
@property() public endDate!: Date; @property() public endDate!: Date;
@property() public ranges?: DateRangePickerRanges; @property() public ranges?: DateRangePickerRanges | false;
@property() public autoApply = false; @property() public autoApply = false;
@ -46,6 +54,70 @@ export class HaDateRangePicker extends LitElement {
@property({ type: String }) private _rtlDirection = "ltr"; @property({ type: String }) private _rtlDirection = "ltr";
protected willUpdate() {
if (!this.hasUpdated && this.ranges === undefined) {
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,
}
);
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.hass.localize(
"ui.components.date-range-picker.ranges.last_week"
)]: [addDays(weekStart, -7), addDays(weekEnd, -7)],
};
}
}
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
if (changedProps.has("hass")) { if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined; const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
@ -65,15 +137,19 @@ export class HaDateRangePicker extends LitElement {
twentyfour-hours=${this._hour24format} twentyfour-hours=${this._hour24format}
start-date=${this.startDate} start-date=${this.startDate}
end-date=${this.endDate} end-date=${this.endDate}
?ranges=${this.ranges !== undefined} ?ranges=${this.ranges !== false}
first-day=${firstWeekdayIndex(this.hass.locale)} first-day=${firstWeekdayIndex(this.hass.locale)}
> >
<div slot="input" class="date-range-inputs"> <div slot="input" class="date-range-inputs">
<ha-svg-icon .path=${mdiCalendar}></ha-svg-icon> <ha-svg-icon .path=${mdiCalendar}></ha-svg-icon>
<ha-textfield <ha-textfield
.value=${this.timePicker .value=${this.timePicker
? formatDateTime(this.startDate, this.hass.locale) ? formatDateTime(
: formatDate(this.startDate, this.hass.locale)} this.startDate,
this.hass.locale,
this.hass.config
)
: formatDate(this.startDate, this.hass.locale, this.hass.config)}
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.components.date-range-picker.start_date" "ui.components.date-range-picker.start_date"
)} )}
@ -83,8 +159,8 @@ export class HaDateRangePicker extends LitElement {
></ha-textfield> ></ha-textfield>
<ha-textfield <ha-textfield
.value=${this.timePicker .value=${this.timePicker
? formatDateTime(this.endDate, this.hass.locale) ? formatDateTime(this.endDate, this.hass.locale, this.hass.config)
: formatDate(this.endDate, this.hass.locale)} : formatDate(this.endDate, this.hass.locale, this.hass.config)}
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.components.date-range-picker.end_date" "ui.components.date-range-picker.end_date"
)} )}

View File

@ -127,7 +127,8 @@ export class HaTracePathDetails extends LitElement {
Executed: Executed:
${formatDateTimeWithSeconds( ${formatDateTimeWithSeconds(
new Date(timestamp), new Date(timestamp),
this.hass.locale this.hass.locale,
this.hass.config
)}<br /> )}<br />
${result ${result
? html`Result: ? html`Result:

View File

@ -335,7 +335,8 @@ class ActionRenderer {
} at } at
${formatDateTimeWithSeconds( ${formatDateTimeWithSeconds(
new Date(triggerStep.timestamp), new Date(triggerStep.timestamp),
this.hass.locale this.hass.locale,
this.hass.config
)}`, )}`,
mdiCircle mdiCircle
); );
@ -632,7 +633,8 @@ export class HaAutomationTracer extends LitElement {
const renderFinishedAt = () => const renderFinishedAt = () =>
formatDateTimeWithSeconds( formatDateTimeWithSeconds(
new Date(this.trace!.timestamp.finish!), new Date(this.trace!.timestamp.finish!),
this.hass.locale this.hass.locale,
this.hass.config
); );
const renderRuntime = () => `(runtime: const renderRuntime = () => `(runtime:
${( ${(

View File

@ -1,26 +1,27 @@
import { HassConfig } from "home-assistant-js-websocket";
import { ensureArray } from "../common/array/ensure-array";
import { formatDuration } from "../common/datetime/format_duration"; import { formatDuration } from "../common/datetime/format_duration";
import { import {
formatTime, formatTime,
formatTimeWithSeconds, formatTimeWithSeconds,
} from "../common/datetime/format_time"; } from "../common/datetime/format_time";
import { FrontendLocaleData } from "./translation";
import secondsToDuration from "../common/datetime/seconds_to_duration"; import secondsToDuration from "../common/datetime/seconds_to_duration";
import { ensureArray } from "../common/array/ensure-array"; import {
computeAttributeNameDisplay,
computeAttributeValueDisplay,
} from "../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../common/entity/compute_state_display";
import { computeStateName } from "../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { Condition, Trigger, ForDict } from "./automation"; import { Condition, ForDict, Trigger } from "./automation";
import { import {
DeviceCondition, DeviceCondition,
DeviceTrigger, DeviceTrigger,
localizeDeviceAutomationCondition, localizeDeviceAutomationCondition,
localizeDeviceAutomationTrigger, localizeDeviceAutomationTrigger,
} from "./device_automation"; } from "./device_automation";
import {
computeAttributeNameDisplay,
computeAttributeValueDisplay,
} from "../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../common/entity/compute_state_display";
import { EntityRegistryEntry } from "./entity_registry"; import { EntityRegistryEntry } from "./entity_registry";
import { FrontendLocaleData } from "./translation";
const describeDuration = (forTime: number | string | ForDict) => { const describeDuration = (forTime: number | string | ForDict) => {
let duration: string | null; let duration: string | null;
@ -34,7 +35,11 @@ const describeDuration = (forTime: number | string | ForDict) => {
return duration; return duration;
}; };
const localizeTimeString = (time: string, locale: FrontendLocaleData) => { const localizeTimeString = (
time: string,
locale: FrontendLocaleData,
config: HassConfig
) => {
const chunks = time.split(":"); const chunks = time.split(":");
if (chunks.length < 2 || chunks.length > 3) { if (chunks.length < 2 || chunks.length > 3) {
return time; return time;
@ -42,9 +47,9 @@ const localizeTimeString = (time: string, locale: FrontendLocaleData) => {
try { try {
const dt = new Date("1970-01-01T" + time); const dt = new Date("1970-01-01T" + time);
if (chunks.length === 2 || Number(chunks[2]) === 0) { if (chunks.length === 2 || Number(chunks[2]) === 0) {
return formatTime(dt, locale); return formatTime(dt, locale, config);
} }
return formatTimeWithSeconds(dt, locale); return formatTimeWithSeconds(dt, locale, config);
} catch { } catch {
return time; return time;
} }
@ -209,6 +214,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
trigger.attribute, trigger.attribute,
state state
@ -217,6 +223,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
state state
) )
@ -232,6 +239,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
trigger.attribute, trigger.attribute,
trigger.from trigger.from
@ -240,6 +248,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
trigger.from.toString() trigger.from.toString()
).toString() ).toString()
@ -263,6 +272,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
trigger.attribute, trigger.attribute,
state state
@ -271,6 +281,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
state state
).toString() ).toString()
@ -286,6 +297,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
trigger.attribute, trigger.attribute,
trigger.to trigger.to
@ -294,6 +306,7 @@ export const describeTrigger = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
trigger.to.toString() trigger.to.toString()
).toString() ).toString()
@ -353,7 +366,7 @@ export const describeTrigger = (
? at ? at
: at.includes(".") : at.includes(".")
? `entity ${hass.states[at] ? computeStateName(hass.states[at]) : at}` ? `entity ${hass.states[at] ? computeStateName(hass.states[at]) : at}`
: localizeTimeString(at, hass.locale) : localizeTimeString(at, hass.locale, hass.config)
); );
const last = result.splice(-1, 1)[0]; const last = result.splice(-1, 1)[0];
@ -738,6 +751,7 @@ export const describeCondition = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
condition.attribute, condition.attribute,
state state
@ -746,6 +760,7 @@ export const describeCondition = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
state state
) )
@ -758,6 +773,7 @@ export const describeCondition = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
condition.attribute, condition.attribute,
condition.state condition.state
@ -766,6 +782,7 @@ export const describeCondition = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
condition.state.toString() condition.state.toString()
).toString() ).toString()
@ -830,7 +847,7 @@ export const describeCondition = (
? computeStateName(hass.states[condition.before]) ? computeStateName(hass.states[condition.before])
: condition.before : condition.before
}` }`
: localizeTimeString(condition.before, hass.locale); : localizeTimeString(condition.before, hass.locale, hass.config);
const after = const after =
typeof condition.after !== "string" typeof condition.after !== "string"
@ -841,7 +858,7 @@ export const describeCondition = (
? computeStateName(hass.states[condition.after]) ? computeStateName(hass.states[condition.after])
: condition.after : condition.after
}` }`
: localizeTimeString(condition.after, hass.locale); : localizeTimeString(condition.after, hass.locale, hass.config);
let result = "Confirm the "; let result = "Confirm the ";
if (after || before) { if (after || before) {

View File

@ -1,4 +1,5 @@
import { createContext } from "@lit-labs/context"; import { createContext } from "@lit-labs/context";
import { HassConfig } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import { EntityRegistryEntry } from "./entity_registry"; import { EntityRegistryEntry } from "./entity_registry";
@ -11,7 +12,7 @@ export const areasContext = createContext<HomeAssistant["areas"]>("areas");
export const localizeContext = export const localizeContext =
createContext<HomeAssistant["localize"]>("localize"); createContext<HomeAssistant["localize"]>("localize");
export const localeContext = createContext<HomeAssistant["locale"]>("locale"); export const localeContext = createContext<HomeAssistant["locale"]>("locale");
export const configContext = createContext<HomeAssistant["config"]>("config"); export const configContext = createContext<HassConfig>("config");
export const themesContext = createContext<HomeAssistant["themes"]>("themes"); export const themesContext = createContext<HomeAssistant["themes"]>("themes");
export const selectedThemeContext = export const selectedThemeContext =
createContext<HomeAssistant["selectedTheme"]>("selectedTheme"); createContext<HomeAssistant["selectedTheme"]>("selectedTheme");

View File

@ -4,12 +4,12 @@ import {
addMilliseconds, addMilliseconds,
addMonths, addMonths,
differenceInDays, differenceInDays,
endOfToday, endOfDay,
endOfYesterday, startOfDay,
startOfToday,
startOfYesterday,
} from "date-fns/esm"; } from "date-fns/esm";
import { Collection, getCollection } from "home-assistant-js-websocket"; import { Collection, getCollection } from "home-assistant-js-websocket";
import { calcDate } from "../common/datetime/calc_date";
import { formatTime24h } from "../common/datetime/format_time";
import { groupBy } from "../common/util/group-by"; import { groupBy } from "../common/util/group-by";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import { ConfigEntry, getConfigEntries } from "./config_entries"; import { ConfigEntry, getConfigEntries } from "./config_entries";
@ -626,18 +626,40 @@ export const getEnergyDataCollection = (
collection._active = 0; collection._active = 0;
collection.prefs = options.prefs; collection.prefs = options.prefs;
const now = new Date(); 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 // Set start to start of today if we have data for today, otherwise yesterday
collection.start = now.getHours() > 0 ? startOfToday() : startOfYesterday(); collection.start = calcDate(
collection.end = now.getHours() > 0 ? endOfToday() : endOfYesterday(); 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 scheduleUpdatePeriod = () => { const scheduleUpdatePeriod = () => {
collection._updatePeriodTimeout = window.setTimeout( collection._updatePeriodTimeout = window.setTimeout(
() => { () => {
collection.start = startOfToday(); collection.start = calcDate(
collection.end = endOfToday(); new Date(),
startOfDay,
hass.locale,
hass.config
);
collection.end = calcDate(
new Date(),
endOfDay,
hass.locale,
hass.config
);
scheduleUpdatePeriod(); scheduleUpdatePeriod();
}, },
addHours(endOfToday(), 1).getTime() - Date.now() // Switch to next day an hour after the day changed addHours(calcDate(now, endOfDay, hass.locale, hass.config), 1).getTime() -
Date.now() // Switch to next day an hour after the day changed
); );
}; };
scheduleUpdatePeriod(); scheduleUpdatePeriod();
@ -649,8 +671,10 @@ export const getEnergyDataCollection = (
collection.start = newStart; collection.start = newStart;
collection.end = newEnd; collection.end = newEnd;
if ( if (
collection.start.getTime() === startOfToday().getTime() && collection.start.getTime() ===
collection.end?.getTime() === endOfToday().getTime() && calcDate(new Date(), startOfDay, hass.locale, hass.config).getTime() &&
collection.end?.getTime() ===
calcDate(new Date(), endOfDay, hass.locale, hass.config).getTime() &&
!collection._updatePeriodTimeout !collection._updatePeriodTimeout
) { ) {
scheduleUpdatePeriod(); scheduleUpdatePeriod();

View File

@ -1,4 +1,5 @@
import { import {
HassConfig,
HassEntities, HassEntities,
HassEntity, HassEntity,
HassEntityAttributeBase, HassEntityAttributeBase,
@ -269,7 +270,8 @@ const equalState = (obj1: LineChartState, obj2: LineChartState) =>
const processTimelineEntity = ( const processTimelineEntity = (
localize: LocalizeFunc, localize: LocalizeFunc,
language: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
entities: HomeAssistant["entities"], entities: HomeAssistant["entities"],
entityId: string, entityId: string,
states: EntityHistoryState[], states: EntityHistoryState[],
@ -290,7 +292,8 @@ const processTimelineEntity = (
data.push({ data.push({
state_localize: computeStateDisplayFromEntityAttributes( state_localize: computeStateDisplayFromEntityAttributes(
localize, localize,
language, locale,
config,
entities[entityId], entities[entityId],
entityId, entityId,
{ {
@ -441,6 +444,7 @@ export const computeHistory = (
processTimelineEntity( processTimelineEntity(
localize, localize,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
entityId, entityId,
stateInfo, stateInfo,

View File

@ -439,6 +439,7 @@ export const localizeStateMessage = (
localize, localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
state state
) )

View File

@ -94,6 +94,7 @@ export const computeDisplayTimer = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities hass.entities
); );
} }
@ -105,6 +106,7 @@ export const computeDisplayTimer = (
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities hass.entities
)})`; )})`;
} }

View File

@ -17,6 +17,11 @@ export enum TimeFormat {
twenty_four = "24", twenty_four = "24",
} }
export enum TimeZone {
local = "local",
server = "server",
}
export enum DateFormat { export enum DateFormat {
language = "language", language = "language",
system = "system", system = "system",
@ -42,6 +47,7 @@ export interface FrontendLocaleData {
time_format: TimeFormat; time_format: TimeFormat;
date_format: DateFormat; date_format: DateFormat;
first_weekday: FirstWeekday; first_weekday: FirstWeekday;
time_zone: TimeZone;
} }
declare global { declare global {

View File

@ -72,6 +72,7 @@ export class HaMoreInfoFanSpeed extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj, this.stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
speed speed
); );

View File

@ -39,6 +39,7 @@ export class HaMoreInfoStateHeader extends LitElement {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass!.locale, this.hass!.locale,
this.hass!.config,
this.hass!.entities this.hass!.entities
); );

View File

@ -203,6 +203,7 @@ class MoreInfoClimate extends LitElement {
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
this.hass.config,
hass.entities, hass.entities,
mode mode
)} )}
@ -236,6 +237,7 @@ class MoreInfoClimate extends LitElement {
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
hass.config,
hass.entities, hass.entities,
"preset_mode", "preset_mode",
mode mode
@ -270,6 +272,7 @@ class MoreInfoClimate extends LitElement {
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
this.hass.config,
hass.entities, hass.entities,
"fan_mode", "fan_mode",
mode mode
@ -304,6 +307,7 @@ class MoreInfoClimate extends LitElement {
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
this.hass.config,
hass.entities, hass.entities,
"swing_mode", "swing_mode",
mode mode

View File

@ -83,6 +83,7 @@ class MoreInfoCover extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj!, this.stateObj!,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
forcedState forcedState
); );

View File

@ -119,6 +119,7 @@ class MoreInfoFan extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj!, this.stateObj!,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
forcedState forcedState
); );
@ -281,6 +282,7 @@ class MoreInfoFan extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj!, this.stateObj!,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"preset_mode", "preset_mode",
this._presetMode this._presetMode
@ -307,6 +309,7 @@ class MoreInfoFan extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj!, this.stateObj!,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"preset_mode", "preset_mode",
mode mode

View File

@ -83,6 +83,7 @@ class MoreInfoHumidifier extends LitElement {
hass.localize, hass.localize,
stateObj, stateObj,
hass.locale, hass.locale,
this.hass.config,
hass.entities, hass.entities,
"mode", "mode",
mode mode

View File

@ -240,6 +240,7 @@ class MoreInfoLight extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj!, this.stateObj!,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"effect", "effect",
this._effect this._effect
@ -261,6 +262,7 @@ class MoreInfoLight extends LitElement {
this.hass.localize, this.hass.localize,
this.stateObj!, this.stateObj!,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"effect", "effect",
effect effect

View File

@ -163,6 +163,7 @@ class MoreInfoMediaPlayer extends LitElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"source", "source",
source source
@ -196,6 +197,7 @@ class MoreInfoMediaPlayer extends LitElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"sound_mode", "sound_mode",
mode mode

View File

@ -44,6 +44,7 @@ class MoreInfoRemote extends LitElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"activity", "activity",
activity activity

View File

@ -44,7 +44,8 @@ class MoreInfoSun extends LitElement {
<div class="value"> <div class="value">
${formatTime( ${formatTime(
item === "ris" ? risingDate : settingDate, item === "ris" ? risingDate : settingDate,
this.hass.locale this.hass.locale,
this.hass.config
)} )}
</div> </div>
</div> </div>

View File

@ -119,6 +119,7 @@ class MoreInfoVacuum extends LitElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"status" "status"
) || ) ||
@ -126,6 +127,7 @@ class MoreInfoVacuum extends LitElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)} )}
</strong> </strong>
@ -201,6 +203,7 @@ class MoreInfoVacuum extends LitElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"fan_speed", "fan_speed",
mode mode
@ -218,6 +221,7 @@ class MoreInfoVacuum extends LitElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"fan_speed" "fan_speed"
)} )}

View File

@ -164,7 +164,8 @@ class MoreInfoWeather extends LitElement {
<div class="main"> <div class="main">
${formatTimeWeekday( ${formatTimeWeekday(
new Date(item.datetime), new Date(item.datetime),
this.hass.locale this.hass.locale,
this.hass.config
)} )}
</div> </div>
` `
@ -172,7 +173,8 @@ class MoreInfoWeather extends LitElement {
<div class="main"> <div class="main">
${formatDateWeekdayDay( ${formatDateWeekdayDay(
new Date(item.datetime), new Date(item.datetime),
this.hass.locale this.hass.locale,
this.hass.config
)} )}
</div> </div>
`} `}

View File

@ -38,6 +38,7 @@ export class HuiConfiguratorNotificationItem extends LitElement {
this.hass.localize, this.hass.localize,
this.notification, this.notification,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)}</mwc-button )}</mwc-button
> >

View File

@ -82,7 +82,7 @@ export class HuiPersistentNotificationItem extends LitElement {
} }
const d = new Date(notification.created_at!); const d = new Date(notification.created_at!);
return formatDateTime(d, hass.locale); return formatDateTime(d, hass.locale, hass.config);
} }
} }

View File

@ -11,6 +11,7 @@ import {
NumberFormat, NumberFormat,
DateFormat, DateFormat,
TimeFormat, TimeFormat,
TimeZone,
} from "../data/translation"; } from "../data/translation";
import { translationMetadata } from "../resources/translations-metadata"; import { translationMetadata } from "../resources/translations-metadata";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
@ -230,6 +231,7 @@ export const provideHass = (
number_format: NumberFormat.language, number_format: NumberFormat.language,
time_format: TimeFormat.language, time_format: TimeFormat.language,
date_format: DateFormat.language, date_format: DateFormat.language,
time_zone: TimeZone.local,
first_weekday: FirstWeekday.language, first_weekday: FirstWeekday.language,
}, },
resources: null as any, resources: null as any,

View File

@ -154,23 +154,28 @@ class DialogCalendarEventDetail extends LitElement {
if (isSameDay(start, end)) { if (isSameDay(start, end)) {
if (isDate(this._data.dtstart)) { if (isDate(this._data.dtstart)) {
// Single date string only // Single date string only
return formatDate(start, this.hass.locale); return formatDate(start, this.hass.locale, this.hass.config);
} }
// Single day with a start/end time range // Single day with a start/end time range
return `${formatDate(start, this.hass.locale)} ${formatTime( return `${formatDate(
start, start,
this.hass.locale this.hass.locale,
)} - ${formatTime(end, this.hass.locale)}`; this.hass.config
)} ${formatTime(
start,
this.hass.locale,
this.hass.config
)} - ${formatTime(end, this.hass.locale, this.hass.config)}`;
} }
// An event across multiple dates, optionally with a time range // An event across multiple dates, optionally with a time range
return `${ return `${
isDate(this._data.dtstart) isDate(this._data.dtstart)
? formatDate(start, this.hass.locale) ? formatDate(start, this.hass.locale, this.hass.config)
: formatDateTime(start, this.hass.locale) : formatDateTime(start, this.hass.locale, this.hass.config)
} - ${ } - ${
isDate(this._data.dtend) isDate(this._data.dtend)
? formatDate(end, this.hass.locale) ? formatDate(end, this.hass.locale, this.hass.config)
: formatDateTime(end, this.hass.locale) : formatDateTime(end, this.hass.locale, this.hass.config)
}`; }`;
} }

View File

@ -247,8 +247,8 @@ export function renderRRuleAsText(hass: HomeAssistant, value: string) {
return ""; return "";
}, },
{ {
dayNames: dayNames(hass.locale), dayNames: dayNames(hass.locale, hass.config),
monthNames: monthNames(hass.locale), monthNames: monthNames(hass.locale, hass.config),
tokens: {}, tokens: {},
}, },
// Format the date // Format the date
@ -263,9 +263,9 @@ export function renderRRuleAsText(hass: HomeAssistant, value: string) {
// need to convert it back to something Date can work with. The already localized // need to convert it back to something Date can work with. The already localized
// months names are a must in the RRule.Language structure (an empty string[] would // months names are a must in the RRule.Language structure (an empty string[] would
// mean we get undefined months input in this method here). // mean we get undefined months input in this method here).
date.setMonth(monthNames(hass.locale).indexOf(month)); date.setMonth(monthNames(hass.locale, hass.config).indexOf(month));
date.setDate(day); date.setDate(day);
return formatDate(date, hass.locale); return formatDate(date, hass.locale, hass.config);
} }
) )
); );

View File

@ -128,7 +128,11 @@ class HaAutomationPicker extends LitElement {
${this.hass.localize("ui.card.automation.last_triggered")}: ${this.hass.localize("ui.card.automation.last_triggered")}:
${automation.attributes.last_triggered ${automation.attributes.last_triggered
? dayDifference > 3 ? dayDifference > 3
? formatShortDateTime(date, this.hass.locale) ? formatShortDateTime(
date,
this.hass.locale,
this.hass.config
)
: relativeTime(date, this.hass.locale) : relativeTime(date, this.hass.locale)
: this.hass.localize("ui.components.relative_time.never")} : this.hass.localize("ui.components.relative_time.never")}
</div> </div>
@ -149,7 +153,11 @@ class HaAutomationPicker extends LitElement {
return html` return html`
${last_triggered ${last_triggered
? dayDifference > 3 ? dayDifference > 3
? formatShortDateTime(date, this.hass.locale) ? formatShortDateTime(
date,
this.hass.locale,
this.hass.config
)
: relativeTime(date, this.hass.locale) : relativeTime(date, this.hass.locale)
: this.hass.localize("ui.components.relative_time.never")} : this.hass.localize("ui.components.relative_time.never")}
`; `;

View File

@ -192,7 +192,8 @@ export class HaAutomationTrace extends LitElement {
html`<option value=${trace.run_id}> html`<option value=${trace.run_id}>
${formatDateTimeWithSeconds( ${formatDateTimeWithSeconds(
new Date(trace.timestamp.start), new Date(trace.timestamp.start),
this.hass.locale this.hass.locale,
this.hass.config
)} )}
</option>` </option>`
)} )}

View File

@ -79,7 +79,8 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
new Date( new Date(
this._subscription.plan_renewal_date * 1000 this._subscription.plan_renewal_date * 1000
), ),
this.hass.locale this.hass.locale,
this.hass.config
) )
: "" : ""
) )

View File

@ -49,7 +49,8 @@ class DialogCloudCertificate extends LitElement {
)} )}
${formatDateTime( ${formatDateTime(
new Date(certificateInfo.expire_date), new Date(certificateInfo.expire_date),
this.hass!.locale this.hass!.locale,
this.hass!.config
)}<br /> )}<br />
(${this.hass!.localize( (${this.hass!.localize(
"ui.panel.config.cloud.dialog_certificate.will_be_auto_renewed" "ui.panel.config.cloud.dialog_certificate.will_be_auto_renewed"

View File

@ -55,7 +55,8 @@ class MQTTMessages extends LitElement {
${this.direction} ${this.direction}
${formatTimeWithSeconds( ${formatTimeWithSeconds(
new Date(message.time), new Date(message.time),
this.hass.locale this.hass.locale,
this.hass.config
)} )}
</div> </div>
${this._renderSingleMessage(message)} ${this._renderSingleMessage(message)}

View File

@ -170,6 +170,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
adapters: { adapters: {
date: { date: {
locale: this.hass.locale, locale: this.hass.locale,
config: this.hass.config,
}, },
}, },
gridLines: { gridLines: {

View File

@ -286,9 +286,9 @@ class HaScheduleForm extends LitElement {
const value = [...this[`_${day}`]]; const value = [...this[`_${day}`]];
const newValue = { ...this._item }; const newValue = { ...this._item };
const endFormatted = formatTime24h(end); const endFormatted = formatTime24h(end, this.hass.locale, this.hass.config);
value.push({ value.push({
from: formatTime24h(start), from: formatTime24h(start, this.hass.locale, this.hass.config),
to: to:
!isSameDay(start, end) || endFormatted === "0:00" !isSameDay(start, end) || endFormatted === "0:00"
? "24:00" ? "24:00"
@ -313,7 +313,7 @@ class HaScheduleForm extends LitElement {
const value = this[`_${day}`][parseInt(index)]; const value = this[`_${day}`][parseInt(index)];
const newValue = { ...this._item }; const newValue = { ...this._item };
const endFormatted = formatTime24h(end); const endFormatted = formatTime24h(end, this.hass.locale, this.hass.config);
newValue[day][index] = { newValue[day][index] = {
from: value.from, from: value.from,
to: to:
@ -338,9 +338,9 @@ class HaScheduleForm extends LitElement {
const newDay = weekdays[start.getDay()]; const newDay = weekdays[start.getDay()];
const newValue = { ...this._item }; const newValue = { ...this._item };
const endFormatted = formatTime24h(end); const endFormatted = formatTime24h(end, this.hass.locale, this.hass.config);
const event = { const event = {
from: formatTime24h(start), from: formatTime24h(start, this.hass.locale, this.hass.config),
to: to:
!isSameDay(start, end) || endFormatted === "0:00" !isSameDay(start, end) || endFormatted === "0:00"
? "24:00" ? "24:00"

View File

@ -1,6 +1,7 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list";
import Fuse from "fuse.js"; import Fuse from "fuse.js";
import { HassConfig } from "home-assistant-js-websocket";
import { import {
css, css,
html, html,
@ -158,7 +159,7 @@ class AddIntegrationDialog extends LitElement {
( (
i: Brands, i: Brands,
h: Integrations, h: Integrations,
components: HomeAssistant["config"]["components"], components: HassConfig["components"],
localize: LocalizeFunc, localize: LocalizeFunc,
filter?: string filter?: string
): IntegrationListItem[] => { ): IntegrationListItem[] => {

View File

@ -105,7 +105,7 @@ class MqttSubscribeCard extends LitElement {
"topic", "topic",
msg.message.topic, msg.message.topic,
"time", "time",
formatTime(msg.time, this.hass!.locale) formatTime(msg.time, this.hass!.locale, this.hass!.config)
)} )}
<pre>${msg.payload}</pre> <pre>${msg.payload}</pre>
<div class="bottom"> <div class="bottom">

View File

@ -145,12 +145,20 @@ class DialogSystemLogDetail extends LitElement {
${item.count > 0 ${item.count > 0
? html` ? html`
First occurred: First occurred:
${formatSystemLogTime(item.first_occurred, this.hass!.locale)} ${formatSystemLogTime(
item.first_occurred,
this.hass!.locale,
this.hass!.config
)}
(${item.count} occurrences) <br /> (${item.count} occurrences) <br />
` `
: ""} : ""}
Last logged: Last logged:
${formatSystemLogTime(item.timestamp, this.hass!.locale)} ${formatSystemLogTime(
item.timestamp,
this.hass!.locale,
this.hass!.config
)}
</p> </p>
${item.message.length > 1 ${item.message.length > 1
? html` ? html`

View File

@ -38,14 +38,22 @@ export class SystemLogCard extends LitElement {
} }
private _timestamp(item: LoggedError): string { private _timestamp(item: LoggedError): string {
return formatSystemLogTime(item.timestamp, this.hass!.locale); return formatSystemLogTime(
item.timestamp,
this.hass.locale,
this.hass.config
);
} }
private _multipleMessages(item: LoggedError): string { private _multipleMessages(item: LoggedError): string {
return this.hass.localize( return this.hass.localize(
"ui.panel.config.logs.multiple_messages", "ui.panel.config.logs.multiple_messages",
"time", "time",
formatSystemLogTime(item.first_occurred, this.hass!.locale), formatSystemLogTime(
item.first_occurred,
this.hass.locale,
this.hass.config
),
"counter", "counter",
item.count item.count
); );

View File

@ -1,13 +1,18 @@
import { HassConfig } from "home-assistant-js-websocket";
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time"; import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
import { formatTimeWithSeconds } from "../../../common/datetime/format_time"; import { formatTimeWithSeconds } from "../../../common/datetime/format_time";
import { FrontendLocaleData } from "../../../data/translation"; import { FrontendLocaleData } from "../../../data/translation";
export const formatSystemLogTime = (date, locale: FrontendLocaleData) => { export const formatSystemLogTime = (
date,
locale: FrontendLocaleData,
config: HassConfig
) => {
const today = new Date().setHours(0, 0, 0, 0); const today = new Date().setHours(0, 0, 0, 0);
const dateTime = new Date(date * 1000); const dateTime = new Date(date * 1000);
const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0); const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0);
return dateTimeDay < today return dateTimeDay < today
? formatDateTimeWithSeconds(dateTime, locale) ? formatDateTimeWithSeconds(dateTime, locale, config)
: formatTimeWithSeconds(dateTime, locale); : formatTimeWithSeconds(dateTime, locale, config);
}; };

View File

@ -100,7 +100,8 @@ class DialogRepairsIssue extends LitElement {
${this._issue.created ${this._issue.created
? formatDateNumeric( ? formatDateNumeric(
new Date(this._issue.created), new Date(this._issue.created),
this.hass.locale this.hass.locale,
this.hass.config
) )
: ""} : ""}
</div> </div>

View File

@ -349,7 +349,11 @@ class DialogSystemInformation extends LitElement {
`} `}
`; `;
} else if (info.type === "date") { } else if (info.type === "date") {
value = formatDateTime(new Date(info.value), this.hass.locale); value = formatDateTime(
new Date(info.value),
this.hass.locale,
this.hass.config
);
} }
} else { } else {
value = domainInfo.info[key]; value = domainInfo.info[key];
@ -425,7 +429,11 @@ class DialogSystemInformation extends LitElement {
} else if (info.type === "failed") { } else if (info.type === "failed") {
value = `failed to load: ${info.error}`; value = `failed to load: ${info.error}`;
} else if (info.type === "date") { } else if (info.type === "date") {
value = formatDateTime(new Date(info.value), this.hass.locale); value = formatDateTime(
new Date(info.value),
this.hass.locale,
this.hass.config
);
} }
} else { } else {
value = domainInfo.info[key]; value = domainInfo.info[key];

View File

@ -118,7 +118,11 @@ class HaSceneDashboard extends LitElement {
return html` return html`
${last_activated && !isUnavailableState(last_activated) ${last_activated && !isUnavailableState(last_activated)
? dayDifference > 3 ? dayDifference > 3
? formatShortDateTime(date, this.hass.locale) ? formatShortDateTime(
date,
this.hass.locale,
this.hass.config
)
: relativeTime(date, this.hass.locale) : relativeTime(date, this.hass.locale)
: this.hass.localize("ui.components.relative_time.never")} : this.hass.localize("ui.components.relative_time.never")}
`; `;

View File

@ -118,7 +118,11 @@ class HaScriptPicker extends LitElement {
${this.hass.localize("ui.card.automation.last_triggered")}: ${this.hass.localize("ui.card.automation.last_triggered")}:
${script.attributes.last_triggered ${script.attributes.last_triggered
? dayDifference > 3 ? dayDifference > 3
? formatShortDateTime(date, this.hass.locale) ? formatShortDateTime(
date,
this.hass.locale,
this.hass.config
)
: relativeTime(date, this.hass.locale) : relativeTime(date, this.hass.locale)
: this.hass.localize("ui.components.relative_time.never")} : this.hass.localize("ui.components.relative_time.never")}
</div> </div>
@ -139,7 +143,7 @@ class HaScriptPicker extends LitElement {
return html` return html`
${last_triggered ${last_triggered
? dayDifference > 3 ? dayDifference > 3
? formatShortDateTime(date, this.hass.locale) ? formatShortDateTime(date, this.hass.locale, this.hass.config)
: relativeTime(date, this.hass.locale) : relativeTime(date, this.hass.locale)
: this.hass.localize("ui.components.relative_time.never")} : this.hass.localize("ui.components.relative_time.never")}
`; `;

View File

@ -191,7 +191,8 @@ export class HaScriptTrace extends LitElement {
html`<option value=${trace.run_id}> html`<option value=${trace.run_id}>
${formatDateTimeWithSeconds( ${formatDateTimeWithSeconds(
new Date(trace.timestamp.start), new Date(trace.timestamp.start),
this.hass.locale this.hass.locale,
this.hass.config
)} )}
</option>` </option>`
)} )}

View File

@ -64,7 +64,8 @@ export class AssistPipelineDebug extends LitElement {
html`<option value=${run.pipeline_run_id}> html`<option value=${run.pipeline_run_id}>
${formatDateTimeWithSeconds( ${formatDateTimeWithSeconds(
new Date(run.timestamp), new Date(run.timestamp),
this.hass.locale this.hass.locale,
this.hass.config
)} )}
</option>` </option>`
)} )}

View File

@ -80,7 +80,8 @@ class EventSubscribeCard extends LitElement {
)} )}
${formatTime( ${formatTime(
new Date(event.event.time_fired), new Date(event.event.time_fired),
this.hass!.locale this.hass!.locale,
this.hass!.config
)}: )}:
<ha-yaml-editor <ha-yaml-editor
.defaultValue=${event.event} .defaultValue=${event.event}

View File

@ -158,7 +158,11 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
> >
<span>${growth} ${unit}</span> <span>${growth} ${unit}</span>
<span slot="secondary"> <span slot="secondary">
${formatDateTime(new Date(stat.start), this.hass.locale)} ${formatDateTime(
new Date(stat.start),
this.hass.locale,
this.hass.config
)}
</span> </span>
<ha-svg-icon slot="meta" .path=${mdiChevronRight}></ha-svg-icon> <ha-svg-icon slot="meta" .path=${mdiChevronRight}></ha-svg-icon>
</mwc-list-item> </mwc-list-item>
@ -213,7 +217,8 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
<span <span
>${formatDateTime( >${formatDateTime(
new Date(this._chosenStat!.start), new Date(this._chosenStat!.start),
this.hass.locale this.hass.locale,
this.hass.config
)}</span )}</span
> >
</div> </div>
@ -223,7 +228,8 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
<span <span
>${formatDateTime( >${formatDateTime(
new Date(this._chosenStat!.end), new Date(this._chosenStat!.end),
this.hass.locale this.hass.locale,
this.hass.config
)}</span )}</span
> >
</div> </div>

View File

@ -1,14 +1,5 @@
import { mdiFilterRemove, mdiRefresh } from "@mdi/js"; import { mdiFilterRemove, mdiRefresh } from "@mdi/js";
import { import { differenceInHours } from "date-fns/esm";
addDays,
differenceInHours,
endOfToday,
endOfWeek,
endOfYesterday,
startOfToday,
startOfWeek,
startOfYesterday,
} from "date-fns/esm";
import { import {
HassServiceTarget, HassServiceTarget,
UnsubscribeFunc, UnsubscribeFunc,
@ -16,7 +7,6 @@ import {
import { css, html, LitElement, PropertyValues } from "lit"; import { css, html, LitElement, PropertyValues } from "lit";
import { property, query, state } from "lit/decorators"; import { property, query, state } from "lit/decorators";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
import { LocalStorage } from "../../common/decorators/local-storage"; import { LocalStorage } from "../../common/decorators/local-storage";
import { navigate } from "../../common/navigate"; import { navigate } from "../../common/navigate";
import { constructUrlCurrentPath } from "../../common/url/construct-url"; import { constructUrlCurrentPath } from "../../common/url/construct-url";
@ -31,10 +21,11 @@ import "../../components/chart/state-history-charts";
import type { StateHistoryCharts } from "../../components/chart/state-history-charts"; import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
import "../../components/ha-circular-progress"; import "../../components/ha-circular-progress";
import "../../components/ha-date-range-picker"; import "../../components/ha-date-range-picker";
import type { DateRangePickerRanges } from "../../components/ha-date-range-picker";
import "../../components/ha-icon-button"; import "../../components/ha-icon-button";
import "../../components/ha-icon-button-arrow-prev";
import "../../components/ha-menu-button"; import "../../components/ha-menu-button";
import "../../components/ha-target-picker"; import "../../components/ha-target-picker";
import "../../components/ha-top-app-bar-fixed";
import { import {
AreaDeviceLookup, AreaDeviceLookup,
AreaEntityLookup, AreaEntityLookup,
@ -55,8 +46,6 @@ import {
import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { haStyle } from "../../resources/styles"; import { haStyle } from "../../resources/styles";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../../components/ha-top-app-bar-fixed";
import "../../components/ha-icon-button-arrow-prev";
class HaPanelHistory extends SubscribeMixin(LitElement) { class HaPanelHistory extends SubscribeMixin(LitElement) {
@property({ attribute: false }) hass!: HomeAssistant; @property({ attribute: false }) hass!: HomeAssistant;
@ -76,8 +65,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
@state() private _stateHistory?: HistoryResult; @state() private _stateHistory?: HistoryResult;
@state() private _ranges?: DateRangePickerRanges;
@state() private _deviceEntityLookup?: DeviceEntityLookup; @state() private _deviceEntityLookup?: DeviceEntityLookup;
@state() private _areaEntityLookup?: AreaEntityLookup; @state() private _areaEntityLookup?: AreaEntityLookup;
@ -178,7 +165,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
?disabled=${this._isLoading} ?disabled=${this._isLoading}
.startDate=${this._startDate} .startDate=${this._startDate}
.endDate=${this._endDate} .endDate=${this._endDate}
.ranges=${this._ranges}
@change=${this._dateRangeChanged} @change=${this._dateRangeChanged}
></ha-date-range-picker> ></ha-date-range-picker>
<ha-target-picker <ha-target-picker
@ -220,24 +206,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
return; return;
} }
const today = new Date();
const weekStartsOn = firstWeekdayIndex(this.hass.locale);
const weekStart = startOfWeek(today, { weekStartsOn });
const weekEnd = endOfWeek(today, { weekStartsOn });
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)],
};
const searchParams = extractSearchParamsObject(); const searchParams = extractSearchParamsObject();
const entityIds = searchParams.entity_id; const entityIds = searchParams.entity_id;
const deviceIds = searchParams.device_id; const deviceIds = searchParams.device_id;

View File

@ -192,7 +192,11 @@ class HaLogbookRenderer extends LitElement {
new Date(previous.when * 1000).toDateString()) new Date(previous.when * 1000).toDateString())
? html` ? html`
<h4 class="date"> <h4 class="date">
${formatDate(new Date(item.when * 1000), this.hass.locale)} ${formatDate(
new Date(item.when * 1000),
this.hass.locale,
this.hass.config
)}
</h4> </h4>
` `
: nothing} : nothing}
@ -229,7 +233,8 @@ class HaLogbookRenderer extends LitElement {
<span <span
>${formatTimeWithSeconds( >${formatTimeWithSeconds(
new Date(item.when * 1000), new Date(item.when * 1000),
this.hass.locale this.hass.locale,
this.hass.config
)}</span )}</span
> >
- -

View File

@ -1,16 +1,6 @@
import { mdiRefresh } from "@mdi/js"; import { mdiRefresh } from "@mdi/js";
import {
addDays,
endOfToday,
endOfWeek,
endOfYesterday,
startOfToday,
startOfWeek,
startOfYesterday,
} from "date-fns/esm";
import { css, html, LitElement, PropertyValues } from "lit"; import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
import { navigate } from "../../common/navigate"; import { navigate } from "../../common/navigate";
import { constructUrlCurrentPath } from "../../common/url/construct-url"; import { constructUrlCurrentPath } from "../../common/url/construct-url";
import { import {
@ -20,7 +10,6 @@ import {
} from "../../common/url/search-params"; } from "../../common/url/search-params";
import "../../components/entity/ha-entity-picker"; import "../../components/entity/ha-entity-picker";
import "../../components/ha-date-range-picker"; import "../../components/ha-date-range-picker";
import type { DateRangePickerRanges } from "../../components/ha-date-range-picker";
import "../../components/ha-icon-button"; import "../../components/ha-icon-button";
import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-icon-button-arrow-prev";
import "../../components/ha-menu-button"; import "../../components/ha-menu-button";
@ -40,8 +29,6 @@ export class HaPanelLogbook extends LitElement {
@state() _entityIds?: string[]; @state() _entityIds?: string[];
@state() private _ranges?: DateRangePickerRanges;
@state() @state()
private _showBack?: boolean; private _showBack?: boolean;
@ -91,7 +78,6 @@ export class HaPanelLogbook extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.startDate=${this._time.range[0]} .startDate=${this._time.range[0]}
.endDate=${this._time.range[1]} .endDate=${this._time.range[1]}
.ranges=${this._ranges}
@change=${this._dateRangeChanged} @change=${this._dateRangeChanged}
></ha-date-range-picker> ></ha-date-range-picker>
@ -123,24 +109,6 @@ export class HaPanelLogbook extends LitElement {
return; return;
} }
const today = new Date();
const weekStartsOn = firstWeekdayIndex(this.hass.locale);
const weekStart = startOfWeek(today, { weekStartsOn });
const weekEnd = endOfWeek(today, { weekStartsOn });
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)],
};
this._applyURLParams(); this._applyURLParams();
} }

View File

@ -60,18 +60,27 @@ export class HuiEnergyCompareCard
<ha-alert dismissable @alert-dismissed-clicked=${this._stopCompare}> <ha-alert dismissable @alert-dismissed-clicked=${this._stopCompare}>
${this.hass.localize("ui.panel.energy.compare.info", { ${this.hass.localize("ui.panel.energy.compare.info", {
start: html`<b start: html`<b
>${formatDate(this._start!, this.hass.locale)}${dayDifference > 0 >${formatDate(
this._start!,
this.hass.locale,
this.hass.config
)}${dayDifference > 0
? ` - ? ` -
${formatDate(this._end || endOfDay(new Date()), this.hass.locale)}` ${formatDate(
this._end || endOfDay(new Date()),
this.hass.locale,
this.hass.config
)}`
: ""}</b : ""}</b
>`, >`,
end: html`<b end: html`<b
>${formatDate( >${formatDate(
this._startCompare, this._startCompare,
this.hass.locale this.hass.locale,
this.hass.config
)}${dayDifference > 0 )}${dayDifference > 0
? ` - ? ` -
${formatDate(this._endCompare, this.hass.locale)}` ${formatDate(this._endCompare, this.hass.locale, this.hass.config)}`
: ""}</b : ""}</b
>`, >`,
})} })}

View File

@ -12,7 +12,7 @@ import {
isToday, isToday,
startOfToday, startOfToday,
} from "date-fns"; } from "date-fns";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
@ -112,6 +112,7 @@ export class HuiEnergyGasGraphCard
this._start, this._start,
this._end, this._end,
this.hass.locale, this.hass.locale,
this.hass.config,
this._unit, this._unit,
this._compareStart, this._compareStart,
this._compareEnd this._compareEnd
@ -137,6 +138,7 @@ export class HuiEnergyGasGraphCard
start: Date, start: Date,
end: Date, end: Date,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
unit?: string, unit?: string,
compareStart?: Date, compareStart?: Date,
compareEnd?: Date compareEnd?: Date
@ -167,7 +169,8 @@ export class HuiEnergyGasGraphCard
suggestedMax: end.getTime(), suggestedMax: end.getTime(),
adapters: { adapters: {
date: { date: {
locale: locale, locale,
config,
}, },
}, },
ticks: { ticks: {
@ -221,10 +224,11 @@ export class HuiEnergyGasGraphCard
} }
const date = new Date(datasets[0].parsed.x); const date = new Date(datasets[0].parsed.x);
return `${ return `${
compare ? `${formatDateShort(date, locale)}: ` : "" compare ? `${formatDateShort(date, locale, config)}: ` : ""
}${formatTime(date, locale)} ${formatTime( }${formatTime(date, locale, config)} ${formatTime(
addHours(date, 1), addHours(date, 1),
locale locale,
config
)}`; )}`;
}, },
label: (context) => label: (context) =>

View File

@ -12,7 +12,7 @@ import {
isToday, isToday,
startOfToday, startOfToday,
} from "date-fns/esm"; } from "date-fns/esm";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
@ -111,6 +111,7 @@ export class HuiEnergySolarGraphCard
this._start, this._start,
this._end, this._end,
this.hass.locale, this.hass.locale,
this.hass.config,
this._compareStart, this._compareStart,
this._compareEnd this._compareEnd
)} )}
@ -135,6 +136,7 @@ export class HuiEnergySolarGraphCard
start: Date, start: Date,
end: Date, end: Date,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
compareStart?: Date, compareStart?: Date,
compareEnd?: Date compareEnd?: Date
): ChartOptions => { ): ChartOptions => {
@ -164,7 +166,8 @@ export class HuiEnergySolarGraphCard
suggestedMax: end.getTime(), suggestedMax: end.getTime(),
adapters: { adapters: {
date: { date: {
locale: locale, locale,
config,
}, },
}, },
ticks: { ticks: {
@ -217,10 +220,11 @@ export class HuiEnergySolarGraphCard
} }
const date = new Date(datasets[0].parsed.x); const date = new Date(datasets[0].parsed.x);
return `${ return `${
compare ? `${formatDateShort(date, locale)}: ` : "" compare ? `${formatDateShort(date, locale, config)}: ` : ""
}${formatTime(date, locale)} ${formatTime( }${formatTime(date, locale, config)} ${formatTime(
addHours(date, 1), addHours(date, 1),
locale locale,
config
)}`; )}`;
}, },
label: (context) => label: (context) =>

View File

@ -12,7 +12,7 @@ import {
isToday, isToday,
startOfToday, startOfToday,
} from "date-fns/esm"; } from "date-fns/esm";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
@ -105,6 +105,7 @@ export class HuiEnergyUsageGraphCard
this._start, this._start,
this._end, this._end,
this.hass.locale, this.hass.locale,
this.hass.config,
this._compareStart, this._compareStart,
this._compareEnd this._compareEnd
)} )}
@ -129,6 +130,7 @@ export class HuiEnergyUsageGraphCard
start: Date, start: Date,
end: Date, end: Date,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
compareStart?: Date, compareStart?: Date,
compareEnd?: Date compareEnd?: Date
): ChartOptions => { ): ChartOptions => {
@ -158,7 +160,8 @@ export class HuiEnergyUsageGraphCard
suggestedMax: end.getTime(), suggestedMax: end.getTime(),
adapters: { adapters: {
date: { date: {
locale: locale, locale,
config,
}, },
}, },
ticks: { ticks: {
@ -213,10 +216,11 @@ export class HuiEnergyUsageGraphCard
} }
const date = new Date(datasets[0].parsed.x); const date = new Date(datasets[0].parsed.x);
return `${ return `${
compare ? `${formatDateShort(date, locale)}: ` : "" compare ? `${formatDateShort(date, locale, config)}: ` : ""
}${formatTime(date, locale)} ${formatTime( }${formatTime(date, locale, config)} ${formatTime(
addHours(date, 1), addHours(date, 1),
locale locale,
config
)}`; )}`;
}, },
label: (context) => label: (context) =>

View File

@ -12,7 +12,7 @@ import {
isToday, isToday,
startOfToday, startOfToday,
} from "date-fns"; } from "date-fns";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
@ -112,6 +112,7 @@ export class HuiEnergyWaterGraphCard
this._start, this._start,
this._end, this._end,
this.hass.locale, this.hass.locale,
this.hass.config,
this._unit, this._unit,
this._compareStart, this._compareStart,
this._compareEnd this._compareEnd
@ -137,6 +138,7 @@ export class HuiEnergyWaterGraphCard
start: Date, start: Date,
end: Date, end: Date,
locale: FrontendLocaleData, locale: FrontendLocaleData,
config: HassConfig,
unit?: string, unit?: string,
compareStart?: Date, compareStart?: Date,
compareEnd?: Date compareEnd?: Date
@ -167,7 +169,8 @@ export class HuiEnergyWaterGraphCard
suggestedMax: end.getTime(), suggestedMax: end.getTime(),
adapters: { adapters: {
date: { date: {
locale: locale, locale,
config,
}, },
}, },
ticks: { ticks: {
@ -221,10 +224,11 @@ export class HuiEnergyWaterGraphCard
} }
const date = new Date(datasets[0].parsed.x); const date = new Date(datasets[0].parsed.x);
return `${ return `${
compare ? `${formatDateShort(date, locale)}: ` : "" compare ? `${formatDateShort(date, locale, config)}: ` : ""
}${formatTime(date, locale)} ${formatTime( }${formatTime(date, locale, config)} ${formatTime(
addHours(date, 1), addHours(date, 1),
locale locale,
config
)}`; )}`;
}, },
label: (context) => label: (context) =>

View File

@ -2,7 +2,11 @@ import { consume } from "@lit-labs/context";
import "@material/mwc-ripple"; import "@material/mwc-ripple";
import type { Ripple } from "@material/mwc-ripple"; import type { Ripple } from "@material/mwc-ripple";
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers"; import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
import { HassEntities, HassEntity } from "home-assistant-js-websocket"; import {
HassConfig,
HassEntities,
HassEntity,
} from "home-assistant-js-websocket";
import { import {
CSSResultGroup, CSSResultGroup,
LitElement, LitElement,
@ -27,6 +31,7 @@ import { iconColorCSS } from "../../../common/style/icon_color_css";
import "../../../components/ha-card"; import "../../../components/ha-card";
import { HVAC_ACTION_TO_MODE } from "../../../data/climate"; import { HVAC_ACTION_TO_MODE } from "../../../data/climate";
import { import {
configContext,
entitiesContext, entitiesContext,
localeContext, localeContext,
localizeContext, localizeContext,
@ -103,6 +108,10 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
@consume({ context: localeContext, subscribe: true }) @consume({ context: localeContext, subscribe: true })
_locale!: FrontendLocaleData; _locale!: FrontendLocaleData;
@state()
@consume({ context: configContext, subscribe: true })
_hassConfig!: HassConfig;
@consume<any>({ context: entitiesContext, subscribe: true }) @consume<any>({ context: entitiesContext, subscribe: true })
@transform<HomeAssistant["entities"], EntityRegistryDisplayEntry>({ @transform<HomeAssistant["entities"], EntityRegistryDisplayEntry>({
transformer: function (this: HuiButtonCard, value) { transformer: function (this: HuiButtonCard, value) {
@ -223,6 +232,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
this._localize, this._localize,
stateObj, stateObj,
this._locale, this._locale,
this._hassConfig,
this._entity this._entity
)} )}
</span>` </span>`

View File

@ -163,6 +163,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
this._config.attribute! this._config.attribute!
) )
@ -180,6 +181,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)}</span )}</span
>${showUnit >${showUnit

View File

@ -337,6 +337,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass!.locale, this.hass!.locale,
this.hass!.config,
this.hass!.entities this.hass!.entities
)} )}
</div> </div>

View File

@ -140,6 +140,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"mode" "mode"
)} )}

View File

@ -161,6 +161,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)} )}
</div> </div>

View File

@ -374,11 +374,19 @@ class HuiMapCard extends LitElement implements LovelaceCard {
if ((config.hours_to_show! ?? DEFAULT_HOURS_TO_SHOW) > 144) { if ((config.hours_to_show! ?? DEFAULT_HOURS_TO_SHOW) > 144) {
// if showing > 6 days in the history trail, show the full // if showing > 6 days in the history trail, show the full
// date and time // date and time
p.tooltip = formatDateTime(t, this.hass.locale); p.tooltip = formatDateTime(t, this.hass.locale, this.hass.config);
} else if (isToday(t)) { } else if (isToday(t)) {
p.tooltip = formatTimeWithSeconds(t, this.hass.locale); p.tooltip = formatTimeWithSeconds(
t,
this.hass.locale,
this.hass.config
);
} else { } else {
p.tooltip = formatTimeWeekday(t, this.hass.locale); p.tooltip = formatTimeWeekday(
t,
this.hass.locale,
this.hass.config
);
} }
points.push(p); points.push(p);
} }

View File

@ -123,6 +123,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
); );

View File

@ -257,6 +257,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass!.locale, this.hass!.locale,
this.hass!.config,
this.hass!.entities this.hass!.entities
)}`} )}`}
> >
@ -280,6 +281,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass!.locale, this.hass!.locale,
this.hass!.config,
this.hass!.entities this.hass!.entities
)} )}
</div> </div>

View File

@ -235,6 +235,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"hvac_action" "hvac_action"
) )
@ -242,6 +243,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
) )
} }
@ -254,6 +256,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"preset_mode" "preset_mode"
)} )}

View File

@ -228,6 +228,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass!.locale, this.hass!.locale,
this.hass!.config,
this.hass!.entities this.hass!.entities
); );

View File

@ -39,6 +39,7 @@ import { loadPolyfillIfNeeded } from "../../../resources/resize-observer.polyfil
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { LovelaceCard, LovelaceCardEditor } from "../types"; import type { LovelaceCard, LovelaceCardEditor } from "../types";
import type { WeatherForecastCardConfig } from "./types"; import type { WeatherForecastCardConfig } from "./types";
import { formatDateWeekdayShort } from "../../../common/datetime/format_date";
const DAY_IN_MILLISECONDS = 86400000; const DAY_IN_MILLISECONDS = 86400000;
@ -222,6 +223,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)} )}
</div> </div>
@ -319,13 +321,15 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
? html` ? html`
${formatTime( ${formatTime(
new Date(item.datetime), new Date(item.datetime),
this.hass!.locale this.hass!.locale,
this.hass!.config
)} )}
` `
: html` : html`
${new Date(item.datetime).toLocaleDateString( ${formatDateWeekdayShort(
this.hass!.language, new Date(item.datetime),
{ weekday: "short" } this.hass!.locale,
this.hass!.config
)} )}
`} `}
</div> </div>

View File

@ -21,6 +21,7 @@ import {
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { calcDate } from "../../../common/datetime/calc_date";
import { firstWeekdayIndex } from "../../../common/datetime/first_weekday"; import { firstWeekdayIndex } from "../../../common/datetime/first_weekday";
import { import {
formatDate, formatDate,
@ -105,17 +106,27 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
<div class="row"> <div class="row">
<div class="label"> <div class="label">
${this._period === "day" ${this._period === "day"
? formatDate(this._startDate, this.hass.locale) ? formatDate(this._startDate, this.hass.locale, this.hass.config)
: this._period === "month" : this._period === "month"
? formatDateMonthYear(this._startDate, this.hass.locale) ? formatDateMonthYear(
this._startDate,
this.hass.locale,
this.hass.config
)
: this._period === "year" : this._period === "year"
? formatDateYear(this._startDate, this.hass.locale) ? formatDateYear(
this._startDate,
this.hass.locale,
this.hass.config
)
: `${formatDateShort( : `${formatDateShort(
this._startDate, this._startDate,
this.hass.locale this.hass.locale,
this.hass.config
)} ${formatDateShort( )} ${formatDateShort(
this._endDate || new Date(), this._endDate || new Date(),
this.hass.locale this.hass.locale,
this.hass.config
)}`} )}`}
<ha-icon-button-prev <ha-icon-button-prev
.label=${this.hass.localize( .label=${this.hass.localize(
@ -186,12 +197,14 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
this._setDate( this._setDate(
this._period === "day" this._period === "day"
? startOfDay(start) ? calcDate(start, startOfDay, this.hass.locale, this.hass.config)
: this._period === "week" : this._period === "week"
? startOfWeek(start, { weekStartsOn }) ? calcDate(start, startOfWeek, this.hass.locale, this.hass.config, {
weekStartsOn,
})
: this._period === "month" : this._period === "month"
? startOfMonth(start) ? calcDate(start, startOfMonth, this.hass.locale, this.hass.config)
: startOfYear(start) : calcDate(start, startOfYear, this.hass.locale, this.hass.config)
); );
} }
@ -200,12 +213,20 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
this._setDate( this._setDate(
this._period === "day" this._period === "day"
? startOfToday() ? calcDate(new Date(), startOfDay, this.hass.locale, this.hass.config)
: this._period === "week" : this._period === "week"
? startOfWeek(new Date(), { weekStartsOn }) ? calcDate(
new Date(),
startOfWeek,
this.hass.locale,
this.hass.config,
{
weekStartsOn,
}
)
: this._period === "month" : this._period === "month"
? startOfMonth(new Date()) ? calcDate(new Date(), startOfMonth, this.hass.locale, this.hass.config)
: startOfYear(new Date()) : calcDate(new Date(), startOfYear, this.hass.locale, this.hass.config)
); );
} }
@ -238,12 +259,14 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
const endDate = const endDate =
this._period === "day" this._period === "day"
? endOfDay(startDate) ? calcDate(startDate, endOfDay, this.hass.locale, this.hass.config)
: this._period === "week" : this._period === "week"
? endOfWeek(startDate, { weekStartsOn }) ? calcDate(startDate, endOfWeek, this.hass.locale, this.hass.config, {
weekStartsOn,
})
: this._period === "month" : this._period === "month"
? endOfMonth(startDate) ? calcDate(startDate, endOfMonth, this.hass.locale, this.hass.config)
: endOfYear(startDate); : calcDate(startDate, endOfYear, this.hass.locale, this.hass.config);
const energyCollection = getEnergyDataCollection(this.hass, { const energyCollection = getEnergyDataCollection(this.hass, {
key: this.collectionKey, key: this.collectionKey,

View File

@ -1,3 +1,4 @@
import { HassConfig } from "home-assistant-js-websocket";
import { html, LitElement, PropertyValues, nothing } from "lit"; import { html, LitElement, PropertyValues, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { formatDate } from "../../../common/datetime/format_date"; import { formatDate } from "../../../common/datetime/format_date";
@ -10,7 +11,11 @@ import { HomeAssistant } from "../../../types";
import { TimestampRenderingFormat } from "./types"; import { TimestampRenderingFormat } from "./types";
const FORMATS: { const FORMATS: {
[key: string]: (ts: Date, lang: FrontendLocaleData) => string; [key: string]: (
ts: Date,
lang: FrontendLocaleData,
config: HassConfig
) => string;
} = { } = {
date: formatDate, date: formatDate,
datetime: formatDateTime, datetime: formatDateTime,
@ -63,7 +68,9 @@ class HuiTimestampDisplay extends LitElement {
return html` ${this._relative} `; return html` ${this._relative} `;
} }
if (format in FORMATS) { if (format in FORMATS) {
return html` ${FORMATS[format](this.ts, this.hass.locale)} `; return html`
${FORMATS[format](this.ts, this.hass.locale, this.hass.config)}
`;
} }
return html`${this.hass.localize( return html`${this.hass.localize(
"ui.panel.lovelace.components.timestamp-display.invalid_format" "ui.panel.lovelace.components.timestamp-display.invalid_format"

View File

@ -87,6 +87,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
) )
: stateObj.attributes[this._config.attribute]}${this._config.suffix} : stateObj.attributes[this._config.attribute]}${this._config.suffix}

View File

@ -70,6 +70,7 @@ class HuiGroupEntityRow extends LitElement implements LovelaceRow {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)} )}
</div> </div>

View File

@ -54,6 +54,7 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
"mode" "mode"
)})` )})`

View File

@ -100,6 +100,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
stateObj.state stateObj.state
)} )}

View File

@ -193,6 +193,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)} )}
> >

View File

@ -104,6 +104,7 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow {
this.hass.localize, this.hass.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities, this.hass.entities,
stateObj.state stateObj.state
)} )}

View File

@ -82,6 +82,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass!.locale, this.hass!.locale,
this.hass!.config,
this.hass!.entities, this.hass!.entities,
option option
)} )}

View File

@ -83,6 +83,7 @@ class HuiSensorEntityRow extends LitElement implements LovelaceRow {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)} )}
</div> </div>

View File

@ -53,6 +53,7 @@ class HuiSimpleEntityRow extends LitElement implements LovelaceRow {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass.locale, this.hass.locale,
this.hass.config,
this.hass.entities this.hass.entities
)} )}
</hui-generic-entity-row> </hui-generic-entity-row>

View File

@ -65,6 +65,7 @@ class HuiToggleEntityRow extends LitElement implements LovelaceRow {
this.hass!.localize, this.hass!.localize,
stateObj, stateObj,
this.hass!.locale, this.hass!.locale,
this.hass.config,
this.hass!.entities this.hass!.entities
)} )}
</div> </div>

Some files were not shown because too many files have changed in this diff Show More