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
This commit is contained in:
parent
1269031d11
commit
65230908c6
31 changed files with 37 additions and 1596 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
# ---
|
||||
|
|
|
@ -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"]
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
|
|
|
@ -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 }}",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue