Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions homeassistant/components/template/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
ATTR_HS_COLOR,
ATTR_WHITE_VALUE,
ENTITY_ID_FORMAT,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_WHITE_VALUE,
Light,
)
from homeassistant.const import (
Expand Down Expand Up @@ -46,6 +48,8 @@
CONF_TEMPERATURE_ACTION = "set_temperature"
CONF_COLOR_TEMPLATE = "color_template"
CONF_COLOR_ACTION = "set_color"
CONF_WHITE_VALUE_TEMPLATE = "white_value_template"
CONF_WHITE_VALUE_ACTION = "set_white_value"

LIGHT_SCHEMA = vol.Schema(
{
Expand All @@ -63,6 +67,8 @@
vol.Optional(CONF_TEMPERATURE_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_COLOR_TEMPLATE): cv.template,
vol.Optional(CONF_COLOR_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_WHITE_VALUE_ACTION): cv.SCRIPT_SCHEMA,
}
)

Expand Down Expand Up @@ -95,6 +101,9 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
color_action = device_config.get(CONF_COLOR_ACTION)
color_template = device_config.get(CONF_COLOR_TEMPLATE)

white_value_action = device_config.get(CONF_WHITE_VALUE_ACTION)
white_value_template = device_config.get(CONF_WHITE_VALUE_TEMPLATE)

templates = {
CONF_VALUE_TEMPLATE: state_template,
CONF_ICON_TEMPLATE: icon_template,
Expand All @@ -103,6 +112,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
CONF_LEVEL_TEMPLATE: level_template,
CONF_TEMPERATURE_TEMPLATE: temperature_template,
CONF_COLOR_TEMPLATE: color_template,
CONF_WHITE_VALUE_TEMPLATE: white_value_template,
}

initialise_templates(hass, templates)
Expand All @@ -128,6 +138,8 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
temperature_template,
color_action,
color_template,
white_value_action,
white_value_template,
)
)

