From 33cc257e759fc3bbcb2c0afc0a7e78600a9302e9 Mon Sep 17 00:00:00 2001 From: Petro31 <35082313+Petro31@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:38:53 -0400 Subject: [PATCH] Consolidate template integration's config schemas (#149018) --- .../template/alarm_control_panel.py | 113 ++++++++---------- .../components/template/binary_sensor.py | 51 ++++---- homeassistant/components/template/button.py | 31 ++--- homeassistant/components/template/config.py | 32 ++--- homeassistant/components/template/const.py | 14 ++- homeassistant/components/template/cover.py | 6 +- homeassistant/components/template/fan.py | 6 +- homeassistant/components/template/helpers.py | 44 +++++++ homeassistant/components/template/image.py | 26 ++-- homeassistant/components/template/light.py | 6 +- homeassistant/components/template/lock.py | 3 +- homeassistant/components/template/number.py | 64 +++++----- homeassistant/components/template/select.py | 64 ++++++---- homeassistant/components/template/sensor.py | 63 ++++++---- homeassistant/components/template/switch.py | 61 +++++----- .../components/template/template_entity.py | 12 +- homeassistant/components/template/vacuum.py | 6 +- homeassistant/components/template/weather.py | 36 +++++- 18 files changed, 385 insertions(+), 253 deletions(-) diff --git a/homeassistant/components/template/alarm_control_panel.py b/homeassistant/components/template/alarm_control_panel.py index cd70a7d44e0..f95fc0dbab7 100644 --- a/homeassistant/components/template/alarm_control_panel.py +++ b/homeassistant/components/template/alarm_control_panel.py @@ -21,7 +21,6 @@ from homeassistant.components.alarm_control_panel import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CODE, - CONF_DEVICE_ID, CONF_NAME, CONF_STATE, CONF_UNIQUE_ID, @@ -31,7 +30,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import TemplateError -from homeassistant.helpers import config_validation as cv, selector, template +from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, @@ -43,8 +42,16 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import DOMAIN from .coordinator import TriggerUpdateCoordinator from .entity import AbstractTemplateEntity -from .helpers import async_setup_template_platform -from .template_entity import TemplateEntity, make_template_entity_common_modern_schema +from .helpers import ( + async_setup_template_entry, + async_setup_template_platform, + async_setup_template_preview, +) +from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, + TemplateEntity, + make_template_entity_common_modern_schema, +) from .trigger_entity import TriggerEntity _LOGGER = logging.getLogger(__name__) @@ -88,27 +95,28 @@ LEGACY_FIELDS = { DEFAULT_NAME = "Template Alarm Control Panel" -ALARM_CONTROL_PANEL_SCHEMA = vol.All( - vol.Schema( - { - vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, - vol.Optional( - CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name - ): cv.enum(TemplateCodeFormat), - vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_STATE): cv.template, - vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA, - } - ).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) +ALARM_CONTROL_PANEL_COMMON_SCHEMA = vol.Schema( + { + vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, + vol.Optional(CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name): cv.enum( + TemplateCodeFormat + ), + vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_STATE): cv.template, + vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA, + } ) +ALARM_CONTROL_PANEL_YAML_SCHEMA = ALARM_CONTROL_PANEL_COMMON_SCHEMA.extend( + make_template_entity_common_modern_schema(DEFAULT_NAME).schema +) -LEGACY_ALARM_CONTROL_PANEL_SCHEMA = vol.Schema( +ALARM_CONTROL_PANEL_LEGACY_YAML_SCHEMA = vol.Schema( { vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA, @@ -130,59 +138,29 @@ LEGACY_ALARM_CONTROL_PANEL_SCHEMA = vol.Schema( PLATFORM_SCHEMA = ALARM_CONTROL_PANEL_PLATFORM_SCHEMA.extend( { vol.Required(CONF_ALARM_CONTROL_PANELS): cv.schema_with_slug_keys( - LEGACY_ALARM_CONTROL_PANEL_SCHEMA + ALARM_CONTROL_PANEL_LEGACY_YAML_SCHEMA ), } ) -ALARM_CONTROL_PANEL_CONFIG_SCHEMA = vol.Schema( - { - vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, - vol.Optional(CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name): cv.enum( - TemplateCodeFormat - ), - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), - vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA, - vol.Required(CONF_NAME): cv.template, - vol.Optional(CONF_STATE): cv.template, - vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA, - } +ALARM_CONTROL_PANEL_CONFIG_ENTRY_SCHEMA = ALARM_CONTROL_PANEL_COMMON_SCHEMA.extend( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema ) -def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str, dict]: - """Rewrite option configuration to modern configuration.""" - option_config = {**option_config} - - if CONF_VALUE_TEMPLATE in option_config: - option_config[CONF_STATE] = option_config.pop(CONF_VALUE_TEMPLATE) - - return option_config - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - _options = rewrite_options_to_modern_conf(_options) - validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA(_options) - async_add_entities( - [ - StateAlarmControlPanelEntity( - hass, - validated_config, - config_entry.entry_id, - ) - ] + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + StateAlarmControlPanelEntity, + ALARM_CONTROL_PANEL_CONFIG_ENTRY_SCHEMA, + True, ) @@ -211,11 +189,14 @@ def async_create_preview_alarm_control_panel( hass: HomeAssistant, name: str, config: dict[str, Any] ) -> StateAlarmControlPanelEntity: """Create a preview alarm control panel.""" - updated_config = rewrite_options_to_modern_conf(config) - validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA( - updated_config | {CONF_NAME: name} + return async_setup_template_preview( + hass, + name, + config, + StateAlarmControlPanelEntity, + ALARM_CONTROL_PANEL_CONFIG_ENTRY_SCHEMA, + True, ) - return StateAlarmControlPanelEntity(hass, validated_config, None) class AbstractTemplateAlarmControlPanel( diff --git a/homeassistant/components/template/binary_sensor.py b/homeassistant/components/template/binary_sensor.py index caac43712a7..e8b8efbda0a 100644 --- a/homeassistant/components/template/binary_sensor.py +++ b/homeassistant/components/template/binary_sensor.py @@ -22,7 +22,6 @@ from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, CONF_DEVICE_CLASS, - CONF_DEVICE_ID, CONF_ENTITY_PICTURE_TEMPLATE, CONF_FRIENDLY_NAME_TEMPLATE, CONF_ICON_TEMPLATE, @@ -38,7 +37,7 @@ from homeassistant.const import ( ) from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.exceptions import TemplateError -from homeassistant.helpers import config_validation as cv, selector, template +from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, @@ -50,8 +49,16 @@ from homeassistant.util import dt as dt_util from . import TriggerUpdateCoordinator from .const import CONF_AVAILABILITY_TEMPLATE -from .helpers import async_setup_template_platform -from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity +from .helpers import ( + async_setup_template_entry, + async_setup_template_platform, + async_setup_template_preview, +) +from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, + TEMPLATE_ENTITY_COMMON_SCHEMA, + TemplateEntity, +) from .trigger_entity import TriggerEntity CONF_DELAY_ON = "delay_on" @@ -64,7 +71,7 @@ LEGACY_FIELDS = { CONF_VALUE_TEMPLATE: CONF_STATE, } -BINARY_SENSOR_SCHEMA = vol.Schema( +BINARY_SENSOR_COMMON_SCHEMA = vol.Schema( { vol.Optional(CONF_AUTO_OFF): vol.Any(cv.positive_time_period, cv.template), vol.Optional(CONF_DELAY_OFF): vol.Any(cv.positive_time_period, cv.template), @@ -73,15 +80,17 @@ BINARY_SENSOR_SCHEMA = vol.Schema( vol.Required(CONF_STATE): cv.template, vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, } -).extend(TEMPLATE_ENTITY_COMMON_SCHEMA.schema) - -BINARY_SENSOR_CONFIG_SCHEMA = BINARY_SENSOR_SCHEMA.extend( - { - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), - } ) -LEGACY_BINARY_SENSOR_SCHEMA = vol.All( +BINARY_SENSOR_YAML_SCHEMA = BINARY_SENSOR_COMMON_SCHEMA.extend( + TEMPLATE_ENTITY_COMMON_SCHEMA.schema +) + +BINARY_SENSOR_CONFIG_ENTRY_SCHEMA = BINARY_SENSOR_COMMON_SCHEMA.extend( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema +) + +BINARY_SENSOR_LEGACY_YAML_SCHEMA = vol.All( cv.deprecated(ATTR_ENTITY_ID), vol.Schema( { @@ -106,7 +115,7 @@ LEGACY_BINARY_SENSOR_SCHEMA = vol.All( PLATFORM_SCHEMA = BINARY_SENSOR_PLATFORM_SCHEMA.extend( { vol.Required(CONF_SENSORS): cv.schema_with_slug_keys( - LEGACY_BINARY_SENSOR_SCHEMA + BINARY_SENSOR_LEGACY_YAML_SCHEMA ), } ) @@ -138,11 +147,12 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - validated_config = BINARY_SENSOR_CONFIG_SCHEMA(_options) - async_add_entities( - [StateBinarySensorEntity(hass, validated_config, config_entry.entry_id)] + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + StateBinarySensorEntity, + BINARY_SENSOR_CONFIG_ENTRY_SCHEMA, ) @@ -151,8 +161,9 @@ def async_create_preview_binary_sensor( hass: HomeAssistant, name: str, config: dict[str, Any] ) -> StateBinarySensorEntity: """Create a preview sensor.""" - validated_config = BINARY_SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name}) - return StateBinarySensorEntity(hass, validated_config, None) + return async_setup_template_preview( + hass, name, config, StateBinarySensorEntity, BINARY_SENSOR_CONFIG_ENTRY_SCHEMA + ) class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity): diff --git a/homeassistant/components/template/button.py b/homeassistant/components/template/button.py index 26d339b7e33..d84005ccc28 100644 --- a/homeassistant/components/template/button.py +++ b/homeassistant/components/template/button.py @@ -14,9 +14,9 @@ from homeassistant.components.button import ( ButtonEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_DEVICE_CLASS, CONF_DEVICE_ID, CONF_NAME +from homeassistant.const import CONF_DEVICE_CLASS from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv, selector +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, @@ -24,29 +24,31 @@ from homeassistant.helpers.entity_platform import ( from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import CONF_PRESS, DOMAIN -from .helpers import async_setup_template_platform -from .template_entity import TemplateEntity, make_template_entity_common_modern_schema +from .helpers import async_setup_template_entry, async_setup_template_platform +from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, + TemplateEntity, + make_template_entity_common_modern_schema, +) _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "Template Button" DEFAULT_OPTIMISTIC = False -BUTTON_SCHEMA = vol.Schema( +BUTTON_YAML_SCHEMA = vol.Schema( { vol.Required(CONF_PRESS): cv.SCRIPT_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, } ).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) -CONFIG_BUTTON_SCHEMA = vol.Schema( +BUTTON_CONFIG_ENTRY_SCHEMA = vol.Schema( { - vol.Optional(CONF_NAME): cv.template, vol.Optional(CONF_PRESS): cv.SCRIPT_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), } -) +).extend(TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema) async def async_setup_platform( @@ -73,11 +75,12 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - validated_config = CONFIG_BUTTON_SCHEMA(_options) - async_add_entities( - [StateButtonEntity(hass, validated_config, config_entry.entry_id)] + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + StateButtonEntity, + BUTTON_CONFIG_ENTRY_SCHEMA, ) diff --git a/homeassistant/components/template/config.py b/homeassistant/components/template/config.py index 1b3e9986d36..a3311c35563 100644 --- a/homeassistant/components/template/config.py +++ b/homeassistant/components/template/config.py @@ -102,57 +102,57 @@ CONFIG_SECTION_SCHEMA = vol.All( { vol.Optional(CONF_ACTIONS): cv.SCRIPT_SCHEMA, vol.Optional(CONF_BINARY_SENSORS): cv.schema_with_slug_keys( - binary_sensor_platform.LEGACY_BINARY_SENSOR_SCHEMA + binary_sensor_platform.BINARY_SENSOR_LEGACY_YAML_SCHEMA ), vol.Optional(CONF_CONDITIONS): cv.CONDITIONS_SCHEMA, vol.Optional(CONF_SENSORS): cv.schema_with_slug_keys( - sensor_platform.LEGACY_SENSOR_SCHEMA + sensor_platform.SENSOR_LEGACY_YAML_SCHEMA ), vol.Optional(CONF_TRIGGERS): cv.TRIGGER_SCHEMA, vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA, vol.Optional(DOMAIN_ALARM_CONTROL_PANEL): vol.All( cv.ensure_list, - [alarm_control_panel_platform.ALARM_CONTROL_PANEL_SCHEMA], + [alarm_control_panel_platform.ALARM_CONTROL_PANEL_YAML_SCHEMA], ), vol.Optional(DOMAIN_BINARY_SENSOR): vol.All( - cv.ensure_list, [binary_sensor_platform.BINARY_SENSOR_SCHEMA] + cv.ensure_list, [binary_sensor_platform.BINARY_SENSOR_YAML_SCHEMA] ), vol.Optional(DOMAIN_BUTTON): vol.All( - cv.ensure_list, [button_platform.BUTTON_SCHEMA] + cv.ensure_list, [button_platform.BUTTON_YAML_SCHEMA] ), vol.Optional(DOMAIN_COVER): vol.All( - cv.ensure_list, [cover_platform.COVER_SCHEMA] + cv.ensure_list, [cover_platform.COVER_YAML_SCHEMA] ), vol.Optional(DOMAIN_FAN): vol.All( - cv.ensure_list, [fan_platform.FAN_SCHEMA] + cv.ensure_list, [fan_platform.FAN_YAML_SCHEMA] ), vol.Optional(DOMAIN_IMAGE): vol.All( - cv.ensure_list, [image_platform.IMAGE_SCHEMA] + cv.ensure_list, [image_platform.IMAGE_YAML_SCHEMA] ), vol.Optional(DOMAIN_LIGHT): vol.All( - cv.ensure_list, [light_platform.LIGHT_SCHEMA] + cv.ensure_list, [light_platform.LIGHT_YAML_SCHEMA] ), vol.Optional(DOMAIN_LOCK): vol.All( - cv.ensure_list, [lock_platform.LOCK_SCHEMA] + cv.ensure_list, [lock_platform.LOCK_YAML_SCHEMA] ), vol.Optional(DOMAIN_NUMBER): vol.All( - cv.ensure_list, [number_platform.NUMBER_SCHEMA] + cv.ensure_list, [number_platform.NUMBER_YAML_SCHEMA] ), vol.Optional(DOMAIN_SELECT): vol.All( - cv.ensure_list, [select_platform.SELECT_SCHEMA] + cv.ensure_list, [select_platform.SELECT_YAML_SCHEMA] ), vol.Optional(DOMAIN_SENSOR): vol.All( - cv.ensure_list, [sensor_platform.SENSOR_SCHEMA] + cv.ensure_list, [sensor_platform.SENSOR_YAML_SCHEMA] ), vol.Optional(DOMAIN_SWITCH): vol.All( - cv.ensure_list, [switch_platform.SWITCH_SCHEMA] + cv.ensure_list, [switch_platform.SWITCH_YAML_SCHEMA] ), vol.Optional(DOMAIN_VACUUM): vol.All( - cv.ensure_list, [vacuum_platform.VACUUM_SCHEMA] + cv.ensure_list, [vacuum_platform.VACUUM_YAML_SCHEMA] ), vol.Optional(DOMAIN_WEATHER): vol.All( - cv.ensure_list, [weather_platform.WEATHER_SCHEMA] + cv.ensure_list, [weather_platform.WEATHER_YAML_SCHEMA] ), }, ), diff --git a/homeassistant/components/template/const.py b/homeassistant/components/template/const.py index 53c0fa3af13..e3e0e4fe9f5 100644 --- a/homeassistant/components/template/const.py +++ b/homeassistant/components/template/const.py @@ -1,6 +1,9 @@ """Constants for the Template Platform Components.""" -from homeassistant.const import Platform +import voluptuous as vol + +from homeassistant.const import CONF_ICON, CONF_NAME, CONF_UNIQUE_ID, Platform +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import ConfigType CONF_ATTRIBUTE_TEMPLATES = "attribute_templates" @@ -16,6 +19,15 @@ CONF_STEP = "step" CONF_TURN_OFF = "turn_off" CONF_TURN_ON = "turn_on" +TEMPLATE_ENTITY_BASE_SCHEMA = vol.Schema( + { + vol.Optional(CONF_ICON): cv.template, + vol.Optional(CONF_NAME): cv.template, + vol.Optional(CONF_PICTURE): cv.template, + vol.Optional(CONF_UNIQUE_ID): cv.string, + } +) + DOMAIN = "template" PLATFORM_STORAGE_KEY = "template_platforms" diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index bceac7811f4..0bbc6b77f57 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -91,7 +91,7 @@ LEGACY_FIELDS = { DEFAULT_NAME = "Template Cover" -COVER_SCHEMA = vol.All( +COVER_YAML_SCHEMA = vol.All( vol.Schema( { vol.Inclusive(CLOSE_ACTION, CONF_OPEN_AND_CLOSE): cv.SCRIPT_SCHEMA, @@ -110,7 +110,7 @@ COVER_SCHEMA = vol.All( cv.has_at_least_one_key(OPEN_ACTION, POSITION_ACTION), ) -LEGACY_COVER_SCHEMA = vol.All( +COVER_LEGACY_YAML_SCHEMA = vol.All( cv.deprecated(CONF_ENTITY_ID), vol.Schema( { @@ -134,7 +134,7 @@ LEGACY_COVER_SCHEMA = vol.All( ) PLATFORM_SCHEMA = COVER_PLATFORM_SCHEMA.extend( - {vol.Required(CONF_COVERS): cv.schema_with_slug_keys(LEGACY_COVER_SCHEMA)} + {vol.Required(CONF_COVERS): cv.schema_with_slug_keys(COVER_LEGACY_YAML_SCHEMA)} ) diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index 34faba353d0..13d2414aea2 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -81,7 +81,7 @@ LEGACY_FIELDS = { DEFAULT_NAME = "Template Fan" -FAN_SCHEMA = vol.All( +FAN_YAML_SCHEMA = vol.All( vol.Schema( { vol.Optional(CONF_DIRECTION): cv.template, @@ -101,7 +101,7 @@ FAN_SCHEMA = vol.All( ).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) ) -LEGACY_FAN_SCHEMA = vol.All( +FAN_LEGACY_YAML_SCHEMA = vol.All( cv.deprecated(CONF_ENTITY_ID), vol.Schema( { @@ -126,7 +126,7 @@ LEGACY_FAN_SCHEMA = vol.All( ) PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( - {vol.Required(CONF_FANS): cv.schema_with_slug_keys(LEGACY_FAN_SCHEMA)} + {vol.Required(CONF_FANS): cv.schema_with_slug_keys(FAN_LEGACY_YAML_SCHEMA)} ) diff --git a/homeassistant/components/template/helpers.py b/homeassistant/components/template/helpers.py index 514255f417a..c0177e9dd5d 100644 --- a/homeassistant/components/template/helpers.py +++ b/homeassistant/components/template/helpers.py @@ -5,14 +5,19 @@ import itertools import logging from typing import Any +import voluptuous as vol + from homeassistant.components import blueprint +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_ENTITY_PICTURE_TEMPLATE, CONF_FRIENDLY_NAME, CONF_ICON, CONF_ICON_TEMPLATE, CONF_NAME, + CONF_STATE, CONF_UNIQUE_ID, + CONF_VALUE_TEMPLATE, SERVICE_RELOAD, ) from homeassistant.core import HomeAssistant, callback @@ -20,6 +25,7 @@ from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers import template from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_platform import ( + AddConfigEntryEntitiesCallback, AddEntitiesCallback, async_get_platforms, ) @@ -228,3 +234,41 @@ async def async_setup_template_platform( discovery_info["entities"], discovery_info["unique_id"], ) + + +async def async_setup_template_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, + state_entity_cls: type[TemplateEntity], + config_schema: vol.Schema, + replace_value_template: bool = False, +) -> None: + """Setup the Template from a config entry.""" + options = dict(config_entry.options) + options.pop("template_type") + + if replace_value_template and CONF_VALUE_TEMPLATE in options: + options[CONF_STATE] = options.pop(CONF_VALUE_TEMPLATE) + + validated_config = config_schema(options) + + async_add_entities( + [state_entity_cls(hass, validated_config, config_entry.entry_id)] + ) + + +def async_setup_template_preview[T: TemplateEntity]( + hass: HomeAssistant, + name: str, + config: ConfigType, + state_entity_cls: type[T], + schema: vol.Schema, + replace_value_template: bool = False, +) -> T: + """Setup the Template preview.""" + if replace_value_template and CONF_VALUE_TEMPLATE in config: + config[CONF_STATE] = config.pop(CONF_VALUE_TEMPLATE) + + validated_config = schema(config | {CONF_NAME: name}) + return state_entity_cls(hass, validated_config, None) diff --git a/homeassistant/components/template/image.py b/homeassistant/components/template/image.py index 57e7c6ffc55..b4513fc2447 100644 --- a/homeassistant/components/template/image.py +++ b/homeassistant/components/template/image.py @@ -13,10 +13,10 @@ from homeassistant.components.image import ( ImageEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_URL, CONF_VERIFY_SSL +from homeassistant.const import CONF_URL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import TemplateError -from homeassistant.helpers import config_validation as cv, selector +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, @@ -26,8 +26,9 @@ from homeassistant.util import dt as dt_util from . import TriggerUpdateCoordinator from .const import CONF_PICTURE -from .helpers import async_setup_template_platform +from .helpers import async_setup_template_entry, async_setup_template_platform from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, TemplateEntity, make_template_entity_common_modern_attributes_schema, ) @@ -39,7 +40,7 @@ DEFAULT_NAME = "Template Image" GET_IMAGE_TIMEOUT = 10 -IMAGE_SCHEMA = vol.Schema( +IMAGE_YAML_SCHEMA = vol.Schema( { vol.Required(CONF_URL): cv.template, vol.Optional(CONF_VERIFY_SSL, default=True): bool, @@ -47,14 +48,12 @@ IMAGE_SCHEMA = vol.Schema( ).extend(make_template_entity_common_modern_attributes_schema(DEFAULT_NAME).schema) -IMAGE_CONFIG_SCHEMA = vol.Schema( +IMAGE_CONFIG_ENTRY_SCHEMA = vol.Schema( { - vol.Optional(CONF_NAME): cv.template, vol.Required(CONF_URL): cv.template, vol.Optional(CONF_VERIFY_SSL, default=True): bool, - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), } -) +).extend(TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema) async def async_setup_platform( @@ -81,11 +80,12 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - validated_config = IMAGE_CONFIG_SCHEMA(_options) - async_add_entities( - [StateImageEntity(hass, validated_config, config_entry.entry_id)] + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + StateImageEntity, + IMAGE_CONFIG_ENTRY_SCHEMA, ) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index fb97d95db3d..802fc145427 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -121,7 +121,7 @@ LEGACY_FIELDS = { DEFAULT_NAME = "Template Light" -LIGHT_SCHEMA = vol.Schema( +LIGHT_YAML_SCHEMA = vol.Schema( { vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA, vol.Inclusive(CONF_EFFECT_LIST, "effect"): cv.template, @@ -147,7 +147,7 @@ LIGHT_SCHEMA = vol.Schema( } ).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) -LEGACY_LIGHT_SCHEMA = vol.All( +LIGHT_LEGACY_YAML_SCHEMA = vol.All( cv.deprecated(CONF_ENTITY_ID), vol.Schema( { @@ -186,7 +186,7 @@ PLATFORM_SCHEMA = vol.All( cv.removed(CONF_WHITE_VALUE_ACTION), cv.removed(CONF_WHITE_VALUE_TEMPLATE), LIGHT_PLATFORM_SCHEMA.extend( - {vol.Required(CONF_LIGHTS): cv.schema_with_slug_keys(LEGACY_LIGHT_SCHEMA)} + {vol.Required(CONF_LIGHTS): cv.schema_with_slug_keys(LIGHT_LEGACY_YAML_SCHEMA)} ), ) diff --git a/homeassistant/components/template/lock.py b/homeassistant/components/template/lock.py index 581a037c3d7..a2f1f56bea2 100644 --- a/homeassistant/components/template/lock.py +++ b/homeassistant/components/template/lock.py @@ -54,7 +54,7 @@ LEGACY_FIELDS = { CONF_VALUE_TEMPLATE: CONF_STATE, } -LOCK_SCHEMA = vol.All( +LOCK_YAML_SCHEMA = vol.All( vol.Schema( { vol.Optional(CONF_CODE_FORMAT): cv.template, @@ -68,7 +68,6 @@ LOCK_SCHEMA = vol.All( ).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) ) - PLATFORM_SCHEMA = LOCK_PLATFORM_SCHEMA.extend( { vol.Optional(CONF_CODE_FORMAT_TEMPLATE): cv.template, diff --git a/homeassistant/components/template/number.py b/homeassistant/components/template/number.py index e0b8e7594ce..31a6338f594 100644 --- a/homeassistant/components/template/number.py +++ b/homeassistant/components/template/number.py @@ -18,14 +18,13 @@ from homeassistant.components.number import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - CONF_DEVICE_ID, CONF_NAME, CONF_OPTIMISTIC, CONF_STATE, CONF_UNIT_OF_MEASUREMENT, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import config_validation as cv, selector +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, @@ -34,8 +33,16 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import TriggerUpdateCoordinator from .const import CONF_MAX, CONF_MIN, CONF_STEP, DOMAIN -from .helpers import async_setup_template_platform -from .template_entity import TemplateEntity, make_template_entity_common_modern_schema +from .helpers import ( + async_setup_template_entry, + async_setup_template_platform, + async_setup_template_preview, +) +from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, + TemplateEntity, + make_template_entity_common_modern_schema, +) from .trigger_entity import TriggerEntity _LOGGER = logging.getLogger(__name__) @@ -45,30 +52,31 @@ CONF_SET_VALUE = "set_value" DEFAULT_NAME = "Template Number" DEFAULT_OPTIMISTIC = False -NUMBER_SCHEMA = vol.Schema( +NUMBER_COMMON_SCHEMA = vol.Schema( { - vol.Required(CONF_STATE): cv.template, - vol.Required(CONF_SET_VALUE): cv.SCRIPT_SCHEMA, - vol.Required(CONF_STEP): cv.template, - vol.Optional(CONF_MIN, default=DEFAULT_MIN_VALUE): cv.template, vol.Optional(CONF_MAX, default=DEFAULT_MAX_VALUE): cv.template, - vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, - } -).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) -NUMBER_CONFIG_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.template, + vol.Optional(CONF_MIN, default=DEFAULT_MIN_VALUE): cv.template, + vol.Required(CONF_SET_VALUE): cv.SCRIPT_SCHEMA, vol.Required(CONF_STATE): cv.template, vol.Required(CONF_STEP): cv.template, - vol.Required(CONF_SET_VALUE): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_MIN): cv.template, - vol.Optional(CONF_MAX): cv.template, vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), } ) +NUMBER_YAML_SCHEMA = ( + vol.Schema( + { + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + } + ) + .extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) + .extend(NUMBER_COMMON_SCHEMA.schema) +) + +NUMBER_CONFIG_ENTRY_SCHEMA = NUMBER_COMMON_SCHEMA.extend( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema +) + async def async_setup_platform( hass: HomeAssistant, @@ -94,11 +102,12 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - validated_config = NUMBER_CONFIG_SCHEMA(_options) - async_add_entities( - [StateNumberEntity(hass, validated_config, config_entry.entry_id)] + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + StateNumberEntity, + NUMBER_CONFIG_ENTRY_SCHEMA, ) @@ -107,8 +116,9 @@ def async_create_preview_number( hass: HomeAssistant, name: str, config: dict[str, Any] ) -> StateNumberEntity: """Create a preview number.""" - validated_config = NUMBER_CONFIG_SCHEMA(config | {CONF_NAME: name}) - return StateNumberEntity(hass, validated_config, None) + return async_setup_template_preview( + hass, name, config, StateNumberEntity, NUMBER_CONFIG_ENTRY_SCHEMA + ) class StateNumberEntity(TemplateEntity, NumberEntity): diff --git a/homeassistant/components/template/select.py b/homeassistant/components/template/select.py index 4273af6db28..0ad99cd6ae8 100644 --- a/homeassistant/components/template/select.py +++ b/homeassistant/components/template/select.py @@ -15,9 +15,9 @@ from homeassistant.components.select import ( SelectEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_OPTIMISTIC, CONF_STATE +from homeassistant.const import CONF_NAME, CONF_OPTIMISTIC, CONF_STATE from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import config_validation as cv, selector +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, @@ -27,8 +27,16 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import TriggerUpdateCoordinator from .const import DOMAIN from .entity import AbstractTemplateEntity -from .helpers import async_setup_template_platform -from .template_entity import TemplateEntity, make_template_entity_common_modern_schema +from .helpers import ( + async_setup_template_entry, + async_setup_template_platform, + async_setup_template_preview, +) +from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, + TemplateEntity, + make_template_entity_common_modern_schema, +) from .trigger_entity import TriggerEntity _LOGGER = logging.getLogger(__name__) @@ -39,26 +47,28 @@ CONF_SELECT_OPTION = "select_option" DEFAULT_NAME = "Template Select" DEFAULT_OPTIMISTIC = False -SELECT_SCHEMA = vol.Schema( +SELECT_COMMON_SCHEMA = vol.Schema( { - vol.Optional(CONF_STATE): cv.template, - vol.Required(CONF_SELECT_OPTION): cv.SCRIPT_SCHEMA, - vol.Required(ATTR_OPTIONS): cv.template, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, - } -).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) - - -SELECT_CONFIG_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.template, - vol.Required(CONF_STATE): cv.template, - vol.Required(CONF_OPTIONS): cv.template, + vol.Optional(ATTR_OPTIONS): cv.template, vol.Optional(CONF_SELECT_OPTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), + vol.Optional(CONF_STATE): cv.template, } ) +SELECT_YAML_SCHEMA = ( + vol.Schema( + { + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + } + ) + .extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) + .extend(SELECT_COMMON_SCHEMA.schema) +) + +SELECT_CONFIG_ENTRY_SCHEMA = SELECT_COMMON_SCHEMA.extend( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema +) + async def async_setup_platform( hass: HomeAssistant, @@ -84,10 +94,13 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - validated_config = SELECT_CONFIG_SCHEMA(_options) - async_add_entities([TemplateSelect(hass, validated_config, config_entry.entry_id)]) + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + TemplateSelect, + SELECT_CONFIG_ENTRY_SCHEMA, + ) @callback @@ -95,8 +108,9 @@ def async_create_preview_select( hass: HomeAssistant, name: str, config: dict[str, Any] ) -> TemplateSelect: """Create a preview select.""" - validated_config = SELECT_CONFIG_SCHEMA(config | {CONF_NAME: name}) - return TemplateSelect(hass, validated_config, None) + return async_setup_template_preview( + hass, name, config, TemplateSelect, SELECT_CONFIG_ENTRY_SCHEMA + ) class AbstractTemplateSelect(AbstractTemplateEntity, SelectEntity): diff --git a/homeassistant/components/template/sensor.py b/homeassistant/components/template/sensor.py index 6fc0588d9c7..ff956c50c6e 100644 --- a/homeassistant/components/template/sensor.py +++ b/homeassistant/components/template/sensor.py @@ -15,6 +15,7 @@ from homeassistant.components.sensor import ( DOMAIN as SENSOR_DOMAIN, ENTITY_ID_FORMAT, PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA, + STATE_CLASSES_SCHEMA, RestoreSensor, SensorDeviceClass, SensorEntity, @@ -25,7 +26,6 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_ENTITY_ID, CONF_DEVICE_CLASS, - CONF_DEVICE_ID, CONF_ENTITY_PICTURE_TEMPLATE, CONF_FRIENDLY_NAME, CONF_FRIENDLY_NAME_TEMPLATE, @@ -43,19 +43,26 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import TemplateError -from homeassistant.helpers import config_validation as cv, selector, template +from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, ) -from homeassistant.helpers.trigger_template_entity import TEMPLATE_SENSOR_BASE_SCHEMA from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt as dt_util from . import TriggerUpdateCoordinator from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE -from .helpers import async_setup_template_platform -from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity +from .helpers import ( + async_setup_template_entry, + async_setup_template_platform, + async_setup_template_preview, +) +from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, + TEMPLATE_ENTITY_COMMON_SCHEMA, + TemplateEntity, +) from .trigger_entity import TriggerEntity LEGACY_FIELDS = { @@ -77,29 +84,31 @@ def validate_last_reset(val): return val -SENSOR_SCHEMA = vol.All( +SENSOR_COMMON_SCHEMA = vol.Schema( + { + vol.Required(CONF_STATE): cv.template, + vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA, + vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, + } +) + +SENSOR_YAML_SCHEMA = vol.All( vol.Schema( { - vol.Required(CONF_STATE): cv.template, vol.Optional(ATTR_LAST_RESET): cv.template, } ) - .extend(TEMPLATE_SENSOR_BASE_SCHEMA.schema) + .extend(SENSOR_COMMON_SCHEMA.schema) .extend(TEMPLATE_ENTITY_COMMON_SCHEMA.schema), validate_last_reset, ) - -SENSOR_CONFIG_SCHEMA = vol.All( - vol.Schema( - { - vol.Required(CONF_STATE): cv.template, - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), - } - ).extend(TEMPLATE_SENSOR_BASE_SCHEMA.schema), +SENSOR_CONFIG_ENTRY_SCHEMA = SENSOR_COMMON_SCHEMA.extend( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema ) -LEGACY_SENSOR_SCHEMA = vol.All( +SENSOR_LEGACY_YAML_SCHEMA = vol.All( cv.deprecated(ATTR_ENTITY_ID), vol.Schema( { @@ -141,7 +150,9 @@ PLATFORM_SCHEMA = vol.All( { vol.Optional(CONF_TRIGGER): cv.match_all, # to raise custom warning vol.Optional(CONF_TRIGGERS): cv.match_all, # to raise custom warning - vol.Required(CONF_SENSORS): cv.schema_with_slug_keys(LEGACY_SENSOR_SCHEMA), + vol.Required(CONF_SENSORS): cv.schema_with_slug_keys( + SENSOR_LEGACY_YAML_SCHEMA + ), } ), extra_validation_checks, @@ -176,11 +187,12 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - validated_config = SENSOR_CONFIG_SCHEMA(_options) - async_add_entities( - [StateSensorEntity(hass, validated_config, config_entry.entry_id)] + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + StateSensorEntity, + SENSOR_CONFIG_ENTRY_SCHEMA, ) @@ -189,8 +201,9 @@ def async_create_preview_sensor( hass: HomeAssistant, name: str, config: dict[str, Any] ) -> StateSensorEntity: """Create a preview sensor.""" - validated_config = SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name}) - return StateSensorEntity(hass, validated_config, None) + return async_setup_template_preview( + hass, name, config, StateSensorEntity, SENSOR_CONFIG_ENTRY_SCHEMA + ) class StateSensorEntity(TemplateEntity, SensorEntity): diff --git a/homeassistant/components/template/switch.py b/homeassistant/components/template/switch.py index 7c1abd6d852..b1d72084ae7 100644 --- a/homeassistant/components/template/switch.py +++ b/homeassistant/components/template/switch.py @@ -16,7 +16,6 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, - CONF_DEVICE_ID, CONF_NAME, CONF_STATE, CONF_SWITCHES, @@ -29,7 +28,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import TemplateError -from homeassistant.helpers import config_validation as cv, selector, template +from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, @@ -39,8 +38,13 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import TriggerUpdateCoordinator from .const import CONF_TURN_OFF, CONF_TURN_ON, DOMAIN -from .helpers import async_setup_template_platform +from .helpers import ( + async_setup_template_entry, + async_setup_template_platform, + async_setup_template_preview, +) from .template_entity import ( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA, TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY, TemplateEntity, make_template_entity_common_modern_schema, @@ -55,16 +59,19 @@ LEGACY_FIELDS = { DEFAULT_NAME = "Template Switch" - -SWITCH_SCHEMA = vol.Schema( +SWITCH_COMMON_SCHEMA = vol.Schema( { vol.Optional(CONF_STATE): cv.template, - vol.Required(CONF_TURN_ON): cv.SCRIPT_SCHEMA, - vol.Required(CONF_TURN_OFF): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_TURN_ON): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_TURN_OFF): cv.SCRIPT_SCHEMA, } -).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) +) -LEGACY_SWITCH_SCHEMA = vol.All( +SWITCH_YAML_SCHEMA = SWITCH_COMMON_SCHEMA.extend( + make_template_entity_common_modern_schema(DEFAULT_NAME).schema +) + +SWITCH_LEGACY_YAML_SCHEMA = vol.All( cv.deprecated(ATTR_ENTITY_ID), vol.Schema( { @@ -79,17 +86,11 @@ LEGACY_SWITCH_SCHEMA = vol.All( ) PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend( - {vol.Required(CONF_SWITCHES): cv.schema_with_slug_keys(LEGACY_SWITCH_SCHEMA)} + {vol.Required(CONF_SWITCHES): cv.schema_with_slug_keys(SWITCH_LEGACY_YAML_SCHEMA)} ) -SWITCH_CONFIG_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.template, - vol.Optional(CONF_STATE): cv.template, - vol.Optional(CONF_TURN_ON): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_TURN_OFF): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), - } +SWITCH_CONFIG_ENTRY_SCHEMA = SWITCH_COMMON_SCHEMA.extend( + TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema ) @@ -129,12 +130,13 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Initialize config entry.""" - _options = dict(config_entry.options) - _options.pop("template_type") - _options = rewrite_options_to_modern_conf(_options) - validated_config = SWITCH_CONFIG_SCHEMA(_options) - async_add_entities( - [StateSwitchEntity(hass, validated_config, config_entry.entry_id)] + await async_setup_template_entry( + hass, + config_entry, + async_add_entities, + StateSwitchEntity, + SWITCH_CONFIG_ENTRY_SCHEMA, + True, ) @@ -143,9 +145,14 @@ def async_create_preview_switch( hass: HomeAssistant, name: str, config: dict[str, Any] ) -> StateSwitchEntity: """Create a preview switch.""" - updated_config = rewrite_options_to_modern_conf(config) - validated_config = SWITCH_CONFIG_SCHEMA(updated_config | {CONF_NAME: name}) - return StateSwitchEntity(hass, validated_config, None) + return async_setup_template_preview( + hass, + name, + config, + StateSwitchEntity, + SWITCH_CONFIG_ENTRY_SCHEMA, + True, + ) class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity): diff --git a/homeassistant/components/template/template_entity.py b/homeassistant/components/template/template_entity.py index b5081189cf3..ae473854502 100644 --- a/homeassistant/components/template/template_entity.py +++ b/homeassistant/components/template/template_entity.py @@ -12,6 +12,7 @@ import voluptuous as vol from homeassistant.components.blueprint import CONF_USE_BLUEPRINT from homeassistant.const import ( + CONF_DEVICE_ID, CONF_ENTITY_PICTURE_TEMPLATE, CONF_ICON, CONF_ICON_TEMPLATE, @@ -30,7 +31,7 @@ from homeassistant.core import ( validate_state, ) from homeassistant.exceptions import TemplateError -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, selector from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import ( TrackTemplate, @@ -46,7 +47,6 @@ from homeassistant.helpers.template import ( result_as_boolean, ) from homeassistant.helpers.trigger_template_entity import ( - TEMPLATE_ENTITY_BASE_SCHEMA, make_template_entity_base_schema, ) from homeassistant.helpers.typing import ConfigType @@ -57,6 +57,7 @@ from .const import ( CONF_AVAILABILITY, CONF_AVAILABILITY_TEMPLATE, CONF_PICTURE, + TEMPLATE_ENTITY_BASE_SCHEMA, ) from .entity import AbstractTemplateEntity @@ -91,6 +92,13 @@ TEMPLATE_ENTITY_COMMON_SCHEMA = ( .extend(TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA.schema) ) +TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA = vol.Schema( + { + vol.Required(CONF_NAME): cv.template, + vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(), + } +).extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA.schema) + def make_template_entity_common_modern_schema( default_name: str, diff --git a/homeassistant/components/template/vacuum.py b/homeassistant/components/template/vacuum.py index 143eb837bb5..0056eca9b99 100644 --- a/homeassistant/components/template/vacuum.py +++ b/homeassistant/components/template/vacuum.py @@ -76,7 +76,7 @@ LEGACY_FIELDS = { CONF_VALUE_TEMPLATE: CONF_STATE, } -VACUUM_SCHEMA = vol.All( +VACUUM_YAML_SCHEMA = vol.All( vol.Schema( { vol.Optional(CONF_BATTERY_LEVEL): cv.template, @@ -94,7 +94,7 @@ VACUUM_SCHEMA = vol.All( ).extend(make_template_entity_common_modern_attributes_schema(DEFAULT_NAME).schema) ) -LEGACY_VACUUM_SCHEMA = vol.All( +VACUUM_LEGACY_YAML_SCHEMA = vol.All( cv.deprecated(CONF_ENTITY_ID), vol.Schema( { @@ -119,7 +119,7 @@ LEGACY_VACUUM_SCHEMA = vol.All( ) PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( - {vol.Required(CONF_VACUUMS): cv.schema_with_slug_keys(LEGACY_VACUUM_SCHEMA)} + {vol.Required(CONF_VACUUMS): cv.schema_with_slug_keys(VACUUM_LEGACY_YAML_SCHEMA)} ) diff --git a/homeassistant/components/template/weather.py b/homeassistant/components/template/weather.py index 671a2ad0bac..15c6fb4db9e 100644 --- a/homeassistant/components/template/weather.py +++ b/homeassistant/components/template/weather.py @@ -31,7 +31,12 @@ from homeassistant.components.weather import ( WeatherEntity, WeatherEntityFeature, ) -from homeassistant.const import CONF_TEMPERATURE_UNIT, STATE_UNAVAILABLE, STATE_UNKNOWN +from homeassistant.const import ( + CONF_NAME, + CONF_TEMPERATURE_UNIT, + STATE_UNAVAILABLE, + STATE_UNKNOWN, +) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import TemplateError from homeassistant.helpers import config_validation as cv, template @@ -100,7 +105,7 @@ CONF_APPARENT_TEMPERATURE_TEMPLATE = "apparent_temperature_template" DEFAULT_NAME = "Template Weather" -WEATHER_SCHEMA = vol.Schema( +WEATHER_YAML_SCHEMA = vol.Schema( { vol.Optional(CONF_APPARENT_TEMPERATURE_TEMPLATE): cv.template, vol.Optional(CONF_ATTRIBUTION_TEMPLATE): cv.template, @@ -126,7 +131,32 @@ WEATHER_SCHEMA = vol.Schema( } ).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) -PLATFORM_SCHEMA = WEATHER_PLATFORM_SCHEMA.extend(WEATHER_SCHEMA.schema) +PLATFORM_SCHEMA = vol.Schema( + { + vol.Optional(CONF_APPARENT_TEMPERATURE_TEMPLATE): cv.template, + vol.Optional(CONF_ATTRIBUTION_TEMPLATE): cv.template, + vol.Optional(CONF_CLOUD_COVERAGE_TEMPLATE): cv.template, + vol.Required(CONF_CONDITION_TEMPLATE): cv.template, + vol.Optional(CONF_DEW_POINT_TEMPLATE): cv.template, + vol.Required(CONF_HUMIDITY_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, + vol.Optional(CONF_OZONE_TEMPLATE): cv.template, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.template, + vol.Optional(CONF_PRECIPITATION_UNIT): vol.In(DistanceConverter.VALID_UNITS), + vol.Optional(CONF_PRESSURE_TEMPLATE): cv.template, + vol.Optional(CONF_PRESSURE_UNIT): vol.In(PressureConverter.VALID_UNITS), + vol.Required(CONF_TEMPERATURE_TEMPLATE): cv.template, + vol.Optional(CONF_TEMPERATURE_UNIT): vol.In(TemperatureConverter.VALID_UNITS), + vol.Optional(CONF_VISIBILITY_TEMPLATE): cv.template, + vol.Optional(CONF_VISIBILITY_UNIT): vol.In(DistanceConverter.VALID_UNITS), + vol.Optional(CONF_WIND_BEARING_TEMPLATE): cv.template, + vol.Optional(CONF_WIND_GUST_SPEED_TEMPLATE): cv.template, + vol.Optional(CONF_WIND_SPEED_TEMPLATE): cv.template, + vol.Optional(CONF_WIND_SPEED_UNIT): vol.In(SpeedConverter.VALID_UNITS), + } +).extend(WEATHER_PLATFORM_SCHEMA.schema) async def async_setup_platform(