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

Support for offsetting start of forecast bar #58

Merged
merged 6 commits into from
Jul 13, 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
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,18 @@ Otherwise, the integration may complain of a duplicate unique ID.

## Options

| Name | Type | Requirement | Description | Default |
| ----------------- | ------ | ------------ | -------------------------------------------- | ------------------- |
| type | string | **Required** | `custom:hourly-weather` | |
| entity | string | **Required** | Home Assistant weather entity ID. | |
| name | string | **Optional** | Card name | `Hourly Weather` |
| icons | bool | **Optional** | Whether to show icons instead of text labels | `false` |
| num_hours | number | **Optional** | Number of hours to show (even integer >= 2) | `12` |
| colors | object | **Optional** | Set colors for all or some conditions | |
| tap_action | object | **Optional** | Action to take on tap | `action: more-info` |
| hold_action | object | **Optional** | Action to take on hold | `none` |
| double_tap_action | object | **Optional** | Action to take on double tap | `none` |
| Name | Type | Requirement | Description | Default |
| ----------------- | ------ | ------------ | ------------------------------------------------ | ------------------- |
| type | string | **Required** | `custom:hourly-weather` | |
| entity | string | **Required** | Home Assistant weather entity ID. | |
| name | string | **Optional** | Card name | `Hourly Weather` |
| icons | bool | **Optional** | Whether to show icons instead of text labels | `false` |
| num_hours | number | **Optional** | Number of hours to show (even integer >= 2) | `12` |
| offset | number | **Optional** | Number of forecast segments to offset from start | `0` |
| colors | object | **Optional** | Set colors for all or some conditions | |
| tap_action | object | **Optional** | Action to take on tap | `action: more-info` |
| hold_action | object | **Optional** | Action to take on hold | `none` |
| double_tap_action | object | **Optional** | Action to take on double tap | `none` |

## Action Options

Expand Down
21 changes: 19 additions & 2 deletions src/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,18 @@ export class HourlyWeatherCardEditor extends ScopedRegistryHost(LitElement) impl
return this._config?.entity || '';
}

