Consolidate template integration's config schemas (#149018)

This commit is contained in:
Petro31 2025-07-18 14:38:53 -04:00 committed by GitHub
parent 3877a6211a
commit 33cc257e75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 385 additions and 253 deletions

View File

@ -21,7 +21,6 @@ from homeassistant.components.alarm_control_panel import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_CODE, ATTR_CODE,
CONF_DEVICE_ID,
CONF_NAME, CONF_NAME,
CONF_STATE, CONF_STATE,
CONF_UNIQUE_ID, CONF_UNIQUE_ID,
@ -31,7 +30,7 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError 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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
@ -43,8 +42,16 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import DOMAIN from .const import DOMAIN
from .coordinator import TriggerUpdateCoordinator from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform from .helpers import (
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema 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 from .trigger_entity import TriggerEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -88,27 +95,28 @@ LEGACY_FIELDS = {
DEFAULT_NAME = "Template Alarm Control Panel" DEFAULT_NAME = "Template Alarm Control Panel"
ALARM_CONTROL_PANEL_SCHEMA = vol.All( ALARM_CONTROL_PANEL_COMMON_SCHEMA = vol.Schema(
vol.Schema( {
{ vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_ARM_CUSTOM_BYPASS_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_HOME_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_ARM_NIGHT_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_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean,
vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, vol.Optional(CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name): cv.enum(
vol.Optional( TemplateCodeFormat
CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name ),
): cv.enum(TemplateCodeFormat), vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_STATE): cv.template,
vol.Optional(CONF_STATE): cv.template, vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA, }
}
).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).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_AWAY_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_ARM_CUSTOM_BYPASS_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( PLATFORM_SCHEMA = ALARM_CONTROL_PANEL_PLATFORM_SCHEMA.extend(
{ {
vol.Required(CONF_ALARM_CONTROL_PANELS): cv.schema_with_slug_keys( 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( ALARM_CONTROL_PANEL_CONFIG_ENTRY_SCHEMA = ALARM_CONTROL_PANEL_COMMON_SCHEMA.extend(
{ TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.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,
}
) )
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( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
_options = rewrite_options_to_modern_conf(_options) config_entry,
validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA(_options) async_add_entities,
async_add_entities( StateAlarmControlPanelEntity,
[ ALARM_CONTROL_PANEL_CONFIG_ENTRY_SCHEMA,
StateAlarmControlPanelEntity( True,
hass,
validated_config,
config_entry.entry_id,
)
]
) )
@ -211,11 +189,14 @@ def async_create_preview_alarm_control_panel(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> StateAlarmControlPanelEntity: ) -> StateAlarmControlPanelEntity:
"""Create a preview alarm control panel.""" """Create a preview alarm control panel."""
updated_config = rewrite_options_to_modern_conf(config) return async_setup_template_preview(
validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA( hass,
updated_config | {CONF_NAME: name} name,
config,
StateAlarmControlPanelEntity,
ALARM_CONTROL_PANEL_CONFIG_ENTRY_SCHEMA,
True,
) )
return StateAlarmControlPanelEntity(hass, validated_config, None)
class AbstractTemplateAlarmControlPanel( class AbstractTemplateAlarmControlPanel(

View File

@ -22,7 +22,6 @@ from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME, ATTR_FRIENDLY_NAME,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_DEVICE_ID,
CONF_ENTITY_PICTURE_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME_TEMPLATE, CONF_FRIENDLY_NAME_TEMPLATE,
CONF_ICON_TEMPLATE, CONF_ICON_TEMPLATE,
@ -38,7 +37,7 @@ from homeassistant.const import (
) )
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.exceptions import TemplateError 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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
@ -50,8 +49,16 @@ from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
from .helpers import async_setup_template_platform from .helpers import (
from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity 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 from .trigger_entity import TriggerEntity
CONF_DELAY_ON = "delay_on" CONF_DELAY_ON = "delay_on"
@ -64,7 +71,7 @@ LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE, 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_AUTO_OFF): vol.Any(cv.positive_time_period, cv.template),
vol.Optional(CONF_DELAY_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.Required(CONF_STATE): cv.template,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, 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), cv.deprecated(ATTR_ENTITY_ID),
vol.Schema( vol.Schema(
{ {
@ -106,7 +115,7 @@ LEGACY_BINARY_SENSOR_SCHEMA = vol.All(
PLATFORM_SCHEMA = BINARY_SENSOR_PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = BINARY_SENSOR_PLATFORM_SCHEMA.extend(
{ {
vol.Required(CONF_SENSORS): cv.schema_with_slug_keys( 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, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
validated_config = BINARY_SENSOR_CONFIG_SCHEMA(_options) config_entry,
async_add_entities( async_add_entities,
[StateBinarySensorEntity(hass, validated_config, config_entry.entry_id)] StateBinarySensorEntity,
BINARY_SENSOR_CONFIG_ENTRY_SCHEMA,
) )
@ -151,8 +161,9 @@ def async_create_preview_binary_sensor(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> StateBinarySensorEntity: ) -> StateBinarySensorEntity:
"""Create a preview sensor.""" """Create a preview sensor."""
validated_config = BINARY_SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name}) return async_setup_template_preview(
return StateBinarySensorEntity(hass, validated_config, None) hass, name, config, StateBinarySensorEntity, BINARY_SENSOR_CONFIG_ENTRY_SCHEMA
)
class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity): class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity):

View File

@ -14,9 +14,9 @@ from homeassistant.components.button import (
ButtonEntity, ButtonEntity,
) )
from homeassistant.config_entries import ConfigEntry 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.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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
@ -24,29 +24,31 @@ from homeassistant.helpers.entity_platform import (
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_PRESS, DOMAIN from .const import CONF_PRESS, DOMAIN
from .helpers import async_setup_template_platform from .helpers import async_setup_template_entry, async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema from .template_entity import (
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
TemplateEntity,
make_template_entity_common_modern_schema,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "Template Button" DEFAULT_NAME = "Template Button"
DEFAULT_OPTIMISTIC = False DEFAULT_OPTIMISTIC = False
BUTTON_SCHEMA = vol.Schema( BUTTON_YAML_SCHEMA = vol.Schema(
{ {
vol.Required(CONF_PRESS): cv.SCRIPT_SCHEMA, vol.Required(CONF_PRESS): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
} }
).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).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_PRESS): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_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( async def async_setup_platform(
@ -73,11 +75,12 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
validated_config = CONFIG_BUTTON_SCHEMA(_options) config_entry,
async_add_entities( async_add_entities,
[StateButtonEntity(hass, validated_config, config_entry.entry_id)] StateButtonEntity,
BUTTON_CONFIG_ENTRY_SCHEMA,
) )

View File

@ -102,57 +102,57 @@ CONFIG_SECTION_SCHEMA = vol.All(
{ {
vol.Optional(CONF_ACTIONS): cv.SCRIPT_SCHEMA, vol.Optional(CONF_ACTIONS): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_BINARY_SENSORS): cv.schema_with_slug_keys( 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_CONDITIONS): cv.CONDITIONS_SCHEMA,
vol.Optional(CONF_SENSORS): cv.schema_with_slug_keys( 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_TRIGGERS): cv.TRIGGER_SCHEMA,
vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA, vol.Optional(CONF_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA,
vol.Optional(DOMAIN_ALARM_CONTROL_PANEL): vol.All( vol.Optional(DOMAIN_ALARM_CONTROL_PANEL): vol.All(
cv.ensure_list, 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( vol.Optional(DOMAIN_WEATHER): vol.All(
cv.ensure_list, [weather_platform.WEATHER_SCHEMA] cv.ensure_list, [weather_platform.WEATHER_YAML_SCHEMA]
), ),
}, },
), ),

View File

@ -1,6 +1,9 @@
"""Constants for the Template Platform Components.""" """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 from homeassistant.helpers.typing import ConfigType
CONF_ATTRIBUTE_TEMPLATES = "attribute_templates" CONF_ATTRIBUTE_TEMPLATES = "attribute_templates"
@ -16,6 +19,15 @@ CONF_STEP = "step"
CONF_TURN_OFF = "turn_off" CONF_TURN_OFF = "turn_off"
CONF_TURN_ON = "turn_on" 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" DOMAIN = "template"
PLATFORM_STORAGE_KEY = "template_platforms" PLATFORM_STORAGE_KEY = "template_platforms"

View File

@ -91,7 +91,7 @@ LEGACY_FIELDS = {
DEFAULT_NAME = "Template Cover" DEFAULT_NAME = "Template Cover"
COVER_SCHEMA = vol.All( COVER_YAML_SCHEMA = vol.All(
vol.Schema( vol.Schema(
{ {
vol.Inclusive(CLOSE_ACTION, CONF_OPEN_AND_CLOSE): cv.SCRIPT_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), 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), cv.deprecated(CONF_ENTITY_ID),
vol.Schema( vol.Schema(
{ {
@ -134,7 +134,7 @@ LEGACY_COVER_SCHEMA = vol.All(
) )
PLATFORM_SCHEMA = COVER_PLATFORM_SCHEMA.extend( 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)}
) )

View File

@ -81,7 +81,7 @@ LEGACY_FIELDS = {
DEFAULT_NAME = "Template Fan" DEFAULT_NAME = "Template Fan"
FAN_SCHEMA = vol.All( FAN_YAML_SCHEMA = vol.All(
vol.Schema( vol.Schema(
{ {
vol.Optional(CONF_DIRECTION): cv.template, vol.Optional(CONF_DIRECTION): cv.template,
@ -101,7 +101,7 @@ FAN_SCHEMA = vol.All(
).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema) ).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), cv.deprecated(CONF_ENTITY_ID),
vol.Schema( vol.Schema(
{ {
@ -126,7 +126,7 @@ LEGACY_FAN_SCHEMA = vol.All(
) )
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( 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)}
) )

View File

@ -5,14 +5,19 @@ import itertools
import logging import logging
from typing import Any from typing import Any
import voluptuous as vol
from homeassistant.components import blueprint from homeassistant.components import blueprint
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_ENTITY_PICTURE_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME, CONF_FRIENDLY_NAME,
CONF_ICON, CONF_ICON,
CONF_ICON_TEMPLATE, CONF_ICON_TEMPLATE,
CONF_NAME, CONF_NAME,
CONF_STATE,
CONF_UNIQUE_ID, CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
SERVICE_RELOAD, SERVICE_RELOAD,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
@ -20,6 +25,7 @@ from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import template from homeassistant.helpers import template
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
async_get_platforms, async_get_platforms,
) )
@ -228,3 +234,41 @@ async def async_setup_template_platform(
discovery_info["entities"], discovery_info["entities"],
discovery_info["unique_id"], 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)

View File

@ -13,10 +13,10 @@ from homeassistant.components.image import (
ImageEntity, ImageEntity,
) )
from homeassistant.config_entries import ConfigEntry 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.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError 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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
@ -26,8 +26,9 @@ from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_PICTURE 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 ( from .template_entity import (
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_attributes_schema, make_template_entity_common_modern_attributes_schema,
) )
@ -39,7 +40,7 @@ DEFAULT_NAME = "Template Image"
GET_IMAGE_TIMEOUT = 10 GET_IMAGE_TIMEOUT = 10
IMAGE_SCHEMA = vol.Schema( IMAGE_YAML_SCHEMA = vol.Schema(
{ {
vol.Required(CONF_URL): cv.template, vol.Required(CONF_URL): cv.template,
vol.Optional(CONF_VERIFY_SSL, default=True): bool, 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) ).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.Required(CONF_URL): cv.template,
vol.Optional(CONF_VERIFY_SSL, default=True): bool, 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( async def async_setup_platform(
@ -81,11 +80,12 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
validated_config = IMAGE_CONFIG_SCHEMA(_options) config_entry,
async_add_entities( async_add_entities,
[StateImageEntity(hass, validated_config, config_entry.entry_id)] StateImageEntity,
IMAGE_CONFIG_ENTRY_SCHEMA,
) )

View File

@ -121,7 +121,7 @@ LEGACY_FIELDS = {
DEFAULT_NAME = "Template Light" 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_ACTION, "effect"): cv.SCRIPT_SCHEMA,
vol.Inclusive(CONF_EFFECT_LIST, "effect"): cv.template, 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) ).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), cv.deprecated(CONF_ENTITY_ID),
vol.Schema( vol.Schema(
{ {
@ -186,7 +186,7 @@ PLATFORM_SCHEMA = vol.All(
cv.removed(CONF_WHITE_VALUE_ACTION), cv.removed(CONF_WHITE_VALUE_ACTION),
cv.removed(CONF_WHITE_VALUE_TEMPLATE), cv.removed(CONF_WHITE_VALUE_TEMPLATE),
LIGHT_PLATFORM_SCHEMA.extend( 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)}
), ),
) )

View File

@ -54,7 +54,7 @@ LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE, CONF_VALUE_TEMPLATE: CONF_STATE,
} }
LOCK_SCHEMA = vol.All( LOCK_YAML_SCHEMA = vol.All(
vol.Schema( vol.Schema(
{ {
vol.Optional(CONF_CODE_FORMAT): cv.template, 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) ).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema)
) )
PLATFORM_SCHEMA = LOCK_PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = LOCK_PLATFORM_SCHEMA.extend(
{ {
vol.Optional(CONF_CODE_FORMAT_TEMPLATE): cv.template, vol.Optional(CONF_CODE_FORMAT_TEMPLATE): cv.template,

View File

@ -18,14 +18,13 @@ from homeassistant.components.number import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_DEVICE_ID,
CONF_NAME, CONF_NAME,
CONF_OPTIMISTIC, CONF_OPTIMISTIC,
CONF_STATE, CONF_STATE,
CONF_UNIT_OF_MEASUREMENT, CONF_UNIT_OF_MEASUREMENT,
) )
from homeassistant.core import HomeAssistant, callback 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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
@ -34,8 +33,16 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_MAX, CONF_MIN, CONF_STEP, DOMAIN from .const import CONF_MAX, CONF_MIN, CONF_STEP, DOMAIN
from .helpers import async_setup_template_platform from .helpers import (
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema 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 from .trigger_entity import TriggerEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -45,30 +52,31 @@ CONF_SET_VALUE = "set_value"
DEFAULT_NAME = "Template Number" DEFAULT_NAME = "Template Number"
DEFAULT_OPTIMISTIC = False 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_MAX, default=DEFAULT_MAX_VALUE): cv.template,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, vol.Optional(CONF_MIN, default=DEFAULT_MIN_VALUE): cv.template,
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Required(CONF_SET_VALUE): cv.SCRIPT_SCHEMA,
}
).extend(make_template_entity_common_modern_schema(DEFAULT_NAME).schema)
NUMBER_CONFIG_SCHEMA = vol.Schema(
{
vol.Required(CONF_NAME): cv.template,
vol.Required(CONF_STATE): cv.template, vol.Required(CONF_STATE): cv.template,
vol.Required(CONF_STEP): 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_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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
@ -94,11 +102,12 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
validated_config = NUMBER_CONFIG_SCHEMA(_options) config_entry,
async_add_entities( async_add_entities,
[StateNumberEntity(hass, validated_config, config_entry.entry_id)] StateNumberEntity,
NUMBER_CONFIG_ENTRY_SCHEMA,
) )
@ -107,8 +116,9 @@ def async_create_preview_number(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> StateNumberEntity: ) -> StateNumberEntity:
"""Create a preview number.""" """Create a preview number."""
validated_config = NUMBER_CONFIG_SCHEMA(config | {CONF_NAME: name}) return async_setup_template_preview(
return StateNumberEntity(hass, validated_config, None) hass, name, config, StateNumberEntity, NUMBER_CONFIG_ENTRY_SCHEMA
)
class StateNumberEntity(TemplateEntity, NumberEntity): class StateNumberEntity(TemplateEntity, NumberEntity):

View File

@ -15,9 +15,9 @@ from homeassistant.components.select import (
SelectEntity, SelectEntity,
) )
from homeassistant.config_entries import ConfigEntry 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.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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
@ -27,8 +27,16 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import DOMAIN from .const import DOMAIN
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform from .helpers import (
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema 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 from .trigger_entity import TriggerEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -39,26 +47,28 @@ CONF_SELECT_OPTION = "select_option"
DEFAULT_NAME = "Template Select" DEFAULT_NAME = "Template Select"
DEFAULT_OPTIMISTIC = False DEFAULT_OPTIMISTIC = False
SELECT_SCHEMA = vol.Schema( SELECT_COMMON_SCHEMA = vol.Schema(
{ {
vol.Optional(CONF_STATE): cv.template, vol.Optional(ATTR_OPTIONS): 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(CONF_SELECT_OPTION): cv.SCRIPT_SCHEMA, 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
@ -84,10 +94,13 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
validated_config = SELECT_CONFIG_SCHEMA(_options) config_entry,
async_add_entities([TemplateSelect(hass, validated_config, config_entry.entry_id)]) async_add_entities,
TemplateSelect,
SELECT_CONFIG_ENTRY_SCHEMA,
)
@callback @callback
@ -95,8 +108,9 @@ def async_create_preview_select(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> TemplateSelect: ) -> TemplateSelect:
"""Create a preview select.""" """Create a preview select."""
validated_config = SELECT_CONFIG_SCHEMA(config | {CONF_NAME: name}) return async_setup_template_preview(
return TemplateSelect(hass, validated_config, None) hass, name, config, TemplateSelect, SELECT_CONFIG_ENTRY_SCHEMA
)
class AbstractTemplateSelect(AbstractTemplateEntity, SelectEntity): class AbstractTemplateSelect(AbstractTemplateEntity, SelectEntity):

View File

@ -15,6 +15,7 @@ from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN, DOMAIN as SENSOR_DOMAIN,
ENTITY_ID_FORMAT, ENTITY_ID_FORMAT,
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA, PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
STATE_CLASSES_SCHEMA,
RestoreSensor, RestoreSensor,
SensorDeviceClass, SensorDeviceClass,
SensorEntity, SensorEntity,
@ -25,7 +26,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_DEVICE_ID,
CONF_ENTITY_PICTURE_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME, CONF_FRIENDLY_NAME,
CONF_FRIENDLY_NAME_TEMPLATE, CONF_FRIENDLY_NAME_TEMPLATE,
@ -43,19 +43,26 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError 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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
) )
from homeassistant.helpers.trigger_template_entity import TEMPLATE_SENSOR_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE
from .helpers import async_setup_template_platform from .helpers import (
from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity 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 from .trigger_entity import TriggerEntity
LEGACY_FIELDS = { LEGACY_FIELDS = {
@ -77,29 +84,31 @@ def validate_last_reset(val):
return 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.Schema(
{ {
vol.Required(CONF_STATE): cv.template,
vol.Optional(ATTR_LAST_RESET): 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), .extend(TEMPLATE_ENTITY_COMMON_SCHEMA.schema),
validate_last_reset, validate_last_reset,
) )
SENSOR_CONFIG_ENTRY_SCHEMA = SENSOR_COMMON_SCHEMA.extend(
SENSOR_CONFIG_SCHEMA = vol.All( TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema
vol.Schema(
{
vol.Required(CONF_STATE): cv.template,
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
}
).extend(TEMPLATE_SENSOR_BASE_SCHEMA.schema),
) )
LEGACY_SENSOR_SCHEMA = vol.All( SENSOR_LEGACY_YAML_SCHEMA = vol.All(
cv.deprecated(ATTR_ENTITY_ID), cv.deprecated(ATTR_ENTITY_ID),
vol.Schema( vol.Schema(
{ {
@ -141,7 +150,9 @@ PLATFORM_SCHEMA = vol.All(
{ {
vol.Optional(CONF_TRIGGER): cv.match_all, # to raise custom warning vol.Optional(CONF_TRIGGER): cv.match_all, # to raise custom warning
vol.Optional(CONF_TRIGGERS): 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, extra_validation_checks,
@ -176,11 +187,12 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
validated_config = SENSOR_CONFIG_SCHEMA(_options) config_entry,
async_add_entities( async_add_entities,
[StateSensorEntity(hass, validated_config, config_entry.entry_id)] StateSensorEntity,
SENSOR_CONFIG_ENTRY_SCHEMA,
) )
@ -189,8 +201,9 @@ def async_create_preview_sensor(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> StateSensorEntity: ) -> StateSensorEntity:
"""Create a preview sensor.""" """Create a preview sensor."""
validated_config = SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name}) return async_setup_template_preview(
return StateSensorEntity(hass, validated_config, None) hass, name, config, StateSensorEntity, SENSOR_CONFIG_ENTRY_SCHEMA
)
class StateSensorEntity(TemplateEntity, SensorEntity): class StateSensorEntity(TemplateEntity, SensorEntity):

View File

@ -16,7 +16,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME, ATTR_FRIENDLY_NAME,
CONF_DEVICE_ID,
CONF_NAME, CONF_NAME,
CONF_STATE, CONF_STATE,
CONF_SWITCHES, CONF_SWITCHES,
@ -29,7 +28,7 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError 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 ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
AddEntitiesCallback, AddEntitiesCallback,
@ -39,8 +38,13 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_TURN_OFF, CONF_TURN_ON, DOMAIN 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 ( from .template_entity import (
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY, TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_schema, make_template_entity_common_modern_schema,
@ -55,16 +59,19 @@ LEGACY_FIELDS = {
DEFAULT_NAME = "Template Switch" DEFAULT_NAME = "Template Switch"
SWITCH_COMMON_SCHEMA = vol.Schema(
SWITCH_SCHEMA = vol.Schema(
{ {
vol.Optional(CONF_STATE): cv.template, vol.Optional(CONF_STATE): cv.template,
vol.Required(CONF_TURN_ON): cv.SCRIPT_SCHEMA, vol.Optional(CONF_TURN_ON): cv.SCRIPT_SCHEMA,
vol.Required(CONF_TURN_OFF): 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), cv.deprecated(ATTR_ENTITY_ID),
vol.Schema( vol.Schema(
{ {
@ -79,17 +86,11 @@ LEGACY_SWITCH_SCHEMA = vol.All(
) )
PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend( 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( SWITCH_CONFIG_ENTRY_SCHEMA = SWITCH_COMMON_SCHEMA.extend(
{ TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.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(),
}
) )
@ -129,12 +130,13 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Initialize config entry.""" """Initialize config entry."""
_options = dict(config_entry.options) await async_setup_template_entry(
_options.pop("template_type") hass,
_options = rewrite_options_to_modern_conf(_options) config_entry,
validated_config = SWITCH_CONFIG_SCHEMA(_options) async_add_entities,
async_add_entities( StateSwitchEntity,
[StateSwitchEntity(hass, validated_config, config_entry.entry_id)] SWITCH_CONFIG_ENTRY_SCHEMA,
True,
) )
@ -143,9 +145,14 @@ def async_create_preview_switch(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> StateSwitchEntity: ) -> StateSwitchEntity:
"""Create a preview switch.""" """Create a preview switch."""
updated_config = rewrite_options_to_modern_conf(config) return async_setup_template_preview(
validated_config = SWITCH_CONFIG_SCHEMA(updated_config | {CONF_NAME: name}) hass,
return StateSwitchEntity(hass, validated_config, None) name,
config,
StateSwitchEntity,
SWITCH_CONFIG_ENTRY_SCHEMA,
True,
)
class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity): class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity):

View File

@ -12,6 +12,7 @@ import voluptuous as vol
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
from homeassistant.const import ( from homeassistant.const import (
CONF_DEVICE_ID,
CONF_ENTITY_PICTURE_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE,
CONF_ICON, CONF_ICON,
CONF_ICON_TEMPLATE, CONF_ICON_TEMPLATE,
@ -30,7 +31,7 @@ from homeassistant.core import (
validate_state, validate_state,
) )
from homeassistant.exceptions import TemplateError 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.entity import Entity
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
TrackTemplate, TrackTemplate,
@ -46,7 +47,6 @@ from homeassistant.helpers.template import (
result_as_boolean, result_as_boolean,
) )
from homeassistant.helpers.trigger_template_entity import ( from homeassistant.helpers.trigger_template_entity import (
TEMPLATE_ENTITY_BASE_SCHEMA,
make_template_entity_base_schema, make_template_entity_base_schema,
) )
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
@ -57,6 +57,7 @@ from .const import (
CONF_AVAILABILITY, CONF_AVAILABILITY,
CONF_AVAILABILITY_TEMPLATE, CONF_AVAILABILITY_TEMPLATE,
CONF_PICTURE, CONF_PICTURE,
TEMPLATE_ENTITY_BASE_SCHEMA,
) )
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
@ -91,6 +92,13 @@ TEMPLATE_ENTITY_COMMON_SCHEMA = (
.extend(TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA.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( def make_template_entity_common_modern_schema(
default_name: str, default_name: str,

View File

@ -76,7 +76,7 @@ LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE, CONF_VALUE_TEMPLATE: CONF_STATE,
} }
VACUUM_SCHEMA = vol.All( VACUUM_YAML_SCHEMA = vol.All(
vol.Schema( vol.Schema(
{ {
vol.Optional(CONF_BATTERY_LEVEL): cv.template, 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) ).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), cv.deprecated(CONF_ENTITY_ID),
vol.Schema( vol.Schema(
{ {
@ -119,7 +119,7 @@ LEGACY_VACUUM_SCHEMA = vol.All(
) )
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( 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)}
) )

View File

@ -31,7 +31,12 @@ from homeassistant.components.weather import (
WeatherEntity, WeatherEntity,
WeatherEntityFeature, 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.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers import config_validation as cv, template
@ -100,7 +105,7 @@ CONF_APPARENT_TEMPERATURE_TEMPLATE = "apparent_temperature_template"
DEFAULT_NAME = "Template Weather" DEFAULT_NAME = "Template Weather"
WEATHER_SCHEMA = vol.Schema( WEATHER_YAML_SCHEMA = vol.Schema(
{ {
vol.Optional(CONF_APPARENT_TEMPERATURE_TEMPLATE): cv.template, vol.Optional(CONF_APPARENT_TEMPERATURE_TEMPLATE): cv.template,
vol.Optional(CONF_ATTRIBUTION_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) ).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( async def async_setup_platform(