From 65230908c6bd7b9f2dac6e8de41b7bd27d28d1be Mon Sep 17 00:00:00 2001 From: G Johansson <goran.johansson@shiftit.se> Date: Wed, 27 Mar 2024 16:51:29 +0100 Subject: [PATCH] Remove deprecated forecast attribute from WeatherEntity (#110761) * Remove deprecated forecast attribute from WeatherEntity * Fix some * Ruff * ipma * buienradar * some more * Some more * More and more * strings * attr_forecast * Fix nws * Fix * remove from coverage * Remove recorder test * Review comments --- .coveragerc | 2 + .../components/accuweather/weather.py | 11 +- .../components/buienradar/weather.py | 5 +- homeassistant/components/ecobee/weather.py | 5 - .../components/environment_canada/weather.py | 5 - homeassistant/components/ipma/weather.py | 7 - .../components/kitchen_sink/weather.py | 60 -- homeassistant/components/met/weather.py | 5 - .../components/met_eireann/weather.py | 5 - .../components/meteo_france/weather.py | 5 - homeassistant/components/metoffice/weather.py | 8 - homeassistant/components/nws/weather.py | 5 - .../components/open_meteo/weather.py | 11 +- .../components/openweathermap/weather.py | 6 +- homeassistant/components/smhi/weather.py | 29 - homeassistant/components/template/weather.py | 13 - .../components/tomorrowio/weather.py | 5 - homeassistant/components/weather/__init__.py | 102 ---- homeassistant/components/weather/strings.json | 8 - tests/components/accuweather/test_weather.py | 60 +- tests/components/ipma/test_weather.py | 55 -- tests/components/metoffice/test_weather.py | 39 -- tests/components/nws/test_weather.py | 12 - .../smhi/snapshots/test_weather.ambr | 481 ---------------- tests/components/smhi/test_weather.py | 16 +- .../template/snapshots/test_weather.ambr | 30 - tests/components/template/test_weather.py | 10 +- tests/components/tomorrowio/test_weather.py | 37 -- tests/components/weather/test_init.py | 532 +----------------- tests/components/weather/test_recorder.py | 59 -- .../custom_components/test/weather.py | 5 - 31 files changed, 37 insertions(+), 1596 deletions(-) delete mode 100644 tests/components/weather/test_recorder.py diff --git a/.coveragerc b/.coveragerc index 306d06c4de0..b88db04035a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -956,7 +956,9 @@ omit = homeassistant/components/openuv/binary_sensor.py homeassistant/components/openuv/coordinator.py homeassistant/components/openuv/sensor.py + homeassistant/components/openweathermap/__init__.py homeassistant/components/openweathermap/sensor.py + homeassistant/components/openweathermap/weather.py homeassistant/components/openweathermap/weather_update_coordinator.py homeassistant/components/opnsense/__init__.py homeassistant/components/opower/__init__.py diff --git a/homeassistant/components/accuweather/weather.py b/homeassistant/components/accuweather/weather.py index a734847fa18..1f2e606f6ea 100644 --- a/homeassistant/components/accuweather/weather.py +++ b/homeassistant/components/accuweather/weather.py @@ -146,9 +146,9 @@ class AccuWeatherEntity( """Return the UV index.""" return cast(float, self.coordinator.data["UVIndex"]) - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast array.""" + @callback + def _async_forecast_daily(self) -> list[Forecast] | None: + """Return the daily forecast in native units.""" if not self.coordinator.forecast: return None # remap keys from library to keys understood by the weather component @@ -177,8 +177,3 @@ class AccuWeatherEntity( } for item in self.coordinator.data[ATTR_FORECAST] ] - - @callback - def _async_forecast_daily(self) -> list[Forecast] | None: - """Return the daily forecast in native units.""" - return self.forecast diff --git a/homeassistant/components/buienradar/weather.py b/homeassistant/components/buienradar/weather.py index a7a6d3dc02e..02e1f444c9c 100644 --- a/homeassistant/components/buienradar/weather.py +++ b/homeassistant/components/buienradar/weather.py @@ -138,13 +138,14 @@ class BrWeather(WeatherEntity): self._attr_unique_id = ( f"{coordinates[CONF_LATITUDE]:2.6f}{coordinates[CONF_LONGITUDE]:2.6f}" ) + self._forecast: list | None = None @callback def data_updated(self, data: BrData) -> None: """Update data.""" self._attr_attribution = data.attribution self._attr_condition = self._calc_condition(data) - self._attr_forecast = self._calc_forecast(data) + self._forecast = self._calc_forecast(data) self._attr_humidity = data.humidity self._attr_name = ( self._stationname or f"BR {data.stationname or '(unknown station)'}" @@ -196,4 +197,4 @@ class BrWeather(WeatherEntity): async def async_forecast_daily(self) -> list[Forecast] | None: """Return the daily forecast in native units.""" - return self._attr_forecast + return self._forecast diff --git a/homeassistant/components/ecobee/weather.py b/homeassistant/components/ecobee/weather.py index 743ee12ddc7..b7961f956eb 100644 --- a/homeassistant/components/ecobee/weather.py +++ b/homeassistant/components/ecobee/weather.py @@ -184,11 +184,6 @@ class EcobeeWeather(WeatherEntity): return forecasts return None - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast array.""" - return self._forecast() - async def async_forecast_daily(self) -> list[Forecast] | None: """Return the daily forecast in native units.""" return self._forecast() diff --git a/homeassistant/components/environment_canada/weather.py b/homeassistant/components/environment_canada/weather.py index 750e3172178..643e7951c23 100644 --- a/homeassistant/components/environment_canada/weather.py +++ b/homeassistant/components/environment_canada/weather.py @@ -174,11 +174,6 @@ class ECWeather(SingleCoordinatorWeatherEntity): return icon_code_to_condition(int(icon_code)) return "" - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast array.""" - return get_forecast(self.ec_data, False) - @callback def _async_forecast_daily(self) -> list[Forecast] | None: """Return the daily forecast in native units.""" diff --git a/homeassistant/components/ipma/weather.py b/homeassistant/components/ipma/weather.py index cd4c650beef..ff6d8c3e86c 100644 --- a/homeassistant/components/ipma/weather.py +++ b/homeassistant/components/ipma/weather.py @@ -205,13 +205,6 @@ class IPMAWeather(WeatherEntity, IPMADevice): for data_in in forecast ] - @property - def forecast(self) -> list[Forecast]: - """Return the forecast array.""" - return self._forecast( - self._hourly_forecast if self._period == 1 else self._daily_forecast - ) - async def _try_update_forecast( self, forecast_type: Literal["daily", "hourly"], diff --git a/homeassistant/components/kitchen_sink/weather.py b/homeassistant/components/kitchen_sink/weather.py index 0ade3e73889..8a12cb4bdb9 100644 --- a/homeassistant/components/kitchen_sink/weather.py +++ b/homeassistant/components/kitchen_sink/weather.py @@ -61,29 +61,6 @@ async def async_setup_entry( """Set up the Demo config entry.""" async_add_entities( [ - DemoWeather( - "Legacy weather", - "Sunshine", - 21.6414, - 92, - 1099, - 0.5, - UnitOfTemperature.CELSIUS, - UnitOfPressure.HPA, - UnitOfSpeed.METERS_PER_SECOND, - [ - [ATTR_CONDITION_RAINY, 1, 22, 15, 60], - [ATTR_CONDITION_RAINY, 5, 19, 8, 30], - [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10], - [ATTR_CONDITION_SUNNY, 0, 12, 6, 0], - [ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20], - [ATTR_CONDITION_RAINY, 15, 18, 7, 0], - [ATTR_CONDITION_FOG, 0.2, 21, 12, 100], - ], - None, - None, - None, - ), DemoWeather( "Legacy + daily weather", "Sunshine", @@ -103,15 +80,6 @@ async def async_setup_entry( [ATTR_CONDITION_RAINY, 15, 18, 7, 0], [ATTR_CONDITION_FOG, 0.2, 21, 12, 100], ], - [ - [ATTR_CONDITION_RAINY, 1, 22, 15, 60], - [ATTR_CONDITION_RAINY, 5, 19, 8, 30], - [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10], - [ATTR_CONDITION_SUNNY, 0, 12, 6, 0], - [ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20], - [ATTR_CONDITION_RAINY, 15, 18, 7, 0], - [ATTR_CONDITION_FOG, 0.2, 21, 12, 100], - ], None, None, ), @@ -125,7 +93,6 @@ async def async_setup_entry( UnitOfTemperature.FAHRENHEIT, UnitOfPressure.INHG, UnitOfSpeed.MILES_PER_HOUR, - None, [ [ATTR_CONDITION_SNOWY, 2, -10, -15, 60], [ATTR_CONDITION_PARTLYCLOUDY, 1, -13, -14, 25], @@ -156,7 +123,6 @@ async def async_setup_entry( UnitOfTemperature.CELSIUS, UnitOfPressure.HPA, UnitOfSpeed.METERS_PER_SECOND, - None, [ [ATTR_CONDITION_RAINY, 1, 22, 15, 60], [ATTR_CONDITION_RAINY, 5, 19, 8, 30], @@ -196,7 +162,6 @@ async def async_setup_entry( UnitOfPressure.HPA, UnitOfSpeed.METERS_PER_SECOND, None, - None, [ [ATTR_CONDITION_CLOUDY, 1, 22, 15, 60], [ATTR_CONDITION_CLOUDY, 5, 19, 8, 30], @@ -226,7 +191,6 @@ async def async_setup_entry( UnitOfTemperature.CELSIUS, UnitOfPressure.HPA, UnitOfSpeed.METERS_PER_SECOND, - None, [ [ATTR_CONDITION_RAINY, 1, 22, 15, 60], [ATTR_CONDITION_RAINY, 5, 19, 8, 30], @@ -268,7 +232,6 @@ class DemoWeather(WeatherEntity): temperature_unit: str, pressure_unit: str, wind_speed_unit: str, - forecast: list[list] | None, forecast_daily: list[list] | None, forecast_hourly: list[list] | None, forecast_twice_daily: list[list] | None, @@ -284,7 +247,6 @@ class DemoWeather(WeatherEntity): self._native_pressure_unit = pressure_unit self._native_wind_speed = wind_speed self._native_wind_speed_unit = wind_speed_unit - self._forecast = forecast self._forecast_daily = forecast_daily self._forecast_hourly = forecast_hourly self._forecast_twice_daily = forecast_twice_daily @@ -360,28 +322,6 @@ class DemoWeather(WeatherEntity): """Return the weather condition.""" return CONDITION_MAP[self._condition.lower()] - @property - def forecast(self) -> list[Forecast]: - """Return legacy forecast.""" - if self._forecast is None: - return [] - reftime = dt_util.now().replace(hour=16, minute=00) - - forecast_data = [] - for entry in self._forecast: - data_dict = Forecast( - datetime=reftime.isoformat(), - condition=entry[0], - precipitation=entry[1], - temperature=entry[2], - templow=entry[3], - precipitation_probability=entry[4], - ) - reftime = reftime + timedelta(hours=24) - forecast_data.append(data_dict) - - return forecast_data - async def async_forecast_daily(self) -> list[Forecast]: """Return the daily forecast.""" if self._forecast_daily is None: diff --git a/homeassistant/components/met/weather.py b/homeassistant/components/met/weather.py index 86ef77f03f6..d0ee4f275ea 100644 --- a/homeassistant/components/met/weather.py +++ b/homeassistant/components/met/weather.py @@ -231,11 +231,6 @@ class MetWeather(SingleCoordinatorWeatherEntity[MetDataUpdateCoordinator]): ha_forecast.append(ha_item) # type: ignore[arg-type] return ha_forecast - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast array.""" - return self._forecast(False) - @callback def _async_forecast_daily(self) -> list[Forecast] | None: """Return the daily forecast in native units.""" diff --git a/homeassistant/components/met_eireann/weather.py b/homeassistant/components/met_eireann/weather.py index ed226714c1d..404ef5d8393 100644 --- a/homeassistant/components/met_eireann/weather.py +++ b/homeassistant/components/met_eireann/weather.py @@ -173,11 +173,6 @@ class MetEireannWeather( ha_forecast.append(ha_item) return ha_forecast - @property - def forecast(self) -> list[Forecast]: - """Return the forecast array.""" - return self._forecast(False) - @callback def _async_forecast_daily(self) -> list[Forecast]: """Return the daily forecast in native units.""" diff --git a/homeassistant/components/meteo_france/weather.py b/homeassistant/components/meteo_france/weather.py index 8454d7672a3..9edc557aafc 100644 --- a/homeassistant/components/meteo_france/weather.py +++ b/homeassistant/components/meteo_france/weather.py @@ -216,11 +216,6 @@ class MeteoFranceWeather( ) return forecast_data - @property - def forecast(self) -> list[Forecast]: - """Return the forecast array.""" - return self._forecast(self._mode) - async def async_forecast_daily(self) -> list[Forecast]: """Return the daily forecast in native units.""" return self._forecast(FORECAST_MODE_DAILY) diff --git a/homeassistant/components/metoffice/weather.py b/homeassistant/components/metoffice/weather.py index 19df765f1f9..33fec874611 100644 --- a/homeassistant/components/metoffice/weather.py +++ b/homeassistant/components/metoffice/weather.py @@ -181,14 +181,6 @@ class MetOfficeWeather( return str(value) if value is not None else None return None - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast array.""" - return [ - _build_forecast_data(timestep) - for timestep in self.coordinator.data.forecast - ] - @callback def _async_forecast_daily(self) -> list[Forecast] | None: """Return the twice daily forecast in native units.""" diff --git a/homeassistant/components/nws/weather.py b/homeassistant/components/nws/weather.py index 0c76c31887c..89414f5acf1 100644 --- a/homeassistant/components/nws/weather.py +++ b/homeassistant/components/nws/weather.py @@ -299,11 +299,6 @@ class NWSWeather(CoordinatorWeatherEntity): forecast.append(data) return forecast - @property - def forecast(self) -> list[Forecast] | None: - """Return forecast.""" - return self._forecast(self._forecast_legacy, DAYNIGHT) - @callback def _async_forecast_hourly(self) -> list[Forecast] | None: """Return the hourly forecast in native units.""" diff --git a/homeassistant/components/open_meteo/weather.py b/homeassistant/components/open_meteo/weather.py index 850ce61bcb5..8ee3edd5183 100644 --- a/homeassistant/components/open_meteo/weather.py +++ b/homeassistant/components/open_meteo/weather.py @@ -88,9 +88,9 @@ class OpenMeteoWeatherEntity( return None return self.coordinator.data.current_weather.wind_direction - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast in native units.""" + @callback + def _async_forecast_daily(self) -> list[Forecast] | None: + """Return the daily forecast in native units.""" if self.coordinator.data.daily is None: return None @@ -124,8 +124,3 @@ class OpenMeteoWeatherEntity( forecasts.append(forecast) return forecasts - - @callback - def _async_forecast_daily(self) -> list[Forecast] | None: - """Return the daily forecast in native units.""" - return self.forecast diff --git a/homeassistant/components/openweathermap/weather.py b/homeassistant/components/openweathermap/weather.py index 063415d0762..62bf18ba813 100644 --- a/homeassistant/components/openweathermap/weather.py +++ b/homeassistant/components/openweathermap/weather.py @@ -185,7 +185,7 @@ class OpenWeatherMapWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordina return self.coordinator.data[ATTR_API_WIND_BEARING] @property - def forecast(self) -> list[Forecast] | None: + def _forecast(self) -> list[Forecast] | None: """Return the forecast array.""" api_forecasts = self.coordinator.data[ATTR_API_FORECAST] forecasts = [ @@ -201,9 +201,9 @@ class OpenWeatherMapWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordina @callback def _async_forecast_daily(self) -> list[Forecast] | None: """Return the daily forecast in native units.""" - return self.forecast + return self._forecast @callback def _async_forecast_hourly(self) -> list[Forecast] | None: """Return the hourly forecast in native units.""" - return self.forecast + return self._forecast diff --git a/homeassistant/components/smhi/weather.py b/homeassistant/components/smhi/weather.py index 9204816c595..bf069f4b26a 100644 --- a/homeassistant/components/smhi/weather.py +++ b/homeassistant/components/smhi/weather.py @@ -195,35 +195,6 @@ class SmhiWeather(WeatherEntity): """Retry refresh weather forecast.""" await self.async_update(no_throttle=True) - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - if self._forecast_daily is None or len(self._forecast_daily) < 2: - return None - - data: list[Forecast] = [] - - for forecast in self._forecast_daily[1:]: - condition = CONDITION_MAP.get(forecast.symbol) - - data.append( - { - ATTR_FORECAST_TIME: forecast.valid_time.isoformat(), - ATTR_FORECAST_NATIVE_TEMP: forecast.temperature_max, - ATTR_FORECAST_NATIVE_TEMP_LOW: forecast.temperature_min, - ATTR_FORECAST_NATIVE_PRECIPITATION: forecast.total_precipitation, - ATTR_FORECAST_CONDITION: condition, - ATTR_FORECAST_NATIVE_PRESSURE: forecast.pressure, - ATTR_FORECAST_WIND_BEARING: forecast.wind_direction, - ATTR_FORECAST_NATIVE_WIND_SPEED: forecast.wind_speed, - ATTR_FORECAST_HUMIDITY: forecast.humidity, - ATTR_FORECAST_NATIVE_WIND_GUST_SPEED: forecast.wind_gust, - ATTR_FORECAST_CLOUD_COVERAGE: forecast.cloudiness, - } - ) - - return data - def _get_forecast_data( self, forecast_data: list[SmhiForecast] | None ) -> list[Forecast] | None: diff --git a/homeassistant/components/template/weather.py b/homeassistant/components/template/weather.py index 6f6861830c7..e8981fb33f9 100644 --- a/homeassistant/components/template/weather.py +++ b/homeassistant/components/template/weather.py @@ -118,7 +118,6 @@ WEATHER_SCHEMA = vol.Schema( vol.Optional(CONF_WIND_BEARING_TEMPLATE): cv.template, vol.Optional(CONF_OZONE_TEMPLATE): cv.template, vol.Optional(CONF_VISIBILITY_TEMPLATE): cv.template, - vol.Optional(CONF_FORECAST_TEMPLATE): cv.template, vol.Optional(CONF_FORECAST_DAILY_TEMPLATE): cv.template, vol.Optional(CONF_FORECAST_HOURLY_TEMPLATE): cv.template, vol.Optional(CONF_FORECAST_TWICE_DAILY_TEMPLATE): cv.template, @@ -193,7 +192,6 @@ class WeatherTemplate(TemplateEntity, WeatherEntity): self._wind_bearing_template = config.get(CONF_WIND_BEARING_TEMPLATE) self._ozone_template = config.get(CONF_OZONE_TEMPLATE) self._visibility_template = config.get(CONF_VISIBILITY_TEMPLATE) - self._forecast_template = config.get(CONF_FORECAST_TEMPLATE) self._forecast_daily_template = config.get(CONF_FORECAST_DAILY_TEMPLATE) self._forecast_hourly_template = config.get(CONF_FORECAST_HOURLY_TEMPLATE) self._forecast_twice_daily_template = config.get( @@ -227,7 +225,6 @@ class WeatherTemplate(TemplateEntity, WeatherEntity): self._cloud_coverage = None self._dew_point = None self._apparent_temperature = None - self._forecast: list[Forecast] = [] self._forecast_daily: list[Forecast] = [] self._forecast_hourly: list[Forecast] = [] self._forecast_twice_daily: list[Forecast] = [] @@ -300,11 +297,6 @@ class WeatherTemplate(TemplateEntity, WeatherEntity): """Return the apparent temperature.""" return self._apparent_temperature - @property - def forecast(self) -> list[Forecast]: - """Return the forecast.""" - return self._forecast - async def async_forecast_daily(self) -> list[Forecast]: """Return the daily forecast in native units.""" return self._forecast_daily @@ -394,11 +386,6 @@ class WeatherTemplate(TemplateEntity, WeatherEntity): "_apparent_temperature", self._apparent_temperature_template, ) - if self._forecast_template: - self.add_template_attribute( - "_forecast", - self._forecast_template, - ) if self._forecast_daily_template: self.add_template_attribute( diff --git a/homeassistant/components/tomorrowio/weather.py b/homeassistant/components/tomorrowio/weather.py index cc46fa3dfab..3b60f171bbe 100644 --- a/homeassistant/components/tomorrowio/weather.py +++ b/homeassistant/components/tomorrowio/weather.py @@ -298,11 +298,6 @@ class TomorrowioWeatherEntity(TomorrowioEntity, SingleCoordinatorWeatherEntity): return forecasts - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast array.""" - return self._forecast(self.forecast_type) - @callback def _async_forecast_daily(self) -> list[Forecast] | None: """Return the daily forecast in native units.""" diff --git a/homeassistant/components/weather/__init__.py b/homeassistant/components/weather/__init__.py index 59e01f03e67..404154ade2b 100644 --- a/homeassistant/components/weather/__init__.py +++ b/homeassistant/components/weather/__init__.py @@ -3,7 +3,6 @@ from __future__ import annotations import abc -import asyncio from collections.abc import Callable, Iterable from contextlib import suppress from datetime import timedelta @@ -48,7 +47,6 @@ from homeassistant.helpers.config_validation import ( # noqa: F401 ) from homeassistant.helpers.entity import ABCCachedProperties, Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.entity_platform import EntityPlatform import homeassistant.helpers.issue_registry as ir from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import ( @@ -56,7 +54,6 @@ from homeassistant.helpers.update_coordinator import ( DataUpdateCoordinator, TimestampDataUpdateCoordinator, ) -from homeassistant.loader import async_get_issue_tracker, async_suggest_report_issue from homeassistant.util.dt import utcnow from homeassistant.util.json import JsonValueType from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM @@ -111,7 +108,6 @@ ATTR_CONDITION_SNOWY_RAINY = "snowy-rainy" ATTR_CONDITION_SUNNY = "sunny" ATTR_CONDITION_WINDY = "windy" ATTR_CONDITION_WINDY_VARIANT = "windy-variant" -ATTR_FORECAST = "forecast" ATTR_FORECAST_IS_DAYTIME: Final = "is_daytime" ATTR_FORECAST_CONDITION: Final = "condition" ATTR_FORECAST_HUMIDITY: Final = "humidity" @@ -306,13 +302,8 @@ CACHED_PROPERTIES_WITH_ATTR_ = { class WeatherEntity(Entity, PostInit, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): """ABC for weather data.""" - _entity_component_unrecorded_attributes = frozenset({ATTR_FORECAST}) - entity_description: WeatherEntityDescription _attr_condition: str | None = None - # _attr_forecast is deprecated, implement async_forecast_daily, - # async_forecast_hourly or async_forecast_twice daily instead - _attr_forecast: list[Forecast] | None = None _attr_humidity: float | None = None _attr_ozone: float | None = None _attr_cloud_coverage: int | None = None @@ -338,8 +329,6 @@ class WeatherEntity(Entity, PostInit, cached_properties=CACHED_PROPERTIES_WITH_A Literal["daily", "hourly", "twice_daily"], list[Callable[[list[JsonValueType] | None], None]], ] - __weather_reported_legacy_forecast = False - __weather_legacy_forecast = False _weather_option_temperature_unit: str | None = None _weather_option_pressure_unit: str | None = None @@ -351,77 +340,6 @@ class WeatherEntity(Entity, PostInit, cached_properties=CACHED_PROPERTIES_WITH_A """Finish initializing.""" self._forecast_listeners = {"daily": [], "hourly": [], "twice_daily": []} - def __init_subclass__(cls, **kwargs: Any) -> None: - """Post initialisation processing.""" - super().__init_subclass__(**kwargs) - if ( - "forecast" in cls.__dict__ - and cls.async_forecast_daily is WeatherEntity.async_forecast_daily - and cls.async_forecast_hourly is WeatherEntity.async_forecast_hourly - and cls.async_forecast_twice_daily - is WeatherEntity.async_forecast_twice_daily - ): - cls.__weather_legacy_forecast = True - - @callback - def add_to_platform_start( - self, - hass: HomeAssistant, - platform: EntityPlatform, - parallel_updates: asyncio.Semaphore | None, - ) -> None: - """Start adding an entity to a platform.""" - super().add_to_platform_start(hass, platform, parallel_updates) - if self.__weather_legacy_forecast: - self._report_legacy_forecast(hass) - - def _report_legacy_forecast(self, hass: HomeAssistant) -> None: - """Log warning and create an issue if the entity imlpements legacy forecast.""" - if "custom_components" not in type(self).__module__: - # Do not report core integrations as they are already fixed or PR is open. - return - - report_issue = async_suggest_report_issue( - hass, - integration_domain=self.platform.platform_name, - module=type(self).__module__, - ) - _LOGGER.warning( - ( - "%s::%s implements the `forecast` property or sets " - "`self._attr_forecast` in a subclass of WeatherEntity, this is " - "deprecated and will be unsupported from Home Assistant 2024.3." - " Please %s" - ), - self.platform.platform_name, - self.__class__.__name__, - report_issue, - ) - - translation_placeholders = {"platform": self.platform.platform_name} - translation_key = "deprecated_weather_forecast_no_url" - issue_tracker = async_get_issue_tracker( - hass, - integration_domain=self.platform.platform_name, - module=type(self).__module__, - ) - if issue_tracker: - translation_placeholders["issue_tracker"] = issue_tracker - translation_key = "deprecated_weather_forecast_url" - ir.async_create_issue( - self.hass, - DOMAIN, - f"deprecated_weather_forecast_{self.platform.platform_name}", - breaks_in_ha_version="2024.3.0", - is_fixable=False, - is_persistent=False, - issue_domain=self.platform.platform_name, - severity=ir.IssueSeverity.WARNING, - translation_key=translation_key, - translation_placeholders=translation_placeholders, - ) - self.__weather_reported_legacy_forecast = True - async def async_internal_added_to_hass(self) -> None: """Call when the weather entity is added to hass.""" await super().async_internal_added_to_hass() @@ -605,23 +523,6 @@ class WeatherEntity(Entity, PostInit, cached_properties=CACHED_PROPERTIES_WITH_A return self._default_visibility_unit - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast in native units. - - Should not be overridden by integrations. Kept for backwards compatibility. - """ - if ( - self._attr_forecast is not None - and type(self).async_forecast_daily is WeatherEntity.async_forecast_daily - and type(self).async_forecast_hourly is WeatherEntity.async_forecast_hourly - and type(self).async_forecast_twice_daily - is WeatherEntity.async_forecast_twice_daily - and not self.__weather_reported_legacy_forecast - ): - self._report_legacy_forecast(self.hass) - return self._attr_forecast - async def async_forecast_daily(self) -> list[Forecast] | None: """Return the daily forecast in native units.""" raise NotImplementedError @@ -804,9 +705,6 @@ class WeatherEntity(Entity, PostInit, cached_properties=CACHED_PROPERTIES_WITH_A data[ATTR_WEATHER_VISIBILITY_UNIT] = self._visibility_unit data[ATTR_WEATHER_PRECIPITATION_UNIT] = self._precipitation_unit - if self.forecast: - data[ATTR_FORECAST] = self._convert_forecast(self.forecast) - return data @final diff --git a/homeassistant/components/weather/strings.json b/homeassistant/components/weather/strings.json index 8879bf158f3..77c9cce864b 100644 --- a/homeassistant/components/weather/strings.json +++ b/homeassistant/components/weather/strings.json @@ -110,14 +110,6 @@ } }, "issues": { - "deprecated_weather_forecast_url": { - "title": "The {platform} custom integration is using deprecated weather forecast", - "description": "The custom integration `{platform}` implements the `forecast` property or sets `self._attr_forecast` in a subclass of WeatherEntity.\n\nPlease create a bug report at {issue_tracker}.\n\nOnce an updated version of `{platform}` is available, install it and restart Home Assistant to fix this issue." - }, - "deprecated_weather_forecast_no_url": { - "title": "[%key:component::weather::issues::deprecated_weather_forecast_url::title%]", - "description": "The custom integration `{platform}` implements the `forecast` property or sets `self._attr_forecast` in a subclass of WeatherEntity.\n\nPlease report it to the author of the {platform} integration.\n\nOnce an updated version of `{platform}` is available, install it and restart Home Assistant to fix this issue." - }, "deprecated_service_weather_get_forecast": { "title": "Detected use of deprecated service `weather.get_forecast`", "fix_flow": { diff --git a/tests/components/accuweather/test_weather.py b/tests/components/accuweather/test_weather.py index 291c1c1dcdf..0b9d3e28fb2 100644 --- a/tests/components/accuweather/test_weather.py +++ b/tests/components/accuweather/test_weather.py @@ -9,18 +9,7 @@ from syrupy.assertion import SnapshotAssertion from homeassistant.components.accuweather.const import ATTRIBUTION from homeassistant.components.weather import ( - ATTR_FORECAST, - ATTR_FORECAST_APPARENT_TEMP, - ATTR_FORECAST_CLOUD_COVERAGE, ATTR_FORECAST_CONDITION, - ATTR_FORECAST_PRECIPITATION, - ATTR_FORECAST_PRECIPITATION_PROBABILITY, - ATTR_FORECAST_TEMP, - ATTR_FORECAST_TEMP_LOW, - ATTR_FORECAST_TIME, - ATTR_FORECAST_WIND_BEARING, - ATTR_FORECAST_WIND_GUST_SPEED, - ATTR_FORECAST_WIND_SPEED, ATTR_WEATHER_APPARENT_TEMPERATURE, ATTR_WEATHER_CLOUD_COVERAGE, ATTR_WEATHER_DEW_POINT, @@ -35,7 +24,6 @@ from homeassistant.components.weather import ( DOMAIN as WEATHER_DOMAIN, LEGACY_SERVICE_GET_FORECAST, SERVICE_GET_FORECASTS, - WeatherEntityFeature, ) from homeassistant.const import ( ATTR_ATTRIBUTION, @@ -58,16 +46,13 @@ from tests.common import ( from tests.typing import WebSocketGenerator -async def test_weather_without_forecast( - hass: HomeAssistant, entity_registry: er.EntityRegistry -) -> None: +async def test_weather(hass: HomeAssistant, entity_registry: er.EntityRegistry) -> None: """Test states of the weather without forecast.""" await init_integration(hass) state = hass.states.get("weather.home") assert state assert state.state == "sunny" - assert not state.attributes.get(ATTR_FORECAST) assert state.attributes.get(ATTR_WEATHER_HUMIDITY) == 67 assert state.attributes.get(ATTR_WEATHER_PRESSURE) == 1012.0 assert state.attributes.get(ATTR_WEATHER_TEMPERATURE) == 22.6 @@ -87,49 +72,6 @@ async def test_weather_without_forecast( assert entry.unique_id == "0123456" -async def test_weather_with_forecast( - hass: HomeAssistant, entity_registry: er.EntityRegistry -) -> None: - """Test states of the weather with forecast.""" - await init_integration(hass, forecast=True) - - state = hass.states.get("weather.home") - assert state - assert state.state == "sunny" - assert state.attributes.get(ATTR_WEATHER_HUMIDITY) == 67 - assert state.attributes.get(ATTR_WEATHER_PRESSURE) == 1012.0 - assert state.attributes.get(ATTR_WEATHER_TEMPERATURE) == 22.6 - assert state.attributes.get(ATTR_WEATHER_VISIBILITY) == 16.1 - assert state.attributes.get(ATTR_WEATHER_WIND_BEARING) == 180 - assert state.attributes.get(ATTR_WEATHER_WIND_SPEED) == 14.5 # 4.03 m/s -> km/h - assert state.attributes.get(ATTR_WEATHER_APPARENT_TEMPERATURE) == 22.8 - assert state.attributes.get(ATTR_WEATHER_DEW_POINT) == 16.2 - assert state.attributes.get(ATTR_WEATHER_CLOUD_COVERAGE) == 10 - assert state.attributes.get(ATTR_WEATHER_WIND_GUST_SPEED) == 20.3 - assert state.attributes.get(ATTR_WEATHER_UV_INDEX) == 6 - assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION - assert ( - state.attributes[ATTR_SUPPORTED_FEATURES] == WeatherEntityFeature.FORECAST_DAILY - ) - forecast = state.attributes.get(ATTR_FORECAST)[0] - assert forecast.get(ATTR_FORECAST_CONDITION) == "lightning-rainy" - assert forecast.get(ATTR_FORECAST_PRECIPITATION) == 2.5 - assert forecast.get(ATTR_FORECAST_PRECIPITATION_PROBABILITY) == 60 - assert forecast.get(ATTR_FORECAST_TEMP) == 29.5 - assert forecast.get(ATTR_FORECAST_TEMP_LOW) == 15.4 - assert forecast.get(ATTR_FORECAST_TIME) == "2020-07-26T05:00:00+00:00" - assert forecast.get(ATTR_FORECAST_WIND_BEARING) == 166 - assert forecast.get(ATTR_FORECAST_WIND_SPEED) == 13.0 # 3.61 m/s -> km/h - assert forecast.get(ATTR_FORECAST_CLOUD_COVERAGE) == 58 - assert forecast.get(ATTR_FORECAST_APPARENT_TEMP) == 29.8 - assert forecast.get(ATTR_FORECAST_WIND_GUST_SPEED) == 29.6 - assert forecast.get(ATTR_WEATHER_UV_INDEX) == 5 - - entry = entity_registry.async_get("weather.home") - assert entry - assert entry.unique_id == "0123456" - - async def test_availability(hass: HomeAssistant) -> None: """Ensure that we mark the entities unavailable correctly when service is offline.""" await init_integration(hass) diff --git a/tests/components/ipma/test_weather.py b/tests/components/ipma/test_weather.py index 06f15922a2e..7150286e4f9 100644 --- a/tests/components/ipma/test_weather.py +++ b/tests/components/ipma/test_weather.py @@ -9,14 +9,6 @@ from syrupy.assertion import SnapshotAssertion from homeassistant.components.ipma.const import MIN_TIME_BETWEEN_UPDATES from homeassistant.components.weather import ( - ATTR_FORECAST, - ATTR_FORECAST_CONDITION, - ATTR_FORECAST_PRECIPITATION_PROBABILITY, - ATTR_FORECAST_TEMP, - ATTR_FORECAST_TEMP_LOW, - ATTR_FORECAST_TIME, - ATTR_FORECAST_WIND_BEARING, - ATTR_FORECAST_WIND_SPEED, ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_PRESSURE, ATTR_WEATHER_TEMPERATURE, @@ -84,53 +76,6 @@ async def test_setup_config_flow(hass: HomeAssistant) -> None: assert state.attributes.get("friendly_name") == "HomeTown" -async def test_daily_forecast(hass: HomeAssistant) -> None: - """Test for successfully getting daily forecast.""" - with patch( - "pyipma.location.Location.get", - return_value=MockLocation(), - ): - entry = MockConfigEntry(domain="ipma", data=TEST_CONFIG) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - state = hass.states.get("weather.hometown") - assert state.state == "rainy" - - forecast = state.attributes.get(ATTR_FORECAST)[0] - assert forecast.get(ATTR_FORECAST_TIME) == datetime.datetime(2020, 1, 16, 0, 0, 0) - assert forecast.get(ATTR_FORECAST_CONDITION) == "rainy" - assert forecast.get(ATTR_FORECAST_TEMP) == 16.2 - assert forecast.get(ATTR_FORECAST_TEMP_LOW) == 10.6 - assert forecast.get(ATTR_FORECAST_PRECIPITATION_PROBABILITY) == "100.0" - assert forecast.get(ATTR_FORECAST_WIND_SPEED) == 10.0 - assert forecast.get(ATTR_FORECAST_WIND_BEARING) == "S" - - -@pytest.mark.freeze_time("2020-01-14 23:00:00") -async def test_hourly_forecast(hass: HomeAssistant) -> None: - """Test for successfully getting daily forecast.""" - with patch( - "pyipma.location.Location.get", - return_value=MockLocation(), - ): - entry = MockConfigEntry(domain="ipma", data=TEST_CONFIG_HOURLY) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - state = hass.states.get("weather.hometown") - assert state.state == "rainy" - - forecast = state.attributes.get(ATTR_FORECAST)[0] - assert forecast.get(ATTR_FORECAST_CONDITION) == "rainy" - assert forecast.get(ATTR_FORECAST_TEMP) == 12.0 - assert forecast.get(ATTR_FORECAST_PRECIPITATION_PROBABILITY) == 80.0 - assert forecast.get(ATTR_FORECAST_WIND_SPEED) == 32.7 - assert forecast.get(ATTR_FORECAST_WIND_BEARING) == "S" - - async def test_failed_get_observation_forecast(hass: HomeAssistant) -> None: """Test for successfully setting up the IPMA platform.""" with patch( diff --git a/tests/components/metoffice/test_weather.py b/tests/components/metoffice/test_weather.py index 520759b46b4..2aa673d4010 100644 --- a/tests/components/metoffice/test_weather.py +++ b/tests/components/metoffice/test_weather.py @@ -163,19 +163,6 @@ async def test_one_weather_site_running( assert weather.attributes.get("wind_bearing") == "SSE" assert weather.attributes.get("humidity") == 50 - # Also has Forecasts added - again, just pick out 1 entry to check - # ensures that daily filters out multiple results per day - assert len(weather.attributes.get("forecast")) == 4 - - assert ( - weather.attributes.get("forecast")[3]["datetime"] == "2020-04-29T12:00:00+00:00" - ) - assert weather.attributes.get("forecast")[3]["condition"] == "rainy" - assert weather.attributes.get("forecast")[3]["precipitation_probability"] == 59 - assert weather.attributes.get("forecast")[3]["temperature"] == 13 - assert weather.attributes.get("forecast")[3]["wind_speed"] == 20.92 - assert weather.attributes.get("forecast")[3]["wind_bearing"] == "SE" - @pytest.mark.freeze_time(datetime.datetime(2020, 4, 25, 12, tzinfo=datetime.UTC)) async def test_two_weather_sites_running( @@ -230,19 +217,6 @@ async def test_two_weather_sites_running( assert weather.attributes.get("wind_bearing") == "SSE" assert weather.attributes.get("humidity") == 50 - # Also has Forecasts added - again, just pick out 1 entry to check - # ensures that daily filters out multiple results per day - assert len(weather.attributes.get("forecast")) == 4 - - assert ( - weather.attributes.get("forecast")[3]["datetime"] == "2020-04-29T12:00:00+00:00" - ) - assert weather.attributes.get("forecast")[3]["condition"] == "rainy" - assert weather.attributes.get("forecast")[3]["precipitation_probability"] == 59 - assert weather.attributes.get("forecast")[3]["temperature"] == 13 - assert weather.attributes.get("forecast")[3]["wind_speed"] == 20.92 - assert weather.attributes.get("forecast")[3]["wind_bearing"] == "SE" - # King's Lynn daily weather platform expected results weather = hass.states.get("weather.met_office_king_s_lynn_daily") assert weather @@ -254,19 +228,6 @@ async def test_two_weather_sites_running( assert weather.attributes.get("wind_bearing") == "ESE" assert weather.attributes.get("humidity") == 75 - # All should have Forecast added - again, just picking out 1 entry to check - # ensures daily filters out multiple results per day - assert len(weather.attributes.get("forecast")) == 4 - - assert ( - weather.attributes.get("forecast")[2]["datetime"] == "2020-04-28T12:00:00+00:00" - ) - assert weather.attributes.get("forecast")[2]["condition"] == "cloudy" - assert weather.attributes.get("forecast")[2]["precipitation_probability"] == 14 - assert weather.attributes.get("forecast")[2]["temperature"] == 11 - assert weather.attributes.get("forecast")[2]["wind_speed"] == 11.27 - assert weather.attributes.get("forecast")[2]["wind_bearing"] == "ESE" - @pytest.mark.freeze_time(datetime.datetime(2020, 4, 25, 12, tzinfo=datetime.UTC)) async def test_new_config_entry( diff --git a/tests/components/nws/test_weather.py b/tests/components/nws/test_weather.py index 1d202277e6f..0fb5654d7ee 100644 --- a/tests/components/nws/test_weather.py +++ b/tests/components/nws/test_weather.py @@ -12,7 +12,6 @@ from homeassistant.components import nws from homeassistant.components.weather import ( ATTR_CONDITION_CLEAR_NIGHT, ATTR_CONDITION_SUNNY, - ATTR_FORECAST, DOMAIN as WEATHER_DOMAIN, LEGACY_SERVICE_GET_FORECAST, SERVICE_GET_FORECASTS, @@ -77,10 +76,6 @@ async def test_imperial_metric( for key, value in result_observation.items(): assert data.get(key) == value - forecast = data.get(ATTR_FORECAST) - for key, value in result_forecast.items(): - assert forecast[0].get(key) == value - async def test_night_clear(hass: HomeAssistant, mock_simple_nws, no_sensor) -> None: """Test with clear-night in observation.""" @@ -119,10 +114,6 @@ async def test_none_values(hass: HomeAssistant, mock_simple_nws, no_sensor) -> N for key in WEATHER_EXPECTED_OBSERVATION_IMPERIAL: assert data.get(key) is None - forecast = data.get(ATTR_FORECAST) - for key in EXPECTED_FORECAST_IMPERIAL: - assert forecast[0].get(key) is None - async def test_none(hass: HomeAssistant, mock_simple_nws, no_sensor) -> None: """Test with None as observation and forecast.""" @@ -146,9 +137,6 @@ async def test_none(hass: HomeAssistant, mock_simple_nws, no_sensor) -> None: for key in WEATHER_EXPECTED_OBSERVATION_IMPERIAL: assert data.get(key) is None - forecast = data.get(ATTR_FORECAST) - assert forecast is None - async def test_error_station(hass: HomeAssistant, mock_simple_nws, no_sensor) -> None: """Test error in setting station.""" diff --git a/tests/components/smhi/snapshots/test_weather.ambr b/tests/components/smhi/snapshots/test_weather.ambr index eb7378b5cba..0fef9e19ec3 100644 --- a/tests/components/smhi/snapshots/test_weather.ambr +++ b/tests/components/smhi/snapshots/test_weather.ambr @@ -1,338 +1,4 @@ # serializer version: 1 -# name: test_forecast_daily - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-07T12:00:00', - 'humidity': 96, - 'precipitation': 0.0, - 'pressure': 991.0, - 'temperature': 18.0, - 'templow': 15.0, - 'wind_bearing': 114, - 'wind_gust_speed': 32.76, - 'wind_speed': 10.08, - }) -# --- -# name: test_forecast_daily.1 - dict({ - 'cloud_coverage': 75, - 'condition': 'partlycloudy', - 'datetime': '2023-08-13T12:00:00', - 'humidity': 59, - 'precipitation': 0.0, - 'pressure': 1013.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 234, - 'wind_gust_speed': 35.64, - 'wind_speed': 14.76, - }) -# --- -# name: test_forecast_daily.2 - dict({ - 'cloud_coverage': 100, - 'condition': 'fog', - 'datetime': '2023-08-07T09:00:00', - 'humidity': 100, - 'precipitation': 0.0, - 'pressure': 992.0, - 'temperature': 18.0, - 'templow': 18.0, - 'wind_bearing': 103, - 'wind_gust_speed': 23.76, - 'wind_speed': 9.72, - }) -# --- -# name: test_forecast_daily.3 - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-07T15:00:00', - 'humidity': 89, - 'precipitation': 0.0, - 'pressure': 991.0, - 'temperature': 16.0, - 'templow': 16.0, - 'wind_bearing': 108, - 'wind_gust_speed': 31.68, - 'wind_speed': 12.24, - }) -# --- -# name: test_forecast_service - dict({ - 'forecast': list([ - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-07T12:00:00', - 'humidity': 96, - 'precipitation': 0.0, - 'pressure': 991.0, - 'temperature': 18.0, - 'templow': 15.0, - 'wind_bearing': 114, - 'wind_gust_speed': 32.76, - 'wind_speed': 10.08, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'rainy', - 'datetime': '2023-08-08T12:00:00', - 'humidity': 97, - 'precipitation': 10.6, - 'pressure': 984.0, - 'temperature': 15.0, - 'templow': 11.0, - 'wind_bearing': 183, - 'wind_gust_speed': 27.36, - 'wind_speed': 11.16, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'rainy', - 'datetime': '2023-08-09T12:00:00', - 'humidity': 95, - 'precipitation': 6.3, - 'pressure': 1001.0, - 'temperature': 12.0, - 'templow': 11.0, - 'wind_bearing': 166, - 'wind_gust_speed': 48.24, - 'wind_speed': 18.0, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-10T12:00:00', - 'humidity': 75, - 'precipitation': 4.8, - 'pressure': 1011.0, - 'temperature': 14.0, - 'templow': 10.0, - 'wind_bearing': 174, - 'wind_gust_speed': 29.16, - 'wind_speed': 11.16, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-11T12:00:00', - 'humidity': 69, - 'precipitation': 0.6, - 'pressure': 1015.0, - 'temperature': 18.0, - 'templow': 12.0, - 'wind_bearing': 197, - 'wind_gust_speed': 27.36, - 'wind_speed': 10.08, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-12T12:00:00', - 'humidity': 82, - 'precipitation': 0.0, - 'pressure': 1014.0, - 'temperature': 17.0, - 'templow': 12.0, - 'wind_bearing': 225, - 'wind_gust_speed': 28.08, - 'wind_speed': 8.64, - }), - dict({ - 'cloud_coverage': 75, - 'condition': 'partlycloudy', - 'datetime': '2023-08-13T12:00:00', - 'humidity': 59, - 'precipitation': 0.0, - 'pressure': 1013.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 234, - 'wind_gust_speed': 35.64, - 'wind_speed': 14.76, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'partlycloudy', - 'datetime': '2023-08-14T12:00:00', - 'humidity': 56, - 'precipitation': 0.0, - 'pressure': 1015.0, - 'temperature': 21.0, - 'templow': 14.0, - 'wind_bearing': 216, - 'wind_gust_speed': 33.12, - 'wind_speed': 13.68, - }), - dict({ - 'cloud_coverage': 88, - 'condition': 'partlycloudy', - 'datetime': '2023-08-15T12:00:00', - 'humidity': 64, - 'precipitation': 3.6, - 'pressure': 1014.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 226, - 'wind_gust_speed': 33.12, - 'wind_speed': 13.68, - }), - dict({ - 'cloud_coverage': 75, - 'condition': 'partlycloudy', - 'datetime': '2023-08-16T12:00:00', - 'humidity': 61, - 'precipitation': 2.4, - 'pressure': 1014.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 233, - 'wind_gust_speed': 33.48, - 'wind_speed': 14.04, - }), - ]), - }) -# --- -# name: test_forecast_service[forecast] - dict({ - 'weather.smhi_test': dict({ - 'forecast': list([ - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-07T12:00:00', - 'humidity': 96, - 'precipitation': 0.0, - 'pressure': 991.0, - 'temperature': 18.0, - 'templow': 15.0, - 'wind_bearing': 114, - 'wind_gust_speed': 32.76, - 'wind_speed': 10.08, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'rainy', - 'datetime': '2023-08-08T12:00:00', - 'humidity': 97, - 'precipitation': 10.6, - 'pressure': 984.0, - 'temperature': 15.0, - 'templow': 11.0, - 'wind_bearing': 183, - 'wind_gust_speed': 27.36, - 'wind_speed': 11.16, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'rainy', - 'datetime': '2023-08-09T12:00:00', - 'humidity': 95, - 'precipitation': 6.3, - 'pressure': 1001.0, - 'temperature': 12.0, - 'templow': 11.0, - 'wind_bearing': 166, - 'wind_gust_speed': 48.24, - 'wind_speed': 18.0, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-10T12:00:00', - 'humidity': 75, - 'precipitation': 4.8, - 'pressure': 1011.0, - 'temperature': 14.0, - 'templow': 10.0, - 'wind_bearing': 174, - 'wind_gust_speed': 29.16, - 'wind_speed': 11.16, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-11T12:00:00', - 'humidity': 69, - 'precipitation': 0.6, - 'pressure': 1015.0, - 'temperature': 18.0, - 'templow': 12.0, - 'wind_bearing': 197, - 'wind_gust_speed': 27.36, - 'wind_speed': 10.08, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-12T12:00:00', - 'humidity': 82, - 'precipitation': 0.0, - 'pressure': 1014.0, - 'temperature': 17.0, - 'templow': 12.0, - 'wind_bearing': 225, - 'wind_gust_speed': 28.08, - 'wind_speed': 8.64, - }), - dict({ - 'cloud_coverage': 75, - 'condition': 'partlycloudy', - 'datetime': '2023-08-13T12:00:00', - 'humidity': 59, - 'precipitation': 0.0, - 'pressure': 1013.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 234, - 'wind_gust_speed': 35.64, - 'wind_speed': 14.76, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'partlycloudy', - 'datetime': '2023-08-14T12:00:00', - 'humidity': 56, - 'precipitation': 0.0, - 'pressure': 1015.0, - 'temperature': 21.0, - 'templow': 14.0, - 'wind_bearing': 216, - 'wind_gust_speed': 33.12, - 'wind_speed': 13.68, - }), - dict({ - 'cloud_coverage': 88, - 'condition': 'partlycloudy', - 'datetime': '2023-08-15T12:00:00', - 'humidity': 64, - 'precipitation': 3.6, - 'pressure': 1014.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 226, - 'wind_gust_speed': 33.12, - 'wind_speed': 13.68, - }), - dict({ - 'cloud_coverage': 75, - 'condition': 'partlycloudy', - 'datetime': '2023-08-16T12:00:00', - 'humidity': 61, - 'precipitation': 2.4, - 'pressure': 1014.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 233, - 'wind_gust_speed': 33.48, - 'wind_speed': 14.04, - }), - ]), - }), - }) -# --- # name: test_forecast_service[get_forecast] dict({ 'forecast': list([ @@ -671,138 +337,6 @@ ReadOnlyDict({ 'attribution': 'Swedish weather institute (SMHI)', 'cloud_coverage': 100, - 'forecast': list([ - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-07T12:00:00', - 'humidity': 96, - 'precipitation': 0.0, - 'pressure': 991.0, - 'temperature': 18.0, - 'templow': 15.0, - 'wind_bearing': 114, - 'wind_gust_speed': 32.76, - 'wind_speed': 10.08, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'rainy', - 'datetime': '2023-08-08T12:00:00', - 'humidity': 97, - 'precipitation': 10.6, - 'pressure': 984.0, - 'temperature': 15.0, - 'templow': 11.0, - 'wind_bearing': 183, - 'wind_gust_speed': 27.36, - 'wind_speed': 11.16, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'rainy', - 'datetime': '2023-08-09T12:00:00', - 'humidity': 95, - 'precipitation': 6.3, - 'pressure': 1001.0, - 'temperature': 12.0, - 'templow': 11.0, - 'wind_bearing': 166, - 'wind_gust_speed': 48.24, - 'wind_speed': 18.0, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-10T12:00:00', - 'humidity': 75, - 'precipitation': 4.8, - 'pressure': 1011.0, - 'temperature': 14.0, - 'templow': 10.0, - 'wind_bearing': 174, - 'wind_gust_speed': 29.16, - 'wind_speed': 11.16, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-11T12:00:00', - 'humidity': 69, - 'precipitation': 0.6, - 'pressure': 1015.0, - 'temperature': 18.0, - 'templow': 12.0, - 'wind_bearing': 197, - 'wind_gust_speed': 27.36, - 'wind_speed': 10.08, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'cloudy', - 'datetime': '2023-08-12T12:00:00', - 'humidity': 82, - 'precipitation': 0.0, - 'pressure': 1014.0, - 'temperature': 17.0, - 'templow': 12.0, - 'wind_bearing': 225, - 'wind_gust_speed': 28.08, - 'wind_speed': 8.64, - }), - dict({ - 'cloud_coverage': 75, - 'condition': 'partlycloudy', - 'datetime': '2023-08-13T12:00:00', - 'humidity': 59, - 'precipitation': 0.0, - 'pressure': 1013.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 234, - 'wind_gust_speed': 35.64, - 'wind_speed': 14.76, - }), - dict({ - 'cloud_coverage': 100, - 'condition': 'partlycloudy', - 'datetime': '2023-08-14T12:00:00', - 'humidity': 56, - 'precipitation': 0.0, - 'pressure': 1015.0, - 'temperature': 21.0, - 'templow': 14.0, - 'wind_bearing': 216, - 'wind_gust_speed': 33.12, - 'wind_speed': 13.68, - }), - dict({ - 'cloud_coverage': 88, - 'condition': 'partlycloudy', - 'datetime': '2023-08-15T12:00:00', - 'humidity': 64, - 'precipitation': 3.6, - 'pressure': 1014.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 226, - 'wind_gust_speed': 33.12, - 'wind_speed': 13.68, - }), - dict({ - 'cloud_coverage': 75, - 'condition': 'partlycloudy', - 'datetime': '2023-08-16T12:00:00', - 'humidity': 61, - 'precipitation': 2.4, - 'pressure': 1014.0, - 'temperature': 20.0, - 'templow': 14.0, - 'wind_bearing': 233, - 'wind_gust_speed': 33.48, - 'wind_speed': 14.04, - }), - ]), 'friendly_name': 'test', 'humidity': 100, 'precipitation_unit': <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>, @@ -820,18 +354,3 @@ 'wind_speed_unit': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>, }) # --- -# name: test_setup_hass.1 - dict({ - 'cloud_coverage': 100, - 'condition': 'rainy', - 'datetime': '2023-08-08T12:00:00', - 'humidity': 97, - 'precipitation': 10.6, - 'pressure': 984.0, - 'temperature': 15.0, - 'templow': 11.0, - 'wind_bearing': 183, - 'wind_gust_speed': 27.36, - 'wind_speed': 11.16, - }) -# --- diff --git a/tests/components/smhi/test_weather.py b/tests/components/smhi/test_weather.py index 87aae74e71d..4d187e7c728 100644 --- a/tests/components/smhi/test_weather.py +++ b/tests/components/smhi/test_weather.py @@ -10,7 +10,6 @@ from syrupy.assertion import SnapshotAssertion from homeassistant.components.smhi.const import ATTR_SMHI_THUNDER_PROBABILITY from homeassistant.components.smhi.weather import CONDITION_CLASSES, RETRY_TIMEOUT from homeassistant.components.weather import ( - ATTR_FORECAST, ATTR_FORECAST_CONDITION, ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_PRESSURE, @@ -65,10 +64,6 @@ async def test_setup_hass( assert state assert state.state == "fog" assert state.attributes == snapshot - assert len(state.attributes["forecast"]) == 10 - - forecast = state.attributes["forecast"][1] - assert forecast == snapshot async def test_properties_no_data(hass: HomeAssistant) -> None: @@ -95,7 +90,6 @@ async def test_properties_no_data(hass: HomeAssistant) -> None: assert ATTR_WEATHER_VISIBILITY not in state.attributes assert ATTR_WEATHER_WIND_SPEED not in state.attributes assert ATTR_WEATHER_WIND_BEARING not in state.attributes - assert ATTR_FORECAST not in state.attributes assert ATTR_WEATHER_CLOUD_COVERAGE not in state.attributes assert ATTR_SMHI_THUNDER_PROBABILITY not in state.attributes assert ATTR_WEATHER_WIND_GUST_SPEED not in state.attributes @@ -183,10 +177,16 @@ async def test_properties_unknown_symbol(hass: HomeAssistant) -> None: assert state assert state.name == "test" assert state.state == STATE_UNKNOWN - assert ATTR_FORECAST in state.attributes + response = await hass.services.async_call( + WEATHER_DOMAIN, + SERVICE_GET_FORECASTS, + {"entity_id": ENTITY_ID, "type": "daily"}, + blocking=True, + return_response=True, + ) assert all( forecast[ATTR_FORECAST_CONDITION] is None - for forecast in state.attributes[ATTR_FORECAST] + for forecast in response[ENTITY_ID]["forecast"] ) diff --git a/tests/components/template/snapshots/test_weather.ambr b/tests/components/template/snapshots/test_weather.ambr index 0ee7f967176..9b0cf2b9471 100644 --- a/tests/components/template/snapshots/test_weather.ambr +++ b/tests/components/template/snapshots/test_weather.ambr @@ -55,22 +55,12 @@ # name: test_forecasts[config0-1-weather-get_forecast] dict({ 'forecast': list([ - dict({ - 'condition': 'cloudy', - 'datetime': '2023-02-17T14:00:00+00:00', - 'temperature': 14.2, - }), ]), }) # --- # name: test_forecasts[config0-1-weather-get_forecast].1 dict({ 'forecast': list([ - dict({ - 'condition': 'cloudy', - 'datetime': '2023-02-17T14:00:00+00:00', - 'temperature': 14.2, - }), ]), }) # --- @@ -89,11 +79,6 @@ # name: test_forecasts[config0-1-weather-get_forecast].3 dict({ 'forecast': list([ - dict({ - 'condition': 'cloudy', - 'datetime': '2023-02-17T14:00:00+00:00', - 'temperature': 16.9, - }), ]), }) # --- @@ -101,11 +86,6 @@ dict({ 'weather.forecast': dict({ 'forecast': list([ - dict({ - 'condition': 'cloudy', - 'datetime': '2023-02-17T14:00:00+00:00', - 'temperature': 14.2, - }), ]), }), }) @@ -114,11 +94,6 @@ dict({ 'weather.forecast': dict({ 'forecast': list([ - dict({ - 'condition': 'cloudy', - 'datetime': '2023-02-17T14:00:00+00:00', - 'temperature': 14.2, - }), ]), }), }) @@ -141,11 +116,6 @@ dict({ 'weather.forecast': dict({ 'forecast': list([ - dict({ - 'condition': 'cloudy', - 'datetime': '2023-02-17T14:00:00+00:00', - 'temperature': 16.9, - }), ]), }), }) diff --git a/tests/components/template/test_weather.py b/tests/components/template/test_weather.py index 4165a509ee4..e457f2e263b 100644 --- a/tests/components/template/test_weather.py +++ b/tests/components/template/test_weather.py @@ -6,7 +6,6 @@ import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.weather import ( - ATTR_FORECAST, ATTR_WEATHER_APPARENT_TEMPERATURE, ATTR_WEATHER_CLOUD_COVERAGE, ATTR_WEATHER_DEW_POINT, @@ -36,6 +35,8 @@ from tests.common import ( mock_restore_cache_with_extra_data, ) +ATTR_FORECAST = "forecast" + @pytest.mark.parametrize(("count", "domain"), [(1, WEATHER_DOMAIN)]) @pytest.mark.parametrize( @@ -49,7 +50,6 @@ from tests.common import ( "name": "test", "attribution_template": "{{ states('sensor.attribution') }}", "condition_template": "sunny", - "forecast_template": "{{ states.weather.demo.attributes.forecast }}", "temperature_template": "{{ states('sensor.temperature') | float }}", "humidity_template": "{{ states('sensor.humidity') | int }}", "pressure_template": "{{ states('sensor.pressure') }}", @@ -111,7 +111,6 @@ async def test_template_state_text(hass: HomeAssistant, start_ha) -> None: "platform": "template", "name": "forecast", "condition_template": "sunny", - "forecast_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_daily_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_hourly_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_twice_daily_template": "{{ states.weather.forecast_twice_daily.attributes.forecast }}", @@ -238,7 +237,6 @@ async def test_forecasts( "platform": "template", "name": "forecast", "condition_template": "sunny", - "forecast_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_daily_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_hourly_template": "{{ states.weather.forecast_hourly.attributes.forecast }}", "temperature_template": "{{ states('sensor.temperature') | float }}", @@ -323,7 +321,6 @@ async def test_forecast_invalid( "platform": "template", "name": "forecast", "condition_template": "sunny", - "forecast_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_twice_daily_template": "{{ states.weather.forecast_twice_daily.attributes.forecast }}", "temperature_template": "{{ states('sensor.temperature') | float }}", "humidity_template": "{{ states('sensor.humidity') | int }}", @@ -393,7 +390,6 @@ async def test_forecast_invalid_is_daytime_missing_in_twice_daily( "platform": "template", "name": "forecast", "condition_template": "sunny", - "forecast_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_twice_daily_template": "{{ states.weather.forecast_twice_daily.attributes.forecast }}", "temperature_template": "{{ states('sensor.temperature') | float }}", "humidity_template": "{{ states('sensor.humidity') | int }}", @@ -463,7 +459,6 @@ async def test_forecast_invalid_datetime_missing( "platform": "template", "name": "forecast", "condition_template": "sunny", - "forecast_template": "{{ states.weather.forecast.attributes.forecast }}", "forecast_daily_template": "{{ states.weather.forecast_daily.attributes.forecast }}", "forecast_hourly_template": "{{ states.weather.forecast_hourly.attributes.forecast }}", "temperature_template": "{{ states('sensor.temperature') | float }}", @@ -728,7 +723,6 @@ async def test_trigger_action( "cloud_coverage_template": "{{ my_variable + 1 }}", "dew_point_template": "{{ my_variable + 1 }}", "apparent_temperature_template": "{{ my_variable + 1 }}", - "forecast_template": "{{ var_forecast_daily }}", "forecast_daily_template": "{{ var_forecast_daily }}", "forecast_hourly_template": "{{ var_forecast_hourly }}", "forecast_twice_daily_template": "{{ var_forecast_twice_daily }}", diff --git a/tests/components/tomorrowio/test_weather.py b/tests/components/tomorrowio/test_weather.py index a87c0363b7a..6f5117df9d5 100644 --- a/tests/components/tomorrowio/test_weather.py +++ b/tests/components/tomorrowio/test_weather.py @@ -23,17 +23,6 @@ from homeassistant.components.tomorrowio.const import ( ) from homeassistant.components.weather import ( ATTR_CONDITION_SUNNY, - ATTR_FORECAST, - ATTR_FORECAST_CONDITION, - ATTR_FORECAST_DEW_POINT, - ATTR_FORECAST_HUMIDITY, - ATTR_FORECAST_PRECIPITATION, - ATTR_FORECAST_PRECIPITATION_PROBABILITY, - ATTR_FORECAST_TEMP, - ATTR_FORECAST_TEMP_LOW, - ATTR_FORECAST_TIME, - ATTR_FORECAST_WIND_BEARING, - ATTR_FORECAST_WIND_SPEED, ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_OZONE, ATTR_WEATHER_PRECIPITATION_UNIT, @@ -216,19 +205,6 @@ async def test_v4_weather(hass: HomeAssistant, tomorrowio_config_entry_update) - assert weather_state.state == ATTR_CONDITION_SUNNY assert weather_state.attributes[ATTR_ATTRIBUTION] == ATTRIBUTION - assert len(weather_state.attributes[ATTR_FORECAST]) == 14 - assert weather_state.attributes[ATTR_FORECAST][0] == { - ATTR_FORECAST_CONDITION: ATTR_CONDITION_SUNNY, - ATTR_FORECAST_TIME: "2021-03-07T11:00:00+00:00", - ATTR_FORECAST_PRECIPITATION: 0, - ATTR_FORECAST_PRECIPITATION_PROBABILITY: 0, - ATTR_FORECAST_TEMP: 45.9, - ATTR_FORECAST_TEMP_LOW: 26.1, - ATTR_FORECAST_DEW_POINT: 12.8, - ATTR_FORECAST_HUMIDITY: 58, - ATTR_FORECAST_WIND_BEARING: 239.6, - ATTR_FORECAST_WIND_SPEED: 34.16, # 9.49 m/s -> km/h - } assert weather_state.attributes[ATTR_FRIENDLY_NAME] == "Tomorrow.io Daily" assert weather_state.attributes[ATTR_WEATHER_HUMIDITY] == 23 assert weather_state.attributes[ATTR_WEATHER_OZONE] == 46.53 @@ -249,19 +225,6 @@ async def test_v4_weather_legacy_entities(hass: HomeAssistant) -> None: weather_state = await _setup_legacy(hass, API_V4_ENTRY_DATA) assert weather_state.state == ATTR_CONDITION_SUNNY assert weather_state.attributes[ATTR_ATTRIBUTION] == ATTRIBUTION - assert len(weather_state.attributes[ATTR_FORECAST]) == 14 - assert weather_state.attributes[ATTR_FORECAST][0] == { - ATTR_FORECAST_CONDITION: ATTR_CONDITION_SUNNY, - ATTR_FORECAST_TIME: "2021-03-07T11:00:00+00:00", - ATTR_FORECAST_DEW_POINT: 12.8, - ATTR_FORECAST_HUMIDITY: 58, - ATTR_FORECAST_PRECIPITATION: 0, - ATTR_FORECAST_PRECIPITATION_PROBABILITY: 0, - ATTR_FORECAST_TEMP: 45.9, - ATTR_FORECAST_TEMP_LOW: 26.1, - ATTR_FORECAST_WIND_BEARING: 239.6, - ATTR_FORECAST_WIND_SPEED: 34.16, # 9.49 m/s -> km/h - } assert weather_state.attributes[ATTR_FRIENDLY_NAME] == "Tomorrow.io Daily" assert weather_state.attributes[ATTR_WEATHER_HUMIDITY] == 23 assert weather_state.attributes[ATTR_WEATHER_OZONE] == 46.53 diff --git a/tests/components/weather/test_init.py b/tests/components/weather/test_init.py index 3dcb78e3257..195a4c9ef67 100644 --- a/tests/components/weather/test_init.py +++ b/tests/components/weather/test_init.py @@ -7,17 +7,6 @@ from syrupy.assertion import SnapshotAssertion from homeassistant.components.weather import ( ATTR_CONDITION_SUNNY, - ATTR_FORECAST, - ATTR_FORECAST_APPARENT_TEMP, - ATTR_FORECAST_DEW_POINT, - ATTR_FORECAST_HUMIDITY, - ATTR_FORECAST_PRECIPITATION, - ATTR_FORECAST_PRESSURE, - ATTR_FORECAST_TEMP, - ATTR_FORECAST_TEMP_LOW, - ATTR_FORECAST_UV_INDEX, - ATTR_FORECAST_WIND_GUST_SPEED, - ATTR_FORECAST_WIND_SPEED, ATTR_WEATHER_APPARENT_TEMPERATURE, ATTR_WEATHER_OZONE, ATTR_WEATHER_PRECIPITATION_UNIT, @@ -79,6 +68,7 @@ class MockWeatherEntity(WeatherEntity): def __init__(self) -> None: """Initiate Entity.""" super().__init__() + self._attr_precision = PRECISION_TENTHS self._attr_condition = ATTR_CONDITION_SUNNY self._attr_native_precipitation_unit = UnitOfLength.MILLIMETERS self._attr_native_pressure = 10 @@ -92,14 +82,6 @@ class MockWeatherEntity(WeatherEntity): self._attr_native_wind_gust_speed = 10 self._attr_native_wind_speed = 3 self._attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND - self._attr_forecast = [ - Forecast( - datetime=datetime(2022, 6, 20, 00, 00, 00, tzinfo=dt_util.UTC), - native_precipitation=1, - native_temperature=20, - native_dew_point=2, - ) - ] self._attr_forecast_twice_daily = [ Forecast( datetime=datetime(2022, 6, 20, 8, 00, 00, tzinfo=dt_util.UTC), @@ -141,14 +123,6 @@ async def test_temperature( dew_point_native_value, native_unit, state_unit ) - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = { "native_temperature": native_value, "native_temperature_unit": native_unit, @@ -156,10 +130,9 @@ async def test_temperature( "native_dew_point": dew_point_native_value, } - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast_daily = state.attributes[ATTR_FORECAST][0] expected = state_value apparent_expected = apparent_state_value @@ -174,20 +147,6 @@ async def test_temperature( dew_point_expected, rel=0.1 ) assert state.attributes[ATTR_WEATHER_TEMPERATURE_UNIT] == state_unit - assert float(forecast_daily[ATTR_FORECAST_TEMP]) == pytest.approx(expected, rel=0.1) - assert float(forecast_daily[ATTR_FORECAST_APPARENT_TEMP]) == pytest.approx( - apparent_expected, rel=0.1 - ) - assert float(forecast_daily[ATTR_FORECAST_DEW_POINT]) == pytest.approx( - dew_point_expected, rel=0.1 - ) - assert float(forecast_daily[ATTR_FORECAST_TEMP_LOW]) == pytest.approx( - expected, rel=0.1 - ) - assert float(forecast_daily[ATTR_FORECAST_TEMP]) == pytest.approx(expected, rel=0.1) - assert float(forecast_daily[ATTR_FORECAST_TEMP_LOW]) == pytest.approx( - expected, rel=0.1 - ) @pytest.mark.parametrize("native_unit", [None]) @@ -214,14 +173,6 @@ async def test_temperature_no_unit( dew_point_state_value = dew_point_native_value apparent_temp_state_value = apparent_temp_native_value - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = { "native_temperature": native_value, "native_temperature_unit": native_unit, @@ -229,10 +180,9 @@ async def test_temperature_no_unit( "native_apparent_temperature": apparent_temp_native_value, } - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] expected = state_value dew_point_expected = dew_point_state_value @@ -247,14 +197,6 @@ async def test_temperature_no_unit( expected_apparent_temp, rel=0.1 ) assert state.attributes[ATTR_WEATHER_TEMPERATURE_UNIT] == state_unit - assert float(forecast[ATTR_FORECAST_TEMP]) == pytest.approx(expected, rel=0.1) - assert float(forecast[ATTR_FORECAST_DEW_POINT]) == pytest.approx( - dew_point_expected, rel=0.1 - ) - assert float(forecast[ATTR_FORECAST_TEMP_LOW]) == pytest.approx(expected, rel=0.1) - assert float(forecast[ATTR_FORECAST_APPARENT_TEMP]) == pytest.approx( - expected_apparent_temp, rel=0.1 - ) @pytest.mark.parametrize( @@ -276,26 +218,16 @@ async def test_pressure( native_value = 30 state_value = PressureConverter.convert(native_value, native_unit, state_unit) - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = {"native_pressure": native_value, "native_pressure_unit": native_unit} - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] expected = state_value assert float(state.attributes[ATTR_WEATHER_PRESSURE]) == pytest.approx( expected, rel=1e-2 ) - assert float(forecast[ATTR_FORECAST_PRESSURE]) == pytest.approx(expected, rel=1e-2) @pytest.mark.parametrize("native_unit", [None]) @@ -315,26 +247,16 @@ async def test_pressure_no_unit( native_value = 30 state_value = native_value - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = {"native_pressure": native_value, "native_pressure_unit": native_unit} - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] expected = state_value assert float(state.attributes[ATTR_WEATHER_PRESSURE]) == pytest.approx( expected, rel=1e-2 ) - assert float(forecast[ATTR_FORECAST_PRESSURE]) == pytest.approx(expected, rel=1e-2) @pytest.mark.parametrize( @@ -364,28 +286,16 @@ async def test_wind_speed( native_value = 10 state_value = SpeedConverter.convert(native_value, native_unit, state_unit) - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = {"native_wind_speed": native_value, "native_wind_speed_unit": native_unit} - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] expected = state_value assert float(state.attributes[ATTR_WEATHER_WIND_SPEED]) == pytest.approx( expected, rel=1e-2 ) - assert float(forecast[ATTR_FORECAST_WIND_SPEED]) == pytest.approx( - expected, rel=1e-2 - ) @pytest.mark.parametrize( @@ -415,31 +325,19 @@ async def test_wind_gust_speed( native_value = 10 state_value = SpeedConverter.convert(native_value, native_unit, state_unit) - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = { "native_wind_gust_speed": native_value, "native_wind_speed_unit": native_unit, } - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] expected = state_value assert float(state.attributes[ATTR_WEATHER_WIND_GUST_SPEED]) == pytest.approx( expected, rel=1e-2 ) - assert float(forecast[ATTR_FORECAST_WIND_GUST_SPEED]) == pytest.approx( - expected, rel=1e-2 - ) @pytest.mark.parametrize("native_unit", [None]) @@ -462,194 +360,16 @@ async def test_wind_speed_no_unit( native_value = 10 state_value = native_value - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = {"native_wind_speed": native_value, "native_wind_speed_unit": native_unit} - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] expected = state_value assert float(state.attributes[ATTR_WEATHER_WIND_SPEED]) == pytest.approx( expected, rel=1e-2 ) - assert float(forecast[ATTR_FORECAST_WIND_SPEED]) == pytest.approx( - expected, rel=1e-2 - ) - - -@pytest.mark.parametrize("native_unit", [UnitOfLength.MILES, UnitOfLength.KILOMETERS]) -@pytest.mark.parametrize( - ("state_unit", "unit_system"), - [ - (UnitOfLength.KILOMETERS, METRIC_SYSTEM), - (UnitOfLength.MILES, US_CUSTOMARY_SYSTEM), - ], -) -async def test_visibility( - hass: HomeAssistant, - config_flow_fixture: None, - native_unit: str, - state_unit: str, - unit_system, -) -> None: - """Test visibility.""" - hass.config.units = unit_system - native_value = 10 - state_value = DistanceConverter.convert(native_value, native_unit, state_unit) - - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - kwargs = {"native_visibility": native_value, "native_visibility_unit": native_unit} - - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) - - state = hass.states.get(entity0.entity_id) - expected = state_value - assert float(state.attributes[ATTR_WEATHER_VISIBILITY]) == pytest.approx( - expected, rel=1e-2 - ) - - -@pytest.mark.parametrize("native_unit", [None]) -@pytest.mark.parametrize( - ("state_unit", "unit_system"), - [ - (UnitOfLength.KILOMETERS, METRIC_SYSTEM), - (UnitOfLength.MILES, US_CUSTOMARY_SYSTEM), - ], -) -async def test_visibility_no_unit( - hass: HomeAssistant, - config_flow_fixture: None, - native_unit: str, - state_unit: str, - unit_system, -) -> None: - """Test visibility when the entity does not declare a native unit.""" - hass.config.units = unit_system - native_value = 10 - state_value = native_value - - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - kwargs = {"native_visibility": native_value, "native_visibility_unit": native_unit} - - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) - - state = hass.states.get(entity0.entity_id) - expected = state_value - assert float(state.attributes[ATTR_WEATHER_VISIBILITY]) == pytest.approx( - expected, rel=1e-2 - ) - - -@pytest.mark.parametrize("native_unit", [UnitOfLength.INCHES, UnitOfLength.MILLIMETERS]) -@pytest.mark.parametrize( - ("state_unit", "unit_system"), - [ - (UnitOfLength.MILLIMETERS, METRIC_SYSTEM), - (UnitOfLength.INCHES, US_CUSTOMARY_SYSTEM), - ], -) -async def test_precipitation( - hass: HomeAssistant, - config_flow_fixture: None, - native_unit: str, - state_unit: str, - unit_system, -) -> None: - """Test precipitation.""" - hass.config.units = unit_system - native_value = 30 - state_value = DistanceConverter.convert(native_value, native_unit, state_unit) - - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - kwargs = { - "native_precipitation": native_value, - "native_precipitation_unit": native_unit, - } - - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) - - state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] - - expected = state_value - assert float(forecast[ATTR_FORECAST_PRECIPITATION]) == pytest.approx( - expected, rel=1e-2 - ) - - -@pytest.mark.parametrize("native_unit", [None]) -@pytest.mark.parametrize( - ("state_unit", "unit_system"), - [ - (UnitOfLength.MILLIMETERS, METRIC_SYSTEM), - (UnitOfLength.INCHES, US_CUSTOMARY_SYSTEM), - ], -) -async def test_precipitation_no_unit( - hass: HomeAssistant, - config_flow_fixture: None, - native_unit: str, - state_unit: str, - unit_system, -) -> None: - """Test precipitation when the entity does not declare a native unit.""" - hass.config.units = unit_system - native_value = 30 - state_value = native_value - - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - kwargs = { - "native_precipitation": native_value, - "native_precipitation_unit": native_unit, - } - - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) - - state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] - - expected = state_value - assert float(forecast[ATTR_FORECAST_PRECIPITATION]) == pytest.approx( - expected, rel=1e-2 - ) async def test_wind_bearing_ozone_and_cloud_coverage_and_uv_index( @@ -662,14 +382,6 @@ async def test_wind_bearing_ozone_and_cloud_coverage_and_uv_index( cloud_coverage = 75 uv_index = 1.2 - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = { "wind_bearing": wind_bearing_value, "ozone": ozone_value, @@ -677,15 +389,13 @@ async def test_wind_bearing_ozone_and_cloud_coverage_and_uv_index( "uv_index": uv_index, } - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] assert float(state.attributes[ATTR_WEATHER_WIND_BEARING]) == 180 assert float(state.attributes[ATTR_WEATHER_OZONE]) == 10 assert float(state.attributes[ATTR_WEATHER_CLOUD_COVERAGE]) == 75 assert float(state.attributes[ATTR_WEATHER_UV_INDEX]) == 1.2 - assert float(forecast[ATTR_FORECAST_UV_INDEX]) == 1.2 async def test_humidity( @@ -695,55 +405,12 @@ async def test_humidity( """Test humidity.""" humidity_value = 80.2 - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = {"humidity": humidity_value} - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] assert float(state.attributes[ATTR_WEATHER_HUMIDITY]) == 80 - assert float(forecast[ATTR_FORECAST_HUMIDITY]) == 80 - - -async def test_none_forecast( - hass: HomeAssistant, - config_flow_fixture: None, -) -> None: - """Test that conversion with None values succeeds.""" - - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - kwargs = { - "native_pressure": None, - "native_pressure_unit": UnitOfPressure.INHG, - "native_wind_speed": None, - "native_wind_speed_unit": UnitOfSpeed.METERS_PER_SECOND, - "native_precipitation": None, - "native_precipitation_unit": UnitOfLength.MILLIMETERS, - } - - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) - - state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] - - assert forecast.get(ATTR_FORECAST_PRESSURE) is None - assert forecast.get(ATTR_FORECAST_WIND_SPEED) is None - assert forecast.get(ATTR_FORECAST_PRECIPITATION) is None async def test_custom_units(hass: HomeAssistant, config_flow_fixture: None) -> None: @@ -773,14 +440,6 @@ async def test_custom_units(hass: HomeAssistant, config_flow_fixture: None) -> N entity_registry.async_update_entity_options(entry.entity_id, "weather", set_options) await hass.async_block_till_done() - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = { "native_temperature": temperature_value, "native_temperature_unit": temperature_unit, @@ -796,10 +455,9 @@ async def test_custom_units(hass: HomeAssistant, config_flow_fixture: None) -> N "unique_id": "very_unique", } - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) state = hass.states.get(entity0.entity_id) - forecast = state.attributes[ATTR_FORECAST][0] expected_wind_speed = round( SpeedConverter.convert( @@ -820,12 +478,6 @@ async def test_custom_units(hass: HomeAssistant, config_flow_fixture: None) -> N ), ROUNDING_PRECISION, ) - expected_precipitation = round( - DistanceConverter.convert( - precipitation_value, precipitation_unit, UnitOfLength.INCHES - ), - ROUNDING_PRECISION, - ) assert float(state.attributes[ATTR_WEATHER_WIND_SPEED]) == pytest.approx( expected_wind_speed @@ -839,9 +491,6 @@ async def test_custom_units(hass: HomeAssistant, config_flow_fixture: None) -> N assert float(state.attributes[ATTR_WEATHER_VISIBILITY]) == pytest.approx( expected_visibility ) - assert float(forecast[ATTR_FORECAST_PRECIPITATION]) == pytest.approx( - expected_precipitation, rel=1e-2 - ) assert ( state.attributes[ATTR_WEATHER_PRECIPITATION_UNIT] @@ -925,14 +574,6 @@ async def test_forecast_twice_daily_missing_is_daytime( ) -> None: """Test forecast_twice_daily missing mandatory attribute is_daytime.""" - class MockWeatherMock(MockWeatherTest): - """Mock weather class.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - kwargs = { "native_temperature": 38, "native_temperature_unit": UnitOfTemperature.CELSIUS, @@ -940,7 +581,7 @@ async def test_forecast_twice_daily_missing_is_daytime( "supported_features": WeatherEntityFeature.FORECAST_TWICE_DAILY, } - entity0 = await create_entity(hass, MockWeatherMock, None, **kwargs) + entity0 = await create_entity(hass, MockWeatherTest, None, **kwargs) client = await hass_ws_client(hass) @@ -1147,155 +788,6 @@ async def test_get_forecast_unsupported( ISSUE_TRACKER = "https://blablabla.com" -@pytest.mark.parametrize( - ("manifest_extra", "translation_key", "translation_placeholders_extra", "report"), - [ - ( - {}, - "deprecated_weather_forecast_no_url", - {}, - "report it to the author of the 'test' custom integration", - ), - ( - {"issue_tracker": ISSUE_TRACKER}, - "deprecated_weather_forecast_url", - {"issue_tracker": ISSUE_TRACKER}, - "create a bug report at https://blablabla.com", - ), - ], -) -async def test_issue_forecast_property_deprecated( - hass: HomeAssistant, - caplog: pytest.LogCaptureFixture, - config_flow_fixture: None, - manifest_extra: dict[str, str], - translation_key: str, - translation_placeholders_extra: dict[str, str], - report: str, -) -> None: - """Test the issue is raised on deprecated forecast attributes.""" - - class MockWeatherMockLegacyForecastOnly(MockWeatherTest): - """Mock weather class with mocked legacy forecast.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - # Fake that the class belongs to a custom integration - MockWeatherMockLegacyForecastOnly.__module__ = "custom_components.test.weather" - - kwargs = { - "native_temperature": 38, - "native_temperature_unit": UnitOfTemperature.CELSIUS, - } - weather_entity = await create_entity( - hass, MockWeatherMockLegacyForecastOnly, manifest_extra, **kwargs - ) - - assert weather_entity.state == ATTR_CONDITION_SUNNY - - issues = ir.async_get(hass) - issue = issues.async_get_issue("weather", "deprecated_weather_forecast_test") - assert issue - assert issue.issue_domain == "test" - assert issue.issue_id == "deprecated_weather_forecast_test" - assert issue.translation_key == translation_key - assert ( - issue.translation_placeholders - == {"platform": "test"} | translation_placeholders_extra - ) - - assert ( - "test::MockWeatherMockLegacyForecastOnly implements the `forecast` property or " - "sets `self._attr_forecast` in a subclass of WeatherEntity, this is deprecated " - f"and will be unsupported from Home Assistant 2024.3. Please {report}" - ) in caplog.text - - -async def test_issue_forecast_attr_deprecated( - hass: HomeAssistant, - issue_registry: ir.IssueRegistry, - config_flow_fixture: None, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the issue is raised on deprecated forecast attributes.""" - - class MockWeatherMockLegacyForecast(MockWeatherTest): - """Mock weather class with legacy forecast.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - kwargs = { - "native_temperature": 38, - "native_temperature_unit": UnitOfTemperature.CELSIUS, - } - - # Fake that the class belongs to a custom integration - MockWeatherMockLegacyForecast.__module__ = "custom_components.test.weather" - - weather_entity = await create_entity( - hass, MockWeatherMockLegacyForecast, None, **kwargs - ) - - assert weather_entity.state == ATTR_CONDITION_SUNNY - - issue = issue_registry.async_get_issue( - "weather", "deprecated_weather_forecast_test" - ) - assert issue - assert issue.issue_domain == "test" - assert issue.issue_id == "deprecated_weather_forecast_test" - assert issue.translation_key == "deprecated_weather_forecast_no_url" - assert issue.translation_placeholders == {"platform": "test"} - - assert ( - "test::MockWeatherMockLegacyForecast implements the `forecast` property or " - "sets `self._attr_forecast` in a subclass of WeatherEntity, this is deprecated " - "and will be unsupported from Home Assistant 2024.3. Please report it to the " - "author of the 'test' custom integration" - ) in caplog.text - - -async def test_issue_forecast_deprecated_no_logging( - hass: HomeAssistant, - config_flow_fixture: None, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the no issue is raised on deprecated forecast attributes if new methods exist.""" - - class MockWeatherMockForecast(MockWeatherTest): - """Mock weather class with mocked new method and legacy forecast.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - async def async_forecast_daily(self) -> list[Forecast] | None: - """Return the forecast_daily.""" - return self.forecast_list - - kwargs = { - "native_temperature": 38, - "native_temperature_unit": UnitOfTemperature.CELSIUS, - } - - weather_entity = await create_entity(hass, MockWeatherMockForecast, None, **kwargs) - - assert weather_entity.state == ATTR_CONDITION_SUNNY - - assert "Setting up test.weather" in caplog.text - assert ( - "custom_components.test_weather.weather::weather.test is using a forecast attribute on an instance of WeatherEntity" - not in caplog.text - ) - - async def test_issue_deprecated_service_weather_get_forecast( hass: HomeAssistant, issue_registry: ir.IssueRegistry, diff --git a/tests/components/weather/test_recorder.py b/tests/components/weather/test_recorder.py deleted file mode 100644 index 2986ec19a8c..00000000000 --- a/tests/components/weather/test_recorder.py +++ /dev/null @@ -1,59 +0,0 @@ -"""The tests for weather recorder.""" - -from __future__ import annotations - -from datetime import timedelta - -from homeassistant.components.recorder import Recorder -from homeassistant.components.recorder.history import get_significant_states -from homeassistant.components.weather import ATTR_FORECAST, Forecast -from homeassistant.const import UnitOfTemperature -from homeassistant.core import HomeAssistant -from homeassistant.util import dt as dt_util -from homeassistant.util.unit_system import METRIC_SYSTEM - -from . import MockWeatherTest, create_entity - -from tests.common import async_fire_time_changed -from tests.components.recorder.common import async_wait_recording_done - - -async def test_exclude_attributes( - recorder_mock: Recorder, - hass: HomeAssistant, - config_flow_fixture: None, -) -> None: - """Test weather attributes to be excluded.""" - now = dt_util.utcnow() - - class MockWeatherMockForecast(MockWeatherTest): - """Mock weather class with mocked legacy forecast.""" - - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - - kwargs = { - "native_temperature": 38, - "native_temperature_unit": UnitOfTemperature.CELSIUS, - } - weather_entity = await create_entity(hass, MockWeatherMockForecast, None, **kwargs) - hass.config.units = METRIC_SYSTEM - await hass.async_block_till_done() - - state = hass.states.get(weather_entity.entity_id) - assert state.attributes[ATTR_FORECAST] - - await hass.async_block_till_done() - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5)) - await hass.async_block_till_done() - await async_wait_recording_done(hass) - - states = await hass.async_add_executor_job( - get_significant_states, hass, now, None, hass.states.async_entity_ids() - ) - assert len(states) >= 1 - for entity_states in states.values(): - for state in entity_states: - assert ATTR_FORECAST not in state.attributes diff --git a/tests/testing_config/custom_components/test/weather.py b/tests/testing_config/custom_components/test/weather.py index a9ba1c99872..0e99ef48680 100644 --- a/tests/testing_config/custom_components/test/weather.py +++ b/tests/testing_config/custom_components/test/weather.py @@ -168,11 +168,6 @@ class MockWeatherMockForecast(MockWeather): } ] - @property - def forecast(self) -> list[Forecast] | None: - """Return the forecast.""" - return self.forecast_list - async def async_forecast_daily(self) -> list[Forecast] | None: """Return the forecast_daily.""" return self.forecast_list -- GitLab