Keep forecast attribute

This commit is contained in:
Jules 2024-04-06 17:05:36 +02:00
parent 0ae257aa4c
commit 4d45275c64
Signed by: jdejaegh
GPG key ID: 99D6D184CA66933A
7 changed files with 96 additions and 17 deletions

View file

@ -48,11 +48,13 @@ STYLE_TO_PARAM_MAP: Final = {
CONF_USE_DEPRECATED_FORECAST: Final = 'use_deprecated_forecast_attribute'
OPTION_DEPRECATED_FORECAST_NOT_USED: Final = 'do_not_use_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 = [
OPTION_DEPRECATED_FORECAST_NOT_USED,
OPTION_DEPRECATED_FORECAST_DAILY,
OPTION_DEPRECATED_FORECAST_TWICE_DAILY,
OPTION_DEPRECATED_FORECAST_HOURLY
]

View file

@ -34,7 +34,8 @@
"options": {
"do_not_use_deprecated_forecast": "Do not use (recommended)",
"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": {

View file

@ -34,7 +34,8 @@
"options": {
"do_not_use_deprecated_forecast": "Ne pas utiliser (recommandé)",
"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": {

View file

@ -34,7 +34,8 @@
"options": {
"do_not_use_deprecated_forecast": "Niet gebruiken (aanbevolen)",
"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": {

View file

@ -1,4 +1,5 @@
"""Support for IRM KMI weather."""
import copy
import logging
from typing import List
@ -14,7 +15,8 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import CONF_USE_DEPRECATED_FORECAST, DOMAIN
from .const import (OPTION_DEPRECATED_FORECAST_DAILY,
OPTION_DEPRECATED_FORECAST_HOURLY,
OPTION_DEPRECATED_FORECAST_NOT_USED)
OPTION_DEPRECATED_FORECAST_NOT_USED,
OPTION_DEPRECATED_FORECAST_TWICE_DAILY)
from .coordinator import IrmKmiCoordinator
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)
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
def supported_features(self) -> WeatherEntityFeature:
features = WeatherEntityFeature(0)
@ -98,17 +105,6 @@ class IrmKmiWeather(CoordinatorEntity, WeatherEntity):
def uv_index(self) -> float | None:
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:
return self.coordinator.data.get('daily_forecast')
@ -137,3 +133,26 @@ class IrmKmiWeather(CoordinatorEntity, WeatherEntity):
(data[0]['native_temperature'], data[0]['native_templow'])
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}

View file

@ -14,7 +14,7 @@ from custom_components.irm_kmi.api import (IrmKmiApiError,
IrmKmiApiParametersError)
from custom_components.irm_kmi.const import (
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:
@ -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
def mock_setup_entry() -> Generator[None, None, None]:
"""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
@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()
def mock_svg_pollen(request: pytest.FixtureRequest) -> Generator[None, MagicMock, None]:
"""Return a mocked IrmKmi api client."""

View file

@ -67,3 +67,27 @@ async def test_weather_higher_temp_at_night(
for f in result:
if f['native_temperature'] is not None and f['native_templow'] is not None:
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