Use irm-kmi-api 1.0.2

This commit is contained in:
Jules 2025-05-10 23:11:52 +02:00
parent 3019d700e4
commit 6451570747
Signed by: jdejaegh
GPG key ID: 99D6D184CA66933A
16 changed files with 97 additions and 103 deletions

View file

@ -6,13 +6,12 @@ import logging
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError from homeassistant.exceptions import ConfigEntryError
from irm_kmi_api.const import OPTION_STYLE_STD from irm_kmi_api import RadarStyle
from .const import (CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE, from .const import (CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE,
CONFIG_FLOW_VERSION, DOMAIN, PLATFORMS, CONF_USE_DEPRECATED_FORECAST, CONFIG_FLOW_VERSION, DOMAIN, PLATFORMS, CONF_USE_DEPRECATED_FORECAST,
OPTION_DEPRECATED_FORECAST_NOT_USED) OPTION_DEPRECATED_FORECAST_NOT_USED)
from .coordinator import IrmKmiCoordinator from .coordinator import IrmKmiCoordinator
from .weather import IrmKmiWeather
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -62,7 +61,7 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry):
new = {**config_entry.data} new = {**config_entry.data}
if config_entry.version == 1: if config_entry.version == 1:
new = new | {CONF_STYLE: OPTION_STYLE_STD, CONF_DARK_MODE: True} new = new | {CONF_STYLE: RadarStyle.OPTION_STYLE_STD, CONF_DARK_MODE: True}
hass.config_entries.async_update_entry(config_entry, data=new, version=2) hass.config_entries.async_update_entry(config_entry, data=new, version=2)
if config_entry.version == 2: if config_entry.version == 2:

View file

@ -10,7 +10,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt from homeassistant.util import dt
from . import DOMAIN, IrmKmiCoordinator from .const import DOMAIN
from .coordinator import IrmKmiCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View file

@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import IrmKmiCoordinator from .coordinator import IrmKmiCoordinator
from .const import DOMAIN from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View file

