From 646c03eea1177bbe2944c9f73e22cfc5a1bac975 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 30 Oct 2017 10:28:37 -0600 Subject: [PATCH] Add entity_picture_template options to Template Cover, Template Light, Template Sensor, and Template Switch (#9854) * Re-adding cover * Re-adding light * Re-adding sensor * Re-adding switch * Re-added tests * Fixing missing imports in rebased test * Fixing broken tests * Owner-requested changes * Owner-requested changes * Fixed exception --- homeassistant/components/cover/template.py | 54 +++++++++--- homeassistant/components/light/template.py | 72 ++++++++++++++-- homeassistant/components/sensor/template.py | 48 ++++++++--- homeassistant/components/switch/template.py | 51 ++++++++--- homeassistant/const.py | 1 + tests/components/cover/test_template.py | 40 +++++++++ tests/components/light/test_template.py | 94 +++++++++++++++++++++ tests/components/sensor/test_template.py | 30 +++++++ tests/components/switch/test_template.py | 39 +++++++++ 9 files changed, 388 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/cover/template.py b/homeassistant/components/cover/template.py index 4c79d19d38d..843f0ed8a4c 100644 --- a/homeassistant/components/cover/template.py +++ b/homeassistant/components/cover/template.py @@ -19,7 +19,8 @@ from homeassistant.const import ( CONF_FRIENDLY_NAME, CONF_ENTITY_ID, EVENT_HOMEASSISTANT_START, MATCH_ALL, CONF_VALUE_TEMPLATE, CONF_ICON_TEMPLATE, - CONF_OPTIMISTIC, STATE_OPEN, STATE_CLOSED) + CONF_ENTITY_PICTURE_TEMPLATE, CONF_OPTIMISTIC, + STATE_OPEN, STATE_CLOSED) from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id @@ -57,6 +58,7 @@ COVER_SCHEMA = vol.Schema({ vol.Optional(CONF_POSITION_TEMPLATE): cv.template, vol.Optional(CONF_TILT_TEMPLATE): cv.template, vol.Optional(CONF_ICON_TEMPLATE): cv.template, + vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE): cv.template, vol.Optional(CONF_OPTIMISTIC): cv.boolean, vol.Optional(CONF_TILT_OPTIMISTIC): cv.boolean, vol.Optional(POSITION_ACTION): cv.SCRIPT_SCHEMA, @@ -81,6 +83,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): position_template = device_config.get(CONF_POSITION_TEMPLATE) tilt_template = device_config.get(CONF_TILT_TEMPLATE) icon_template = device_config.get(CONF_ICON_TEMPLATE) + entity_picture_template = device_config.get( + CONF_ENTITY_PICTURE_TEMPLATE) open_action = device_config.get(OPEN_ACTION) close_action = device_config.get(CLOSE_ACTION) stop_action = device_config.get(STOP_ACTION) @@ -114,6 +118,11 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): if str(temp_ids) != MATCH_ALL: template_entity_ids |= set(temp_ids) + if entity_picture_template is not None: + temp_ids = entity_picture_template.extract_entities() + if str(temp_ids) != MATCH_ALL: + template_entity_ids |= set(temp_ids) + if not template_entity_ids: template_entity_ids = MATCH_ALL @@ -124,8 +133,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hass, device, friendly_name, state_template, position_template, tilt_template, icon_template, - open_action, close_action, stop_action, - position_action, tilt_action, + entity_picture_template, open_action, close_action, + stop_action, position_action, tilt_action, optimistic, tilt_optimistic, entity_ids ) ) @@ -142,8 +151,8 @@ class CoverTemplate(CoverDevice): def __init__(self, hass, device_id, friendly_name, state_template, position_template, tilt_template, icon_template, - open_action, close_action, stop_action, - position_action, tilt_action, + entity_picture_template, open_action, close_action, + stop_action, position_action, tilt_action, optimistic, tilt_optimistic, entity_ids): """Initialize the Template cover.""" self.hass = hass @@ -154,6 +163,7 @@ class CoverTemplate(CoverDevice): self._position_template = position_template self._tilt_template = tilt_template self._icon_template = icon_template + self._entity_picture_template = entity_picture_template self._open_script = None if open_action is not None: self._open_script = Script(hass, open_action) @@ -173,6 +183,7 @@ class CoverTemplate(CoverDevice): (not state_template and not position_template)) self._tilt_optimistic = tilt_optimistic or not tilt_template self._icon = None + self._entity_picture = None self._position = None self._tilt_value = None self._entities = entity_ids @@ -185,6 +196,8 @@ class CoverTemplate(CoverDevice): self._tilt_template.hass = self.hass if self._icon_template is not None: self._icon_template.hass = self.hass + if self._entity_picture_template is not None: + self._entity_picture_template.hass = self.hass @asyncio.coroutine def async_added_to_hass(self): @@ -236,6 +249,11 @@ class CoverTemplate(CoverDevice): """Return the icon to use in the frontend, if any.""" return self._icon + @property + def entity_picture(self): + """Return the entity picture to use in the frontend, if any.""" + return self._entity_picture + @property def supported_features(self): """Flag supported features.""" @@ -369,16 +387,28 @@ class CoverTemplate(CoverDevice): except ValueError as ex: _LOGGER.error(ex) self._tilt_value = None - if self._icon_template is not None: + + for property_name, template in ( + ('_icon', self._icon_template), + ('_entity_picture', self._entity_picture_template)): + if template is None: + continue + try: - self._icon = self._icon_template.async_render() + setattr(self, property_name, template.async_render()) except TemplateError as ex: + friendly_property_name = property_name[1:].replace('_', ' ') if ex.args and ex.args[0].startswith( "UndefinedError: 'None' has no attribute"): # Common during HA startup - so just a warning - _LOGGER.warning('Could not render icon template %s,' - ' the state is unknown.', self._name) + _LOGGER.warning('Could not render %s template %s,' + ' the state is unknown.', + friendly_property_name, self._name) return - self._icon = super().icon - _LOGGER.error('Could not render icon template %s: %s', - self._name, ex) + + try: + setattr(self, property_name, + getattr(super(), property_name)) + except AttributeError: + _LOGGER.error('Could not render %s template %s: %s', + friendly_property_name, self._name, ex) diff --git a/homeassistant/components/light/template.py b/homeassistant/components/light/template.py index b2a9e97f11e..d8aa93c3db1 100644 --- a/homeassistant/components/light/template.py +++ b/homeassistant/components/light/template.py @@ -13,8 +13,10 @@ from homeassistant.core import callback from homeassistant.components.light import ( ATTR_BRIGHTNESS, ENTITY_ID_FORMAT, Light, SUPPORT_BRIGHTNESS) from homeassistant.const import ( - CONF_VALUE_TEMPLATE, CONF_ENTITY_ID, CONF_FRIENDLY_NAME, STATE_ON, - STATE_OFF, EVENT_HOMEASSISTANT_START, MATCH_ALL, CONF_LIGHTS) + CONF_VALUE_TEMPLATE, CONF_ICON_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE, + CONF_ENTITY_ID, CONF_FRIENDLY_NAME, STATE_ON, STATE_OFF, + EVENT_HOMEASSISTANT_START, MATCH_ALL, CONF_LIGHTS +) from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv @@ -34,6 +36,8 @@ LIGHT_SCHEMA = vol.Schema({ vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA, vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_VALUE_TEMPLATE, default=None): cv.template, + vol.Optional(CONF_ICON_TEMPLATE, default=None): cv.template, + vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE, default=None): cv.template, vol.Optional(CONF_LEVEL_ACTION, default=None): cv.SCRIPT_SCHEMA, vol.Optional(CONF_LEVEL_TEMPLATE, default=None): cv.template, vol.Optional(CONF_FRIENDLY_NAME, default=None): cv.string, @@ -53,6 +57,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for device, device_config in config[CONF_LIGHTS].items(): friendly_name = device_config.get(CONF_FRIENDLY_NAME, device) state_template = device_config[CONF_VALUE_TEMPLATE] + icon_template = device_config.get(CONF_ICON_TEMPLATE) + entity_picture_template = device_config.get( + CONF_ENTITY_PICTURE_TEMPLATE) on_action = device_config[CONF_ON_ACTION] off_action = device_config[CONF_OFF_ACTION] level_action = device_config.get(CONF_LEVEL_ACTION) @@ -70,6 +77,16 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): if str(temp_ids) != MATCH_ALL: template_entity_ids |= set(temp_ids) + if icon_template is not None: + temp_ids = icon_template.extract_entities() + if str(temp_ids) != MATCH_ALL: + template_entity_ids |= set(temp_ids) + + if entity_picture_template is not None: + temp_ids = entity_picture_template.extract_entities() + if str(temp_ids) != MATCH_ALL: + template_entity_ids |= set(temp_ids) + if not template_entity_ids: template_entity_ids = MATCH_ALL @@ -78,8 +95,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): lights.append( LightTemplate( hass, device, friendly_name, state_template, - on_action, off_action, level_action, level_template, - entity_ids) + icon_template, entity_picture_template, on_action, + off_action, level_action, level_template, entity_ids) ) if not lights: @@ -94,14 +111,16 @@ class LightTemplate(Light): """Representation of a templated Light, including dimmable.""" def __init__(self, hass, device_id, friendly_name, state_template, - on_action, off_action, level_action, level_template, - entity_ids): + icon_template, entity_picture_template, on_action, + off_action, level_action, level_template, entity_ids): """Initialize the light.""" self.hass = hass self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass) self._name = friendly_name self._template = state_template + self._icon_template = icon_template + self._entity_picture_template = entity_picture_template self._on_script = Script(hass, on_action) self._off_script = Script(hass, off_action) self._level_script = None @@ -110,6 +129,8 @@ class LightTemplate(Light): self._level_template = level_template self._state = False + self._icon = None + self._entity_picture = None self._brightness = None self._entities = entity_ids @@ -117,6 +138,10 @@ class LightTemplate(Light): self._template.hass = self.hass if self._level_template is not None: self._level_template.hass = self.hass + if self._icon_template is not None: + self._icon_template.hass = self.hass + if self._entity_picture_template is not None: + self._entity_picture_template.hass = self.hass @property def brightness(self): @@ -146,6 +171,16 @@ class LightTemplate(Light): """Return the polling state.""" return False + @property + def icon(self): + """Return the icon to use in the frontend, if any.""" + return self._icon + + @property + def entity_picture(self): + """Return the entity picture to use in the frontend, if any.""" + return self._entity_picture + @asyncio.coroutine def async_added_to_hass(self): """Register callbacks.""" @@ -234,3 +269,28 @@ class LightTemplate(Light): 'Expected: 0-255', brightness) self._brightness = None + + for property_name, template in ( + ('_icon', self._icon_template), + ('_entity_picture', self._entity_picture_template)): + if template is None: + continue + + try: + setattr(self, property_name, template.async_render()) + except TemplateError as ex: + friendly_property_name = property_name[1:].replace('_', ' ') + if ex.args and ex.args[0].startswith( + "UndefinedError: 'None' has no attribute"): + # Common during HA startup - so just a warning + _LOGGER.warning('Could not render %s template %s,' + ' the state is unknown.', + friendly_property_name, self._name) + return + + try: + setattr(self, property_name, + getattr(super(), property_name)) + except AttributeError: + _LOGGER.error('Could not render %s template %s: %s', + friendly_property_name, self._name, ex) diff --git a/homeassistant/components/sensor/template.py b/homeassistant/components/sensor/template.py index ff426951d3f..b347439e08d 100644 --- a/homeassistant/components/sensor/template.py +++ b/homeassistant/components/sensor/template.py @@ -13,8 +13,8 @@ from homeassistant.core import callback from homeassistant.components.sensor import ENTITY_ID_FORMAT, PLATFORM_SCHEMA from homeassistant.const import ( ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE, - CONF_ICON_TEMPLATE, ATTR_ENTITY_ID, CONF_SENSORS, - EVENT_HOMEASSISTANT_START) + CONF_ICON_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE, ATTR_ENTITY_ID, + CONF_SENSORS, EVENT_HOMEASSISTANT_START) from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity, async_generate_entity_id @@ -25,6 +25,7 @@ _LOGGER = logging.getLogger(__name__) SENSOR_SCHEMA = vol.Schema({ vol.Required(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_ICON_TEMPLATE): cv.template, + vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE): cv.template, vol.Optional(ATTR_FRIENDLY_NAME): cv.string, vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string, vol.Optional(ATTR_ENTITY_ID): cv.entity_ids @@ -44,6 +45,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for device, device_config in config[CONF_SENSORS].items(): state_template = device_config[CONF_VALUE_TEMPLATE] icon_template = device_config.get(CONF_ICON_TEMPLATE) + entity_picture_template = device_config.get( + CONF_ENTITY_PICTURE_TEMPLATE) entity_ids = (device_config.get(ATTR_ENTITY_ID) or state_template.extract_entities()) friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) @@ -54,6 +57,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): if icon_template is not None: icon_template.hass = hass + if entity_picture_template is not None: + entity_picture_template.hass = hass + sensors.append( SensorTemplate( hass, @@ -62,6 +68,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): unit_of_measurement, state_template, icon_template, + entity_picture_template, entity_ids) ) if not sensors: @@ -75,8 +82,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): class SensorTemplate(Entity): """Representation of a Template Sensor.""" - def __init__(self, hass, device_id, friendly_name, unit_of_measurement, - state_template, icon_template, entity_ids): + def __init__(self, hass, device_id, friendly_name, + unit_of_measurement, state_template, icon_template, + entity_picture_template, entity_ids): """Initialize the sensor.""" self.hass = hass self.entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, device_id, @@ -86,7 +94,9 @@ class SensorTemplate(Entity): self._template = state_template self._state = None self._icon_template = icon_template + self._entity_picture_template = entity_picture_template self._icon = None + self._entity_picture = None self._entities = entity_ids @asyncio.coroutine @@ -123,6 +133,11 @@ class SensorTemplate(Entity): """Return the icon to use in the frontend, if any.""" return self._icon + @property + def entity_picture(self): + """Return the entity_picture to use in the frontend, if any.""" + return self._entity_picture + @property def unit_of_measurement(self): """Return the unit_of_measurement of the device.""" @@ -148,16 +163,27 @@ class SensorTemplate(Entity): self._state = None _LOGGER.error('Could not render template %s: %s', self._name, ex) - if self._icon_template is not None: + for property_name, template in ( + ('_icon', self._icon_template), + ('_entity_picture', self._entity_picture_template)): + if template is None: + continue + try: - self._icon = self._icon_template.async_render() + setattr(self, property_name, template.async_render()) except TemplateError as ex: + friendly_property_name = property_name[1:].replace('_', ' ') if ex.args and ex.args[0].startswith( "UndefinedError: 'None' has no attribute"): # Common during HA startup - so just a warning - _LOGGER.warning('Could not render icon template %s,' - ' the state is unknown.', self._name) + _LOGGER.warning('Could not render %s template %s,' + ' the state is unknown.', + friendly_property_name, self._name) return - self._icon = super().icon - _LOGGER.error('Could not render icon template %s: %s', - self._name, ex) + + try: + setattr(self, property_name, + getattr(super(), property_name)) + except AttributeError: + _LOGGER.error('Could not render %s template %s: %s', + friendly_property_name, self._name, ex) diff --git a/homeassistant/components/switch/template.py b/homeassistant/components/switch/template.py index 2d50363bb2b..b9753ba6629 100644 --- a/homeassistant/components/switch/template.py +++ b/homeassistant/components/switch/template.py @@ -13,8 +13,9 @@ from homeassistant.core import callback from homeassistant.components.switch import ( ENTITY_ID_FORMAT, SwitchDevice, PLATFORM_SCHEMA) from homeassistant.const import ( - ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE, CONF_ICON_TEMPLATE, STATE_OFF, - STATE_ON, ATTR_ENTITY_ID, CONF_SWITCHES, EVENT_HOMEASSISTANT_START) + ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE, CONF_ICON_TEMPLATE, + CONF_ENTITY_PICTURE_TEMPLATE, STATE_OFF, STATE_ON, ATTR_ENTITY_ID, + CONF_SWITCHES, EVENT_HOMEASSISTANT_START) from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id @@ -30,6 +31,7 @@ OFF_ACTION = 'turn_off' SWITCH_SCHEMA = vol.Schema({ vol.Required(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_ICON_TEMPLATE): cv.template, + vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE): cv.template, vol.Required(ON_ACTION): cv.SCRIPT_SCHEMA, vol.Required(OFF_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(ATTR_FRIENDLY_NAME): cv.string, @@ -51,6 +53,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) state_template = device_config[CONF_VALUE_TEMPLATE] icon_template = device_config.get(CONF_ICON_TEMPLATE) + entity_picture_template = device_config.get( + CONF_ENTITY_PICTURE_TEMPLATE) on_action = device_config[ON_ACTION] off_action = device_config[OFF_ACTION] entity_ids = (device_config.get(ATTR_ENTITY_ID) or @@ -61,10 +65,14 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): if icon_template is not None: icon_template.hass = hass + if entity_picture_template is not None: + entity_picture_template.hass = hass + switches.append( SwitchTemplate( - hass, device, friendly_name, state_template, icon_template, - on_action, off_action, entity_ids) + hass, device, friendly_name, state_template, + icon_template, entity_picture_template, on_action, + off_action, entity_ids) ) if not switches: _LOGGER.error("No switches added") @@ -78,7 +86,8 @@ class SwitchTemplate(SwitchDevice): """Representation of a Template switch.""" def __init__(self, hass, device_id, friendly_name, state_template, - icon_template, on_action, off_action, entity_ids): + icon_template, entity_picture_template, on_action, + off_action, entity_ids): """Initialize the Template switch.""" self.hass = hass self.entity_id = async_generate_entity_id( @@ -89,7 +98,9 @@ class SwitchTemplate(SwitchDevice): self._off_script = Script(hass, off_action) self._state = False self._icon_template = icon_template + self._entity_picture_template = entity_picture_template self._icon = None + self._entity_picture = None self._entities = entity_ids @asyncio.coroutine @@ -136,6 +147,11 @@ class SwitchTemplate(SwitchDevice): """Return the icon to use in the frontend, if any.""" return self._icon + @property + def entity_picture(self): + """Return the entity_picture to use in the frontend, if any.""" + return self._entity_picture + def turn_on(self, **kwargs): """Fire the on action.""" self._on_script.run() @@ -162,16 +178,27 @@ class SwitchTemplate(SwitchDevice): _LOGGER.error(ex) self._state = None - if self._icon_template is not None: + for property_name, template in ( + ('_icon', self._icon_template), + ('_entity_picture', self._entity_picture_template)): + if template is None: + continue + try: - self._icon = self._icon_template.async_render() + setattr(self, property_name, template.async_render()) except TemplateError as ex: + friendly_property_name = property_name[1:].replace('_', ' ') if ex.args and ex.args[0].startswith( "UndefinedError: 'None' has no attribute"): # Common during HA startup - so just a warning - _LOGGER.warning('Could not render icon template %s,' - ' the state is unknown.', self._name) + _LOGGER.warning('Could not render %s template %s,' + ' the state is unknown.', + friendly_property_name, self._name) return - self._icon = super().icon - _LOGGER.error('Could not render icon template %s: %s', - self._name, ex) + + try: + setattr(self, property_name, + getattr(super(), property_name)) + except AttributeError: + _LOGGER.error('Could not render %s template %s: %s', + friendly_property_name, self._name, ex) diff --git a/homeassistant/const.py b/homeassistant/const.py index 97bb3a01975..0ebb8d16bd3 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -98,6 +98,7 @@ CONF_EMAIL = 'email' CONF_ENTITIES = 'entities' CONF_ENTITY_ID = 'entity_id' CONF_ENTITY_NAMESPACE = 'entity_namespace' +CONF_ENTITY_PICTURE_TEMPLATE = 'entity_picture_template' CONF_EVENT = 'event' CONF_EXCLUDE = 'exclude' CONF_FILE_PATH = 'file_path' diff --git a/tests/components/cover/test_template.py b/tests/components/cover/test_template.py index 495508203b3..af114135da9 100644 --- a/tests/components/cover/test_template.py +++ b/tests/components/cover/test_template.py @@ -682,3 +682,43 @@ class TestTemplateCover(unittest.TestCase): state = self.hass.states.get('cover.test_template_cover') assert state.attributes['icon'] == 'mdi:check' + + def test_entity_picture_template(self): + """Test icon template.""" + with assert_setup_component(1, 'cover'): + assert setup.setup_component(self.hass, 'cover', { + 'cover': { + 'platform': 'template', + 'covers': { + 'test_template_cover': { + 'value_template': + "{{ states.cover.test_state.state }}", + 'open_cover': { + 'service': 'cover.open_cover', + 'entity_id': 'cover.test_state' + }, + 'close_cover': { + 'service': 'cover.close_cover', + 'entity_id': 'cover.test_state' + }, + 'entity_picture_template': + "{% if states.cover.test_state.state %}" + "/local/cover.png" + "{% endif %}" + } + } + } + }) + + self.hass.start() + self.hass.block_till_done() + + state = self.hass.states.get('cover.test_template_cover') + assert state.attributes.get('entity_picture') == '' + + state = self.hass.states.set('cover.test_state', STATE_OPEN) + self.hass.block_till_done() + + state = self.hass.states.get('cover.test_template_cover') + + assert state.attributes['entity_picture'] == '/local/cover.png' diff --git a/tests/components/light/test_template.py b/tests/components/light/test_template.py index 5c32a1050a2..aaac0617590 100644 --- a/tests/components/light/test_template.py +++ b/tests/components/light/test_template.py @@ -625,3 +625,97 @@ class TestTemplateLight: assert state is not None assert state.attributes.get('friendly_name') == 'Template light' + + def test_icon_template(self): + """Test icon template.""" + with assert_setup_component(1, 'light'): + assert setup.setup_component(self.hass, 'light', { + 'light': { + 'platform': 'template', + 'lights': { + 'test_template_light': { + 'friendly_name': '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_level': { + 'service': 'light.turn_on', + 'data_template': { + 'entity_id': 'light.test_state', + 'brightness': '{{brightness}}' + } + }, + 'icon_template': + "{% if states.light.test_state.state %}" + "mdi:check" + "{% endif %}" + } + } + } + }) + + self.hass.start() + self.hass.block_till_done() + + state = self.hass.states.get('light.test_template_light') + assert state.attributes.get('icon') == '' + + state = self.hass.states.set('light.test_state', STATE_ON) + self.hass.block_till_done() + + state = self.hass.states.get('light.test_template_light') + + assert state.attributes['icon'] == 'mdi:check' + + def test_entity_picture_template(self): + """Test entity_picture template.""" + with assert_setup_component(1, 'light'): + assert setup.setup_component(self.hass, 'light', { + 'light': { + 'platform': 'template', + 'lights': { + 'test_template_light': { + 'friendly_name': '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_level': { + 'service': 'light.turn_on', + 'data_template': { + 'entity_id': 'light.test_state', + 'brightness': '{{brightness}}' + } + }, + 'entity_picture_template': + "{% if states.light.test_state.state %}" + "/local/light.png" + "{% endif %}" + } + } + } + }) + + self.hass.start() + self.hass.block_till_done() + + state = self.hass.states.get('light.test_template_light') + assert state.attributes.get('entity_picture') == '' + + state = self.hass.states.set('light.test_state', STATE_ON) + self.hass.block_till_done() + + state = self.hass.states.get('light.test_template_light') + + assert state.attributes['entity_picture'] == '/local/light.png' diff --git a/tests/components/sensor/test_template.py b/tests/components/sensor/test_template.py index 5e6a4957c04..3033b41b142 100644 --- a/tests/components/sensor/test_template.py +++ b/tests/components/sensor/test_template.py @@ -74,6 +74,36 @@ class TestTemplateSensor: state = self.hass.states.get('sensor.test_template_sensor') assert state.attributes['icon'] == 'mdi:check' + def test_entity_picture_template(self): + """Test entity_picture template.""" + with assert_setup_component(1): + assert setup_component(self.hass, 'sensor', { + 'sensor': { + 'platform': 'template', + 'sensors': { + 'test_template_sensor': { + 'value_template': "State", + 'entity_picture_template': + "{% if states.sensor.test_state.state == " + "'Works' %}" + "/local/sensor.png" + "{% endif %}" + } + } + } + }) + + self.hass.start() + self.hass.block_till_done() + + state = self.hass.states.get('sensor.test_template_sensor') + assert state.attributes.get('entity_picture') == '' + + self.hass.states.set('sensor.test_state', 'Works') + self.hass.block_till_done() + state = self.hass.states.get('sensor.test_template_sensor') + assert state.attributes['entity_picture'] == '/local/sensor.png' + def test_template_syntax_error(self): """Test templating syntax error.""" with assert_setup_component(0): diff --git a/tests/components/switch/test_template.py b/tests/components/switch/test_template.py index e4a1a1af558..7456ae11a0d 100644 --- a/tests/components/switch/test_template.py +++ b/tests/components/switch/test_template.py @@ -166,6 +166,45 @@ class TestTemplateSwitch: state = self.hass.states.get('switch.test_template_switch') assert state.attributes['icon'] == 'mdi:check' + def test_entity_picture_template(self): + """Test entity_picture template.""" + with assert_setup_component(1, 'switch'): + assert setup.setup_component(self.hass, 'switch', { + 'switch': { + 'platform': 'template', + 'switches': { + 'test_template_switch': { + 'value_template': + "{{ states.switch.test_state.state }}", + 'turn_on': { + 'service': 'switch.turn_on', + 'entity_id': 'switch.test_state' + }, + 'turn_off': { + 'service': 'switch.turn_off', + 'entity_id': 'switch.test_state' + }, + 'entity_picture_template': + "{% if states.switch.test_state.state %}" + "/local/switch.png" + "{% endif %}" + } + } + } + }) + + self.hass.start() + self.hass.block_till_done() + + state = self.hass.states.get('switch.test_template_switch') + assert state.attributes.get('entity_picture') == '' + + state = self.hass.states.set('switch.test_state', STATE_ON) + self.hass.block_till_done() + + state = self.hass.states.get('switch.test_template_switch') + assert state.attributes['entity_picture'] == '/local/switch.png' + def test_template_syntax_error(self): """Test templating syntax error.""" with assert_setup_component(0, 'switch'):