weather: fix UV index display and add WeatherFlow precipitation (#4108)

While looking at the WeatherFlow provider (to evaluate #4107), I noticed
a few things that weren't quite right.

1. **UV index was broken for most providers in forecast/hourly views.**
The templates read `uv_index`, but only the WeatherAPI provider actually
wrote that key. All other providers (OpenWeatherMap, WeatherFlow,
PirateWeather, etc.) use `uvIndex` - so UV was silently never displayed
for them. This went unnoticed because `showUVIndex` defaults to `false`
and there were no test assertions for it. Standardized everything on
`uvIndex` and added test coverage.

2. **WeatherFlow didn't map precipitation for current weather.** The API
provides `precip_accum_local_day` and `precip_probability`, but they
weren't passed through. While adding them I also noticed the template
used truthiness checks, which hid valid zero values. Fixed both.

3. **`||` vs `??` in WeatherFlow provider.** Several numeric fields used
`|| null`, replacing valid `0` with `null`. Switched to `??` for
correctness.
This commit is contained in:
Kristjan ESPERANTO
2026-04-19 12:50:20 +02:00
committed by GitHub
parent d3a3ad9caf
commit de3f57f8af
7 changed files with 31 additions and 23 deletions

View File

@@ -38,7 +38,7 @@
{% if config.showUVIndex %}
<td class="align-right bright uv-index">
<div class="wi dimmed wi-hot"></div>
{{ current.uv_index }}
{{ current.uvIndex }}
</td>
{% endif %}
</div>
@@ -78,11 +78,11 @@
</span>
<br />
{% endif %}
{% if config.showPrecipitationAmount and current.precipitationAmount %}
{% if config.showPrecipitationAmount and current.precipitationAmount is defined and current.precipitationAmount is not none %}
<span class="dimmed"> <span class="precipitationLeadText">{{ "PRECIP_AMOUNT" | translate }}</span> {{ current.precipitationAmount | unit("precip", current.precipitationUnits) }} </span>
<br />
{% endif %}
{% if config.showPrecipitationProbability and current.precipitationProbability %}
{% if config.showPrecipitationProbability and current.precipitationProbability is defined and current.precipitationProbability is not none %}
<span class="dimmed"> <span class="precipitationLeadText">{{ "PRECIP_POP" | translate }}</span> {{ current.precipitationProbability }}% </span>
{% endif %}
</div>

View File

@@ -31,7 +31,7 @@
{% endif %}
{% if config.showUVIndex %}
<td class="align-right dimmed uv-index">
{{ f.uv_index }}
{{ f.uvIndex }}
<span class="wi dimmed weathericon wi-hot"></span>
</td>
{% endif %}

View File

@@ -15,8 +15,8 @@
<td class="align-right bright">{{ hour.temperature | roundValue | unit("temperature") }}</td>
{% if config.showUVIndex %}
<td class="align-right bright uv-index">
{% if hour.uv_index!=0 %}
{{ hour.uv_index }}
{% if hour.uvIndex!=0 %}
{{ hour.uvIndex }}
<span class="wi weathericon wi-hot"></span>
{% endif %}
</td>

View File

@@ -351,7 +351,7 @@ class WeatherAPIProvider {
weather.precipitationProbability = precipitationProbability;
}
weather.uv_index = this.#toNumber(forecastDay.day?.uv);
weather.uvIndex = this.#toNumber(forecastDay.day?.uv);
days.push(weather);
@@ -410,7 +410,7 @@ class WeatherAPIProvider {
const willSnow = this.#toNumber(hourData.will_it_snow) ?? 0;
weather.precipitationProbability = (willRain + willSnow) * 50;
weather.uv_index = this.#toNumber(hourData.uv);
weather.uvIndex = this.#toNumber(hourData.uv);
hours.push(weather);

View File

@@ -145,12 +145,15 @@ class WeatherFlowProvider {
const weather = {
date: new Date(),
humidity: current.relative_humidity || null,
temperature: current.air_temperature || null,
feelsLikeTemp: current.feels_like || null,
humidity: current.relative_humidity ?? null,
temperature: current.air_temperature ?? null,
feelsLikeTemp: current.feels_like ?? null,
windSpeed: current.wind_avg != null ? convertKmhToMs(current.wind_avg) : null,
windFromDirection: current.wind_direction || null,
windFromDirection: current.wind_direction ?? null,
weatherType: this.#convertWeatherType(current.icon),
precipitationAmount: current.precip_accum_local_day ?? null,
precipitationUnits: "mm",
precipitationProbability: current.precip_probability ?? null,
uvIndex: current.uv || null,
sunrise: daily.sunrise ? new Date(daily.sunrise * 1000) : null,
sunset: daily.sunset ? new Date(daily.sunset * 1000) : null
@@ -175,9 +178,9 @@ class WeatherFlowProvider {
for (const forecast of data.forecast.daily) {
const weather = {
date: new Date(forecast.day_start_local * 1000),
minTemperature: forecast.air_temp_low || null,
maxTemperature: forecast.air_temp_high || null,
precipitationProbability: forecast.precip_probability || null,
minTemperature: forecast.air_temp_low ?? null,
maxTemperature: forecast.air_temp_high ?? null,
precipitationProbability: forecast.precip_probability ?? null,
weatherType: this.#convertWeatherType(forecast.icon),
precipitationAmount: 0.0,
precipitationUnits: "mm",
@@ -193,8 +196,8 @@ class WeatherFlowProvider {
if (hourDate.getFullYear() === forecastDate.getFullYear()
&& hourDate.getMonth() === forecastDate.getMonth()
&& hourDate.getDate() === forecastDate.getDate()) {
weather.uvIndex = Math.max(weather.uvIndex, hour.uv || 0);
weather.precipitationAmount += hour.precip || 0;
weather.uvIndex = Math.max(weather.uvIndex, hour.uv ?? 0);
weather.precipitationAmount += hour.precip ?? 0;
} else if (hourDate > forecastDate) {
// Check if we've moved to the next day
const diffMs = hourDate - forecastDate;
@@ -224,14 +227,14 @@ class WeatherFlowProvider {
for (const hour of data.forecast.hourly) {
const weather = {
date: new Date(hour.time * 1000),
temperature: hour.air_temperature || null,
feelsLikeTemp: hour.feels_like || null,
humidity: hour.relative_humidity || null,
temperature: hour.air_temperature ?? null,
feelsLikeTemp: hour.feels_like ?? null,
humidity: hour.relative_humidity ?? null,
windSpeed: hour.wind_avg != null ? convertKmhToMs(hour.wind_avg) : null,
windFromDirection: hour.wind_direction || null,
windFromDirection: hour.wind_direction ?? null,
weatherType: this.#convertWeatherType(hour.icon),
precipitationProbability: hour.precip_probability || null,
precipitationAmount: hour.precip || 0,
precipitationProbability: hour.precip_probability ?? null,
precipitationAmount: hour.precip ?? 0,
precipitationUnits: "mm",
uvIndex: hour.uv || null
};

View File

@@ -240,6 +240,7 @@ describe("WeatherAPIProvider", () => {
expect(result[0].minTemperature).toBe(-8);
expect(result[0].maxTemperature).toBe(-1);
expect(result[0].weatherType).toBe("day-sprinkle");
expect(result[0].uvIndex).toBe(1);
expect(result[0].sunrise).toBeInstanceOf(Date);
expect(result[0].sunset).toBeInstanceOf(Date);
});
@@ -275,6 +276,7 @@ describe("WeatherAPIProvider", () => {
expect(result[0].humidity).toBe(85);
expect(result[0].windFromDirection).toBe(210);
expect(result[0].weatherType).toBe("night-sprinkle");
expect(result[0].uvIndex).toBe(0);
expect(result[0].precipitationProbability).toBe(50);
});
});

View File

@@ -85,6 +85,9 @@ describe("WeatherFlowProvider", () => {
expect(result).toBeDefined();
expect(result.temperature).toBe(16);
expect(result.humidity).toBe(28);
expect(result.precipitationAmount).toBe(0);
expect(result.precipitationUnits).toBe("mm");
expect(result.precipitationProbability).toBe(0);
expect(result.weatherType).not.toBeNull();
});