Expand Down Expand Up @@ -155,6 +167,8 @@ def __init__(
temperature_template,
color_action,
color_template,
white_value_action,
white_value_template,
):
"""Initialize the light."""
self.hass = hass
Expand All @@ -180,13 +194,18 @@ def __init__(
if color_action is not None:
self._color_script = Script(hass, color_action)
self._color_template = color_template
self._white_value_script = None
if white_value_action is not None:
self._white_value_script = Script(hass, white_value_action)
self._white_value_template = white_value_template

self._state = False
self._icon = None
self._entity_picture = None
self._brightness = None
self._temperature = None
self._color = None
self._white_value = None
self._entities = entity_ids
self._available = True

Expand All @@ -200,6 +219,11 @@ def color_temp(self):
"""Return the CT color value in mireds."""
return self._temperature

@property
def white_value(self):
"""Return the white value."""
return self._white_value

@property
def hs_color(self):
"""Return the hue and saturation color value [float, float]."""
Expand All @@ -220,6 +244,8 @@ def supported_features(self):
supported_features |= SUPPORT_COLOR_TEMP
if self._color_script is not None:
supported_features |= SUPPORT_COLOR
if self._white_value_script is not None:
supported_features |= SUPPORT_WHITE_VALUE
return supported_features

@property
Expand Down Expand Up @@ -263,6 +289,7 @@ def template_light_startup(event):
or self._level_template is not None
or self._temperature_template is not None
or self._color_template is not None
or self._white_value_template is not None
or self._availability_template is not None
):
async_track_state_change(
Expand Down Expand Up @@ -290,6 +317,13 @@ async def async_turn_on(self, **kwargs):
self._brightness = kwargs[ATTR_BRIGHTNESS]
optimistic_set = True

if self._white_value_template is None and ATTR_WHITE_VALUE in kwargs:
_LOGGER.info(
"Optimistically setting white value to %s", kwargs[ATTR_WHITE_VALUE]
)
self._white_value = kwargs[ATTR_WHITE_VALUE]
optimistic_set = True

if self._temperature_template is None and ATTR_COLOR_TEMP in kwargs:
_LOGGER.info(
"Optimistically setting color temperature to %s",
Expand All @@ -306,6 +340,10 @@ async def async_turn_on(self, **kwargs):
await self._temperature_script.async_run(
{"color_temp": kwargs[ATTR_COLOR_TEMP]}, context=self._context
)
elif ATTR_WHITE_VALUE in kwargs and self._white_value_script:
await self._white_value_script.async_run(
{"white_value": kwargs[ATTR_WHITE_VALUE]}, context=self._context
)
elif ATTR_HS_COLOR in kwargs and self._color_script:
hs_value = kwargs[ATTR_HS_COLOR]
await self._color_script.async_run(
Expand Down Expand Up @@ -335,6 +373,8 @@ async def async_update(self):

self.update_color()

self.update_white_value()

for property_name, template in (
("_icon", self._icon_template),
("_entity_picture", self._entity_picture_template),
Expand Down Expand Up @@ -389,6 +429,33 @@ def update_brightness(self):
_LOGGER.error(ex)
self._state = None

@callback
def update_white_value(self):
"""Update the white value from the template."""
if self._white_value_template is None:
return
try:
white_value = self._white_value_template.async_render()
if white_value in ("None", ""):
self._white_value = None
return
if 0 <= int(white_value) <= 255:
Comment thread
MartinHjelmare marked this conversation as resolved.
self._white_value = int(white_value)
else:
_LOGGER.error(
"Received invalid white value: %s. Expected: 0-255", white_value
)
self._white_value = None
except ValueError:
_LOGGER.error(
"Template must supply an integer white_value from 0-255, or 'None'",
exc_info=True,
)
self._white_value = None
except TemplateError as ex:
Comment thread
MartinHjelmare marked this conversation as resolved.
_LOGGER.error(ex)
self._state = None

@callback
def update_state(self):
"""Update the state from the template."""
Expand Down
103 changes: 101 additions & 2 deletions tests/components/template/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
ATTR_HS_COLOR,
ATTR_WHITE_VALUE,
)
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE
from homeassistant.core import callback
Expand Down Expand Up @@ -35,7 +36,7 @@ def setup_method(self, method):

@callback
def record_call(service):
"""Track function calls.."""
"""Track function calls."""
self.calls.append(service)

self.hass.services.register("test", "automation", record_call)
Expand Down Expand Up @@ -494,6 +495,105 @@ def test_off_action_optimistic(self):
state = self.hass.states.get("light.test_template_light")
assert state.state == STATE_OFF

def test_white_value_action_no_template(self):
"""Test setting white value with optimistic template."""
assert setup.setup_component(
self.hass,
"light",
{
"light": {
"platform": "template",
"lights": {
"test_template_light": {
"value_template": "{{1 == 1}}",
"turn_on": {
"service": "light.turn_on",
"entity_id": "light.test_state",
},
"turn_off": {
"service": "light.turn_off",
"entity_id": "light.test_state",
},
"set_white_value": {
"service": "test.automation",
"data_template": {
"entity_id": "test.test_state",
"white_value": "{{white_value}}",
},
},
}
},
}
},
)
self.hass.start()
self.hass.block_till_done()

state = self.hass.states.get("light.test_template_light")
assert state.attributes.get("white_value") is None

common.turn_on(
self.hass, "light.test_template_light", **{ATTR_WHITE_VALUE: 124}
)
self.hass.block_till_done()
assert len(self.calls) == 1
assert self.calls[0].data["white_value"] == "124"

state = self.hass.states.get("light.test_template_light")
assert state is not None
assert state.attributes.get("white_value") == 124

@pytest.mark.parametrize(
"expected_white_value,template",
[
(255, "{{255}}"),
(None, "{{256}}"),
(None, "{{x - 12}}"),
(None, "{{ none }}"),
(None, ""),
],
)
def test_white_value_template(self, expected_white_value, template):
"""Test the template for the white value."""
with assert_setup_component(1, "light"):
assert setup.setup_component(
self.hass,
"light",
{
"light": {
"platform": "template",
"lights": {
"test_template_light": {
"value_template": "{{ 1 == 1 }}",
"turn_on": {
"service": "light.turn_on",
"entity_id": "light.test_state",
},
"turn_off": {
"service": "light.turn_off",
"entity_id": "light.test_state",
},
"set_white_value": {
"service": "light.turn_on",
"data_template": {
"entity_id": "light.test_state",
"white_value": "{{white_value}}",
},
},
"white_value_template": template,
}
},
}
},
)

self.hass.start()
self.hass.block_till_done()

state = self.hass.states.get("light.test_template_light")
assert state is not None
assert state.attributes.get("white_value") == expected_white_value

def test_level_action_no_template(self):
"""Test setting brightness with optimistic template."""
assert setup.setup_component(
Expand Down Expand Up @@ -940,7 +1040,6 @@ def test_color_template(self, expected_hs, template):

async def test_available_template_with_entities(hass):
"""Test availability templates with values from other entities."""

await setup.async_setup_component(
hass,
"light",
Expand Down