Skip to content

Commit

Permalink
Merge 3f1f7c7 into 4c995fe
Browse files Browse the repository at this point in the history
  • Loading branch information
decompil3d authored Aug 5, 2024
2 parents 4c995fe + 3f1f7c7 commit 26b7d50
Show file tree
Hide file tree
Showing 23 changed files with 294 additions and 130 deletions.
62 changes: 38 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
- `<n>` (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
Expand All @@ -231,5 +244,6 @@ structure.

[color]: #color-options
[wind]: #wind-options
[icon_fill]: #icon-fill-options
[dates]: #date-options
[action]: #action-options
19 changes: 19 additions & 0 deletions cypress/e2e/config.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
80 changes: 80 additions & 0 deletions cypress/e2e/weather-bar.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
12 changes: 12 additions & 0 deletions src/hourly-weather.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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';
Expand Down Expand Up @@ -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}
Expand Down
Loading

0 comments on commit 26b7d50

Please sign in to comment.