get _numHours(): number {
return this._config?.num_hours ?? 12;
get _numHours(): string {
return this._config?.num_hours ?? '12';
}

get _icons(): boolean {
return this._config?.icons ?? false;
}

get _offset(): string {
return this._config?.offset ?? '0';
}

protected render(): TemplateResult | void {
if (!this.hass || !this._helpers) {
return html``;
Expand Down Expand Up @@ -90,10 +94,23 @@ export class HourlyWeatherCardEditor extends ScopedRegistryHost(LitElement) impl
.value=${this._numHours}
.configValue=${'num_hours'}
@input=${this._valueChanged}
.type=${'number'}
.min=${2}
.step=${2}
.pattern=${"([1-9][0-9]*[02468])|([2468])"}
.autoValidate=${true}
validationMessage=${localize('errors.must_be_int')}
></mwc-textfield>
<mwc-textfield
label=${localize('editor.offset')}
.value=${this._offset}
.configValue=${'offset'}
@input=${this._valueChanged}
.type=${'number'}
.min=${0}
.autoValidate=${true}
validationMessage=${localize('errors.must_be_positive_int')}
></mwc-textfield>
<mwc-formfield .label=${localize('editor.icons')}>
<mwc-switch
.checked=${this._icons === true}
Expand Down
31 changes: 20 additions & 11 deletions src/hourly-weather.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,18 @@ export class HourlyWeatherCard extends LitElement {
const entityId: string = this.config.entity;
const state = this.hass.states[entityId];
const { forecast } = state.attributes as { forecast: ForecastSegment[] };
const numHours = this.config.num_hours ?? 12;
const numHours = parseInt(this.config.num_hours ?? '12', 10);
const offset = parseInt(this.config.offset ?? '0', 10);

const hoursPerSegment = this.determineHoursPerSegment(forecast);

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

const isForecastDaily = this.isForecastDaily(forecast);
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 = Math.floor(numHours / hoursPerSegment);
const conditionList = this.getConditionListFromForecast(forecast, numHours, hoursPerSegment, offset);
const temperatures = this.getTemperatures(forecast, numHours, hoursPerSegment, offset);
const numHoursNotMultiple = numHours % hoursPerSegment !== 0;

const colorSettings = this.getColorSettings(this.config.colors);
Expand Down Expand Up @@ -144,11 +141,11 @@ export class HourlyWeatherCard extends LitElement {
return Math.round(delta / 1000 / 3600);
}

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

private getTemperatures(forecast: ForecastSegment[], numHours: number, hoursPerSegment: number, offset: number): HourTemperature[] {
const temperatures: HourTemperature[] = [];
for (let i = offset; i < Math.floor(numHours / hoursPerSegment) + offset; i++) {
const fs = forecast[i];
temperatures.push({
hour: this.formatHour(new Date(fs.datetime), this.hass.locale),
temperature: formatNumber(fs.temperature, this.hass.locale)
})
}
return temperatures;
}

private isForecastDaily(forecast: ForecastSegment[]): boolean {
const dates = forecast.map(f => new Date(f.datetime).getDate());
const uniqueDates = new Set(dates);
Expand Down
8 changes: 5 additions & 3 deletions src/localize/languages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"entity": "Entität",
"name": "Bezeichnung (optional)",
"hours_to_show": "Anzahl der anzuzeigenden Stunden (optional)",
"icons": "Zeigen Sie Symbole anstelle von Textbeschriftungen an"
"icons": "Zeigen Sie Symbole anstelle von Textbeschriftungen an",
"offset": "Anzahl der Prognosesegmente zum Versetzen beginnen um (optional)"
},
"errors": {
"missing_entity": "Keine Wetter-Entität festgelegt",
"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:",
"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."
"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.",
"must_be_positive_int": "Muss eine positive Ganzzahl sein"
},
"conditions": {
"clear": "Klar",
Expand All @@ -34,4 +36,4 @@
"sunny": "Sonnig",
"windy": "Windig"
}
}
}
4 changes: 3 additions & 1 deletion src/localize/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"entity": "Entity (Required)",
"name": "Name (Optional)",
"hours_to_show": "Number of hours to show (Optional)",
"offset": "Number of forecast segments to offset start by (Optional)",
"icons": "Show icons instead of text labels"
},
"errors": {
Expand All @@ -18,7 +19,8 @@
"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:",
"num_hours_not_multiple": "The selected weather entity provides forecasts in {hoursPerSegment} hour intervals. Consider making num_hours a multiple of {hoursPerSegment}."
"num_hours_not_multiple": "The selected weather entity provides forecasts in {hoursPerSegment} hour intervals. Consider making num_hours a multiple of {hoursPerSegment}.",
"must_be_positive_int": "Must be a positive integer"
},
"conditions": {
"clear": "Clear",
Expand Down
8 changes: 5 additions & 3 deletions src/localize/languages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"entity": "Entidad (Requerida)",
"name": "Nombre (Opcional)",
"hours_to_show": "Número de horas a mostrar (Opcional)",
"icons": "Mostrar iconos en vez de texto"
"icons": "Mostrar iconos en vez de texto",
"offset": "Número de segmentos de pronóstico para compensar el inicio por (Opcional)"
},
"errors": {
"missing_entity": "falta la entidad en la configuración",
"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:",
"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}."
"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}.",
"must_be_positive_int": "Debe ser un entero positivo"
},
"conditions": {
"clear": "Despejado",
Expand All @@ -34,4 +36,4 @@
"sunny": "Soleado",
"windy": "Ventoso"
}
}
}
6 changes: 4 additions & 2 deletions src/localize/languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"entity": "Entité (requis)",
"name": "Nom (facultatif)",
"hours_to_show": "Nombre d'heures à afficher (facultatif)",
"icons": "Afficher des icônes à la place du texte"
"icons": "Afficher des icônes à la place du texte",
"offset": "Nombre de segments de prévision par lesquels décaler le début (facultatif)"
},
"errors": {
"missing_entity": "Entité manquante dans la configuration",
"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 :",
"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}."
"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}.",
"must_be_positive_int": "Doit être un entier positif"
},
"conditions": {
"clear": "Dégagé",
Expand Down
6 changes: 4 additions & 2 deletions src/localize/languages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"entity": "Entità (Richiesta)",
"name": "Nome (Facoltativo)",
"hours_to_show": "Numero delle ore da mostrare (Facoltativo)",
"icons": "Mostra le icone invece delle etichette di testo"
"icons": "Mostra le icone invece delle etichette di testo",
"offset": "Numero di segmenti di previsione di cui compensare l'inizio (Facoltativo)"
},
"errors": {
"missing_entity": "entità mancante nella configurazione",
"too_many_hours_requested": "Troppe ore richieste in num_hours. Deve essere <= al numero di ore nell'entità previsione.",
"daily_forecasts": "L'entità meteo selezionata sembra fornire previsioni giornaliere. Prendi in considerazione il passaggio a un'entità oraria.",
"must_be_int": "Deve essere un numero intero pari, maggiore o uguale a 2",
"invalid_colors": "I seguenti colori nella tua configurazione non sono validi:",
"num_hours_not_multiple": "L'entità meteo selezionata fornisce previsioni a intervalli in {hoursPerSegment}. Considera di rendere num_hours un multiplo di {hoursPerSegment}."
"num_hours_not_multiple": "L'entità meteo selezionata fornisce previsioni a intervalli in {hoursPerSegment}. Considera di rendere num_hours un multiplo di {hoursPerSegment}.",
"must_be_positive_int": "Deve essere un numero intero positivo"
},
"conditions": {
"clear": "Limpido",
Expand Down
6 changes: 4 additions & 2 deletions src/localize/languages/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"icons": "Vis ikoner i stedet for tekstetiketter",
"entity": "Entitet (obligatorisk)",
"name": "Navn (valgfritt)",
"hours_to_show": "Antall timer å vise (valgfritt)"
"hours_to_show": "Antall timer å vise (valgfritt)",
"offset": "Antall prognosesegmenter å utligne start med (valgfritt)"
},
"errors": {
"missing_entity": "entity mangler i konfigurasjonen",
"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:",
"num_hours_not_multiple": "Den valgte værenheten gir prognoser i {hoursPerSegment} timers intervaller. Vurder å gjøre num_hours til et multiplum av {hoursPerSegment}."
"num_hours_not_multiple": "Den valgte værenheten gir prognoser i {hoursPerSegment} timers intervaller. Vurder å gjøre num_hours til et multiplum av {hoursPerSegment}.",
"must_be_positive_int": "Må være et positivt heltall"
},
"conditions": {
"clear": "Klar",
Expand Down
6 changes: 4 additions & 2 deletions src/localize/languages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"entity": "Encja",
"name": "Nazwa (opcjonalnie)",
"hours_to_show": "Liczba godzin do wyświetlenia (opcjonalnie)",
"icons": "Pokaż ikony zamiast etykiet tekstowych"
"icons": "Pokaż ikony zamiast etykiet tekstowych",
"offset": "Liczba segmentów prognozy, o które należy skompensować początek (opcjonalnie)"
},
"errors": {
"missing_entity": "encja nie istnieje",
"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:",
"num_hours_not_multiple": "Wybrana jednostka pogody zapewnia prognozy w {hoursPerSegment} interwałach godzinowych. Zastanów się, czy liczba_godzin jest wielokrotnością {hoursPerSegment}."
"num_hours_not_multiple": "Wybrana jednostka pogody zapewnia prognozy w {hoursPerSegment} interwałach godzinowych. Zastanów się, czy liczba_godzin jest wielokrotnością {hoursPerSegment}.",
"must_be_positive_int": "Musi być dodatnią liczbą całkowitą"
},
"conditions": {
"clear": "Bezchmurnie",
Expand Down
6 changes: 4 additions & 2 deletions src/localize/languages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"entity": "Entidade",
"name": "Nome (opcional)",
"hours_to_show": "Número de horas para mostrar",
"icons": "Mostrar ícones em vez de rótulos de texto"
"icons": "Mostrar ícones em vez de rótulos de texto",
"offset": "Número de segmentos de previsão para começar a compensar (opcional)"
},
"errors": {
"missing_entity": "A entidade não existe na configuração",
"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:",
"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}."
"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}.",
"must_be_positive_int": "Deve ser um número inteiro positivo"
},
"conditions": {
"clear": "Limpo",
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ declare global {
export interface HourlyWeatherCardConfig extends LovelaceCardConfig {
type: string;
entity: string;
num_hours?: number;
num_hours?: string;
name?: string;
icons?: boolean;
offset?: string;
colors?: ColorConfig;
test_gui?: boolean;
tap_action?: ActionConfig;
Expand Down