Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle entities with coarser hours per segment better #56

Merged
merged 7 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/translate.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: 'Translate strings'

on:
pull_request:
branches:
push:
branches-ignore:
- main

jobs:
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ forecasts, it will show a warning. I've tested with the OpenWeatherMap integrati
that works very well.

> ℹ️ NOTE: If your selected weather entity provides forecasts in increments of greater than one hour at a time, each
> segment of the bar will be for one segment, not one hour.
> segment of the bar will be for one segment, not one hour. The `num_hours` option will still refer to how many hours
> to show, not how many segments. If you configure `num_hours` to not be a multiple of the hours per segment, the card
> will show a warning.

If you already use OpenWeatherMap for daily data, you can add a second integration of the same
component for hourly -- just adjust the latitude or longitude a tiny bit (i.e. change the last decimal by 1).
Expand Down
35 changes: 24 additions & 11 deletions src/hourly-weather.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} 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';

import type { ColorConfig, ColorMap, ColorSettings, ConditionSpan, ForecastHour, HourlyWeatherCardConfig, HourTemperature } from './types';
import type { ColorConfig, ColorMap, ColorSettings, ConditionSpan, ForecastSegment, HourlyWeatherCardConfig, HourTemperature } from './types';
import { actionHandler } from './action-handler-directive';
import { version } from '../package.json';
import { localize } from './localize/localize';
Expand Down Expand Up @@ -88,20 +88,23 @@ export class HourlyWeatherCard extends LitElement {
protected render(): TemplateResult | void {
const entityId: string = this.config.entity;
const state = this.hass.states[entityId];
const { forecast } = state.attributes as { forecast: ForecastHour[] };
const { forecast } = state.attributes as { forecast: ForecastSegment[] };
const numHours = this.config.num_hours ?? 12;

if (numHours > forecast.length) {
const hoursPerSegment = this.determineHoursPerSegment(forecast);

if (numHours > forecast.length * hoursPerSegment) {
return this._showError(localize('errors.too_many_hours_requested'));
}

const isForecastDaily = this.isForecastDaily(forecast);
const conditionList = this.getConditionListFromForecast(forecast, numHours);
const temperatures: HourTemperature[] = forecast.map(fh => ({
hour: this.formatHour(new Date(fh.datetime), this.hass.locale),
temperature: formatNumber(fh.temperature, this.hass.locale)
const conditionList = this.getConditionListFromForecast(forecast, numHours, hoursPerSegment);
const temperatures: HourTemperature[] = forecast.map(fs => ({
hour: this.formatHour(new Date(fs.datetime), this.hass.locale),
temperature: formatNumber(fs.temperature, this.hass.locale)
}));
temperatures.length = numHours;
temperatures.length = Math.floor(numHours / hoursPerSegment);
const numHoursNotMultiple = numHours % hoursPerSegment !== 0;

const colorSettings = this.getColorSettings(this.config.colors);

Expand All @@ -119,6 +122,9 @@ export class HourlyWeatherCard extends LitElement {
<div class="card-content">
${isForecastDaily ?
this._showWarning(localize('errors.daily_forecasts')) : ''}
${numHoursNotMultiple ?
this._showWarning(localize('errors.num_hours_not_multiple')
.replace(/\{hoursPerSegment\}/g, formatNumber(hoursPerSegment, this.hass.locale))) : ''}
${colorSettings.warnings.length ?
this._showWarning(localize('errors.invalid_colors') + colorSettings.warnings.join(', ')) : ''}
<weather-bar
Expand All @@ -131,11 +137,18 @@ export class HourlyWeatherCard extends LitElement {
`;
}

private getConditionListFromForecast(forecast: ForecastHour[], numHours = 12): ConditionSpan[] {
private determineHoursPerSegment(forecast: ForecastSegment[]): number {
if (forecast.length < 2) return 1;
const [fs1, fs2] = forecast;
const delta = new Date(fs2.datetime).getTime() - new Date(fs1.datetime).getTime();
return Math.round(delta / 1000 / 3600);
}

private getConditionListFromForecast(forecast: ForecastSegment[], numHours = 12, hoursPerSegment = 1): ConditionSpan[] {
let lastCond: string = forecast[0].condition;
let j = 0;
const res: ConditionSpan[] = [[lastCond, 1]];
for (let i = 1; i < numHours; i++) {
for (let i = 1; i * hoursPerSegment < numHours; i++) {
const cond: string = forecast[i].condition;
if (cond === lastCond) {
res[j][1]++;
Expand All @@ -148,7 +161,7 @@ export class HourlyWeatherCard extends LitElement {
return res;
}

private isForecastDaily(forecast: ForecastHour[]): boolean {
private isForecastDaily(forecast: ForecastSegment[]): boolean {
const dates = forecast.map(f => new Date(f.datetime).getDate());
const uniqueDates = new Set(dates);
return uniqueDates.size >= forecast.length - 1;
Expand Down
3 changes: 2 additions & 1 deletion src/localize/languages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"too_many_hours_requested": "Anzuzeigender Zeitraum kann nicht größer sein, als in der Wetter-Entität festgelegt.",
"daily_forecasts": "Die gewählte Wetter-Entität scheint tägliche Vorhersagen zu liefern. Bitte eine Wetter-Entität mit stündlicher Vorhersage wählen.",
"must_be_int": "Muss eine gerade ganze Zahl größer oder gleich 2 sein.",
"invalid_colors": "Die folgenden Farben in Ihrer Konfiguration sind ungültig:"
"invalid_colors": "Die folgenden Farben in Ihrer Konfiguration sind ungültig:",
"num_hours_not_multiple": "Die ausgewählte Wetterentität stellt Vorhersagen in {hoursPerSegment} Stundenintervallen bereit. Erwägen Sie, num_hours zu einem Vielfachen von {hoursPerSegment} zu machen."
},
"conditions": {
"clear": "Klar",
Expand Down
5 changes: 3 additions & 2 deletions src/localize/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"too_many_hours_requested": "Too many hours requested in num_hours. Must be <= number of hours in forecast entity.",
"daily_forecasts": "The selected weather entity seems to provide daily forecasts. Consider switching to an hourly entity.",
"must_be_int": "Must be an even integer greater than or equal to 2",
"invalid_colors": "The following colors in your configuration are invalid:"
"invalid_colors": "The following colors in your configuration are invalid:",
"num_hours_not_multiple": "The selected weather entity provides forecasts in {hoursPerSegment} hour intervals. Consider making num_hours a multiple of {hoursPerSegment}."
},
"conditions": {
"clear": "Clear",
Expand All @@ -33,4 +34,4 @@
"sunny": "Sunny",
"windy": "Windy"
}
}
}
5 changes: 3 additions & 2 deletions src/localize/languages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"too_many_hours_requested": "Demasiadas horas pedidas en num_hours. Debe ser <= que el número de horas en la entidad de pronóstico.",
"daily_forecasts": "La entidad seleccionada parece proveer pronósticos diarios. Considera cambiar a una entidad que provea pronósticos cada hora.",
"must_be_int": "Debe ser un entero mayor o igual a 2",
"invalid_colors": "Los siguientes colores en su configuración no son válidos:"
"invalid_colors": "Los siguientes colores en su configuración no son válidos:",
"num_hours_not_multiple": "La entidad meteorológica seleccionada proporciona pronósticos en intervalos de {hoursPerSegment} horas. Considere hacer que num_horas sea un múltiplo de {hoursPerSegment}."
},
"conditions": {
"clear": "Despejado",
Expand All @@ -33,4 +34,4 @@
"sunny": "Soleado",
"windy": "Ventoso"
}
}
}
3 changes: 2 additions & 1 deletion src/localize/languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"too_many_hours_requested": "Le nombre d'heure à afficher ne peut pas être supérieur à ce que fournit l'entité.",
"daily_forecasts": "L'entité sélectionnée semble fournir des prévisions quotidiennes. Envisagez de passer à une entité fournissant des prévisions par heure.",
"must_be_int": "Doit être un nombre entier pair supérieur ou égal à 2",
"invalid_colors": "Les couleurs suivantes dans votre configuration ne sont pas valides :"
"invalid_colors": "Les couleurs suivantes dans votre configuration ne sont pas valides :",
"num_hours_not_multiple": "L'entité météo sélectionnée fournit des prévisions à intervalles de {hoursPerSegment} heures. Envisagez de faire de num_hours un multiple de {hoursPerSegment}."
},
"conditions": {
"clear": "Dégagé",
Expand Down
3 changes: 2 additions & 1 deletion src/localize/languages/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"too_many_hours_requested": "For mange timer forespurt i num_hours. Må være <= antall timer i prognoseenheten.",
"daily_forecasts": "Den valgte værenheten ser ut til å gi daglige prognoser. Vurder å bytte til en timebasert enhet.",
"must_be_int": "Må være et jevnt heltall større enn eller lik 2",
"invalid_colors": "Følgende farger i konfigurasjonen din er ugyldige:"
"invalid_colors": "Følgende farger i konfigurasjonen din er ugyldige:",
"num_hours_not_multiple": "Den valgte værenheten gir prognoser i {hoursPerSegment} timers intervaller. Vurder å gjøre num_hours til et multiplum av {hoursPerSegment}."
},
"conditions": {
"clear": "Klar",
Expand Down
3 changes: 2 additions & 1 deletion src/localize/languages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"too_many_hours_requested": "Liczba godzin nie może być większa od ilości godzin dostępnych w encji pogodowej.",
"daily_forecasts": "Wybrana encja dostarcza pogodę dniową, wybierz encję z pogodą godzinową.",
"must_be_int": "Musi być parzystą liczbą całkowitą większą lub równą 2",
"invalid_colors": "Następujące kolory w Twojej konfiguracji są nieprawidłowe:"
"invalid_colors": "Następujące kolory w Twojej konfiguracji są nieprawidłowe:",
"num_hours_not_multiple": "Wybrana jednostka pogody zapewnia prognozy w {hoursPerSegment} interwałach godzinowych. Zastanów się, czy liczba_godzin jest wielokrotnością {hoursPerSegment}."
},
"conditions": {
"clear": "Bezchmurnie",
Expand Down
3 changes: 2 additions & 1 deletion src/localize/languages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"too_many_hours_requested": "Demasiadas horas solicitadas em num_hours. Deve ser menor que o número de horas disponíveis na entidade de meteo.",
"daily_forecasts": "A entidade meteorológica selecionada parece fornecer previsões diárias. Considere mudar para uma entidade horária.",
"must_be_int": "Deve ser um número inteiro par maior ou igual a 2",
"invalid_colors": "As seguintes cores em sua configuração são inválidas:"
"invalid_colors": "As seguintes cores em sua configuração são inválidas:",
"num_hours_not_multiple": "A entidade meteorológica selecionada fornece previsões em intervalos de {hoursPerSegment} horas. Considere tornar num_hours um múltiplo de {hoursPerSegment}."
},
"conditions": {
"clear": "Limpo",
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface ColorConfig {
'exceptional'?: string;
}

export interface ForecastHour {
export interface ForecastSegment {
clouds: number; // 100
condition: string; // "cloudy"
datetime: string; // "2022-06-03T22:00:00+00:00"
Expand Down