@ -1,7 +1,7 @@
"""Config flow to set up IRM KMI integration via the UI.""" """Config flow to set up IRM KMI integration via the UI."""
import asyncio
import logging import logging
import async_timeout
import voluptuous as vol import voluptuous as vol
from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN
from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
@ -14,12 +14,11 @@ from homeassistant.helpers.selector import (EntitySelector,
SelectSelector, SelectSelector,
SelectSelectorConfig, SelectSelectorConfig,
SelectSelectorMode) SelectSelectorMode)
from irm_kmi_api.api import IrmKmiApiClient from irm_kmi_api import IrmKmiApiClient, RadarStyle
from . import OPTION_STYLE_STD
from .const import (CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, from .const import (CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE,
CONF_LANGUAGE_OVERRIDE_OPTIONS, CONF_STYLE, CONF_LANGUAGE_OVERRIDE_OPTIONS, CONF_STYLE,
CONF_STYLE_OPTIONS, CONFIG_FLOW_VERSION, DOMAIN, CONFIG_FLOW_VERSION, DOMAIN,
OUT_OF_BENELUX, USER_AGENT) OUT_OF_BENELUX, USER_AGENT)
from .utils import get_config_value from .utils import get_config_value
@ -49,7 +48,7 @@ class IrmKmiConfigFlow(ConfigFlow, domain=DOMAIN):
if not errors: if not errors:
api_data = {} api_data = {}
try: try:
async with (async_timeout.timeout(60)): async with asyncio.timeout(60):
api_data = await IrmKmiApiClient( api_data = await IrmKmiApiClient(
session=async_get_clientsession(self.hass), session=async_get_clientsession(self.hass),
user_agent=USER_AGENT user_agent=USER_AGENT
@ -84,8 +83,8 @@ class IrmKmiConfigFlow(ConfigFlow, domain=DOMAIN):
vol.Required(CONF_ZONE): vol.Required(CONF_ZONE):
EntitySelector(EntitySelectorConfig(domain=ZONE_DOMAIN)), EntitySelector(EntitySelectorConfig(domain=ZONE_DOMAIN)),
vol.Optional(CONF_STYLE, default=OPTION_STYLE_STD): vol.Optional(CONF_STYLE, default=RadarStyle.OPTION_STYLE_STD.value):
SelectSelector(SelectSelectorConfig(options=CONF_STYLE_OPTIONS, SelectSelector(SelectSelectorConfig(options=[o.value for o in RadarStyle],
mode=SelectSelectorMode.DROPDOWN, mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_STYLE)), translation_key=CONF_STYLE)),
@ -115,7 +114,7 @@ class IrmKmiOptionFlow(OptionsFlow):
data_schema=vol.Schema( data_schema=vol.Schema(
{ {
vol.Optional(CONF_STYLE, default=get_config_value(self.current_config_entry, CONF_STYLE)): vol.Optional(CONF_STYLE, default=get_config_value(self.current_config_entry, CONF_STYLE)):
SelectSelector(SelectSelectorConfig(options=CONF_STYLE_OPTIONS, SelectSelector(SelectSelectorConfig(options=[o.value for o in RadarStyle],
mode=SelectSelectorMode.DROPDOWN, mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_STYLE)), translation_key=CONF_STYLE)),

View file

@ -14,8 +14,7 @@ from homeassistant.components.weather import (ATTR_CONDITION_CLEAR_NIGHT,
ATTR_CONDITION_SUNNY) ATTR_CONDITION_SUNNY)
from homeassistant.const import (DEGREE, Platform, UnitOfPressure, UnitOfSpeed, from homeassistant.const import (DEGREE, Platform, UnitOfPressure, UnitOfSpeed,
UnitOfTemperature) UnitOfTemperature)
from irm_kmi_api.const import (OPTION_STYLE_CONTRAST, OPTION_STYLE_SATELLITE, from irm_kmi_api import PollenName
OPTION_STYLE_STD, OPTION_STYLE_YELLOW_RED)
DOMAIN: Final = 'irm_kmi' DOMAIN: Final = 'irm_kmi'
PLATFORMS: Final = [Platform.WEATHER, Platform.CAMERA, Platform.BINARY_SENSOR, Platform.SENSOR] PLATFORMS: Final = [Platform.WEATHER, Platform.CAMERA, Platform.BINARY_SENSOR, Platform.SENSOR]
@ -29,13 +28,6 @@ LANGS: Final = ['en', 'fr', 'nl', 'de']
CONF_STYLE: Final = "style" CONF_STYLE: Final = "style"
CONF_STYLE_OPTIONS: Final = [
OPTION_STYLE_STD,
OPTION_STYLE_CONTRAST,
OPTION_STYLE_YELLOW_RED,
OPTION_STYLE_SATELLITE
]
CONF_DARK_MODE: Final = "dark_mode" CONF_DARK_MODE: Final = "dark_mode"
# TODO delete those two constants when integrating with Home Assistant Core # TODO delete those two constants when integrating with Home Assistant Core
@ -114,8 +106,13 @@ IRM_KMI_TO_HA_CONDITION_MAP: Final = {
} }
POLLEN_TO_ICON_MAP: Final = { POLLEN_TO_ICON_MAP: Final = {
'alder': 'mdi:tree', 'ash': 'mdi:tree', 'birch': 'mdi:tree', 'grasses': 'mdi:grass', 'hazel': 'mdi:tree', PollenName.ALDER: 'mdi:tree',
'mugwort': 'mdi:sprout', 'oak': 'mdi:tree' PollenName.ASH: 'mdi:tree',
PollenName.BIRCH: 'mdi:tree',
PollenName.GRASSES: 'mdi:grass',
PollenName.HAZEL: 'mdi:tree',
PollenName.MUGWORT: 'mdi:sprout',
PollenName.OAK: 'mdi:tree'
} }
IRM_KMI_NAME: Final = { IRM_KMI_NAME: Final = {

View file

@ -1,8 +1,8 @@
"""DataUpdateCoordinator for the IRM KMI integration.""" """DataUpdateCoordinator for the IRM KMI integration."""
import asyncio
import logging import logging
from datetime import timedelta from datetime import timedelta
import async_timeout
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, CONF_ZONE from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, CONF_ZONE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -13,9 +13,7 @@ from homeassistant.helpers.update_coordinator import (
TimestampDataUpdateCoordinator, UpdateFailed) TimestampDataUpdateCoordinator, UpdateFailed)
from homeassistant.util import dt from homeassistant.util import dt
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from irm_kmi_api.api import IrmKmiApiClientHa, IrmKmiApiError from irm_kmi_api import IrmKmiApiClientHa, IrmKmiApiError, PollenParser, RainGraph, RadarStyle
from irm_kmi_api.pollen import PollenParser
from irm_kmi_api.rain_graph import RainGraph
from .const import CONF_DARK_MODE, CONF_STYLE, DOMAIN, IRM_KMI_NAME from .const import CONF_DARK_MODE, CONF_STYLE, DOMAIN, IRM_KMI_NAME
from .const import IRM_KMI_TO_HA_CONDITION_MAP as CDT_MAP from .const import IRM_KMI_TO_HA_CONDITION_MAP as CDT_MAP
@ -43,7 +41,10 @@ class IrmKmiCoordinator(TimestampDataUpdateCoordinator):
self._api = IrmKmiApiClientHa(session=async_get_clientsession(hass), user_agent=USER_AGENT, cdt_map=CDT_MAP) self._api = IrmKmiApiClientHa(session=async_get_clientsession(hass), user_agent=USER_AGENT, cdt_map=CDT_MAP)
self._zone = get_config_value(entry, CONF_ZONE) self._zone = get_config_value(entry, CONF_ZONE)
self._dark_mode = get_config_value(entry, CONF_DARK_MODE) self._dark_mode = get_config_value(entry, CONF_DARK_MODE)
self._style = get_config_value(entry, CONF_STYLE) try:
self._style = RadarStyle(get_config_value(entry, CONF_STYLE))
except ValueError:
self._style = RadarStyle.OPTION_STYLE_STD
self.shared_device_info = DeviceInfo( self.shared_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE, entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, entry.entry_id)}, identifiers={(DOMAIN, entry.entry_id)},
@ -66,7 +67,7 @@ class IrmKmiCoordinator(TimestampDataUpdateCoordinator):
try: try:
# Note: asyncio.TimeoutError and aiohttp.ClientError are already # Note: asyncio.TimeoutError and aiohttp.ClientError are already
# handled by the data update coordinator. # handled by the data update coordinator.
async with async_timeout.timeout(60): async with asyncio.timeout(60):
await self._api.refresh_forecasts_coord( await self._api.refresh_forecasts_coord(
{'lat': zone.attributes[ATTR_LATITUDE], {'lat': zone.attributes[ATTR_LATITUDE],
'long': zone.attributes[ATTR_LONGITUDE]} 'long': zone.attributes[ATTR_LONGITUDE]}

View file

@ -1,15 +1,14 @@
from typing import List, TypedDict from typing import List, TypedDict
from homeassistant.components.weather import Forecast from homeassistant.components.weather import Forecast
from irm_kmi_api.data import CurrentWeatherData, IrmKmiForecast, WarningData from irm_kmi_api import CurrentWeatherData, ExtendedForecast, WarningData, RainGraph
from irm_kmi_api.rain_graph import RainGraph
class ProcessedCoordinatorData(TypedDict, total=False): class ProcessedCoordinatorData(TypedDict, total=False):
"""Data class that will be exposed to the entities consuming data from an IrmKmiCoordinator""" """Data class that will be exposed to the entities consuming data from an IrmKmiCoordinator"""
current_weather: CurrentWeatherData current_weather: CurrentWeatherData
hourly_forecast: List[Forecast] | None hourly_forecast: List[Forecast] | None
daily_forecast: List[IrmKmiForecast] | None daily_forecast: List[ExtendedForecast] | None
radar_forecast: List[Forecast] | None radar_forecast: List[Forecast] | None
animation: RainGraph | None animation: RainGraph | None
warnings: List[WarningData] warnings: List[WarningData]

View file

@ -9,7 +9,7 @@
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"issue_tracker": "https://github.com/jdejaegh/irm-kmi-ha/issues", "issue_tracker": "https://github.com/jdejaegh/irm-kmi-ha/issues",
"requirements": [ "requirements": [
"irm-kmi-api>=0.1.4,<1.0.0" "irm-kmi-api>=1.0.2,<2.0.0"
], ],
"version": "0.3.0" "version": "0.3.0"
} }

View file

@ -1,6 +1,6 @@
import asyncio
import logging import logging
import async_timeout
import voluptuous as vol import voluptuous as vol
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components.repairs import RepairsFlow from homeassistant.components.repairs import RepairsFlow
@ -8,7 +8,7 @@ from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
from irm_kmi_api.api import IrmKmiApiClient from irm_kmi_api import IrmKmiApiClient
from . import async_reload_entry from . import async_reload_entry
from .const import (OUT_OF_BENELUX, REPAIR_OPT_DELETE, REPAIR_OPT_MOVE, from .const import (OUT_OF_BENELUX, REPAIR_OPT_DELETE, REPAIR_OPT_MOVE,
@ -47,7 +47,7 @@ class OutOfBeneluxRepairFlow(RepairsFlow):
if not errors: if not errors:
api_data = {} api_data = {}
try: try:
async with async_timeout.timeout(10): async with asyncio.timeout(10):
api_data = await IrmKmiApiClient( api_data = await IrmKmiApiClient(
session=async_get_clientsession(self.hass), session=async_get_clientsession(self.hass),
user_agent=USER_AGENT user_agent=USER_AGENT

View file

@ -9,14 +9,12 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt from homeassistant.util import dt
from irm_kmi_api.const import POLLEN_NAMES from irm_kmi_api import ExtendedForecast, PollenParser, PollenName, RadarForecast
from irm_kmi_api.data import IrmKmiForecast, IrmKmiRadarForecast
from irm_kmi_api.pollen import PollenParser
from . import DOMAIN, IrmKmiCoordinator from .coordinator import IrmKmiCoordinator
from .const import (CURRENT_WEATHER_SENSOR_CLASS, CURRENT_WEATHER_SENSOR_ICON, from .const import (CURRENT_WEATHER_SENSOR_CLASS, CURRENT_WEATHER_SENSOR_ICON,
CURRENT_WEATHER_SENSOR_UNITS, CURRENT_WEATHER_SENSORS, CURRENT_WEATHER_SENSOR_UNITS, CURRENT_WEATHER_SENSORS,
POLLEN_TO_ICON_MAP) POLLEN_TO_ICON_MAP, DOMAIN)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -24,7 +22,7 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback):
"""Set up the sensor platform""" """Set up the sensor platform"""
coordinator = hass.data[DOMAIN][entry.entry_id] coordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities([IrmKmiPollen(coordinator, entry, pollen.lower()) for pollen in POLLEN_NAMES]) async_add_entities([IrmKmiPollen(coordinator, entry, pollen) for pollen in PollenName])
async_add_entities([IrmKmiCurrentWeather(coordinator, entry, name) for name in CURRENT_WEATHER_SENSORS]) async_add_entities([IrmKmiCurrentWeather(coordinator, entry, name) for name in CURRENT_WEATHER_SENSORS])
async_add_entities([IrmKmiNextWarning(coordinator, entry), async_add_entities([IrmKmiNextWarning(coordinator, entry),
IrmKmiCurrentRainfall(coordinator, entry)]) IrmKmiCurrentRainfall(coordinator, entry)])
@ -42,22 +40,23 @@ class IrmKmiPollen(CoordinatorEntity, SensorEntity):
def __init__(self, def __init__(self,
coordinator: IrmKmiCoordinator, coordinator: IrmKmiCoordinator,
entry: ConfigEntry, entry: ConfigEntry,
pollen: str pollen: PollenName
) -> None: ) -> None:
super().__init__(coordinator) super().__init__(coordinator)
SensorEntity.__init__(self) SensorEntity.__init__(self)
self._attr_unique_id = f"{entry.entry_id}-pollen-{pollen}" self._attr_unique_id = f"{entry.entry_id}-pollen-{pollen.value}"
self.entity_id = sensor.ENTITY_ID_FORMAT.format(f"{str(entry.title).lower()}_{pollen}_level") self.entity_id = sensor.ENTITY_ID_FORMAT.format(f"{str(entry.title).lower()}_{pollen.value}_level")
self._attr_options = PollenParser.get_option_values() self._attr_options = [p.value for p in PollenParser.get_option_values()]
self._attr_device_info = coordinator.shared_device_info self._attr_device_info = coordinator.shared_device_info
self._pollen = pollen self._pollen = pollen
self._attr_translation_key = f"pollen_{pollen}" self._attr_translation_key = f"pollen_{pollen.value}"
self._attr_icon = POLLEN_TO_ICON_MAP[pollen] self._attr_icon = POLLEN_TO_ICON_MAP[pollen]
@property @property
def native_value(self) -> str | None: def native_value(self) -> str | None:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.coordinator.data.get('pollen', {}).get(self._pollen, None) r = self.coordinator.data.get('pollen', {}).get(self._pollen, None)
return r.value if r is not None else None
class IrmKmiNextWarning(CoordinatorEntity, SensorEntity): class IrmKmiNextWarning(CoordinatorEntity, SensorEntity):
@ -132,7 +131,7 @@ class IrmKmiNextSunMove(CoordinatorEntity, SensorEntity):
def native_value(self) -> datetime | None: def native_value(self) -> datetime | None:
"""Return the timestamp for the next sunrise or sunset""" """Return the timestamp for the next sunrise or sunset"""
now = dt.now() now = dt.now()
data: list[IrmKmiForecast] = self.coordinator.data.get('daily_forecast') data: list[ExtendedForecast] = self.coordinator.data.get('daily_forecast')
upcoming = [datetime.fromisoformat(f.get(self._move)) for f in data upcoming = [datetime.fromisoformat(f.get(self._move)) for f in data
if f.get(self._move) is not None and datetime.fromisoformat(f.get(self._move)) >= now] if f.get(self._move) is not None and datetime.fromisoformat(f.get(self._move)) >= now]
@ -195,7 +194,7 @@ class IrmKmiCurrentRainfall(CoordinatorEntity, SensorEntity):
self._attr_translation_key = "current_rainfall" self._attr_translation_key = "current_rainfall"
self._attr_icon = 'mdi:weather-pouring' self._attr_icon = 'mdi:weather-pouring'
def _current_forecast(self) -> IrmKmiRadarForecast | None: def _current_forecast(self) -> RadarForecast | None:
now = dt.now() now = dt.now()
forecasts = self.coordinator.data.get('radar_forecast', None) forecasts = self.coordinator.data.get('radar_forecast', None)

View file

@ -1,4 +1,4 @@
aiohttp>=3.11.13 aiohttp>=3.11.13
homeassistant==2025.4.4 homeassistant==2025.4.4
voluptuous==0.15.2 voluptuous==0.15.2
irm-kmi-api>=0.1.4,<1.0.0 irm-kmi-api>=1.0.2,<2.0.0

View file

@ -8,13 +8,10 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from homeassistant.const import CONF_ZONE from homeassistant.const import CONF_ZONE
from irm_kmi_api.api import (IrmKmiApiClientHa, IrmKmiApiError, from irm_kmi_api import IrmKmiApiClientHa, IrmKmiApiError, AnimationFrameData, RadarAnimationData, RadarStyle
IrmKmiApiParametersError)
from irm_kmi_api.data import AnimationFrameData, RadarAnimationData
from pytest_homeassistant_custom_component.common import (MockConfigEntry, from pytest_homeassistant_custom_component.common import (MockConfigEntry,
load_fixture) load_fixture)
from custom_components.irm_kmi import OPTION_STYLE_STD
from custom_components.irm_kmi.const import ( from custom_components.irm_kmi.const import (
CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE, CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE,
CONF_USE_DEPRECATED_FORECAST, DOMAIN, IRM_KMI_TO_HA_CONDITION_MAP, CONF_USE_DEPRECATED_FORECAST, DOMAIN, IRM_KMI_TO_HA_CONDITION_MAP,
@ -43,7 +40,7 @@ def mock_config_entry() -> MockConfigEntry:
title="Home", title="Home",
domain=DOMAIN, domain=DOMAIN,
data={CONF_ZONE: "zone.home", data={CONF_ZONE: "zone.home",
CONF_STYLE: OPTION_STYLE_STD, CONF_STYLE: RadarStyle.OPTION_STYLE_STD,
CONF_DARK_MODE: True, CONF_DARK_MODE: True,
CONF_USE_DEPRECATED_FORECAST: OPTION_DEPRECATED_FORECAST_NOT_USED, CONF_USE_DEPRECATED_FORECAST: OPTION_DEPRECATED_FORECAST_NOT_USED,
CONF_LANGUAGE_OVERRIDE: 'none'}, CONF_LANGUAGE_OVERRIDE: 'none'},
@ -141,7 +138,7 @@ def mock_exception_irm_kmi_api(request: pytest.FixtureRequest) -> Generator[None
"custom_components.irm_kmi.coordinator.IrmKmiApiClientHa", autospec=True "custom_components.irm_kmi.coordinator.IrmKmiApiClientHa", autospec=True
) as irm_kmi_api_mock: ) as irm_kmi_api_mock:
irm_kmi = irm_kmi_api_mock.return_value irm_kmi = irm_kmi_api_mock.return_value
irm_kmi.refresh_forecasts_coord.side_effect = IrmKmiApiParametersError irm_kmi.refresh_forecasts_coord.side_effect = IrmKmiApiError
yield irm_kmi yield irm_kmi
def get_radar_animation_data() -> RadarAnimationData: def get_radar_animation_data() -> RadarAnimationData:

View file

@ -7,14 +7,13 @@ from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_ZONE from homeassistant.const import CONF_ZONE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
from irm_kmi_api.const import OPTION_STYLE_SATELLITE, OPTION_STYLE_STD from irm_kmi_api import RadarStyle
from pytest_homeassistant_custom_component.common import MockConfigEntry from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.irm_kmi import async_migrate_entry from custom_components.irm_kmi import async_migrate_entry
from custom_components.irm_kmi.const import ( from custom_components.irm_kmi.const import (
CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE, CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE,
CONF_USE_DEPRECATED_FORECAST, CONFIG_FLOW_VERSION, DOMAIN, CONFIG_FLOW_VERSION, DOMAIN)
OPTION_DEPRECATED_FORECAST_NOT_USED)
async def test_full_user_flow( async def test_full_user_flow(
@ -33,13 +32,13 @@ async def test_full_user_flow(
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={CONF_ZONE: ENTITY_ID_HOME, user_input={CONF_ZONE: ENTITY_ID_HOME,
CONF_STYLE: OPTION_STYLE_STD, CONF_STYLE: RadarStyle.OPTION_STYLE_STD.value,
CONF_DARK_MODE: False}, CONF_DARK_MODE: False},
) )
assert result2.get("type") == FlowResultType.CREATE_ENTRY assert result2.get("type") == FlowResultType.CREATE_ENTRY
assert result2.get("title") == "test home" assert result2.get("title") == "test home"
assert result2.get("data") == {CONF_ZONE: ENTITY_ID_HOME, assert result2.get("data") == {CONF_ZONE: ENTITY_ID_HOME,
CONF_STYLE: OPTION_STYLE_STD, CONF_STYLE: RadarStyle.OPTION_STYLE_STD.value,
CONF_DARK_MODE: False, CONF_DARK_MODE: False,
CONF_LANGUAGE_OVERRIDE: 'none'} CONF_LANGUAGE_OVERRIDE: 'none'}
@ -56,7 +55,7 @@ async def test_config_flow_out_benelux_zone(
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={CONF_ZONE: ENTITY_ID_HOME, user_input={CONF_ZONE: ENTITY_ID_HOME,
CONF_STYLE: OPTION_STYLE_STD, CONF_STYLE: RadarStyle.OPTION_STYLE_STD.value,
CONF_DARK_MODE: False}, CONF_DARK_MODE: False},
) )
@ -77,7 +76,7 @@ async def test_config_flow_with_api_error(
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={CONF_ZONE: ENTITY_ID_HOME, user_input={CONF_ZONE: ENTITY_ID_HOME,
CONF_STYLE: OPTION_STYLE_STD, CONF_STYLE: RadarStyle.OPTION_STYLE_STD.value,
CONF_DARK_MODE: False}, CONF_DARK_MODE: False},
) )
@ -94,7 +93,7 @@ async def test_config_flow_unknown_zone(hass: HomeAssistant) -> None:
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={CONF_ZONE: "zone.what", user_input={CONF_ZONE: "zone.what",
CONF_STYLE: OPTION_STYLE_STD, CONF_STYLE: RadarStyle.OPTION_STYLE_STD.value,
CONF_DARK_MODE: False}, CONF_DARK_MODE: False},
) )
@ -119,14 +118,14 @@ async def test_option_flow(
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_STYLE: OPTION_STYLE_SATELLITE, CONF_STYLE: RadarStyle.OPTION_STYLE_SATELLITE.value,
CONF_DARK_MODE: True, CONF_DARK_MODE: True,
} }
) )
assert result["type"] == FlowResultType.CREATE_ENTRY assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
CONF_STYLE: OPTION_STYLE_SATELLITE, CONF_STYLE: RadarStyle.OPTION_STYLE_SATELLITE.value,
CONF_DARK_MODE: True, CONF_DARK_MODE: True,
CONF_LANGUAGE_OVERRIDE: 'none' CONF_LANGUAGE_OVERRIDE: 'none'
} }

