Skip to content

Commit

Permalink
Feature/display hourly forecast (#102)
Browse files Browse the repository at this point in the history
Co-authored-by: Jan-Philipp Giese <[email protected]>
Co-authored-by: jan-philippgieseext <[email protected]>
  • Loading branch information
3 people authored Mar 29, 2023
1 parent 2759748 commit 7b969d3
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 28 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ The basic idea of the forecast bars is to be able to understand the weather tren
hide_forecast_section: false
hide_clock: false
hide_date: false
hourly_forecast: false
```

### Options
Expand All @@ -126,9 +127,10 @@ The basic idea of the forecast bars is to be able to understand the weather tren
| 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_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` |

## Footnotes

Expand Down
58 changes: 32 additions & 26 deletions src/clock-weather-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,38 +183,42 @@ export class ClockWeatherCard extends LitElement {
private renderForecast(): TemplateResult[] {
const weather = this.getWeather();
const currentTemp = roundIfNotNull(this.getCurrentTemperature());
const days = this.config.forecast_days;
const temperatueUnit = weather.attributes.temperature_unit;
const items = this.config.forecast_days;
const hourly = this.config.hourly_forecast;
const temperatureUnit = weather.attributes.temperature_unit;

const dailyForecasts = this.extractDailyForecasts(weather.attributes.forecast, days);
const forecasts = this.extractForecasts(weather.attributes.forecast, items, hourly);

const minTemps = dailyForecasts.map((f) => f.templow);
const maxTemps = dailyForecasts.map((f) => f.temperature);
const minTemps = forecasts.map((f) => f.templow);
const maxTemps = forecasts.map((f) => f.temperature);
if (currentTemp !== null) {
minTemps.push(currentTemp);
maxTemps.push(currentTemp);
}
const minTemp = Math.round(min(minTemps));
const maxTemp = Math.round(max(maxTemps));

const gradientRange = this.gradientRange(minTemp, maxTemp, temperatueUnit);
return dailyForecasts.map((forecast) => safeRender(() => this.renderForecastDay(forecast, gradientRange, minTemp, maxTemp, currentTemp)));
const gradientRange = this.gradientRange(minTemp, maxTemp, temperatureUnit);
return forecasts.map((forecast) => safeRender(() => this.renderForecastItem(forecast, gradientRange, minTemp, maxTemp, currentTemp, hourly)));
}

private renderForecastDay(forecast: MergedWeatherForecast, gradientRange: Rgb[], minTemp: number, maxTemp: number, currentTemp: number | null): TemplateResult {
const dayText = this.localize(`day.${new Date(forecast.datetime).getDay()}`);
private renderForecastItem(forecast: MergedWeatherForecast, gradientRange: Rgb[], minTemp: number, maxTemp: number, currentTemp: number | null, hourly: boolean): TemplateResult {
const twelveHour = this.getTimeFormat() === '12';
const displayText = !hourly ? this.localize('day.' + forecast.datetime.getDay()) : this.time(forecast.datetime);
const weatherState = forecast.condition === 'pouring' ? 'raindrops' : forecast.condition === 'rainy' ? 'raindrop' : forecast.condition;
const weatherIcon = this.toIcon(weatherState, 'fill', true, 'static');
const tempUnit = this.getWeather().attributes.temperature_unit;
const isToday = new Date().getDate() === new Date(forecast.datetime).getDate();
const minTempDay = Math.round(isToday && currentTemp !== null ? Math.min(currentTemp, forecast.templow) : forecast.templow);
const maxTempDay = Math.round(isToday && currentTemp !== null ? Math.max(currentTemp, forecast.temperature) : forecast.temperature);
const isNow = !hourly ? new Date().getDate() === forecast.datetime.getDate() : new Date().getHours() === forecast.datetime.getHours();
const minTempDay = Math.round(isNow && currentTemp !== null ? Math.min(currentTemp, forecast.templow) : forecast.templow);
const maxTempDay = Math.round(isNow && currentTemp !== null ? Math.max(currentTemp, forecast.temperature) : forecast.temperature);
const colOneSize = hourly && twelveHour ? '3rem' : hourly ? '2.5rem' : '2rem';

return html`
<clock-weather-card-forecast-row>
${this.renderText(dayText)}
<clock-weather-card-forecast-row style="--col-one-size: ${colOneSize};">
${this.renderText(displayText)}
${this.renderIcon(weatherIcon)}
${this.renderText(this.toConfiguredTempWithUnit(tempUnit, minTempDay), 'right')}
${this.renderForecastTemperatureBar(gradientRange, minTemp, maxTemp, minTempDay, maxTempDay, isToday, currentTemp)}
${this.renderForecastTemperatureBar(gradientRange, minTemp, maxTemp, minTempDay, maxTempDay, isNow, currentTemp)}
${this.renderText(this.toConfiguredTempWithUnit(tempUnit, maxTempDay))}
</clock-weather-card-forecast-row>
`;
Expand All @@ -236,7 +240,7 @@ export class ClockWeatherCard extends LitElement {
`;
}

private renderForecastTemperatureBar(gradientRange: Rgb[], minTemp: number, maxTemp: number, minTempDay: number, maxTempDay: number, isToday: boolean, currentTemp: number | null): TemplateResult {
private renderForecastTemperatureBar(gradientRange: Rgb[], minTemp: number, maxTemp: number, minTempDay: number, maxTempDay: number, isNow: boolean, currentTemp: number | null): TemplateResult {
const { startPercent, endPercent } = this.calculateBarRangePercents(minTemp, maxTemp, minTempDay, maxTempDay)
return html`
<forecast-temperature-bar>
Expand All @@ -248,7 +252,7 @@ export class ClockWeatherCard extends LitElement {
endPercent,
)};"
>
${isToday ? this.renderForecastCurrentTemp(minTempDay, maxTempDay, currentTemp) : ''}
${isNow ? this.renderForecastCurrentTemp(minTempDay, maxTempDay, currentTemp) : ''}
</forecast-temperature-bar-range>
</forecast-temperature-bar>
`;
Expand Down Expand Up @@ -340,6 +344,7 @@ export class ClockWeatherCard extends LitElement {
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,
time_format: config.time_format?.toString() as '12' | '24' | undefined,
hide_forecast_section: config.hide_forecast_section || false,
Expand Down Expand Up @@ -412,8 +417,8 @@ export class ClockWeatherCard extends LitElement {
return`${weekday}, ${date}`
}

private time(): string {
return format(this.currentDate, this.getTimeFormat() === '24' ? 'HH:mm' : 'h:mm aa');
private time(date: Date = this.currentDate): string {
return format(date, this.getTimeFormat() === '24' ? 'HH:mm' : 'h:mm aa');
}

private getIconAnimationKind(): 'static' | 'animated' {
Expand Down Expand Up @@ -478,26 +483,27 @@ export class ClockWeatherCard extends LitElement {
return localize(key, this.getLocale());
}

private extractDailyForecasts(forecasts: WeatherForecast[], days: number): MergedWeatherForecast[] {
private extractForecasts(forecasts: WeatherForecast[], items: number, hourly: boolean): MergedWeatherForecast[] {
const agg = forecasts.reduce((forecasts, forecast) => {
const day = new Date(forecast.datetime).getDate();
forecasts[day] = forecasts[day] || [];
forecasts[day].push(forecast);
const d = new Date(forecast.datetime);
const unit = !hourly ? d.getDate() : d.getDate()+"_"+d.getHours();
forecasts[unit] = forecasts[unit] || [];
forecasts[unit].push(forecast);
return forecasts;
}, {} as Record<number, WeatherForecast[]>);

return Object.values(agg)
.reduce((agg: MergedWeatherForecast[], forecasts) => {
if (!forecasts.length) return agg;
const avg = this.calculateAverageDailyForecast(forecasts);
const avg = this.calculateAverageForecast(forecasts);
agg.push(avg);
return agg;
}, [])
.sort((a,b) => a.datetime.getTime() - b.datetime.getTime())
.slice(0, days);
.slice(0, items);
}

private calculateAverageDailyForecast(forecasts: WeatherForecast[]): MergedWeatherForecast {
private calculateAverageForecast(forecasts: WeatherForecast[]): MergedWeatherForecast {
const minTemps = forecasts.map((f) => f.templow ?? f.temperature ?? this.getCurrentTemperature() ?? 0);
const minTemp = min(minTemps);

Expand Down
2 changes: 1 addition & 1 deletion src/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default css`
clock-weather-card-forecast-row {
display: grid;
grid-template-columns: 2rem 2rem 2.1rem auto 2.1rem;
grid-template-columns: var(--col-one-size) 2rem 2.1rem auto 2.1rem;
align-items: center;
grid-gap: 0.5rem;
}
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface ClockWeatherCardConfig extends LovelaceCardConfig {
date_pattern?: string;
hide_today_section?: boolean;
hide_forecast_section?: boolean;
hourly_forecast?: boolean;
hide_clock?: boolean;
hide_date?: boolean;
}
Expand All @@ -37,6 +38,7 @@ export interface MergedClockWeatherCardConfig extends LovelaceCardConfig {
date_pattern: string;
hide_today_section: boolean;
hide_forecast_section: boolean;
hourly_forecast: boolean;
hide_clock?: boolean;
hide_date?: boolean;
}
Expand Down

0 comments on commit 7b969d3

Please sign in to comment.