diff --git a/README.md b/README.md index 891ed3b2..b63520a0 100644 --- a/README.md +++ b/README.md @@ -109,28 +109,30 @@ The basic idea of the forecast bars is to be able to understand the weather tren hide_clock: false hide_date: false hourly_forecast: false + use_browser_time: true ``` ### Options -| Name | Type | Requirement | Description | Default | -|-----------------------|------------------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| -| type | string | **Required** | `custom:clock-weather-card` | | -| entity | string | **Required** | ID of the weather entity | | -| title | string | **Optional** | Title of the card | `''` | -| sun_entity | boolean | **Optional** | ID of the sun entity. Used to determine whether to show a day or night icon. If sun integration is not enabled, day icon will be shown | `sun.sun` | -| temperature_sensor | string | **Optional** | ID of the temperature sensor entity. Used to show the current temperature based on a sensor value instead of the weather forecast | `''` | -| weather_icon_type | `line` \| `fill` | **Optional** | Appearance of the large weather icon | `line` | -| animated_icon | boolean | **Optional** | Whether the large weather icon should be animated | `true` | -| forecast_days | number | **Optional** | Days of weather forecast to show | `5` | -| locale | string[^2] | **Optional** | Language to use for language specific text. If not provided, falls back to the locale set in HA | `en-GB` | -| time_format | `24` \| `12` | **Optional** | Format used to display the time. If not provided, falls back to the time format set in HA | `24` | -| date_pattern | string | **Optional** | Pattern to use for time formatting. If not provided, falls back to the default date formatting of the configured language. See [date-fns](https://date-fns.org/v2.29.3/docs/format) for valid patterns | `P` | -| hide_today_section | boolean | **Optional** | Hides the cards today section (upper section), containing the large weather icon, clock and current date | `false` | -| hide_forecast_section | boolean | **Optional** | Hides the cards forecast section (lower section),containing the weather forecast | `false` | -| hide_clock | boolean | **Optional** | Hides the clock from the today section and prominently displays the current temperature instead. | `false` | -| hide_date | boolean | **Optional** | Hides the date from the today section | `false` | -| hourly_forecast | boolean | **Optional** | Displays an hourly forecast instead of daily. | `false` | +| Name | Type | Requirement | Description | Default | +| --------------------- | ---------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| type | string | **Required** | `custom:clock-weather-card` | | +| entity | string | **Required** | ID of the weather entity | | +| title | string | **Optional** | Title of the card | `''` | +| sun_entity | boolean | **Optional** | ID of the sun entity. Used to determine whether to show a day or night icon. If sun integration is not enabled, day icon will be shown | `sun.sun` | +| temperature_sensor | string | **Optional** | ID of the temperature sensor entity. Used to show the current temperature based on a sensor value instead of the weather forecast | `''` | +| weather_icon_type | `line` \| `fill` | **Optional** | Appearance of the large weather icon | `line` | +| animated_icon | boolean | **Optional** | Whether the large weather icon should be animated | `true` | +| forecast_days | number | **Optional** | Days of weather forecast to show | `5` | +| locale | string[^2] | **Optional** | Language to use for language specific text. If not provided, falls back to the locale set in HA | `en-GB` | +| time_format | `24` \| `12` | **Optional** | Format used to display the time. If not provided, falls back to the time format set in HA | `24` | +| date_pattern | string | **Optional** | Pattern to use for time formatting. If not provided, falls back to the default date formatting of the configured language. See [date-fns](https://date-fns.org/v2.29.3/docs/format) for valid patterns | `P` | +| hide_today_section | boolean | **Optional** | Hides the cards today section (upper section), containing the large weather icon, clock and current date | `false` | +| hide_forecast_section | boolean | **Optional** | Hides the cards forecast section (lower section),containing the weather forecast | `false` | +| hide_clock | boolean | **Optional** | Hides the clock from the today section and prominently displays the current temperature instead | `false` | +| hide_date | boolean | **Optional** | Hides the date from the today section | `false` | +| hourly_forecast | boolean | **Optional** | Displays an hourly forecast instead of daily | `false` | +| use_browser_time | boolean | **Optional** | Uses the time from your browser to indicate the current time. If not provided, falls back to the [`time_zone`](https://www.home-assistant.io/blog/2015/05/09/utc-time-zone-awareness/#setting-up-your-time-zone) configured in HA | `true` | ## Footnotes diff --git a/package.json b/package.json index cb5164ee..f3fee2dc 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "custom-card-helpers": "^1.8.0", "date-fns": "^2.29.3", "home-assistant-js-websocket": "^8.0.0", - "lit": "^2.0.0" + "lit": "^2.0.0", + "luxon": "^3.3.0" }, "devDependencies": { "@babel/core": "^7.15.0", @@ -27,6 +28,7 @@ "@babel/plugin-proposal-decorators": "^7.14.5", "@rollup/plugin-image": "^2.1.1", "@rollup/plugin-json": "^5.0.0", + "@types/luxon": "^3.3.0", "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.33.0", "eslint": "^7.32.0", diff --git a/src/clock-weather-card.ts b/src/clock-weather-card.ts index 6d929951..b1dd2d54 100644 --- a/src/clock-weather-card.ts +++ b/src/clock-weather-card.ts @@ -30,6 +30,7 @@ import { version } from '../package.json'; import { safeRender } from './helpers'; import { format, Locale } from 'date-fns'; import * as locales from 'date-fns/locale'; +import { DateTime } from 'luxon'; console.info( `%c CLOCK-WEATHER-CARD \n%c Version: ${version}`, @@ -341,18 +342,19 @@ export class ClockWeatherCard extends LitElement { private mergeConfig(config: ClockWeatherCardConfig): MergedClockWeatherCardConfig { return { ...config, - sun_entity: config.sun_entity || 'sun.sun', + sun_entity: config.sun_entity ?? 'sun.sun', temperature_sensor: config.temperature_sensor, - weather_icon_type: config.weather_icon_type || 'line', - forecast_days: config.forecast_days || 5, - hourly_forecast: config.hourly_forecast || false, - animated_icon: config.animated_icon === undefined ? true : config.animated_icon, + weather_icon_type: config.weather_icon_type ?? 'line', + forecast_days: config.forecast_days ?? 5, + hourly_forecast: config.hourly_forecast ?? false, + animated_icon: config.animated_icon ?? true, time_format: config.time_format?.toString() as '12' | '24' | undefined, - hide_forecast_section: config.hide_forecast_section || false, - hide_today_section: config.hide_today_section || false, - hide_clock: config.hide_clock || false, - hide_date: config.hide_date || false, - date_pattern: config.date_pattern || 'P' + hide_forecast_section: config.hide_forecast_section ?? false, + hide_today_section: config.hide_today_section ?? false, + hide_clock: config.hide_clock ?? false, + hide_date: config.hide_date ?? false, + date_pattern: config.date_pattern ?? 'P', + use_browser_time: config.use_browser_time ?? true }; } @@ -413,13 +415,15 @@ export class ClockWeatherCard extends LitElement { } private date(): string { - const weekday = this.localize(`day.${this.currentDate.getDay()}`); - const date = format(this.currentDate, this.config.date_pattern, { locale: this.getDateFnsLocale() }); + const zonedDate = this.toZonedDate(this.currentDate); + const weekday = this.localize(`day.${zonedDate.getDay()}`); + const date = format(zonedDate, this.config.date_pattern, { locale: this.getDateFnsLocale() }); return`${weekday}, ${date}` } private time(date: Date = this.currentDate): string { - return format(date, this.getTimeFormat() === '24' ? 'HH:mm' : 'h:mm aa'); + const withTimeZone = this.toZonedDate(date); + return format(withTimeZone, this.getTimeFormat() === '24' ? 'HH:mm' : 'h:mm aa'); } private getIconAnimationKind(): 'static' | 'animated' { @@ -504,6 +508,17 @@ export class ClockWeatherCard extends LitElement { .slice(0, items); } + private toZonedDate(date: Date): Date { + if (this.config.use_browser_time) return date; + const timeZone = this.hass?.config?.time_zone + const withTimeZone = DateTime.fromJSDate(date).setZone(timeZone, { keepLocalTime: true }); + if (!withTimeZone.isValid) { + console.error(`clock-weather-card - Time Zone [${timeZone}] not supported. Falling back to browser time.`); + return date; + } + return withTimeZone.toJSDate(); + } + private calculateAverageForecast(forecasts: WeatherForecast[]): MergedWeatherForecast { const minTemps = forecasts.map((f) => f.templow ?? f.temperature ?? this.getCurrentTemperature() ?? 0); const minTemp = min(minTemps); diff --git a/src/types.ts b/src/types.ts index 46627471..48f0a666 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,7 @@ export interface ClockWeatherCardConfig extends LovelaceCardConfig { hourly_forecast?: boolean; hide_clock?: boolean; hide_date?: boolean; + use_browser_time?: boolean; } export interface MergedClockWeatherCardConfig extends LovelaceCardConfig { @@ -39,8 +40,9 @@ export interface MergedClockWeatherCardConfig extends LovelaceCardConfig { hide_today_section: boolean; hide_forecast_section: boolean; hourly_forecast: boolean; - hide_clock?: boolean; - hide_date?: boolean; + hide_clock: boolean; + hide_date: boolean; + use_browser_time: boolean; } export interface Weather extends HassEntity { diff --git a/yarn.lock b/yarn.lock index 0ae4b8cb..0b63a28d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -468,6 +468,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/luxon@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.0.tgz#a61043a62c0a72696c73a0a305c544c96501e006" + integrity sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg== + "@types/node@*": version "12.7.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44" @@ -1818,6 +1823,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +luxon@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48" + integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg== + magic-string@^0.25.2: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"