View file

@ -2,8 +2,11 @@ from datetime import timedelta
from homeassistant.components.weather import ATTR_CONDITION_CLOUDY from homeassistant.components.weather import ATTR_CONDITION_CLOUDY
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from irm_kmi_api.data import CurrentWeatherData, IrmKmiRadarForecast from irm_kmi_api import (
from irm_kmi_api.pollen import PollenParser CurrentWeatherData,
PollenParser,
RadarForecast,
)
from pytest_homeassistant_custom_component.common import MockConfigEntry from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.irm_kmi.coordinator import IrmKmiCoordinator from custom_components.irm_kmi.coordinator import IrmKmiCoordinator
@ -58,27 +61,27 @@ def test_radar_forecast() -> None:
result = api.get_radar_forecast() result = api.get_radar_forecast()
expected = [ expected = [
IrmKmiRadarForecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1, might_rain=False, RadarForecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01, might_rain=False, RadarForecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12, might_rain=False, RadarForecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2, might_rain=False, RadarForecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2, might_rain=False, RadarForecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min') rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min')
] ]
@ -89,7 +92,7 @@ def test_radar_forecast_rain_interval() -> None:
api = get_api_with_data('forecast_with_rain_on_radar.json') api = get_api_with_data('forecast_with_rain_on_radar.json')
result = api.get_radar_forecast() result = api.get_radar_forecast()
_12 = IrmKmiRadarForecast( _12 = RadarForecast(
datetime='2024-05-30T18:00:00+02:00', datetime='2024-05-30T18:00:00+02:00',
native_precipitation=0.89, native_precipitation=0.89,
might_rain=True, might_rain=True,
@ -98,7 +101,7 @@ def test_radar_forecast_rain_interval() -> None:
unit='mm/10min' unit='mm/10min'
) )
_13 = IrmKmiRadarForecast( _13 = RadarForecast(
datetime="2024-05-30T18:10:00+02:00", datetime="2024-05-30T18:10:00+02:00",
native_precipitation=0.83, native_precipitation=0.83,
might_rain=True, might_rain=True,

View file

@ -6,13 +6,13 @@ import pytest
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_ZONE from homeassistant.const import CONF_ZONE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from irm_kmi_api import RadarStyle
from pytest_homeassistant_custom_component.common import MockConfigEntry from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.irm_kmi import OPTION_STYLE_STD, async_migrate_entry from custom_components.irm_kmi import async_migrate_entry
from custom_components.irm_kmi.const import ( from custom_components.irm_kmi.const import (
CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE, CONF_DARK_MODE, CONF_LANGUAGE_OVERRIDE, CONF_STYLE,
CONF_USE_DEPRECATED_FORECAST, CONFIG_FLOW_VERSION, DOMAIN, CONFIG_FLOW_VERSION, DOMAIN)
OPTION_DEPRECATED_FORECAST_NOT_USED)
async def test_load_unload_config_entry( async def test_load_unload_config_entry(
@ -96,7 +96,7 @@ async def test_config_entry_migration(
assert mock_config_entry.data == { assert mock_config_entry.data == {
CONF_ZONE: "zone.castle", CONF_ZONE: "zone.castle",
CONF_STYLE: OPTION_STYLE_STD, CONF_STYLE: RadarStyle.OPTION_STYLE_STD,
CONF_DARK_MODE: True, CONF_DARK_MODE: True,
CONF_LANGUAGE_OVERRIDE: 'none' CONF_LANGUAGE_OVERRIDE: 'none'
} }

View file

@ -1,15 +1,15 @@
import json import json
from datetime import datetime from datetime import datetime
from typing import List from typing import List, Dict
from freezegun import freeze_time from freezegun import freeze_time
from homeassistant.components.weather import Forecast from homeassistant.components.weather import Forecast
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from irm_kmi_api.data import IrmKmiRadarForecast from irm_kmi_api.data import RadarForecast
from pytest_homeassistant_custom_component.common import (MockConfigEntry, from pytest_homeassistant_custom_component.common import (MockConfigEntry,
load_fixture) load_fixture)
from custom_components.irm_kmi import IrmKmiCoordinator, IrmKmiWeather from custom_components.irm_kmi.weather import IrmKmiCoordinator, IrmKmiWeather
from custom_components.irm_kmi.data import ProcessedCoordinatorData from custom_components.irm_kmi.data import ProcessedCoordinatorData
from tests.conftest import get_api_with_data from tests.conftest import get_api_with_data
@ -77,35 +77,35 @@ async def test_radar_forecast_service(
weather = IrmKmiWeather(coordinator, mock_config_entry) weather = IrmKmiWeather(coordinator, mock_config_entry)
result_service: List[Forecast] = weather.get_forecasts_radar_service(False) result_service: Dict[str, List[Forecast]] = weather.get_forecasts_radar_service(False)
l = [ l = [
IrmKmiRadarForecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:00:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:10:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:20:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T17:30:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1, might_rain=False, RadarForecast(datetime="2023-12-26T17:40:00+01:00", native_precipitation=0.1, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01, might_rain=False, RadarForecast(datetime="2023-12-26T17:50:00+01:00", native_precipitation=0.01, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12, might_rain=False, RadarForecast(datetime="2023-12-26T18:00:00+01:00", native_precipitation=0.12, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2, might_rain=False, RadarForecast(datetime="2023-12-26T18:10:00+01:00", native_precipitation=1.2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2, might_rain=False, RadarForecast(datetime="2023-12-26T18:20:00+01:00", native_precipitation=2, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T18:30:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'), rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min'),
IrmKmiRadarForecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0, might_rain=False, RadarForecast(datetime="2023-12-26T18:40:00+01:00", native_precipitation=0, might_rain=False,
rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min') rain_forecast_max=0, rain_forecast_min=0, unit='mm/10min')
] ]
assert result_service == {'forecast': l[5:]} assert result_service == {'forecast': l[5:]}
result_service: List[Forecast] = weather.get_forecasts_radar_service(True) result_service: Dict[str, List[Forecast]] = weather.get_forecasts_radar_service(True)
assert result_service == {'forecast': l} assert result_service == {'forecast': l}