1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Make template weather consistent with itself and other platforms (#159607)

This commit is contained in:
Petro31
2026-01-28 09:04:03 -05:00
committed by GitHub
parent 6fd27ec7ec
commit 84a09bec0e
5 changed files with 679 additions and 690 deletions

View File

@@ -239,7 +239,13 @@ CONFIG_SECTION_SCHEMA = vol.All(
cv.ensure_list, [vacuum_platform.VACUUM_YAML_SCHEMA]
),
vol.Optional(DOMAIN_WEATHER): vol.All(
cv.ensure_list, [weather_platform.WEATHER_YAML_SCHEMA]
cv.ensure_list,
[
vol.Any(
weather_platform.WEATHER_YAML_SCHEMA,
weather_platform.WEATHER_MODERN_YAML_SCHEMA,
)
],
),
},
),

View File

@@ -5,6 +5,7 @@ from __future__ import annotations
from collections.abc import Callable
from dataclasses import asdict, dataclass
from functools import partial
import logging
from typing import TYPE_CHECKING, Any, Literal, Self
import voluptuous as vol
@@ -71,6 +72,8 @@ from .schemas import (
from .template_entity import TemplateEntity
from .trigger_entity import TriggerEntity
_LOGGER = logging.getLogger(__name__)
CHECK_FORECAST_KEYS = (
set()
.union(Forecast.__annotations__.keys())
@@ -159,8 +162,9 @@ LEGACY_FIELDS = {
CONF_WIND_SPEED_TEMPLATE: CONF_WIND_SPEED,
}
WEATHER_COMMON_SCHEMA = vol.Schema(
# These options that are templates all have _template. These fields will
# enter deprecation after legacy templates are removed.
WEATHER_COMMON_LEGACY_SCHEMA = vol.Schema(
{
vol.Optional(CONF_APPARENT_TEMPERATURE_TEMPLATE): cv.template,
vol.Optional(CONF_ATTRIBUTION_TEMPLATE): cv.template,
@@ -186,32 +190,7 @@ WEATHER_COMMON_SCHEMA = vol.Schema(
}
)
WEATHER_YAML_SCHEMA = (
vol.Schema(
{
vol.Optional(CONF_UV_INDEX_TEMPLATE): cv.template,
}
)
.extend(WEATHER_COMMON_SCHEMA.schema)
.extend(
make_template_entity_common_modern_schema(WEATHER_DOMAIN, DEFAULT_NAME).schema
)
)
PLATFORM_SCHEMA = (
vol.Schema(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.template,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
.extend(WEATHER_COMMON_SCHEMA.schema)
.extend(WEATHER_PLATFORM_SCHEMA.schema)
)
WEATHER_CONFIG_ENTRY_SCHEMA = vol.Schema(
WEATHER_COMMON_MODERN_SCHEMA = vol.Schema(
{
vol.Optional(CONF_APPARENT_TEMPERATURE): cv.template,
vol.Optional(CONF_ATTRIBUTION): cv.template,
@@ -236,7 +215,40 @@ WEATHER_CONFIG_ENTRY_SCHEMA = vol.Schema(
vol.Optional(CONF_WIND_SPEED): cv.template,
vol.Optional(CONF_WIND_SPEED_UNIT): vol.In(SpeedConverter.VALID_UNITS),
}
).extend(TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema)
)
WEATHER_YAML_SCHEMA = (
vol.Schema(
{
vol.Optional(CONF_UV_INDEX_TEMPLATE): cv.template,
}
)
.extend(WEATHER_COMMON_LEGACY_SCHEMA.schema)
.extend(
make_template_entity_common_modern_schema(WEATHER_DOMAIN, DEFAULT_NAME).schema
)
)
WEATHER_MODERN_YAML_SCHEMA = WEATHER_COMMON_MODERN_SCHEMA.extend(
make_template_entity_common_modern_schema(WEATHER_DOMAIN, DEFAULT_NAME).schema
)
PLATFORM_SCHEMA = (
vol.Schema(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.template,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
.extend(WEATHER_COMMON_LEGACY_SCHEMA.schema)
.extend(WEATHER_PLATFORM_SCHEMA.schema)
)
WEATHER_CONFIG_ENTRY_SCHEMA = WEATHER_COMMON_MODERN_SCHEMA.extend(
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema
)
async def async_setup_platform(
@@ -699,6 +711,7 @@ class TriggerWeatherEntity(TriggerEntity, AbstractTemplateWeather, RestoreEntity
for key in (
CONF_APPARENT_TEMPERATURE,
CONF_ATTRIBUTION,
CONF_CLOUD_COVERAGE,
CONF_DEW_POINT,
CONF_FORECAST_DAILY,
@@ -752,6 +765,7 @@ class TriggerWeatherEntity(TriggerEntity, AbstractTemplateWeather, RestoreEntity
write_ha_state = False
for key, updater in (
(CONF_APPARENT_TEMPERATURE, self._update_apparent_temperature),
(CONF_ATTRIBUTION, self._update_attribution),
(CONF_CLOUD_COVERAGE, self._update_coverage),
(CONF_CONDITION, self._update_condition),
(CONF_DEW_POINT, self._update_dew_point),
@@ -772,28 +786,39 @@ class TriggerWeatherEntity(TriggerEntity, AbstractTemplateWeather, RestoreEntity
if write_ha_state:
self.async_write_ha_state()
def _check_forecast(
self,
forecast_type: Literal["daily", "hourly", "twice_daily"],
key: str,
) -> list[Forecast]:
result = self._rendered.get(key)
try:
return self._validate_forecast(forecast_type, result) or []
except vol.Invalid as err:
_LOGGER.error(
(
"Error validating template result '%s' "
"for attribute '%s' in entity %s "
"validation message '%s'"
),
result,
key,
self.entity_id,
err.msg,
)
return []
async def async_forecast_daily(self) -> list[Forecast]:
"""Return the daily forecast in native units."""
return (
self._validate_forecast("daily", self._rendered.get(CONF_FORECAST_DAILY))
or []
)
return self._check_forecast("daily", CONF_FORECAST_DAILY)
async def async_forecast_hourly(self) -> list[Forecast]:
"""Return the daily forecast in native units."""
return (
self._validate_forecast("hourly", self._rendered.get(CONF_FORECAST_HOURLY))
or []
)
return self._check_forecast("hourly", CONF_FORECAST_HOURLY)
async def async_forecast_twice_daily(self) -> list[Forecast]:
"""Return the daily forecast in native units."""
return (
self._validate_forecast(
"twice_daily", self._rendered.get(CONF_FORECAST_TWICE_DAILY)
)
or []
)
return self._check_forecast("twice_daily", CONF_FORECAST_TWICE_DAILY)
@property
def extra_restore_state_data(self) -> WeatherExtraStoredData:

View File

@@ -163,19 +163,20 @@ async def setup_entity(
) -> None:
"""Do setup of a template entity based on the configuration style."""
if style == ConfigurationStyle.LEGACY:
entity_config = {
**({"value_template": state_template} if state_template else {}),
**config,
**(extra_config or {}),
**({"attribute_templates": attributes} if attributes else {}),
}
# Lock and weather platforms do not use a slug.
if platform_setup.legacy_slug is None:
config = {"name": platform_setup.object_id, **entity_config}
else:
config = {platform_setup.object_id: entity_config}
await async_setup_legacy_platforms(
hass,
platform_setup.domain,
platform_setup.legacy_slug,
count,
{
platform_setup.object_id: {
**({"value_template": state_template} if state_template else {}),
**config,
**(extra_config or {}),
**({"attribute_templates": attributes} if attributes else {}),
}
},
hass, platform_setup.domain, platform_setup.legacy_slug, count, config
)
return

View File

@@ -1,23 +1,33 @@
# serializer version: 1
# name: test_forecasts[config0-1-weather-get_forecasts]
# name: test_forecasts[ConfigurationStyle.LEGACY-config0]
dict({
'weather.forecast': dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[config0-1-weather-get_forecasts].1
# name: test_forecasts[ConfigurationStyle.LEGACY-config0].1
dict({
'weather.forecast': dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[config0-1-weather-get_forecasts].2
# name: test_forecasts[ConfigurationStyle.LEGACY-config0].2
dict({
'weather.forecast': dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'fog',
@@ -29,10 +39,227 @@
}),
})
# ---
# name: test_forecasts[config0-1-weather-get_forecasts].3
# name: test_forecasts[ConfigurationStyle.LEGACY-config0].3
dict({
'weather.forecast': dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 16.9,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config1]
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config1].1
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config1].2
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'fog',
'datetime': '2023-02-17T14:00:00+00:00',
'is_daytime': True,
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config1].3
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 16.9,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config3]
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config3].1
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config3].2
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'fog',
'datetime': '2023-02-17T14:00:00+00:00',
'is_daytime': True,
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.MODERN-config3].3
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 16.9,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config2]
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config2].1
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config2].2
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'fog',
'datetime': '2023-02-17T14:00:00+00:00',
'is_daytime': True,
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config2].3
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 16.9,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config4]
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config4].1
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config4].2
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'fog',
'datetime': '2023-02-17T14:00:00+00:00',
'is_daytime': True,
'temperature': 14.2,
}),
]),
}),
})
# ---
# name: test_forecasts[ConfigurationStyle.TRIGGER-config4].3
dict({
'weather.template_weather': dict({
'forecast': list([
dict({
'condition': 'cloudy',
'datetime': '2023-02-17T14:00:00+00:00',
'temperature': 16.9,
}),
]),
}),
})
@@ -76,49 +303,3 @@
'state': 'unknown',
})
# ---
# name: test_trigger_weather_services[config0-1-template-get_forecasts]
dict({
'weather.test': dict({
'forecast': list([
dict({
'condition': 'sunny',
'datetime': '2023-10-19T06:50:05-07:00',
'precipitation': 20.0,
'temperature': 20.0,
'templow': 15.0,
}),
]),
}),
})
# ---
# name: test_trigger_weather_services[config0-1-template-get_forecasts].1
dict({
'weather.test': dict({
'forecast': list([
dict({
'condition': 'sunny',
'datetime': '2023-10-19T06:50:05-07:00',
'precipitation': 20.0,
'temperature': 20.0,
'templow': 15.0,
}),
]),
}),
})
# ---
# name: test_trigger_weather_services[config0-1-template-get_forecasts].2
dict({
'weather.test': dict({
'forecast': list([
dict({
'condition': 'sunny',
'datetime': '2023-10-19T06:50:05-07:00',
'is_daytime': True,
'precipitation': 20.0,
'temperature': 20.0,
'templow': 15.0,
}),
]),
}),
})
# ---

File diff suppressed because it is too large Load Diff