diff --git a/README.md b/README.md index fd8ab9d4..1ec98a1d 100644 --- a/README.md +++ b/README.md @@ -74,11 +74,14 @@ Otherwise, the integration may complain of a duplicate unique ID. | `show_wind` | [string][wind] | **Optional** | Whether to show wind speed and/or direction under the bar | `'false'` | | `show_precipitation_amounts` | bool | **Optional** | Whether to show precipitation (rain) amount under the bar | `false` | | `show_precipitation_probability` | bool | **Optional** | Whether to show precipitation (rain) probability under the bar | `false` | +| `show_date` | [string][dates] | **Optional** | Whether to show date under the bar | `'false'` | | `tap_action` | [object][action] | **Optional** | Action to take on tap | `action: more-info` | | `hold_action` | [object][action] | **Optional** | Action to take on hold | `none` | | `double_tap_action` | [object][action] | **Optional** | Action to take on double tap | `none` | | `language` | string | **Optional** | Language to use for card (overrides HA & user settings) | | +> Note that some of the more advanced options are not available in the card editor UI and must be configured via YAML. + ### Templating The following options allow Home Assistant Jinja templates as values: @@ -185,6 +188,14 @@ colors: > Note: If your weather entity uses cardinal directions (e.g. 'N', 'SW', etc.) rather than numeric degrees for > `wind_bearing`, the `barb` option is not supported and will result in an error. +### Date Options + +`show_date` can be one of the following values: + +- `false` Don't show date (default) +- `boundary` Show date at the boundary between days +- `all` Always show date + ## Upgrades ### Version 3 ➡️ 4 @@ -203,4 +214,5 @@ clear that it operates on whatever size forecast segment your entity provides. T [color]: #color-options [wind]: #wind-options +[dates]: #date-options [action]: #action-options diff --git a/cypress/e2e/weather-bar.cy.ts b/cypress/e2e/weather-bar.cy.ts index a5eb7573..2bf3044b 100644 --- a/cypress/e2e/weather-bar.cy.ts +++ b/cypress/e2e/weather-bar.cy.ts @@ -243,6 +243,65 @@ describe('Weather bar', () => { }); }); + it('does not show date labels on axes by default', () => { + cy.get('weather-bar') + .shadow() + .find('div.axes > div.bar-block div.date:not(:empty)') + .should('have.length', 0); + }); + + const expectedDates = [ + 'Jul 21', + 'Jul 22', + 'Jul 22', + 'Jul 22', + 'Jul 22', + 'Jul 23' + ]; + + it('shows all date labels on axes when specified in config', () => { + cy.configure({ + show_date: 'all', + label_spacing: '6', + num_segments: '32' + }); + cy.get('weather-bar') + .shadow() + .find('div.axes > div.bar-block div.date:not(:empty)') + .should('have.length', 6) + .each((el, i) => { + cy.wrap(el).should('have.text', expectedDates[i]); + }); + }); + + const expectedBoundaryDates = [ + 'Jul 21', + 'Jul 22', + null, + null, + null, + 'Jul 23' + ]; + + it('shows date labels on day boundaries when specified in config', () => { + cy.configure({ + show_date: 'boundary', + label_spacing: '6', + num_segments: '32' + }); + cy.get('weather-bar') + .shadow() + .find('div.axes > div.bar-block div.date:not(:empty)') + .should('have.length', 6) + .each((el, i) => { + if (expectedBoundaryDates[i]) { + cy.wrap(el).should('have.text', expectedBoundaryDates[i]); + } else { + cy.wrap(el).should('contain.html', ' '); + } + }); + }); + const expectedTemperatures = [ 85, 84, diff --git a/src/hourly-weather.ts b/src/hourly-weather.ts index f204c7e0..9e184d47 100644 --- a/src/hourly-weather.ts +++ b/src/hourly-weather.ts @@ -13,6 +13,7 @@ import { formatNumber, formatTime, FrontendLocaleData, + formatDateShort, } from 'custom-card-helpers'; // This is a community maintained npm module with common helper functions/types. https://github.com/custom-cards/custom-card-helpers import { isValidColorName, isValidHSL, isValidRGB } from 'is-valid-css-color'; @@ -317,6 +318,7 @@ export class HourlyWeatherCard extends LitElement { .show_wind=${config.show_wind} .show_precipitation_amounts=${!!config.show_precipitation_amounts} .show_precipitation_probability=${!!config.show_precipitation_probability} + .show_date=${config.show_date} .label_spacing=${labelSpacing} .labels=${this.labels}> @@ -345,8 +347,10 @@ export class HourlyWeatherCard extends LitElement { const temperatures: SegmentTemperature[] = []; for (let i = offset; i < numSegments + offset; i++) { const fs = forecast[i]; + const dt = new Date(fs.datetime) temperatures.push({ - hour: this.formatHour(new Date(fs.datetime), this.hass.locale), + date: formatDateShort(dt, this.hass.locale), + hour: this.formatHour(dt, this.hass.locale), temperature: formatNumber(fs.temperature, this.hass.locale) }) } diff --git a/src/types.ts b/src/types.ts index 8b07ef49..c5c88d47 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,6 +8,7 @@ declare global { } export type WindType = 'true' | 'false' | 'speed' | 'direction' | 'barb'; +export type ShowDateType = 'false' | 'boundary' | 'all'; export interface HourlyWeatherCardConfig extends LovelaceCardConfig { type: string; @@ -24,6 +25,7 @@ export interface HourlyWeatherCardConfig extends LovelaceCardConfig { show_wind?: WindType; // 'true' | 'false' | 'speed' | 'direction' | 'barb' show_precipitation_amounts?: boolean; show_precipitation_probability?: boolean; + show_date?: ShowDateType; // 'false' | 'boundary' | 'all' label_spacing?: string; // number test_gui?: boolean; tap_action?: ActionConfig; @@ -76,6 +78,7 @@ export type ConditionSpan = [ export interface SegmentTemperature { hour: string, + date: string, temperature: string } diff --git a/src/weather-bar.ts b/src/weather-bar.ts index 3c1dbba1..8279343c 100644 --- a/src/weather-bar.ts +++ b/src/weather-bar.ts @@ -4,7 +4,7 @@ import { StyleInfo, styleMap } from 'lit/directives/style-map.js'; import tippy, { Instance } from 'tippy.js'; import { LABELS, ICONS } from "./conditions"; import { getWindBarbSVG } from "./lib/svg-wind-barbs"; -import type { ColorMap, ConditionSpan, SegmentTemperature, SegmentWind, SegmentPrecipitation, WindType } from "./types"; +import type { ColorMap, ConditionSpan, SegmentTemperature, SegmentWind, SegmentPrecipitation, WindType, ShowDateType } from "./types"; const tippyStyles: string = process.env.TIPPY_CSS || ''; @@ -42,6 +42,9 @@ export class WeatherBar extends LitElement { @property({ type: Boolean }) show_precipitation_probability = false; + @property({ type: Boolean }) + show_date: ShowDateType = 'false'; + @property({ type: Number }) label_spacing = 2; @@ -69,6 +72,7 @@ export class WeatherBar extends LitElement { } const barBlocks: TemplateResult[] = []; + let lastDate: string | null = null; for (let i = 1; i < this.temperatures.length; i += 2) { const skipLabel = (i - 1) % this.label_spacing !== 0; const hideHours = this.hide_hours || skipLabel; @@ -78,7 +82,19 @@ export class WeatherBar extends LitElement { const showWindBarb = this.show_wind === 'barb' && !skipLabel; const showPrecipitationAmounts = this.show_precipitation_amounts && !skipLabel; const showPrecipitationProbability = this.show_precipitation_probability && !skipLabel; - const { hour, temperature } = this.temperatures[i]; + const { hour, date, temperature } = this.temperatures[i]; + let renderedDate: string | TemplateResult | null = null; + if (!skipLabel && this.show_date && this.show_date !== 'false') { + if (this.show_date === 'all') renderedDate = date; + else if (this.show_date === 'boundary') { + if (lastDate !== date) { + renderedDate = date; + lastDate = date; + } else { + renderedDate = html` `; + } + } + } const { windSpeed, windSpeedRawMS, windDirection, windDirectionRaw } = this.wind[i]; const wind: TemplateResult[] = []; @@ -103,6 +119,7 @@ export class WeatherBar extends LitElement {