Refactor template components to extract common routines (#27064)

* Added availability_template to Template Cover platform

* Added availability_template to Template Binary Sensor platform

* Added availability_template to Template Fan platform

* Added availability_template to Template Light platform

* Added availability_template to Template Sensor platform

* Added availability_template to Template Switch platform

* Added availability_template to Template Vacuum platform

* Added availability_template to Template Lock platform

* Added to test for invalid values in availability_template

* Black and Lint fix

* black formatting

* Added to test for invalid values in availability_template

* black

* Added to test for invalid values in availability_template

* Added to test for invalid values in availability_template

* simplified exception handler

* Fixed Entity discovery big and coverage

* Added to test for invalid values in availability_template

* flake8

* fixed component ID in test

* Added to test for invalid values in availability_template

* Added to test for invalid values in availability_template

* Made availability_template redering erorr more concise

* Cleaned template setup

* I'll remember to run black every time one of these days...

* Refactored Template initialisation

* Refactored Template initialisation

* Updated AVAILABILITY_TEMPLATE Rendering error

* Updated AVAILABILITY_TEMPLATE Rendering error

* Updated AVAILABILITY_TEMPLATE Rendering error

* Updated AVAILABILITY_TEMPLATE Rendering error

* Updated AVAILABILITY_TEMPLATE Rendering error

* Updated AVAILABILITY_TEMPLATE Rendering error

* Updated AVAILABILITY_TEMPLATE Rendering error

* Updated AVAILABILITY_TEMPLATE Rendering error

* Moved const to package Const.py

* Moved const to package Const.py

* Moved const to package Const.py

* Moved const to package Const.py

* Moved const to package Const.py

* Moved const to package Const.py

* Moved const to package Const.py

* Moved const to package Const.py

* Fix import order (pylint)

* Fix import order (pylint)

* Fix import order (pylint)

* Fix import order (pylint)

* Fix import order (pylint)

* Fix import order (pylint)

* Fix import order (pylint)

* Fixed linting issues

* Moved availability_template rendering to common loop

* Moved availability_template rendering to common loop

* Moved availability_template rendering to common loop

* Moved availability_template rendering to common loop

* Removed 'Magic' string

* Removed 'Magic' string and removed duplicate code

* Removed 'Magic' string

* Removed 'Magic' string

* Brought contant into line

* Refactored availability_tempalte rendering to common loop

* Removed 'Magic' string

* Cleaned up const and compare lowercase result to 'true'

* Cleaned up const and compare lowercase result to 'true'

* Cleaned up const and compare lowercase result to 'true'

* Cleaned up const and compare lowercase result to 'true'

* Cleaned up const and compare lowercase result to 'true'

* Cleaned up const and compare lowercase result to 'true'

* Cleaned up const and compare lowercase result to 'true'

* reverted _available back to boolean

* reverted _available back to boolean

* reverted _available back to boolean

* reverted _available back to boolean

* reverted _available back to boolean

* reverted _available back to boolean

* reverted _available back to boolean

* Fixed tests (magic values and state checks)

* Fixed tests (magic values and state checks)

* Fixed tests (async, magic values and state checks)

* Fixed tests (async, magic values and state checks)

* Fixed tests (async, magic values and state checks)

* Fixed tests (async, magic values and state checks)

* Fixed tests (async, magic values and state checks)

* Removed duplicate

* Clean up and remove debug

* Reverted Dev Container Change
This commit is contained in:
Gil Peeters 2019-11-26 11:30:49 +11:00 committed by Paulus Schoutsen
parent d5db55354e
commit 0088995b98
12 changed files with 202 additions and 276 deletions

View file

@ -1 +1,60 @@
"""The template component.""" """The template component."""
import logging
from itertools import chain
from homeassistant.const import MATCH_ALL
_LOGGER = logging.getLogger(__name__)
def initialise_templates(hass, templates, attribute_templates=None):
"""Initialise templates and attribute templates."""
if attribute_templates is None:
attribute_templates = dict()
for template in chain(templates.values(), attribute_templates.values()):
if template is None:
continue
template.hass = hass
def extract_entities(
device_name, device_type, manual_entity_ids, templates, attribute_templates=None
):
"""Extract entity ids from templates and attribute templates."""
if attribute_templates is None:
attribute_templates = dict()
entity_ids = set()
if manual_entity_ids is None:
invalid_templates = []
for template_name, template in chain(
templates.items(), attribute_templates.items()
):
if template is None:
continue
template_entity_ids = template.extract_entities()
if template_entity_ids != MATCH_ALL:
entity_ids |= set(template_entity_ids)
else:
invalid_templates.append(template_name.replace("_template", ""))
if invalid_templates:
entity_ids = MATCH_ALL
_LOGGER.warning(
"Template %s '%s' has no entity ids configured to track nor"
" were we able to extract the entities to track from the %s "
"template(s). This entity will only be able to be updated "
"manually.",
device_type,
device_name,
", ".join(invalid_templates),
)
else:
entity_ids = list(entity_ids)
else:
entity_ids = manual_entity_ids
return entity_ids

View file

@ -1,6 +1,5 @@
"""Support for exposing a templated binary sensor.""" """Support for exposing a templated binary sensor."""
import logging import logging
from itertools import chain
import voluptuous as vol import voluptuous as vol
@ -26,6 +25,7 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change, async_track_same_state from homeassistant.helpers.event import async_track_state_change, async_track_same_state
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -63,11 +63,12 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
icon_template = device_config.get(CONF_ICON_TEMPLATE) icon_template = device_config.get(CONF_ICON_TEMPLATE)
entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE) entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE)
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE) availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
entity_ids = set()
manual_entity_ids = device_config.get(ATTR_ENTITY_ID)
attribute_templates = device_config.get(CONF_ATTRIBUTE_TEMPLATES, {}) attribute_templates = device_config.get(CONF_ATTRIBUTE_TEMPLATES, {})
invalid_templates = [] friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
device_class = device_config.get(CONF_DEVICE_CLASS)
delay_on = device_config.get(CONF_DELAY_ON)
delay_off = device_config.get(CONF_DELAY_OFF)
templates = { templates = {
CONF_VALUE_TEMPLATE: value_template, CONF_VALUE_TEMPLATE: value_template,
@ -76,42 +77,15 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
CONF_AVAILABILITY_TEMPLATE: availability_template, CONF_AVAILABILITY_TEMPLATE: availability_template,
} }
for tpl_name, template in chain(templates.items(), attribute_templates.items()): initialise_templates(hass, templates, attribute_templates)
if template is None: entity_ids = extract_entities(
continue
template.hass = hass
if manual_entity_ids is not None:
continue
template_entity_ids = template.extract_entities()
if template_entity_ids == MATCH_ALL:
entity_ids = MATCH_ALL
# Cut off _template from name
invalid_templates.append(tpl_name.replace("_template", ""))
elif entity_ids != MATCH_ALL:
entity_ids |= set(template_entity_ids)
if manual_entity_ids is not None:
entity_ids = manual_entity_ids
elif entity_ids != MATCH_ALL:
entity_ids = list(entity_ids)
if invalid_templates:
_LOGGER.warning(
"Template binary sensor %s has no entity ids configured to"
" track nor were we able to extract the entities to track"
" from the %s template(s). This entity will only be able"
" to be updated manually.",
device, device,
", ".join(invalid_templates), "binary sensor",
device_config.get(ATTR_ENTITY_ID),
templates,
attribute_templates,
) )
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
device_class = device_config.get(CONF_DEVICE_CLASS)
delay_on = device_config.get(CONF_DELAY_ON)
delay_off = device_config.get(CONF_DELAY_OFF)
sensors.append( sensors.append(
BinarySensorTemplate( BinarySensorTemplate(
hass, hass,

View file

@ -24,7 +24,6 @@ from homeassistant.const import (
CONF_FRIENDLY_NAME, CONF_FRIENDLY_NAME,
CONF_ENTITY_ID, CONF_ENTITY_ID,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
MATCH_ALL,
CONF_VALUE_TEMPLATE, CONF_VALUE_TEMPLATE,
CONF_ICON_TEMPLATE, CONF_ICON_TEMPLATE,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
@ -38,6 +37,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.script import Script from homeassistant.helpers.script import Script
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -100,13 +100,14 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
covers = [] covers = []
for device, device_config in config[CONF_COVERS].items(): for device, device_config in config[CONF_COVERS].items():
friendly_name = device_config.get(CONF_FRIENDLY_NAME, device)
state_template = device_config.get(CONF_VALUE_TEMPLATE) state_template = device_config.get(CONF_VALUE_TEMPLATE)
position_template = device_config.get(CONF_POSITION_TEMPLATE) position_template = device_config.get(CONF_POSITION_TEMPLATE)
tilt_template = device_config.get(CONF_TILT_TEMPLATE) tilt_template = device_config.get(CONF_TILT_TEMPLATE)
icon_template = device_config.get(CONF_ICON_TEMPLATE) icon_template = device_config.get(CONF_ICON_TEMPLATE)
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE) availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE) entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE)
friendly_name = device_config.get(CONF_FRIENDLY_NAME, device)
device_class = device_config.get(CONF_DEVICE_CLASS) device_class = device_config.get(CONF_DEVICE_CLASS)
open_action = device_config.get(OPEN_ACTION) open_action = device_config.get(OPEN_ACTION)
close_action = device_config.get(CLOSE_ACTION) close_action = device_config.get(CLOSE_ACTION)
@ -121,41 +122,18 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
"Must specify at least one of %s" or "%s", OPEN_ACTION, POSITION_ACTION "Must specify at least one of %s" or "%s", OPEN_ACTION, POSITION_ACTION
) )
continue continue
template_entity_ids = set()
if state_template is not None:
temp_ids = state_template.extract_entities()
if str(temp_ids) != MATCH_ALL:
template_entity_ids |= set(temp_ids)
if position_template is not None: templates = {
temp_ids = position_template.extract_entities() CONF_VALUE_TEMPLATE: state_template,
if str(temp_ids) != MATCH_ALL: CONF_POSITION_TEMPLATE: position_template,
template_entity_ids |= set(temp_ids) CONF_TILT_TEMPLATE: tilt_template,
CONF_ICON_TEMPLATE: icon_template,
CONF_AVAILABILITY_TEMPLATE: availability_template,
CONF_ENTITY_PICTURE_TEMPLATE: entity_picture_template,
}
if tilt_template is not None: initialise_templates(hass, templates)
temp_ids = tilt_template.extract_entities() entity_ids = extract_entities(device, "cover", None, templates)
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 availability_template is not None:
temp_ids = availability_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
entity_ids = device_config.get(CONF_ENTITY_ID, template_entity_ids)
covers.append( covers.append(
CoverTemplate( CoverTemplate(

View file

@ -25,7 +25,6 @@ from homeassistant.const import (
CONF_ENTITY_ID, CONF_ENTITY_ID,
STATE_ON, STATE_ON,
STATE_OFF, STATE_OFF,
MATCH_ALL,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
STATE_UNKNOWN, STATE_UNKNOWN,
) )
@ -33,6 +32,7 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.script import Script from homeassistant.helpers.script import Script
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -98,33 +98,16 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
speed_list = device_config[CONF_SPEED_LIST] speed_list = device_config[CONF_SPEED_LIST]
entity_ids = set() templates = {
manual_entity_ids = device_config.get(CONF_ENTITY_ID) CONF_VALUE_TEMPLATE: state_template,
CONF_SPEED_TEMPLATE: speed_template,
CONF_OSCILLATING_TEMPLATE: oscillating_template,
CONF_DIRECTION_TEMPLATE: direction_template,
CONF_AVAILABILITY_TEMPLATE: availability_template,
}
for template in ( initialise_templates(hass, templates)
state_template, entity_ids = extract_entities(device, "fan", None, templates)
speed_template,
oscillating_template,
direction_template,
availability_template,
):
if template is None:
continue
template.hass = hass
if entity_ids == MATCH_ALL or manual_entity_ids is not None:
continue
template_entity_ids = template.extract_entities()
if template_entity_ids == MATCH_ALL:
entity_ids = MATCH_ALL
else:
entity_ids |= set(template_entity_ids)
if manual_entity_ids is not None:
entity_ids = manual_entity_ids
elif entity_ids != MATCH_ALL:
entity_ids = list(entity_ids)
fans.append( fans.append(
TemplateFan( TemplateFan(

View file

@ -19,7 +19,6 @@ from homeassistant.const import (
STATE_ON, STATE_ON,
STATE_OFF, STATE_OFF,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
MATCH_ALL,
CONF_LIGHTS, CONF_LIGHTS,
) )
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
@ -28,6 +27,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.script import Script from homeassistant.helpers.script import Script
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -64,46 +64,27 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
for device, device_config in config[CONF_LIGHTS].items(): for device, device_config in config[CONF_LIGHTS].items():
friendly_name = device_config.get(CONF_FRIENDLY_NAME, device) friendly_name = device_config.get(CONF_FRIENDLY_NAME, device)
state_template = device_config.get(CONF_VALUE_TEMPLATE) state_template = device_config.get(CONF_VALUE_TEMPLATE)
icon_template = device_config.get(CONF_ICON_TEMPLATE) icon_template = device_config.get(CONF_ICON_TEMPLATE)
entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE) entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE)
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE) availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
level_template = device_config.get(CONF_LEVEL_TEMPLATE)
on_action = device_config[CONF_ON_ACTION] on_action = device_config[CONF_ON_ACTION]
off_action = device_config[CONF_OFF_ACTION] off_action = device_config[CONF_OFF_ACTION]
level_action = device_config.get(CONF_LEVEL_ACTION) level_action = device_config.get(CONF_LEVEL_ACTION)
level_template = device_config.get(CONF_LEVEL_TEMPLATE)
template_entity_ids = set() templates = {
CONF_VALUE_TEMPLATE: state_template,
CONF_ICON_TEMPLATE: icon_template,
CONF_ENTITY_PICTURE_TEMPLATE: entity_picture_template,
CONF_AVAILABILITY_TEMPLATE: availability_template,
CONF_LEVEL_TEMPLATE: level_template,
}
if state_template is not None: initialise_templates(hass, templates)
temp_ids = state_template.extract_entities() entity_ids = extract_entities(device, "light", None, templates)
if str(temp_ids) != MATCH_ALL:
template_entity_ids |= set(temp_ids)
if level_template is not None:
temp_ids = level_template.extract_entities()
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 availability_template is not None:
temp_ids = availability_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
entity_ids = device_config.get(CONF_ENTITY_ID, template_entity_ids)
lights.append( lights.append(
LightTemplate( LightTemplate(

View file

@ -19,6 +19,7 @@ from homeassistant.const import (
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.script import Script from homeassistant.helpers.script import Script
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -43,39 +44,26 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): async def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Template lock.""" """Set up the Template lock."""
name = config.get(CONF_NAME) device = config.get(CONF_NAME)
value_template = config.get(CONF_VALUE_TEMPLATE) value_template = config.get(CONF_VALUE_TEMPLATE)
value_template.hass = hass
value_template_entity_ids = value_template.extract_entities()
if value_template_entity_ids == MATCH_ALL:
_LOGGER.warning(
"Template lock '%s' has no entity ids configured to track nor "
"were we able to extract the entities to track from the '%s' "
"template. This entity will only be able to be updated "
"manually.",
name,
CONF_VALUE_TEMPLATE,
)
template_entity_ids = set()
template_entity_ids |= set(value_template_entity_ids)
availability_template = config.get(CONF_AVAILABILITY_TEMPLATE) availability_template = config.get(CONF_AVAILABILITY_TEMPLATE)
if availability_template is not None:
availability_template.hass = hass templates = {
temp_ids = availability_template.extract_entities() CONF_VALUE_TEMPLATE: value_template,
if str(temp_ids) != MATCH_ALL: CONF_AVAILABILITY_TEMPLATE: availability_template,
template_entity_ids |= set(temp_ids) }
initialise_templates(hass, templates)
entity_ids = extract_entities(device, "lock", None, templates)
async_add_devices( async_add_devices(
[ [
TemplateLock( TemplateLock(
hass, hass,
name, device,
value_template, value_template,
availability_template, availability_template,
template_entity_ids, entity_ids,
config.get(CONF_LOCK), config.get(CONF_LOCK),
config.get(CONF_UNLOCK), config.get(CONF_UNLOCK),
config.get(CONF_OPTIMISTIC), config.get(CONF_OPTIMISTIC),

View file

@ -1,7 +1,6 @@
"""Allows the creation of a sensor that breaks out state_attributes.""" """Allows the creation of a sensor that breaks out state_attributes."""
import logging import logging
from typing import Optional from typing import Optional
from itertools import chain
import voluptuous as vol import voluptuous as vol
@ -29,6 +28,7 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity, async_generate_entity_id from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
CONF_ATTRIBUTE_TEMPLATES = "attribute_templates" CONF_ATTRIBUTE_TEMPLATES = "attribute_templates"
@ -72,10 +72,6 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
device_class = device_config.get(CONF_DEVICE_CLASS) device_class = device_config.get(CONF_DEVICE_CLASS)
attribute_templates = device_config[CONF_ATTRIBUTE_TEMPLATES] attribute_templates = device_config[CONF_ATTRIBUTE_TEMPLATES]
entity_ids = set()
manual_entity_ids = device_config.get(ATTR_ENTITY_ID)
invalid_templates = []
templates = { templates = {
CONF_VALUE_TEMPLATE: state_template, CONF_VALUE_TEMPLATE: state_template,
CONF_ICON_TEMPLATE: icon_template, CONF_ICON_TEMPLATE: icon_template,
@ -84,37 +80,15 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
CONF_AVAILABILITY_TEMPLATE: availability_template, CONF_AVAILABILITY_TEMPLATE: availability_template,
} }
for tpl_name, template in chain(templates.items(), attribute_templates.items()): initialise_templates(hass, templates, attribute_templates)
if template is None: entity_ids = extract_entities(
continue
template.hass = hass
if manual_entity_ids is not None:
continue
template_entity_ids = template.extract_entities()
if template_entity_ids == MATCH_ALL:
entity_ids = MATCH_ALL
# Cut off _template from name
invalid_templates.append(tpl_name.replace("_template", ""))
elif entity_ids != MATCH_ALL:
entity_ids |= set(template_entity_ids)
if invalid_templates:
_LOGGER.warning(
"Template sensor %s has no entity ids configured to track nor"
" were we able to extract the entities to track from the %s "
"template(s). This entity will only be able to be updated "
"manually.",
device, device,
", ".join(invalid_templates), "sensor",
device_config.get(ATTR_ENTITY_ID),
templates,
attribute_templates,
) )
if manual_entity_ids is not None:
entity_ids = manual_entity_ids
elif entity_ids != MATCH_ALL:
entity_ids = list(entity_ids)
sensors.append( sensors.append(
SensorTemplate( SensorTemplate(
hass, hass,
@ -131,7 +105,9 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
attribute_templates, attribute_templates,
) )
) )
async_add_entities(sensors) async_add_entities(sensors)
return True return True

View file

@ -19,13 +19,13 @@ from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
CONF_SWITCHES, CONF_SWITCHES,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
MATCH_ALL,
) )
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.script import Script from homeassistant.helpers.script import Script
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -64,8 +64,6 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE) availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
on_action = device_config[ON_ACTION] on_action = device_config[ON_ACTION]
off_action = device_config[OFF_ACTION] off_action = device_config[OFF_ACTION]
manual_entity_ids = device_config.get(ATTR_ENTITY_ID)
entity_ids = set()
templates = { templates = {
CONF_VALUE_TEMPLATE: state_template, CONF_VALUE_TEMPLATE: state_template,
@ -73,35 +71,11 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
CONF_ENTITY_PICTURE_TEMPLATE: entity_picture_template, CONF_ENTITY_PICTURE_TEMPLATE: entity_picture_template,
CONF_AVAILABILITY_TEMPLATE: availability_template, CONF_AVAILABILITY_TEMPLATE: availability_template,
} }
invalid_templates = []
for template_name, template in templates.items(): initialise_templates(hass, templates)
if template is not None: entity_ids = extract_entities(
template.hass = hass device, "switch", device_config.get(ATTR_ENTITY_ID), templates
if manual_entity_ids is not None:
continue
template_entity_ids = template.extract_entities()
if template_entity_ids == MATCH_ALL:
invalid_templates.append(template_name.replace("_template", ""))
entity_ids = MATCH_ALL
elif entity_ids != MATCH_ALL:
entity_ids |= set(template_entity_ids)
if invalid_templates:
_LOGGER.warning(
"Template sensor %s has no entity ids configured to track nor"
" were we able to extract the entities to track from the %s "
"template(s). This entity will only be able to be updated "
"manually.",
device,
", ".join(invalid_templates),
) )
else:
if manual_entity_ids is None:
entity_ids = list(entity_ids)
else:
entity_ids = manual_entity_ids
switches.append( switches.append(
SwitchTemplate( SwitchTemplate(
@ -117,6 +91,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
entity_ids, entity_ids,
) )
) )
if not switches: if not switches:
_LOGGER.error("No switches added") _LOGGER.error("No switches added")
return False return False

View file

@ -43,7 +43,7 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.script import Script from homeassistant.helpers.script import Script
from . import extract_entities, initialise_templates
from .const import CONF_AVAILABILITY_TEMPLATE from .const import CONF_AVAILABILITY_TEMPLATE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -109,45 +109,15 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
fan_speed_list = device_config[CONF_FAN_SPEED_LIST] fan_speed_list = device_config[CONF_FAN_SPEED_LIST]
entity_ids = set() templates = {
manual_entity_ids = device_config.get(CONF_ENTITY_ID) CONF_VALUE_TEMPLATE: state_template,
invalid_templates = [] CONF_BATTERY_LEVEL_TEMPLATE: battery_level_template,
CONF_FAN_SPEED_TEMPLATE: fan_speed_template,
CONF_AVAILABILITY_TEMPLATE: availability_template,
}
for tpl_name, template in ( initialise_templates(hass, templates)
(CONF_VALUE_TEMPLATE, state_template), entity_ids = extract_entities(device, "vacuum", None, templates)
(CONF_BATTERY_LEVEL_TEMPLATE, battery_level_template),
(CONF_FAN_SPEED_TEMPLATE, fan_speed_template),
(CONF_AVAILABILITY_TEMPLATE, availability_template),
):
if template is None:
continue
template.hass = hass
if manual_entity_ids is not None:
continue
template_entity_ids = template.extract_entities()
if template_entity_ids == MATCH_ALL:
entity_ids = MATCH_ALL
# Cut off _template from name
invalid_templates.append(tpl_name[:-9])
elif entity_ids != MATCH_ALL:
entity_ids |= set(template_entity_ids)
if invalid_templates:
_LOGGER.warning(
"Template vacuum %s has no entity ids configured to track nor"
" were we able to extract the entities to track from the %s "
"template(s). This entity will only be able to be updated "
"manually.",
device,
", ".join(invalid_templates),
)
if manual_entity_ids is not None:
entity_ids = manual_entity_ids
elif entity_ids != MATCH_ALL:
entity_ids = list(entity_ids)
vacuums.append( vacuums.append(
TemplateVacuum( TemplateVacuum(

View file

@ -576,22 +576,22 @@ async def test_no_update_template_match_all(hass, caplog):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 5
assert ( assert (
"Template binary sensor all_state has no entity ids " "Template binary sensor 'all_state' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the value template" "track from the value template"
) in caplog.text ) in caplog.text
assert ( assert (
"Template binary sensor all_icon has no entity ids " "Template binary sensor 'all_icon' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the icon template" "track from the icon template"
) in caplog.text ) in caplog.text
assert ( assert (
"Template binary sensor all_entity_picture has no entity ids " "Template binary sensor 'all_entity_picture' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the entity_picture template" "track from the entity_picture template"
) in caplog.text ) in caplog.text
assert ( assert (
"Template binary sensor all_attribute has no entity ids " "Template binary sensor 'all_attribute' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the test_attribute template" "track from the test_attribute template"
) in caplog.text ) in caplog.text

View file

@ -254,10 +254,9 @@ class TestTemplateLock:
assert state.state == lock.STATE_UNLOCKED assert state.state == lock.STATE_UNLOCKED
assert ( assert (
"Template lock 'Template Lock' has no entity ids configured " "Template lock 'Template Lock' has no entity ids configured to track "
"to track nor were we able to extract the entities to track " "nor were we able to extract the entities to track from the value "
"from the 'value_template' template. This entity will only " "template(s). This entity will only be able to be updated manually"
"be able to be updated manually."
) in caplog.text ) in caplog.text
self.hass.states.set("lock.template_lock", lock.STATE_LOCKED) self.hass.states.set("lock.template_lock", lock.STATE_LOCKED)
@ -343,7 +342,7 @@ async def test_available_template_with_entities(hass):
{ {
"lock": { "lock": {
"platform": "template", "platform": "template",
"value_template": "{{ 'on' }}", "value_template": "{{ states('switch.test_state') }}",
"lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"}, "lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"},
"unlock": { "unlock": {
"service": "switch.turn_off", "service": "switch.turn_off",

View file

@ -377,6 +377,49 @@ class TestTemplateSensor:
state = self.hass.states.get("sensor.test2") state = self.hass.states.get("sensor.test2")
assert "device_class" not in state.attributes assert "device_class" not in state.attributes
def test_available_template_with_entities(self):
"""Test availability tempalates with values from other entities."""
with assert_setup_component(1):
assert setup_component(
self.hass,
"sensor",
{
"sensor": {
"platform": "template",
"sensors": {
"test_template_sensor": {
"value_template": "{{ states.sensor.test_state.state }}",
"availability_template": "{{ is_state('availability_boolean.state', 'on') }}",
}
},
}
},
)
self.hass.start()
self.hass.block_till_done()
# When template returns true..
self.hass.states.set("availability_boolean.state", STATE_ON)
self.hass.block_till_done()
# Device State should not be unavailable
assert (
self.hass.states.get("sensor.test_template_sensor").state
!= STATE_UNAVAILABLE
)
# When Availability template returns false
self.hass.states.set("availability_boolean.state", STATE_OFF)
self.hass.block_till_done()
# device state should be unavailable
assert (
self.hass.states.get("sensor.test_template_sensor").state
== STATE_UNAVAILABLE
)
async def test_available_template_with_entities(hass): async def test_available_template_with_entities(hass):
"""Test availability tempalates with values from other entities.""" """Test availability tempalates with values from other entities."""
@ -511,27 +554,27 @@ async def test_no_template_match_all(hass, caplog):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(hass.states.async_all()) == 6 assert len(hass.states.async_all()) == 6
assert ( assert (
"Template sensor invalid_state has no entity ids " "Template sensor 'invalid_state' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the value template" "track from the value template"
) in caplog.text ) in caplog.text
assert ( assert (
"Template sensor invalid_icon has no entity ids " "Template sensor 'invalid_icon' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the icon template" "track from the icon template"
) in caplog.text ) in caplog.text
assert ( assert (
"Template sensor invalid_entity_picture has no entity ids " "Template sensor 'invalid_entity_picture' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the entity_picture template" "track from the entity_picture template"
) in caplog.text ) in caplog.text
assert ( assert (
"Template sensor invalid_friendly_name has no entity ids " "Template sensor 'invalid_friendly_name' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the friendly_name template" "track from the friendly_name template"
) in caplog.text ) in caplog.text
assert ( assert (
"Template sensor invalid_attribute has no entity ids " "Template sensor 'invalid_attribute' has no entity ids "
"configured to track nor were we able to extract the entities to " "configured to track nor were we able to extract the entities to "
"track from the test_attribute template" "track from the test_attribute template"
) in caplog.text ) in caplog.text