mirror of
https://github.com/jdejaegh/irm-kmi-ha.git
synced 2025-06-27 11:39:26 +02:00
commit
f98f846d71
8 changed files with 97 additions and 18 deletions
|
@ -48,11 +48,13 @@ STYLE_TO_PARAM_MAP: Final = {
|
||||||
CONF_USE_DEPRECATED_FORECAST: Final = 'use_deprecated_forecast_attribute'
|
CONF_USE_DEPRECATED_FORECAST: Final = 'use_deprecated_forecast_attribute'
|
||||||
OPTION_DEPRECATED_FORECAST_NOT_USED: Final = 'do_not_use_deprecated_forecast'
|
OPTION_DEPRECATED_FORECAST_NOT_USED: Final = 'do_not_use_deprecated_forecast'
|
||||||
OPTION_DEPRECATED_FORECAST_DAILY: Final = 'daily_in_deprecated_forecast'
|
OPTION_DEPRECATED_FORECAST_DAILY: Final = 'daily_in_deprecated_forecast'
|
||||||
OPTION_DEPRECATED_FORECAST_HOURLY: Final = 'hourly_in_use_deprecated_forecast'
|
OPTION_DEPRECATED_FORECAST_TWICE_DAILY: Final = 'twice_daily_in_deprecated_forecast'
|
||||||
|
OPTION_DEPRECATED_FORECAST_HOURLY: Final = 'hourly_in_deprecated_forecast'
|
||||||
|
|
||||||
CONF_USE_DEPRECATED_FORECAST_OPTIONS: Final = [
|
CONF_USE_DEPRECATED_FORECAST_OPTIONS: Final = [
|
||||||
OPTION_DEPRECATED_FORECAST_NOT_USED,
|
OPTION_DEPRECATED_FORECAST_NOT_USED,
|
||||||
OPTION_DEPRECATED_FORECAST_DAILY,
|
OPTION_DEPRECATED_FORECAST_DAILY,
|
||||||
|
OPTION_DEPRECATED_FORECAST_TWICE_DAILY,
|
||||||
OPTION_DEPRECATED_FORECAST_HOURLY
|
OPTION_DEPRECATED_FORECAST_HOURLY
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -12,5 +12,5 @@
|
||||||
"pytz==2024.1",
|
"pytz==2024.1",
|
||||||
"svgwrite==1.4.3"
|
"svgwrite==1.4.3"
|
||||||
],
|
],
|
||||||
"version": "0.2.6"
|
"version": "0.2.7"
|
||||||
}
|
}
|
|
@ -34,7 +34,8 @@
|
||||||
"options": {
|
"options": {
|
||||||
"do_not_use_deprecated_forecast": "Do not use (recommended)",
|
"do_not_use_deprecated_forecast": "Do not use (recommended)",
|
||||||
"daily_in_deprecated_forecast": "Use for daily forecast",
|
"daily_in_deprecated_forecast": "Use for daily forecast",
|
||||||
"hourly_in_use_deprecated_forecast": "Use for hourly forecast"
|
"twice_daily_in_deprecated_forecast": "Use for daily forecast",
|
||||||
|
"hourly_in_deprecated_forecast": "Use for hourly forecast"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repair_solution": {
|
"repair_solution": {
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
"options": {
|
"options": {
|
||||||
"do_not_use_deprecated_forecast": "Ne pas utiliser (recommandé)",
|
"do_not_use_deprecated_forecast": "Ne pas utiliser (recommandé)",
|
||||||
"daily_in_deprecated_forecast": "Utiliser pour les prévisions quotidiennes",
|
"daily_in_deprecated_forecast": "Utiliser pour les prévisions quotidiennes",
|
||||||
"hourly_in_use_deprecated_forecast": "Utiliser pour les prévisions horaires"
|
"twice_daily_in_deprecated_forecast": "Utiliser pour les prévisions biquotidiennes",
|
||||||
|
"hourly_in_deprecated_forecast": "Utiliser pour les prévisions horaires"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repair_solution": {
|
"repair_solution": {
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
"options": {
|
"options": {
|
||||||
"do_not_use_deprecated_forecast": "Niet gebruiken (aanbevolen)",
|
"do_not_use_deprecated_forecast": "Niet gebruiken (aanbevolen)",
|
||||||
"daily_in_deprecated_forecast": "Gebruik voor dagelijkse voorspellingen",
|
"daily_in_deprecated_forecast": "Gebruik voor dagelijkse voorspellingen",
|
||||||
"hourly_in_use_deprecated_forecast": "Gebruik voor uurlijkse voorspellingen"
|
"twice_daily_in_deprecated_forecast": "Gebruik voor tweemaal daags voorspellingen",
|
||||||
|
"hourly_in_deprecated_forecast": "Gebruik voor uurlijkse voorspellingen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repair_solution": {
|
"repair_solution": {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Support for IRM KMI weather."""
|
"""Support for IRM KMI weather."""
|
||||||
|
import copy
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
@ -14,7 +15,8 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
from . import CONF_USE_DEPRECATED_FORECAST, DOMAIN
|
from . import CONF_USE_DEPRECATED_FORECAST, DOMAIN
|
||||||
from .const import (OPTION_DEPRECATED_FORECAST_DAILY,
|
from .const import (OPTION_DEPRECATED_FORECAST_DAILY,
|
||||||
OPTION_DEPRECATED_FORECAST_HOURLY,
|
OPTION_DEPRECATED_FORECAST_HOURLY,
|
||||||
OPTION_DEPRECATED_FORECAST_NOT_USED)
|
OPTION_DEPRECATED_FORECAST_NOT_USED,
|
||||||
|
OPTION_DEPRECATED_FORECAST_TWICE_DAILY)
|
||||||
from .coordinator import IrmKmiCoordinator
|
from .coordinator import IrmKmiCoordinator
|
||||||
from .utils import get_config_value
|
from .utils import get_config_value
|
||||||
|
|
||||||
|
@ -42,6 +44,11 @@ class IrmKmiWeather(CoordinatorEntity, WeatherEntity):
|
||||||
|
|
||||||
self._deprecated_forecast_as = get_config_value(entry, CONF_USE_DEPRECATED_FORECAST)
|
self._deprecated_forecast_as = get_config_value(entry, CONF_USE_DEPRECATED_FORECAST)
|
||||||
|
|
||||||
|
if self._deprecated_forecast_as != OPTION_DEPRECATED_FORECAST_NOT_USED:
|
||||||
|
_LOGGER.warning(f"You are using the forecast attribute for {entry.title} weather. Home Assistant deleted "
|
||||||
|
f"that attribute in 2024.4. Consider using the service weather.get_forecasts instead "
|
||||||
|
f"as the attribute will be delete from this integration in a future release.")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> WeatherEntityFeature:
|
def supported_features(self) -> WeatherEntityFeature:
|
||||||
features = WeatherEntityFeature(0)
|
features = WeatherEntityFeature(0)
|
||||||
|
@ -98,17 +105,6 @@ class IrmKmiWeather(CoordinatorEntity, WeatherEntity):
|
||||||
def uv_index(self) -> float | None:
|
def uv_index(self) -> float | None:
|
||||||
return self.coordinator.data.get('current_weather', {}).get('uv_index')
|
return self.coordinator.data.get('current_weather', {}).get('uv_index')
|
||||||
|
|
||||||
@property
|
|
||||||
def forecast(self) -> list[Forecast] | None:
|
|
||||||
"""This attribute is deprecated by Home Assistant by still implemented for compatibility
|
|
||||||
with older components. Newer components should use the service weather.get_forecasts instead."""
|
|
||||||
if self._deprecated_forecast_as == OPTION_DEPRECATED_FORECAST_NOT_USED:
|
|
||||||
return None
|
|
||||||
elif self._deprecated_forecast_as == OPTION_DEPRECATED_FORECAST_HOURLY:
|
|
||||||
return self.coordinator.data.get('hourly_forecast')
|
|
||||||
elif self._deprecated_forecast_as == OPTION_DEPRECATED_FORECAST_DAILY:
|
|
||||||
return self.daily_forecast()
|
|
||||||
|
|
||||||
async def async_forecast_twice_daily(self) -> List[Forecast] | None:
|
async def async_forecast_twice_daily(self) -> List[Forecast] | None:
|
||||||
return self.coordinator.data.get('daily_forecast')
|
return self.coordinator.data.get('daily_forecast')
|
||||||
|
|
||||||
|
@ -137,3 +133,26 @@ class IrmKmiWeather(CoordinatorEntity, WeatherEntity):
|
||||||
(data[0]['native_temperature'], data[0]['native_templow'])
|
(data[0]['native_temperature'], data[0]['native_templow'])
|
||||||
|
|
||||||
return [f for f in data if f.get('is_daytime')]
|
return [f for f in data if f.get('is_daytime')]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra_state_attributes(self) -> dict:
|
||||||
|
"""Here to keep the DEPRECATED forecast attribute.
|
||||||
|
This attribute is deprecated by Home Assistant by still implemented for compatibility
|
||||||
|
with older components. Newer components should use the service weather.get_forecasts instead.
|
||||||
|
"""
|
||||||
|
data: List[Forecast] = list()
|
||||||
|
if self._deprecated_forecast_as == OPTION_DEPRECATED_FORECAST_NOT_USED:
|
||||||
|
return {}
|
||||||
|
elif self._deprecated_forecast_as == OPTION_DEPRECATED_FORECAST_HOURLY:
|
||||||
|
data = self.coordinator.data.get('hourly_forecast')
|
||||||
|
elif self._deprecated_forecast_as == OPTION_DEPRECATED_FORECAST_DAILY:
|
||||||
|
data = self.daily_forecast()
|
||||||
|
elif self._deprecated_forecast_as == OPTION_DEPRECATED_FORECAST_TWICE_DAILY:
|
||||||
|
data = self.coordinator.data.get('daily_forecast')
|
||||||
|
|
||||||
|
for forecast in data:
|
||||||
|
for k in list(forecast.keys()):
|
||||||
|
if k.startswith('native_'):
|
||||||
|
forecast[k[7:]] = forecast[k]
|
||||||
|
|
||||||
|
return {'forecast': data}
|
||||||
|
|
|
@ -14,7 +14,7 @@ from custom_components.irm_kmi.api import (IrmKmiApiError,
|
||||||
IrmKmiApiParametersError)
|
IrmKmiApiParametersError)
|
||||||
from custom_components.irm_kmi.const import (
|
from custom_components.irm_kmi.const import (
|
||||||
CONF_DARK_MODE, CONF_STYLE, CONF_USE_DEPRECATED_FORECAST, DOMAIN,
|
CONF_DARK_MODE, CONF_STYLE, CONF_USE_DEPRECATED_FORECAST, DOMAIN,
|
||||||
OPTION_DEPRECATED_FORECAST_NOT_USED, OPTION_STYLE_STD)
|
OPTION_DEPRECATED_FORECAST_NOT_USED, OPTION_STYLE_STD, OPTION_DEPRECATED_FORECAST_TWICE_DAILY)
|
||||||
|
|
||||||
|
|
||||||
def get_api_data(fixture: str) -> dict:
|
def get_api_data(fixture: str) -> dict:
|
||||||
|
@ -56,6 +56,20 @@ def mock_config_entry() -> MockConfigEntry:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_config_entry_with_deprecated() -> MockConfigEntry:
|
||||||
|
"""Return the default mocked config entry."""
|
||||||
|
return MockConfigEntry(
|
||||||
|
title="Home",
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={CONF_ZONE: "zone.home",
|
||||||
|
CONF_STYLE: OPTION_STYLE_STD,
|
||||||
|
CONF_DARK_MODE: True,
|
||||||
|
CONF_USE_DEPRECATED_FORECAST: OPTION_DEPRECATED_FORECAST_TWICE_DAILY},
|
||||||
|
unique_id="zone.home",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_setup_entry() -> Generator[None, None, None]:
|
def mock_setup_entry() -> Generator[None, None, None]:
|
||||||
"""Mock setting up a config entry."""
|
"""Mock setting up a config entry."""
|
||||||
|
@ -196,6 +210,23 @@ def mock_image_and_high_temp_irm_kmi_api(request: pytest.FixtureRequest) -> Gene
|
||||||
yield irm_kmi
|
yield irm_kmi
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def mock_image_and_simple_forecast_irm_kmi_api(request: pytest.FixtureRequest) -> Generator[None, MagicMock, None]:
|
||||||
|
"""Return a mocked IrmKmi api client."""
|
||||||
|
fixture: str = "forecast.json"
|
||||||
|
|
||||||
|
forecast = json.loads(load_fixture(fixture))
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"custom_components.irm_kmi.coordinator.IrmKmiApiClient", autospec=True
|
||||||
|
) as irm_kmi_api_mock:
|
||||||
|
irm_kmi = irm_kmi_api_mock.return_value
|
||||||
|
irm_kmi.get_image.side_effect = patched
|
||||||
|
irm_kmi.get_svg.return_value = ""
|
||||||
|
irm_kmi.get_forecasts_coord.return_value = forecast
|
||||||
|
yield irm_kmi
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def mock_svg_pollen(request: pytest.FixtureRequest) -> Generator[None, MagicMock, None]:
|
def mock_svg_pollen(request: pytest.FixtureRequest) -> Generator[None, MagicMock, None]:
|
||||||
"""Return a mocked IrmKmi api client."""
|
"""Return a mocked IrmKmi api client."""
|
||||||
|
|
|
@ -67,3 +67,27 @@ async def test_weather_higher_temp_at_night(
|
||||||
for f in result:
|
for f in result:
|
||||||
if f['native_temperature'] is not None and f['native_templow'] is not None:
|
if f['native_temperature'] is not None and f['native_templow'] is not None:
|
||||||
assert f['native_temperature'] >= f['native_templow']
|
assert f['native_temperature'] >= f['native_templow']
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time(datetime.fromisoformat("2023-12-26T18:30:00+01:00"))
|
||||||
|
async def test_forecast_attribute_same_as_service_call(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_image_and_simple_forecast_irm_kmi_api: AsyncMock,
|
||||||
|
mock_config_entry_with_deprecated: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
hass.states.async_set(
|
||||||
|
"zone.home",
|
||||||
|
0,
|
||||||
|
{"latitude": 50.738681639, "longitude": 4.054077148},
|
||||||
|
)
|
||||||
|
hass.config.config_dir = os.getcwd()
|
||||||
|
|
||||||
|
coordinator = IrmKmiCoordinator(hass, mock_config_entry_with_deprecated)
|
||||||
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
|
weather = IrmKmiWeather(coordinator, mock_config_entry_with_deprecated)
|
||||||
|
|
||||||
|
result_service: List[Forecast] = await weather.async_forecast_twice_daily()
|
||||||
|
result_forecast: List[Forecast] = weather.extra_state_attributes['forecast']
|
||||||
|
|
||||||
|
assert result_service == result_forecast
|
||||||
|
|
Loading…
Add table
Reference in a new issue