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 exceptionpull/10230/head
parent
05ece53ec2
commit
646c03eea1
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'):
|
||||
|
|
Loading…
Reference in New Issue