mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Migrate template light to new style (#140326)
* Migrate template light to new style * add modern templates to tests * fix comments
This commit is contained in:
parent
e42a6c5d4f
commit
84667fd32d
@ -13,6 +13,7 @@ from homeassistant.components.blueprint import (
|
||||
)
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
|
||||
from homeassistant.components.image import DOMAIN as IMAGE_DOMAIN
|
||||
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
|
||||
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
@ -36,6 +37,7 @@ from . import (
|
||||
binary_sensor as binary_sensor_platform,
|
||||
button as button_platform,
|
||||
image as image_platform,
|
||||
light as light_platform,
|
||||
number as number_platform,
|
||||
select as select_platform,
|
||||
sensor as sensor_platform,
|
||||
@ -104,11 +106,14 @@ CONFIG_SECTION_SCHEMA = vol.Schema(
|
||||
vol.Optional(IMAGE_DOMAIN): vol.All(
|
||||
cv.ensure_list, [image_platform.IMAGE_SCHEMA]
|
||||
),
|
||||
vol.Optional(LIGHT_DOMAIN): vol.All(
|
||||
cv.ensure_list, [light_platform.LIGHT_SCHEMA]
|
||||
),
|
||||
vol.Optional(WEATHER_DOMAIN): vol.All(
|
||||
cv.ensure_list, [weather_platform.WEATHER_SCHEMA]
|
||||
),
|
||||
},
|
||||
ensure_domains_do_not_have_trigger_or_action(BUTTON_DOMAIN),
|
||||
ensure_domains_do_not_have_trigger_or_action(BUTTON_DOMAIN, LIGHT_DOMAIN),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -26,9 +26,13 @@ from homeassistant.components.light import (
|
||||
filter_supported_color_modes,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_EFFECT,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_FRIENDLY_NAME,
|
||||
CONF_LIGHTS,
|
||||
CONF_NAME,
|
||||
CONF_RGB,
|
||||
CONF_STATE,
|
||||
CONF_UNIQUE_ID,
|
||||
CONF_VALUE_TEMPLATE,
|
||||
STATE_OFF,
|
||||
@ -36,15 +40,18 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import config_validation as cv, template
|
||||
from homeassistant.helpers.entity import async_generate_entity_id
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import color as color_util
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import CONF_OBJECT_ID, CONF_PICTURE, DOMAIN
|
||||
from .template_entity import (
|
||||
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
|
||||
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA,
|
||||
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
|
||||
TEMPLATE_ENTITY_ICON_SCHEMA,
|
||||
TemplateEntity,
|
||||
rewrite_common_legacy_to_modern_conf,
|
||||
)
|
||||
@ -56,33 +63,96 @@ _VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"]
|
||||
CONF_COLOR_ACTION = "set_color"
|
||||
CONF_COLOR_TEMPLATE = "color_template"
|
||||
|
||||
CONF_HS = "hs"
|
||||
CONF_HS_ACTION = "set_hs"
|
||||
CONF_HS_TEMPLATE = "hs_template"
|
||||
CONF_RGB_ACTION = "set_rgb"
|
||||
CONF_RGB_TEMPLATE = "rgb_template"
|
||||
CONF_RGBW = "rgbw"
|
||||
CONF_RGBW_ACTION = "set_rgbw"
|
||||
CONF_RGBW_TEMPLATE = "rgbw_template"
|
||||
CONF_RGBWW = "rgbww"
|
||||
CONF_RGBWW_ACTION = "set_rgbww"
|
||||
CONF_RGBWW_TEMPLATE = "rgbww_template"
|
||||
CONF_EFFECT_ACTION = "set_effect"
|
||||
CONF_EFFECT_LIST = "effect_list"
|
||||
CONF_EFFECT_LIST_TEMPLATE = "effect_list_template"
|
||||
CONF_EFFECT_TEMPLATE = "effect_template"
|
||||
CONF_LEVEL = "level"
|
||||
CONF_LEVEL_ACTION = "set_level"
|
||||
CONF_LEVEL_TEMPLATE = "level_template"
|
||||
CONF_MAX_MIREDS = "max_mireds"
|
||||
CONF_MAX_MIREDS_TEMPLATE = "max_mireds_template"
|
||||
CONF_MIN_MIREDS = "min_mireds"
|
||||
CONF_MIN_MIREDS_TEMPLATE = "min_mireds_template"
|
||||
CONF_OFF_ACTION = "turn_off"
|
||||
CONF_ON_ACTION = "turn_on"
|
||||
CONF_SUPPORTS_TRANSITION = "supports_transition_template"
|
||||
CONF_SUPPORTS_TRANSITION = "supports_transition"
|
||||
CONF_SUPPORTS_TRANSITION_TEMPLATE = "supports_transition_template"
|
||||
CONF_TEMPERATURE_ACTION = "set_temperature"
|
||||
CONF_TEMPERATURE = "temperature"
|
||||
CONF_TEMPERATURE_TEMPLATE = "temperature_template"
|
||||
CONF_WHITE_VALUE_ACTION = "set_white_value"
|
||||
CONF_WHITE_VALUE = "white_value"
|
||||
CONF_WHITE_VALUE_TEMPLATE = "white_value_template"
|
||||
|
||||
DEFAULT_MIN_MIREDS = 153
|
||||
DEFAULT_MAX_MIREDS = 500
|
||||
|
||||
LIGHT_SCHEMA = vol.All(
|
||||
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
|
||||
CONF_COLOR_ACTION: CONF_HS_ACTION,
|
||||
CONF_COLOR_TEMPLATE: CONF_HS,
|
||||
CONF_EFFECT_LIST_TEMPLATE: CONF_EFFECT_LIST,
|
||||
CONF_EFFECT_TEMPLATE: CONF_EFFECT,
|
||||
CONF_HS_TEMPLATE: CONF_HS,
|
||||
CONF_LEVEL_TEMPLATE: CONF_LEVEL,
|
||||
CONF_MAX_MIREDS_TEMPLATE: CONF_MAX_MIREDS,
|
||||
CONF_MIN_MIREDS_TEMPLATE: CONF_MIN_MIREDS,
|
||||
CONF_RGB_TEMPLATE: CONF_RGB,
|
||||
CONF_RGBW_TEMPLATE: CONF_RGBW,
|
||||
CONF_RGBWW_TEMPLATE: CONF_RGBWW,
|
||||
CONF_SUPPORTS_TRANSITION_TEMPLATE: CONF_SUPPORTS_TRANSITION,
|
||||
CONF_TEMPERATURE_TEMPLATE: CONF_TEMPERATURE,
|
||||
CONF_VALUE_TEMPLATE: CONF_STATE,
|
||||
CONF_WHITE_VALUE_TEMPLATE: CONF_WHITE_VALUE,
|
||||
}
|
||||
|
||||
DEFAULT_NAME = "Template Light"
|
||||
|
||||
LIGHT_SCHEMA = (
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA,
|
||||
vol.Inclusive(CONF_EFFECT_LIST, "effect"): cv.template,
|
||||
vol.Inclusive(CONF_EFFECT, "effect"): cv.template,
|
||||
vol.Optional(CONF_HS_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_HS): cv.template,
|
||||
vol.Optional(CONF_LEVEL_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_LEVEL): cv.template,
|
||||
vol.Optional(CONF_MAX_MIREDS): cv.template,
|
||||
vol.Optional(CONF_MIN_MIREDS): cv.template,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.template,
|
||||
vol.Optional(CONF_PICTURE): cv.template,
|
||||
vol.Optional(CONF_RGB_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_RGB): cv.template,
|
||||
vol.Optional(CONF_RGBW_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_RGBW): cv.template,
|
||||
vol.Optional(CONF_RGBWW_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_RGBWW): cv.template,
|
||||
vol.Optional(CONF_STATE): cv.template,
|
||||
vol.Optional(CONF_SUPPORTS_TRANSITION): cv.template,
|
||||
vol.Optional(CONF_TEMPERATURE_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_TEMPERATURE): cv.template,
|
||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||
vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
|
||||
}
|
||||
)
|
||||
.extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA.schema)
|
||||
.extend(TEMPLATE_ENTITY_ICON_SCHEMA.schema)
|
||||
)
|
||||
|
||||
LEGACY_LIGHT_SCHEMA = vol.All(
|
||||
cv.deprecated(CONF_ENTITY_ID),
|
||||
vol.Schema(
|
||||
{
|
||||
@ -107,7 +177,7 @@ LIGHT_SCHEMA = vol.All(
|
||||
vol.Optional(CONF_MIN_MIREDS_TEMPLATE): cv.template,
|
||||
vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_SUPPORTS_TRANSITION): cv.template,
|
||||
vol.Optional(CONF_SUPPORTS_TRANSITION_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_TEMPERATURE_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_TEMPERATURE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||
@ -121,29 +191,50 @@ 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(LIGHT_SCHEMA)}
|
||||
{vol.Required(CONF_LIGHTS): cv.schema_with_slug_keys(LEGACY_LIGHT_SCHEMA)}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def _async_create_entities(hass: HomeAssistant, config):
|
||||
def rewrite_legacy_to_modern_conf(
|
||||
hass: HomeAssistant, config: dict[str, dict]
|
||||
) -> list[dict]:
|
||||
"""Rewrite legacy switch configuration definitions to modern ones."""
|
||||
lights = []
|
||||
for object_id, entity_conf in config.items():
|
||||
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
|
||||
|
||||
entity_conf = rewrite_common_legacy_to_modern_conf(
|
||||
hass, entity_conf, LEGACY_FIELDS
|
||||
)
|
||||
|
||||
if CONF_NAME not in entity_conf:
|
||||
entity_conf[CONF_NAME] = template.Template(object_id, hass)
|
||||
|
||||
lights.append(entity_conf)
|
||||
|
||||
return lights
|
||||
|
||||
|
||||
@callback
|
||||
def _async_create_template_tracking_entities(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant,
|
||||
definitions: list[dict],
|
||||
unique_id_prefix: str | None,
|
||||
) -> None:
|
||||
"""Create the Template Lights."""
|
||||
lights = []
|
||||
|
||||
for object_id, entity_config in config[CONF_LIGHTS].items():
|
||||
entity_config = rewrite_common_legacy_to_modern_conf(hass, entity_config)
|
||||
unique_id = entity_config.get(CONF_UNIQUE_ID)
|
||||
for entity_conf in definitions:
|
||||
unique_id = entity_conf.get(CONF_UNIQUE_ID)
|
||||
|
||||
lights.append(
|
||||
LightTemplate(
|
||||
hass,
|
||||
object_id,
|
||||
entity_config,
|
||||
unique_id,
|
||||
)
|
||||
)
|
||||
if unique_id and unique_id_prefix:
|
||||
unique_id = f"{unique_id_prefix}-{unique_id}"
|
||||
|
||||
return lights
|
||||
lights.append(LightTemplate(hass, entity_conf, unique_id))
|
||||
|
||||
async_add_entities(lights)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
@ -153,7 +244,21 @@ async def async_setup_platform(
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the template lights."""
|
||||
async_add_entities(await _async_create_entities(hass, config))
|
||||
if discovery_info is None:
|
||||
_async_create_template_tracking_entities(
|
||||
async_add_entities,
|
||||
hass,
|
||||
rewrite_legacy_to_modern_conf(hass, config[CONF_LIGHTS]),
|
||||
None,
|
||||
)
|
||||
return
|
||||
|
||||
_async_create_template_tracking_entities(
|
||||
async_add_entities,
|
||||
hass,
|
||||
discovery_info["entities"],
|
||||
discovery_info["unique_id"],
|
||||
)
|
||||
|
||||
|
||||
class LightTemplate(TemplateEntity, LightEntity):
|
||||
@ -164,33 +269,30 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
object_id,
|
||||
config: dict[str, Any],
|
||||
unique_id,
|
||||
unique_id: str | None,
|
||||
) -> None:
|
||||
"""Initialize the light."""
|
||||
super().__init__(
|
||||
hass, config=config, fallback_name=object_id, unique_id=unique_id
|
||||
)
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT, object_id, hass=hass
|
||||
)
|
||||
super().__init__(hass, config=config, fallback_name=None, unique_id=unique_id)
|
||||
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT, object_id, hass=hass
|
||||
)
|
||||
name = self._attr_name
|
||||
if TYPE_CHECKING:
|
||||
assert name is not None
|
||||
|
||||
self._template = config.get(CONF_VALUE_TEMPLATE)
|
||||
self._level_template = config.get(CONF_LEVEL_TEMPLATE)
|
||||
self._temperature_template = config.get(CONF_TEMPERATURE_TEMPLATE)
|
||||
self._color_template = config.get(CONF_COLOR_TEMPLATE)
|
||||
self._hs_template = config.get(CONF_HS_TEMPLATE)
|
||||
self._rgb_template = config.get(CONF_RGB_TEMPLATE)
|
||||
self._rgbw_template = config.get(CONF_RGBW_TEMPLATE)
|
||||
self._rgbww_template = config.get(CONF_RGBWW_TEMPLATE)
|
||||
self._effect_list_template = config.get(CONF_EFFECT_LIST_TEMPLATE)
|
||||
self._effect_template = config.get(CONF_EFFECT_TEMPLATE)
|
||||
self._max_mireds_template = config.get(CONF_MAX_MIREDS_TEMPLATE)
|
||||
self._min_mireds_template = config.get(CONF_MIN_MIREDS_TEMPLATE)
|
||||
self._template = config.get(CONF_STATE)
|
||||
self._level_template = config.get(CONF_LEVEL)
|
||||
self._temperature_template = config.get(CONF_TEMPERATURE)
|
||||
self._hs_template = config.get(CONF_HS)
|
||||
self._rgb_template = config.get(CONF_RGB)
|
||||
self._rgbw_template = config.get(CONF_RGBW)
|
||||
self._rgbww_template = config.get(CONF_RGBWW)
|
||||
self._effect_list_template = config.get(CONF_EFFECT_LIST)
|
||||
self._effect_template = config.get(CONF_EFFECT)
|
||||
self._max_mireds_template = config.get(CONF_MAX_MIREDS)
|
||||
self._min_mireds_template = config.get(CONF_MIN_MIREDS)
|
||||
self._supports_transition_template = config.get(CONF_SUPPORTS_TRANSITION)
|
||||
|
||||
for action_id in (CONF_ON_ACTION, CONF_OFF_ACTION, CONF_EFFECT_ACTION):
|
||||
@ -216,7 +318,6 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
for action_id, color_mode in (
|
||||
(CONF_TEMPERATURE_ACTION, ColorMode.COLOR_TEMP),
|
||||
(CONF_LEVEL_ACTION, ColorMode.BRIGHTNESS),
|
||||
(CONF_COLOR_ACTION, ColorMode.HS),
|
||||
(CONF_HS_ACTION, ColorMode.HS),
|
||||
(CONF_RGB_ACTION, ColorMode.RGB),
|
||||
(CONF_RGBW_ACTION, ColorMode.RGBW),
|
||||
@ -349,14 +450,6 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._update_temperature,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._color_template:
|
||||
self.add_template_attribute(
|
||||
"_hs_color",
|
||||
self._color_template,
|
||||
None,
|
||||
self._update_hs,
|
||||
none_on_template_error=True,
|
||||
)
|
||||
if self._hs_template:
|
||||
self.add_template_attribute(
|
||||
"_hs_color",
|
||||
@ -440,7 +533,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
)
|
||||
self._color_mode = ColorMode.COLOR_TEMP
|
||||
self._temperature = color_temp
|
||||
if self._hs_template is None and self._color_template is None:
|
||||
if self._hs_template is None:
|
||||
self._hs_color = None
|
||||
if self._rgb_template is None:
|
||||
self._rgb_color = None
|
||||
@ -450,11 +543,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._rgbww_color = None
|
||||
optimistic_set = True
|
||||
|
||||
if (
|
||||
self._hs_template is None
|
||||
and self._color_template is None
|
||||
and ATTR_HS_COLOR in kwargs
|
||||
):
|
||||
if self._hs_template is None and ATTR_HS_COLOR in kwargs:
|
||||
_LOGGER.debug(
|
||||
"Optimistically setting hs color to %s",
|
||||
kwargs[ATTR_HS_COLOR],
|
||||
@ -480,7 +569,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._rgb_color = kwargs[ATTR_RGB_COLOR]
|
||||
if self._temperature_template is None:
|
||||
self._temperature = None
|
||||
if self._hs_template is None and self._color_template is None:
|
||||
if self._hs_template is None:
|
||||
self._hs_color = None
|
||||
if self._rgbw_template is None:
|
||||
self._rgbw_color = None
|
||||
@ -497,7 +586,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._rgbw_color = kwargs[ATTR_RGBW_COLOR]
|
||||
if self._temperature_template is None:
|
||||
self._temperature = None
|
||||
if self._hs_template is None and self._color_template is None:
|
||||
if self._hs_template is None:
|
||||
self._hs_color = None
|
||||
if self._rgb_template is None:
|
||||
self._rgb_color = None
|
||||
@ -514,7 +603,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._rgbww_color = kwargs[ATTR_RGBWW_COLOR]
|
||||
if self._temperature_template is None:
|
||||
self._temperature = None
|
||||
if self._hs_template is None and self._color_template is None:
|
||||
if self._hs_template is None:
|
||||
self._hs_color = None
|
||||
if self._rgb_template is None:
|
||||
self._rgb_color = None
|
||||
@ -561,17 +650,6 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
await self.async_run_script(
|
||||
effect_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
elif ATTR_HS_COLOR in kwargs and (
|
||||
color_script := self._action_scripts.get(CONF_COLOR_ACTION)
|
||||
):
|
||||
hs_value = kwargs[ATTR_HS_COLOR]
|
||||
common_params["hs"] = hs_value
|
||||
common_params["h"] = int(hs_value[0])
|
||||
common_params["s"] = int(hs_value[1])
|
||||
|
||||
await self.async_run_script(
|
||||
color_script, run_variables=common_params, context=self._context
|
||||
)
|
||||
elif ATTR_HS_COLOR in kwargs and (
|
||||
hs_script := self._action_scripts.get(CONF_HS_ACTION)
|
||||
):
|
||||
|
@ -1,5 +1,7 @@
|
||||
"""template conftest."""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
@ -9,6 +11,13 @@ from homeassistant.setup import async_setup_component
|
||||
from tests.common import assert_setup_component, async_mock_service
|
||||
|
||||
|
||||
class ConfigurationStyle(Enum):
|
||||
"""Configuration Styles for template testing."""
|
||||
|
||||
LEGACY = "Legacy"
|
||||
MODERN = "Modern"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def calls(hass: HomeAssistant) -> list[ServiceCall]:
|
||||
"""Track calls to a mock service."""
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user