From b9eb56ccd8888783510aa5bedfb4f37f3261241f Mon Sep 17 00:00:00 2001 From: PhracturedBlue Date: Thu, 1 Jun 2017 14:06:29 -0700 Subject: [PATCH 1/4] Add 'icon_template' to switch templates (similar to sensor template) --- homeassistant/components/switch/template.py | 34 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switch/template.py b/homeassistant/components/switch/template.py index b81c702bc02a51..6fc6e63e93f7ed 100644 --- a/homeassistant/components/switch/template.py +++ b/homeassistant/components/switch/template.py @@ -25,11 +25,14 @@ _LOGGER = logging.getLogger(__name__) _VALID_STATES = [STATE_ON, STATE_OFF, 'true', 'false'] +CONF_ICON_TEMPLATE = 'icon_template' + ON_ACTION = 'turn_on' OFF_ACTION = 'turn_off' SWITCH_SCHEMA = vol.Schema({ vol.Required(CONF_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_ICON_TEMPLATE): cv.template, vol.Required(ON_ACTION): cv.SCRIPT_SCHEMA, vol.Required(OFF_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(ATTR_FRIENDLY_NAME): cv.string, @@ -50,6 +53,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for device, device_config in config[CONF_SWITCHES].items(): friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) state_template = device_config[CONF_VALUE_TEMPLATE] + icon_template = device_config.get(CONF_ICON_TEMPLATE) on_action = device_config[ON_ACTION] off_action = device_config[OFF_ACTION] entity_ids = (device_config.get(ATTR_ENTITY_ID) or @@ -57,10 +61,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): state_template.hass = hass + if icon_template is not None: + icon_template.hass = hass + switches.append( SwitchTemplate( - hass, device, friendly_name, state_template, on_action, - off_action, entity_ids) + hass, device, friendly_name, state_template, icon_template, + on_action, off_action, entity_ids) ) if not switches: _LOGGER.error("No switches added") @@ -74,7 +81,7 @@ class SwitchTemplate(SwitchDevice): """Representation of a Template switch.""" def __init__(self, hass, device_id, friendly_name, state_template, - on_action, off_action, entity_ids): + icon_template, on_action, off_action, entity_ids): """Initialize the Template switch.""" self.hass = hass self.entity_id = async_generate_entity_id( @@ -84,6 +91,8 @@ def __init__(self, hass, device_id, friendly_name, state_template, self._on_script = Script(hass, on_action) self._off_script = Script(hass, off_action) self._state = False + self._icon_template = icon_template + self._icon = None self._entities = entity_ids @asyncio.coroutine @@ -129,6 +138,11 @@ def available(self): """If switch is available.""" return self._state is not None + @property + def icon(self): + """Return the icon to use in the frontend, if any.""" + return self._icon + def turn_on(self, **kwargs): """Fire the on action.""" self._on_script.run() @@ -154,3 +168,17 @@ def async_update(self): except TemplateError as ex: _LOGGER.error(ex) self._state = None + + if self._icon_template is not None: + try: + self._icon = self._icon_template.async_render() + except TemplateError as ex: + 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) + return + self._icon = super().icon + _LOGGER.error('Could not render icon template %s: %s', + self._name, ex) From 01d5ade101df6aafde41c03865ad600e06465371 Mon Sep 17 00:00:00 2001 From: PhracturedBlue Date: Sat, 3 Jun 2017 17:49:24 -0700 Subject: [PATCH 2/4] Add test for template switch 'icon_template' --- tests/components/switch/test_template.py | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/components/switch/test_template.py b/tests/components/switch/test_template.py index 4a03877b2fa05b..e14eea01189216 100644 --- a/tests/components/switch/test_template.py +++ b/tests/components/switch/test_template.py @@ -130,6 +130,45 @@ def test_template_state_boolean_off(self): state = self.hass.states.get('switch.test_template_switch') assert state.state == STATE_OFF + def test_icon_template(self): + """Test icon template.""" + with assert_setup_component(1): + 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' + }, + 'icon_template': + "{% if states.switch.test_state.state %}" + "mdi:check" + "{% endif %}" + } + } + } + }) + + self.hass.start() + self.hass.block_till_done() + + state = self.hass.states.get('switch.test_template_switch') + assert 'icon' not in state.attributes + + 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['icon'] == 'mdi:check' + def test_template_syntax_error(self): """Test templating syntax error.""" with assert_setup_component(0): From c62ad6e8fdc04513f555223e35f32a914e19ec52 Mon Sep 17 00:00:00 2001 From: PhracturedBlue Date: Mon, 5 Jun 2017 05:59:39 -0700 Subject: [PATCH 3/4] Define 'CONF_ICON_TEMPLATE' constant centrally --- homeassistant/components/sensor/template.py | 5 ++--- homeassistant/components/switch/template.py | 6 ++---- homeassistant/const.py | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sensor/template.py b/homeassistant/components/sensor/template.py index 8cf3083d7ede89..fdd0ef9c2ad686 100644 --- a/homeassistant/components/sensor/template.py +++ b/homeassistant/components/sensor/template.py @@ -13,7 +13,8 @@ from homeassistant.components.sensor import ENTITY_ID_FORMAT, PLATFORM_SCHEMA from homeassistant.const import ( ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE, - ATTR_ENTITY_ID, CONF_SENSORS, EVENT_HOMEASSISTANT_START) + CONF_ICON_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 @@ -22,8 +23,6 @@ _LOGGER = logging.getLogger(__name__) -CONF_ICON_TEMPLATE = 'icon_template' - SENSOR_SCHEMA = vol.Schema({ vol.Required(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_ICON_TEMPLATE): cv.template, diff --git a/homeassistant/components/switch/template.py b/homeassistant/components/switch/template.py index 6fc6e63e93f7ed..fc076f32e883e0 100644 --- a/homeassistant/components/switch/template.py +++ b/homeassistant/components/switch/template.py @@ -13,8 +13,8 @@ from homeassistant.components.switch import ( ENTITY_ID_FORMAT, SwitchDevice, PLATFORM_SCHEMA) from homeassistant.const import ( - ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE, STATE_OFF, STATE_ON, - ATTR_ENTITY_ID, CONF_SWITCHES, EVENT_HOMEASSISTANT_START) + ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE, CONF_ICON_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 @@ -25,8 +25,6 @@ _LOGGER = logging.getLogger(__name__) _VALID_STATES = [STATE_ON, STATE_OFF, 'true', 'false'] -CONF_ICON_TEMPLATE = 'icon_template' - ON_ACTION = 'turn_on' OFF_ACTION = 'turn_off' diff --git a/homeassistant/const.py b/homeassistant/const.py index 6a13612fce7586..311d901e2d2adc 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -104,6 +104,7 @@ CONF_HOST = 'host' CONF_HOSTS = 'hosts' CONF_ICON = 'icon' +CONF_ICON_TEMPLATE = 'icon_template' CONF_INCLUDE = 'include' CONF_ID = 'id' CONF_LATITUDE = 'latitude' From 4af3d189da233e978f247200da7520ba9d4a62a3 Mon Sep 17 00:00:00 2001 From: PhracturedBlue Date: Mon, 5 Jun 2017 06:10:49 -0700 Subject: [PATCH 4/4] Missed a redundant definition --- homeassistant/components/switch/template.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/components/switch/template.py b/homeassistant/components/switch/template.py index 1f1092e87ac6e5..fc076f32e883e0 100644 --- a/homeassistant/components/switch/template.py +++ b/homeassistant/components/switch/template.py @@ -25,8 +25,6 @@ _LOGGER = logging.getLogger(__name__) _VALID_STATES = [STATE_ON, STATE_OFF, 'true', 'false'] -CONF_ICON_TEMPLATE = 'icon_template' - ON_ACTION = 'turn_on' OFF_ACTION = 'turn_off'