From 21df65dc1cd5382a5aabd936227c1a38b796eada Mon Sep 17 00:00:00 2001 From: Jonathan Keslin Date: Mon, 5 Aug 2024 13:22:17 -0700 Subject: [PATCH] Adds an option to fill the forecast segment with icons (translations for #715) (#726) * Adds an option to fill the forecast segment with icons (#715) * Adds an option to fill the forecast segment with icons * Fix typos * Improved error detection and error message * undefined icon_fill behaves like icon_fill:single * Move icon_fill config check to renderCore * Tests for icon_fill errors * Add tests for new behaviour * [Auto] Adding updated localization files Files changed: M src/localize/languages/cs.json M src/localize/languages/da.json M src/localize/languages/de.json M src/localize/languages/es.json M src/localize/languages/fr.json M src/localize/languages/hu.json M src/localize/languages/it.json M src/localize/languages/nb.json M src/localize/languages/nl.json M src/localize/languages/nn-NO.json M src/localize/languages/pl.json M src/localize/languages/pt-BR.json M src/localize/languages/pt.json M src/localize/languages/ru.json M src/localize/languages/sk.json M src/localize/languages/zh.json * Massage the auto-translation a bit * [Auto] Adding updated localization files Files changed: M src/localize/languages/pt-BR.json * Add note to README about hidden icons on narrow segments * Update readme table * Tiny edit on readme --------- Co-authored-by: Sergio Cinos --- README.md | 62 +++++++----- cypress/e2e/config.cy.ts | 19 ++++ cypress/e2e/weather-bar.cy.ts | 80 ++++++++++++++++ src/hourly-weather.ts | 12 +++ src/localize/languages/cs.json | 152 +++++++++++++++--------------- src/localize/languages/da.json | 5 +- src/localize/languages/de.json | 3 +- src/localize/languages/en.json | 3 +- src/localize/languages/es.json | 5 +- src/localize/languages/fr.json | 5 +- src/localize/languages/hu.json | 5 +- src/localize/languages/it.json | 5 +- src/localize/languages/nb.json | 3 +- src/localize/languages/nl.json | 5 +- src/localize/languages/nn-NO.json | 3 +- src/localize/languages/pl.json | 5 +- src/localize/languages/pt-BR.json | 3 +- src/localize/languages/pt.json | 5 +- src/localize/languages/ru.json | 3 +- src/localize/languages/sk.json | 5 +- src/localize/languages/zh.json | 5 +- src/types.ts | 2 + src/weather-bar.ts | 29 +++++- 23 files changed, 294 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index f669da1b..0df1e817 100644 --- a/README.md +++ b/README.md @@ -61,30 +61,31 @@ decimal by 1). 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. | | -| `forecast_type` | string | **Optional** | The type of forecast data to use. One of `hourly`, `daily`, or `twice-daily`. If not specified, the card will attempt to use the finest-grained data available. | | -| `name` | string | **Optional** | Card name (set to `null` to hide) | `Hourly Weather` | -| `icons` | bool | **Optional** | Whether to show icons instead of text labels | `false` | -| `num_segments` | number | **Optional** | Number of forecast segments to show (integer >= 1) | `12` | -| ~~`num_hours`~~ | number | **Optional** | _Deprecated:_ Use `num_segments` instead | `12` | -| `offset` | number | **Optional** | Number of forecast segments to offset from start | `0` | -| `label_spacing` | number | **Optional** | Space between time/temperature labels (integer >= 1) | `2` | -| `colors` | [object][color] | **Optional** | Set colors for all or some conditions | | -| `hide_hours` | bool | **Optional** | Whether to hide hour labels under the bar | `false` | -| `hide_temperatures` | bool | **Optional** | Whether to hide temperatures under the bar | `false` | -| `round_temperatures` | bool | **Optional** | Whether to round temperatures to the nearest whole number | `false` | -| `hide_bar` | bool | **Optional** | Whether to hide the bar itself | `false` | -| `show_wind` | [Wind][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) | | +| Name | Type | Requirement | Description | Default | +|----------------------------------|------------------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------| +| `type` | string | **Required** | `custom:hourly-weather` | | +| `entity` | string | **Required** | Home Assistant weather entity ID. | | +| `forecast_type` | string | **Optional** | The type of forecast data to use. One of `hourly`, `daily`, or `twice-daily`. If not specified, the card will attempt to use the finest-grained data available. | | +| `name` | string | **Optional** | Card name (set to `null` to hide) | `Hourly Weather` | +| `icons` | bool | **Optional** | Whether to show icons instead of text labels | `false` | +| `num_segments` | number | **Optional** | Number of forecast segments to show (integer >= 1) | `12` | +| ~~`num_hours`~~ | number | **Optional** | _Deprecated:_ Use `num_segments` instead | `12` | +| `offset` | number | **Optional** | Number of forecast segments to offset from start | `0` | +| `label_spacing` | number | **Optional** | Space between time/temperature labels (integer >= 1) | `2` | +| `colors` | [object][color] | **Optional** | Set colors for all or some conditions | | +| `hide_hours` | bool | **Optional** | Whether to hide hour labels under the bar | `false` | +| `hide_temperatures` | bool | **Optional** | Whether to hide temperatures under the bar | `false` | +| `round_temperatures` | bool | **Optional** | Whether to round temperatures to the nearest whole number | `false` | +| `hide_bar` | bool | **Optional** | Whether to hide the bar itself | `false` | +| `icon_fill` | [Icon Fill][icon_fill] | **Optional** | Whether to repeat the icon inside the bar | `'single` | +| `show_wind` | [Wind][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. @@ -205,6 +206,18 @@ colors: - `boundary` Show date at the boundary between days - `all` Always show date +### Icon Fill Options + +`icon_fill` can be one of the following values: + +- `single` Show one icon per forecast span (default) +- `full` Show one icon per forecast segment +- `` (an integer). Show icons every _n-th_ forecast segment (will show at least one icon per forecast span) + +> **NOTE:** If a forecast segment is not wide enough to fit an icon, the icon will not be shown. That means that on +> narrow screens or with many segments, you may not see all icons. If that happens, consider using a larger numeric +> value for `icon_fill` or use `single` to show one icon per forecast span. + ## Upgrades ### Version 3 ➡️ 4 @@ -231,5 +244,6 @@ structure. [color]: #color-options [wind]: #wind-options +[icon_fill]: #icon-fill-options [dates]: #date-options [action]: #action-options diff --git a/cypress/e2e/config.cy.ts b/cypress/e2e/config.cy.ts index 7bc3a574..5802b27f 100644 --- a/cypress/e2e/config.cy.ts +++ b/cypress/e2e/config.cy.ts @@ -284,6 +284,25 @@ describe('Config', () => { .and('contain', 'exceptional: {\n "foreground": "#12345678"\n}') .and('contain', 'hail: {\n "background": "foo(240, 100%, 50%)",\n "foreground": "rgb(0, 255, 0, 0)"\n}'); }); + it('errors for invalid string values for icon_fill', () => { + cy.configure({ + //@ts-expect-error This is testing invalid config + icon_fill: 'all' //valid values are 'single', 'full' or integer + }); + cy.get('hui-error-card') + .shadow() + .find('p') + .should('have.text', "icon_fill must be either a positive integer or one of 'single' or 'full'"); + }); + it('errors for invalid integer values for icon_fill', () => { + cy.configure({ + icon_fill: -1 + }); + cy.get('hui-error-card') + .shadow() + .find('p') + .should('have.text', "icon_fill must be either a positive integer or one of 'single' or 'full'"); + }); describe('Templates', () => { it('supports templated name', () => { cy.configure({ diff --git a/cypress/e2e/weather-bar.cy.ts b/cypress/e2e/weather-bar.cy.ts index 3909d2c9..16ec67be 100644 --- a/cypress/e2e/weather-bar.cy.ts +++ b/cypress/e2e/weather-bar.cy.ts @@ -213,6 +213,86 @@ describe('Weather bar', () => { }); }); }); + describe('Icon fill', () => { + function verifyIcons (cy, expectedIcons) { + cy.get('weather-bar') + .shadow() + .find('div.bar > div > span.condition-icon') + .should('have.length', expectedIcons.length) + .find('ha-icon') + .each((el, i) => { + cy.wrap(el).invoke('attr', 'icon') + .should('exist') + .and('eq', expectedIcons[i]); + }); + } + + it('fills the blocks with icons', () => { + const expectedIcons = [ + 'mdi:weather-cloudy', + 'mdi:weather-cloudy', + 'mdi:weather-cloudy', + 'mdi:weather-partly-cloudy', + 'mdi:weather-partly-cloudy', + 'mdi:weather-partly-cloudy', + 'mdi:weather-sunny', + 'mdi:weather-sunny', + 'mdi:weather-sunny', + 'mdi:weather-sunny', + 'mdi:weather-sunny', + 'mdi:weather-night' + ]; + cy.configure({ + icons: true, + icon_fill: 'full' + }); + verifyIcons(cy, expectedIcons); + }); + it('a single icon for each block', () => { + const expectedIcons = [ + 'mdi:weather-cloudy', + 'mdi:weather-partly-cloudy', + 'mdi:weather-sunny', + 'mdi:weather-night' + ]; + cy.configure({ + icons: true, + icon_fill: 'single' + }); + verifyIcons(cy, expectedIcons); + }); + it('spaces icons', () => { + const expectedIcons = [ + 'mdi:weather-cloudy', + 'mdi:weather-cloudy', + 'mdi:weather-partly-cloudy', + 'mdi:weather-partly-cloudy', + 'mdi:weather-sunny', + 'mdi:weather-sunny', + 'mdi:weather-sunny', + 'mdi:weather-night' + ]; + cy.configure({ + icons: true, + icon_fill: 2 + }); + verifyIcons(cy, expectedIcons); + }); + it('shows at least one icon per block', () => { + const expectedIcons = [ + 'mdi:weather-cloudy', + 'mdi:weather-partly-cloudy', + 'mdi:weather-sunny', + 'mdi:weather-sunny', + 'mdi:weather-night' + ]; + cy.configure({ + icons: true, + icon_fill: 4 + }); + verifyIcons(cy, expectedIcons); + }) + }); describe('Axes', () => { it('shows the correct number of axes', () => { cy.get('weather-bar') diff --git a/src/hourly-weather.ts b/src/hourly-weather.ts index a8a1e04d..288472c0 100644 --- a/src/hourly-weather.ts +++ b/src/hourly-weather.ts @@ -320,6 +320,7 @@ export class HourlyWeatherCard extends LitElement { const offset = parseInt(config.offset ?? '0', 10); const labelSpacing = parseInt(config.label_spacing ?? '2', 10); const forecastNotAvailable = !forecast || !forecast.length; + const icon_fill = config.icon_fill; if (numSegments < 1) { // REMARK: Ok, so I'm re-using a localized string here. Probably not the best, but it avoids repeating for no good reason @@ -340,6 +341,16 @@ export class HourlyWeatherCard extends LitElement { return await this._showError(this.localize('errors.offset_must_be_positive_int', 'offset', 'label_spacing')); } + if (icon_fill) { + const isFull = config.icon_fill === 'full'; + const isSingle = config.icon_fill === 'single'; + const valueAsNumber = Number(config.icon_fill); //Undefined and non-numerical strings will be converted NaN, but null is 0 + const isPositiveInteger = Number.isInteger(valueAsNumber) && valueAsNumber > 0; + if (!isFull && !isSingle && !isPositiveInteger) { + return await this._showError(this.localize('errors.invalid_value_icon_fill')); + } + } + let showWind = config.show_wind; if (typeof showWind === 'boolean') { showWind = showWind ? 'true' : 'false'; @@ -401,6 +412,7 @@ export class HourlyWeatherCard extends LitElement { .hide_temperatures=${!!config.hide_temperatures} .round_temperatures=${!!config.round_temperatures} .hide_bar=${!!config.hide_bar} + .icon_fill=${config.icon_fill} .show_wind=${showWind} .show_precipitation_amounts=${!!config.show_precipitation_amounts} .show_precipitation_probability=${!!config.show_precipitation_probability} diff --git a/src/localize/languages/cs.json b/src/localize/languages/cs.json index 63b3191d..dfedce5c 100644 --- a/src/localize/languages/cs.json +++ b/src/localize/languages/cs.json @@ -1,78 +1,78 @@ { - "common": { - "version": "Verze", - "title": "Hodinnová předpověď", - "title_card": "Karta Hodinnová předpověď", - "description": "Karta zobrazující hodinovou předpověď v řádku.", - "invalid_configuration": "Neplatná konfigurace" - }, - "editor": { - "entity": "Entita (Povinné)", - "name": "Název (Nepovinné)", - "segments_to_show": "Počet dílků předpovědi k vykreslení (Nepovinné)", - "offset": "Počet dílků, které se přeskočí před začátkem (Nepovinné)", - "icons": "Zobrazit ikony namísto textových popisků", - "label_spacing": "Po kolika dílcích se vykreslí čas a teplota (Nepovinné)", - "show_wind": "Zobrazit rychlost a směr větru", - "show_date": "Zobrazit datumy", - "show_precipitation_amounts": "Zobrazit množství srážek", - "show_precipitation_probability": "Zobrazit pravdědpodobnost srážek", - "none": "Žádné", - "speed_and_direction": "Rychlost a směr", - "speed_only": "Jen rychlost", - "direction_only": "Jen směr", - "barb": "Jako šipku větru", - "barb_and_speed": "Jako šipku větru a rychlost", - "barb_and_direction": "Jako šipku větru a směr", - "barb_speed_and_direction": "Jako šipku větru, rychlost a směr", - "all": "Všechny", - "on_day_boundaries": "Při změně dne" - }, - "errors": { - "missing_entity": "Hodnota 'entity' nebyla zadána", - "too_many_segments_requested": "Je nastaveno příliš mnoho dílků předpovědi v 'num_segments'. Hodnota musí být <= počtu hodnot v zadané entitě.", - "must_be_int": "Hodnota musí být kladné sudé celé číslo", - "invalid_colors": "Následující hodnoty v konfiguraci jsou neplatné:", - "must_be_positive_int": "Hodnota musí být kladné celé číslo", - "offset_must_be_positive_int": "Hodnota 'offset' musí být kladné celé číslo", - "forecast_not_available": "Předpověď není dostupná", - "check_entity": "Zkontrolujte zadanou entity předpovědi.", - "no_wind_barbs_with_string_bearing": "Šipky větru nejsou podporovány, pokud entita počasí používá světové směry pro směr větru." - }, - "conditions": { - "clear": "Jasno", - "cloudy": "Oblačno", - "fog": "Mlha", - "hail": "Kroupy", - "thunderstorm": "Bouřka", - "partlyCloudy": "Polojasno", - "heavyRain": "Silný déšť", - "rain": "Déšť", - "snow": "Sníh", - "mixedPrecip": "Smíšené srážky", - "sunny": "Slunečno", - "windy": "Větrno" - }, - "direction": { - "n": "S", - "nne": "SSV", - "ne": "SV", - "ene": "VSV", - "e": "V", - "ese": "VJV", - "se": "JV", - "sse": "JJV", - "s": "J", - "ssw": "JJZ", - "sw": "JZ", - "wsw": "ZJZ", - "w": "Z", - "wnw": "ZSZ", - "nw": "SZ", - "nnw": "SSZ" - }, - "card": { - "chance_of_precipitation": "{0}% šance srážek" - } + "common": { + "version": "Verze", + "title": "Hodinnová předpověď", + "title_card": "Karta Hodinnová předpověď", + "description": "Karta zobrazující hodinovou předpověď v řádku.", + "invalid_configuration": "Neplatná konfigurace" + }, + "editor": { + "entity": "Entita (Povinné)", + "name": "Název (Nepovinné)", + "segments_to_show": "Počet dílků předpovědi k vykreslení (Nepovinné)", + "offset": "Počet dílků, které se přeskočí před začátkem (Nepovinné)", + "icons": "Zobrazit ikony namísto textových popisků", + "label_spacing": "Po kolika dílcích se vykreslí čas a teplota (Nepovinné)", + "show_wind": "Zobrazit rychlost a směr větru", + "show_date": "Zobrazit datumy", + "show_precipitation_amounts": "Zobrazit množství srážek", + "show_precipitation_probability": "Zobrazit pravdědpodobnost srážek", + "none": "Žádné", + "speed_and_direction": "Rychlost a směr", + "speed_only": "Jen rychlost", + "direction_only": "Jen směr", + "barb": "Jako šipku větru", + "barb_and_speed": "Jako šipku větru a rychlost", + "barb_and_direction": "Jako šipku větru a směr", + "barb_speed_and_direction": "Jako šipku větru, rychlost a směr", + "all": "Všechny", + "on_day_boundaries": "Při změně dne" + }, + "errors": { + "missing_entity": "Hodnota 'entity' nebyla zadána", + "too_many_segments_requested": "Je nastaveno příliš mnoho dílků předpovědi v 'num_segments'. Hodnota musí být <= počtu hodnot v zadané entitě.", + "must_be_int": "Hodnota musí být kladné sudé celé číslo", + "invalid_colors": "Následující hodnoty v konfiguraci jsou neplatné:", + "must_be_positive_int": "Hodnota musí být kladné celé číslo", + "offset_must_be_positive_int": "Hodnota 'offset' musí být kladné celé číslo", + "forecast_not_available": "Předpověď není dostupná", + "check_entity": "Zkontrolujte zadanou entity předpovědi.", + "no_wind_barbs_with_string_bearing": "Šipky větru nejsou podporovány, pokud entita počasí používá světové směry pro směr větru.", + "invalid_value_icon_fill": "icon_fill musí být buď kladné celé číslo, nebo jedno z 'single'; nebo 'full';" + }, + "conditions": { + "clear": "Jasno", + "cloudy": "Oblačno", + "fog": "Mlha", + "hail": "Kroupy", + "thunderstorm": "Bouřka", + "partlyCloudy": "Polojasno", + "heavyRain": "Silný déšť", + "rain": "Déšť", + "snow": "Sníh", + "mixedPrecip": "Smíšené srážky", + "sunny": "Slunečno", + "windy": "Větrno" + }, + "direction": { + "n": "S", + "nne": "SSV", + "ne": "SV", + "ene": "VSV", + "e": "V", + "ese": "VJV", + "se": "JV", + "sse": "JJV", + "s": "J", + "ssw": "JJZ", + "sw": "JZ", + "wsw": "ZJZ", + "w": "Z", + "wnw": "ZSZ", + "nw": "SZ", + "nnw": "SSZ" + }, + "card": { + "chance_of_precipitation": "{0}% šance srážek" } - \ No newline at end of file +} diff --git a/src/localize/languages/da.json b/src/localize/languages/da.json index f1122bfb..c07df83a 100644 --- a/src/localize/languages/da.json +++ b/src/localize/languages/da.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset skal være et positivt heltal", "forecast_not_available": "Vejrudsigt ikke tilgængelig", "check_entity": "Kontroller den definerede vejrudsigtsentitet.", - "no_wind_barbs_with_string_bearing": "Vindrose understøttes ikke, når vejrudsigtsentitet bruger kompasretninger for vindretning." + "no_wind_barbs_with_string_bearing": "Vindrose understøttes ikke, når vejrudsigtsentitet bruger kompasretninger for vindretning.", + "invalid_value_icon_fill": "icon_fill skal enten være et positivt heltal eller et af 'single' eller 'full'" }, "conditions": { "clear": "Klart", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% risiko for nedbør" } -} \ No newline at end of file +} diff --git a/src/localize/languages/de.json b/src/localize/languages/de.json index 3d2923a5..af34c84b 100644 --- a/src/localize/languages/de.json +++ b/src/localize/languages/de.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset muss eine positive Ganzzahl sein", "forecast_not_available": "Prognose nicht verfügbar", "check_entity": "Überprüfen Sie die konfigurierte Prognoseentität.", - "no_wind_barbs_with_string_bearing": "Windfahnen werden nicht unterstützt, wenn die Wetterentität Himmelsrichtungen für die Windpeilung verwendet." + "no_wind_barbs_with_string_bearing": "Windfahnen werden nicht unterstützt, wenn die Wetterentität Himmelsrichtungen für die Windpeilung verwendet.", + "invalid_value_icon_fill": "icon_fill muss entweder eine positive Ganzzahl oder einer der Werte 'single' oder 'full' sein." }, "conditions": { "clear": "Klar", diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index 887c4186..5b7bb806 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset must be a positive integer", "forecast_not_available": "Forecast not available", "check_entity": "Check the configured forecast entity.", - "no_wind_barbs_with_string_bearing": "Wind barbs are not supported when weather entity uses cardinal directions for wind bearing." + "no_wind_barbs_with_string_bearing": "Wind barbs are not supported when weather entity uses cardinal directions for wind bearing.", + "invalid_value_icon_fill": "icon_fill must be either a positive integer or one of 'single' or 'full'" }, "conditions": { "clear": "Clear", diff --git a/src/localize/languages/es.json b/src/localize/languages/es.json index 39d8a842..aa815f5a 100644 --- a/src/localize/languages/es.json +++ b/src/localize/languages/es.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset debe ser un número entero positivo", "forecast_not_available": "Pronóstico no disponible", "check_entity": "Verifique la entidad de pronóstico configurada.", - "no_wind_barbs_with_string_bearing": "Las púas de viento no son compatibles cuando la entidad meteorológica utiliza direcciones cardinales para la dirección del viento." + "no_wind_barbs_with_string_bearing": "Las púas de viento no son compatibles cuando la entidad meteorológica utiliza direcciones cardinales para la dirección del viento.", + "invalid_value_icon_fill": "icon_fill debe ser un número entero positivo o uno de los valores 'single' o 'full'" }, "conditions": { "clear": "Despejado", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% probabilidad de precipitación" } -} \ No newline at end of file +} diff --git a/src/localize/languages/fr.json b/src/localize/languages/fr.json index 4675fa9c..7b07d8dd 100644 --- a/src/localize/languages/fr.json +++ b/src/localize/languages/fr.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset doit être un entier positif", "forecast_not_available": "Prévision non disponible", "check_entity": "Vérifiez l'entité de prévision configurée.", - "no_wind_barbs_with_string_bearing": "Les barbes de vent ne sont pas prises en charge lorsque l'entité météo utilise des directions cardinales pour le relèvement du vent." + "no_wind_barbs_with_string_bearing": "Les barbes de vent ne sont pas prises en charge lorsque l'entité météo utilise des directions cardinales pour le relèvement du vent.", + "invalid_value_icon_fill": "icon_fill doit être soit un entier positif, soit l'un des nombres 'single' ou 'full'" }, "conditions": { "clear": "Dégagé", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% probabilité de précipitations" } -} \ No newline at end of file +} diff --git a/src/localize/languages/hu.json b/src/localize/languages/hu.json index ea827e31..2ba041d5 100644 --- a/src/localize/languages/hu.json +++ b/src/localize/languages/hu.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset pozitív egész számnak kell lennie", "forecast_not_available": "Előrejelzés nem elérhető", "check_entity": "Ellenőrizze a beállított előrejelző egységet.", - "no_wind_barbs_with_string_bearing": "A szélsávok nem támogatottak, ha az időjárási egység kardinális irányokat használ a szél irányítására." + "no_wind_barbs_with_string_bearing": "A szélsávok nem támogatottak, ha az időjárási egység kardinális irányokat használ a szél irányítására.", + "invalid_value_icon_fill": "Az ikon_kitöltésének pozitív egésznek vagy az 'single' vagy 'full'" }, "conditions": { "clear": "Tiszta", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% a csapadék valószínűsége" } -} \ No newline at end of file +} diff --git a/src/localize/languages/it.json b/src/localize/languages/it.json index fa3b3b8d..c61ff606 100644 --- a/src/localize/languages/it.json +++ b/src/localize/languages/it.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset deve essere un numero intero positivo", "forecast_not_available": "Previsione non disponibile", "check_entity": "Controllare l'entità di previsione configurata.", - "no_wind_barbs_with_string_bearing": "I picchi di vento non sono supportati quando l'entità meteorologica utilizza le direzioni cardinali per il rilevamento del vento." + "no_wind_barbs_with_string_bearing": "I picchi di vento non sono supportati quando l'entità meteorologica utilizza le direzioni cardinali per il rilevamento del vento.", + "invalid_value_icon_fill": "icon_fill deve essere un numero intero positivo o uno dei valori 'single' o 'full'" }, "conditions": { "clear": "Limpido", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% possibilità di precipitazioni" } -} \ No newline at end of file +} diff --git a/src/localize/languages/nb.json b/src/localize/languages/nb.json index 2cbc4a23..d79e5ab2 100644 --- a/src/localize/languages/nb.json +++ b/src/localize/languages/nb.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset må være et positivt heltall", "forecast_not_available": "Værvarsel er ikke tilgjengelig", "check_entity": "Sjekk den konfigurerte prognoseenheten.", - "no_wind_barbs_with_string_bearing": "Vindmottak støttes ikke når værenheter bruker kardinalretninger for vindføring." + "no_wind_barbs_with_string_bearing": "Vindmottak støttes ikke når værenheter bruker kardinalretninger for vindføring.", + "invalid_value_icon_fill": "icon_fill må enten være et positivt heltall eller et av 'single' eller 'full'" }, "conditions": { "clear": "Klar", diff --git a/src/localize/languages/nl.json b/src/localize/languages/nl.json index f37e6535..7cd92cb4 100644 --- a/src/localize/languages/nl.json +++ b/src/localize/languages/nl.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset moet een positief geheel getal zijn", "forecast_not_available": "Prognose niet beschikbaar", "check_entity": "Controleer de geconfigureerde prognose-entiteit.", - "no_wind_barbs_with_string_bearing": "Windweerhaken worden niet ondersteund wanneer de weerentiteit kardinale richtingen gebruikt voor windrichting." + "no_wind_barbs_with_string_bearing": "Windweerhaken worden niet ondersteund wanneer de weerentiteit kardinale richtingen gebruikt voor windrichting.", + "invalid_value_icon_fill": "icon_fill moet een positief geheel getal zijn of een van de 'single'; of 'full'" }, "conditions": { "clear": "Helder", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% kans op neerslag" } -} \ No newline at end of file +} diff --git a/src/localize/languages/nn-NO.json b/src/localize/languages/nn-NO.json index b2a8d75b..5875cfc2 100644 --- a/src/localize/languages/nn-NO.json +++ b/src/localize/languages/nn-NO.json @@ -39,7 +39,8 @@ "offset_must_be_positive_int": "offset må vere eit positivt heiltal", "forecast_not_available": "Vervarsel er ikkje tilgjengeleg", "check_entity": "Sjekk den konfigurerte prognoseeininga.", - "no_wind_barbs_with_string_bearing": "Vindmottak blir ikkje støtta når vêreiningar bruker kardinalretningar for vindføring." + "no_wind_barbs_with_string_bearing": "Vindmottak blir ikkje støtta når vêreiningar bruker kardinalretningar for vindføring.", + "invalid_value_icon_fill": "icon_fill må enten være et positivt heltall eller et av 'single' eller 'full'" }, "conditions": { "clear": "Klart", diff --git a/src/localize/languages/pl.json b/src/localize/languages/pl.json index b036d8e4..8c28a01e 100644 --- a/src/localize/languages/pl.json +++ b/src/localize/languages/pl.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset musi być dodatnią liczbą całkowitą", "forecast_not_available": "Prognoza niedostępna", "check_entity": "Sprawdź skonfigurowaną encję prognozy.", - "no_wind_barbs_with_string_bearing": "Zadziory wiatru nie są obsługiwane, gdy jednostka pogodowa używa kierunków kardynalnych dla kierunku wiatru." + "no_wind_barbs_with_string_bearing": "Zadziory wiatru nie są obsługiwane, gdy jednostka pogodowa używa kierunków kardynalnych dla kierunku wiatru.", + "invalid_value_icon_fill": "icon_fill musi być dodatnią liczbą całkowitą lub jedną z wartości 'single' lub 'full'" }, "conditions": { "clear": "Bezchmurnie", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% szans na opady" } -} \ No newline at end of file +} diff --git a/src/localize/languages/pt-BR.json b/src/localize/languages/pt-BR.json index 3beadd5b..87384d16 100644 --- a/src/localize/languages/pt-BR.json +++ b/src/localize/languages/pt-BR.json @@ -39,7 +39,8 @@ "offset_must_be_positive_int": "offset deve ser um número inteiro positivo", "forecast_not_available": "Previsão não disponível", "check_entity": "Verifique a entidade de previsão configurada.", - "no_wind_barbs_with_string_bearing": "Farpas de vento não são suportadas quando a entidade meteorológica usa direções cardeais para direção do vento." + "no_wind_barbs_with_string_bearing": "Farpas de vento não são suportadas quando a entidade meteorológica usa direções cardeais para direção do vento.", + "invalid_value_icon_fill": "icon_fill deve ser um número inteiro positivo ou um dos valores 'single' ou 'full'" }, "conditions": { "clear": "Claro", diff --git a/src/localize/languages/pt.json b/src/localize/languages/pt.json index 5dd710fc..8308f929 100644 --- a/src/localize/languages/pt.json +++ b/src/localize/languages/pt.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset deve ser um número inteiro positivo", "forecast_not_available": "Previsão não disponível", "check_entity": "Verifique a entidade de previsão configurada.", - "no_wind_barbs_with_string_bearing": "Farpas de vento não são suportadas quando a entidade meteorológica usa direções cardeais para direção do vento." + "no_wind_barbs_with_string_bearing": "Farpas de vento não são suportadas quando a entidade meteorológica usa direções cardeais para direção do vento.", + "invalid_value_icon_fill": "icon_fill deve ser um número inteiro positivo ou um dos valores 'single' ou 'full'" }, "conditions": { "clear": "Limpo", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% chance de chuva" } -} \ No newline at end of file +} diff --git a/src/localize/languages/ru.json b/src/localize/languages/ru.json index 547cfc17..bfa80094 100644 --- a/src/localize/languages/ru.json +++ b/src/localize/languages/ru.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "Смещение должно быть положительным целым числом", "forecast_not_available": "Прогноз недоступен", "check_entity": "Проверьте настроенную сущность прогноза.", - "no_wind_barbs_with_string_bearing": "Ветровые флажки не поддерживаются, когда сущность погоды использует основные направления для указания направления ветра." + "no_wind_barbs_with_string_bearing": "Ветровые флажки не поддерживаются, когда сущность погоды использует основные направления для указания направления ветра.", + "invalid_value_icon_fill": "icon_fill должен быть либо положительным целым числом, либо одним из значений 'single' или 'full'" }, "conditions": { "clear": "Ясно", diff --git a/src/localize/languages/sk.json b/src/localize/languages/sk.json index 5ba60827..e6f1415a 100644 --- a/src/localize/languages/sk.json +++ b/src/localize/languages/sk.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "offset musí byť kladné celé číslo", "forecast_not_available": "Predpoveď nie je k dispozícii", "check_entity": "Skontrolujte nakonfigurovanú entitu predpovede.", - "no_wind_barbs_with_string_bearing": "Veterné ostne nie sú podporované, keď entita počasia používa hlavné smery pre smer vetra." + "no_wind_barbs_with_string_bearing": "Veterné ostne nie sú podporované, keď entita počasia používa hlavné smery pre smer vetra.", + "invalid_value_icon_fill": "icon_fill musí byť buď kladné celé číslo alebo jedno z 'single'; alebo 'full';" }, "conditions": { "clear": "Čisté", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}% možnosť zrážok" } -} \ No newline at end of file +} diff --git a/src/localize/languages/zh.json b/src/localize/languages/zh.json index ec0c28a9..904ae2e5 100644 --- a/src/localize/languages/zh.json +++ b/src/localize/languages/zh.json @@ -37,7 +37,8 @@ "offset_must_be_positive_int": "偏移必须是正整数", "forecast_not_available": "预测不可用", "check_entity": "检查配置的预测实体。", - "no_wind_barbs_with_string_bearing": "当天气实体使用风向作为风向时,不支持风向图标。" + "no_wind_barbs_with_string_bearing": "当天气实体使用风向作为风向时,不支持风向图标。", + "invalid_value_icon_fill": "icon_fill 必须是正整数,或者是'single'或'full'之一" }, "conditions": { "clear": "晴朗", @@ -74,4 +75,4 @@ "card": { "chance_of_precipitation": "{0}%的降雨概率" } -} \ No newline at end of file +} diff --git a/src/types.ts b/src/types.ts index 5b6ace36..34440a71 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,6 +9,7 @@ declare global { export type WindType = 'true' | 'false' | 'speed' | 'direction' | 'barb' | 'barb-and-speed' | 'barb-and-direction' | 'barb-speed-and-direction'; export type ShowDateType = 'false' | 'boundary' | 'all'; +export type IconFillType = 'single' | 'full' | number; export interface HourlyWeatherCardConfig extends LovelaceCardConfig { type: string; @@ -22,6 +23,7 @@ export interface HourlyWeatherCardConfig extends LovelaceCardConfig { offset?: string; // number colors?: ColorConfig; hide_bar?: boolean; + icon_fill?: IconFillType; hide_hours?: boolean; hide_temperatures?: boolean; round_temperatures?: boolean; diff --git a/src/weather-bar.ts b/src/weather-bar.ts index f1959b49..e25b8ca6 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, ShowDateType } from "./types"; +import type { ColorMap, ConditionSpan, SegmentTemperature, SegmentWind, SegmentPrecipitation, WindType, ShowDateType, IconFillType } from "./types"; const tippyStyles: string = process.env.TIPPY_CSS || ''; @@ -39,6 +39,9 @@ export class WeatherBar extends LitElement { @property({ type: Boolean }) hide_bar = false; + @property({ type: String }) + icon_fill: IconFillType = 'single'; + @property({ type: String }) show_wind: WindType = 'false'; @@ -68,12 +71,30 @@ export class WeatherBar extends LitElement { let icon = ICONS[cond[0]]; if (icon === cond[0]) icon = 'mdi:weather-' + icon; else icon = 'mdi:' + icon; + + const iconMarkup: TemplateResult[] = []; + if (!this.icons) { + iconMarkup.push(html`${label}`); + } else { + let iconSize: IconFillType; + if (!this.icon_fill || this.icon_fill === 'single') { + iconSize = cond[1]; // grid width of segment, so one icon + } else if (this.icon_fill === 'full') { + iconSize = 1; + } else { + iconSize = Math.max(Number(this.icon_fill) || 0, 1); //`Number(this.icon_fill) || 0` will evaluate as 0 if icon_fill is null or undefined. + } + let iconGridStart = 1; + for (let i = 0; i < cond[1]; i += iconSize) { + const iconStyles: Readonly = { gridColumnStart: String(iconGridStart), gridColumnEnd: String(iconGridStart += iconSize * 2) }; + iconMarkup.push(html``) + } + } + const barStyles: Readonly = { gridColumnStart: String(gridStart), gridColumnEnd: String(gridStart += cond[1] * 2) }; conditionBars.push(html`
- ${this.icons ? - html`` : - html`${label}`} + ${iconMarkup}
`); }