Merge pull request #42 from jdejaegh/37-confidence-intervals-get_forecasts_radar

Add  confidence intervals for irm_kmi.get_forecasts_radar
This commit is contained in:
Jules 2024-06-01 20:50:43 +02:00 committed by GitHub
commit 89b08dca0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 1630 additions and 28 deletions

View file

@ -2,6 +2,7 @@
import asyncio import asyncio
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from statistics import mean
from typing import Any, List, Tuple from typing import Any, List, Tuple
import async_timeout import async_timeout
@ -23,7 +24,8 @@ from .const import IRM_KMI_TO_HA_CONDITION_MAP as CDT_MAP
from .const import MAP_WARNING_ID_TO_SLUG as SLUG_MAP from .const import MAP_WARNING_ID_TO_SLUG as SLUG_MAP
from .const import OPTION_STYLE_SATELLITE, OUT_OF_BENELUX, STYLE_TO_PARAM_MAP from .const import OPTION_STYLE_SATELLITE, OUT_OF_BENELUX, STYLE_TO_PARAM_MAP
from .data import (AnimationFrameData, CurrentWeatherData, IrmKmiForecast, from .data import (AnimationFrameData, CurrentWeatherData, IrmKmiForecast,
ProcessedCoordinatorData, RadarAnimationData, WarningData) IrmKmiRadarForecast, ProcessedCoordinatorData,
RadarAnimationData, WarningData)
from .pollen import PollenParser from .pollen import PollenParser
from .rain_graph import RainGraph from .rain_graph import RainGraph
from .utils import disable_from_config, get_config_value, preferred_language from .utils import disable_from_config, get_config_value, preferred_language
@ -140,6 +142,7 @@ class IrmKmiCoordinator(TimestampDataUpdateCoordinator):
return radar_animation return radar_animation
async def _async_pollen_data(self, api_data: dict) -> dict: async def _async_pollen_data(self, api_data: dict) -> dict:
"""Get SVG pollen info from the API, return the pollen data dict"""
_LOGGER.debug("Getting pollen data from API") _LOGGER.debug("Getting pollen data from API")
svg_url = None svg_url = None
for module in api_data.get('module', []): for module in api_data.get('module', []):
@ -322,16 +325,27 @@ class IrmKmiCoordinator(TimestampDataUpdateCoordinator):
return forecasts return forecasts
@staticmethod @staticmethod
def radar_list_to_forecast(data: dict | None) -> List[Forecast] | None: def radar_list_to_forecast(data: dict | None) -> List[IrmKmiRadarForecast] | None:
"""Create a list of short term forecasts for rain based on the data provided by the rain radar"""
if data is None: if data is None:
return None return None
sequence = data.get("sequence", [])
ratios = [f['value'] / f['position'] for f in sequence if f['position'] > 0]
if len(ratios) > 0:
ratio = mean(ratios)
else:
ratio = 0
forecast = list() forecast = list()
for f in data.get("sequence", []): for f in sequence:
forecast.append( forecast.append(
Forecast( IrmKmiRadarForecast(
datetime=f.get("time"), datetime=f.get("time"),
native_precipitation=f.get('value') native_precipitation=f.get('value'),
rain_forecast_max=round(f.get('positionHigher')*ratio, 2),
rain_forecast_min=round(f.get('positionLower')*ratio, 2),
might_rain=f.get('positionHigher') > 0
) )
) )
return forecast return forecast

View file

@ -12,6 +12,13 @@ class IrmKmiForecast(Forecast):
text: str | None text: str | None
class IrmKmiRadarForecast(Forecast):
"""Forecast class to handle rain forecast from the IRM KMI rain radar"""
rain_forecast_max: float
rain_forecast_min: float
might_rain: bool
class CurrentWeatherData(TypedDict, total=False): class CurrentWeatherData(TypedDict, total=False):
"""Class to hold the currently observable weather at a given location""" """Class to hold the currently observable weather at a given location"""
condition: str | None condition: str | None

View file

@ -69,6 +69,7 @@ class RainGraph:
self._dwg_still: Drawing = Drawing() self._dwg_still: Drawing = Drawing()
async def build(self) -> Self: async def build(self) -> Self:
"""Build the rain graph by calling all the method in the right order. Returns self when done"""
await self.draw_svg_frame() await self.draw_svg_frame()
self.draw_hour_bars() self.draw_hour_bars()
self.draw_chances_path() self.draw_chances_path()

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@ from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.irm_kmi.const import CONF_LANGUAGE_OVERRIDE from custom_components.irm_kmi.const import CONF_LANGUAGE_OVERRIDE
from custom_components.irm_kmi.coordinator import IrmKmiCoordinator from custom_components.irm_kmi.coordinator import IrmKmiCoordinator
from custom_components.irm_kmi.data import (CurrentWeatherData, IrmKmiForecast, from custom_components.irm_kmi.data import (CurrentWeatherData, IrmKmiForecast,
IrmKmiRadarForecast,
ProcessedCoordinatorData, ProcessedCoordinatorData,
RadarAnimationData) RadarAnimationData)
from custom_components.irm_kmi.pollen import PollenParser from custom_components.irm_kmi.pollen import PollenParser
@ -238,17 +239,52 @@ def test_radar_forecast() -> None:
result = IrmKmiCoordinator.radar_list_to_forecast(api_data.get('animation')) result = IrmKmiCoordinator.radar_list_to_forecast(api_data.get('animation'))
expected = [ expected = [
Forecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0), IrmKmiRadarForecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0), IrmKmiRadarForecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1), IrmKmiRadarForecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12), IrmKmiRadarForecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2), IrmKmiRadarForecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1, might_rain=False,
Forecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0) IrmKmiRadarForecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0)
] ]
assert expected == result assert expected == result
def test_radar_forecast_rain_interval() -> None:
api_data = get_api_data('forecast_with_rain_on_radar.json')
result = IrmKmiCoordinator.radar_list_to_forecast(api_data.get('animation'))
_12 = IrmKmiRadarForecast(
datetime='2024-05-30T18:00:00+02:00',
native_precipitation=0.89,
might_rain=True,
rain_forecast_max=1.12,
rain_forecast_min=0.50
)
_13 = IrmKmiRadarForecast(
datetime="2024-05-30T18:10:00+02:00",
native_precipitation=0.83,
might_rain=True,
rain_forecast_max=1.09,
rain_forecast_min=0.64
)
assert result[12] == _12
assert result[13] == _13

View file

@ -9,7 +9,8 @@ from homeassistant.core import HomeAssistant
from pytest_homeassistant_custom_component.common import MockConfigEntry from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.irm_kmi import IrmKmiCoordinator, IrmKmiWeather from custom_components.irm_kmi import IrmKmiCoordinator, IrmKmiWeather
from custom_components.irm_kmi.data import ProcessedCoordinatorData from custom_components.irm_kmi.data import (IrmKmiRadarForecast,
ProcessedCoordinatorData)
from tests.conftest import get_api_data from tests.conftest import get_api_data
@ -115,17 +116,28 @@ async def test_radar_forecast_service(
result_service: List[Forecast] = weather.get_forecasts_radar_service(False) result_service: List[Forecast] = weather.get_forecasts_radar_service(False)
expected = [ expected = [
Forecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0), IrmKmiRadarForecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0), IrmKmiRadarForecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1), IrmKmiRadarForecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12), IrmKmiRadarForecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0, might_rain=False,
Forecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2), IrmKmiRadarForecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1, might_rain=False,
Forecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0), rain_forecast_max=0, rain_forecast_min=0),
Forecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0) IrmKmiRadarForecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0),
IrmKmiRadarForecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0)
] ]
assert result_service == expected[5:] assert result_service == expected[5:]