Add optional unique_id attribute to the template platforms (#38011)

* Add unique_id to template platforms

* Update test_binary_sensor.py

* Update test_binary_sensor.py
This commit is contained in:
Michaël Arnauts 2020-08-02 00:45:55 +02:00 committed by GitHub
parent 6b85e23408
commit f09a9abc1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 444 additions and 0 deletions

View file

@ -17,6 +17,7 @@ from homeassistant.components.alarm_control_panel.const import (
from homeassistant.const import (
ATTR_CODE,
CONF_NAME,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -62,6 +63,7 @@ ALARM_CONTROL_PANEL_SCHEMA = vol.Schema(
vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -86,6 +88,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
arm_home_action = device_config.get(CONF_ARM_HOME_ACTION)
arm_night_action = device_config.get(CONF_ARM_NIGHT_ACTION)
code_arm_required = device_config[CONF_CODE_ARM_REQUIRED]
unique_id = device_config.get(CONF_UNIQUE_ID)
template_entity_ids = set()
@ -111,6 +114,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
arm_night_action,
code_arm_required,
template_entity_ids,
unique_id,
)
)
@ -132,6 +136,7 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity):
arm_night_action,
code_arm_required,
template_entity_ids,
unique_id,
):
"""Initialize the panel."""
self.hass = hass
@ -156,6 +161,7 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity):
self._state = None
self._entities = template_entity_ids
self._unique_id = unique_id
if self._template is not None:
self._template.hass = self.hass
@ -165,6 +171,11 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity):
"""Return the display name of this alarm control panel."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this alarm control panel."""
return self._unique_id
@property
def should_poll(self):
"""Return the polling state."""

View file

@ -16,6 +16,7 @@ from homeassistant.const import (
CONF_ENTITY_PICTURE_TEMPLATE,
CONF_ICON_TEMPLATE,
CONF_SENSORS,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -50,6 +51,7 @@ SENSOR_SCHEMA = vol.Schema(
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DELAY_ON): vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_DELAY_OFF): vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -73,6 +75,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
device_class = device_config.get(CONF_DEVICE_CLASS)
delay_on = device_config.get(CONF_DELAY_ON)
delay_off = device_config.get(CONF_DELAY_OFF)
unique_id = device_config.get(CONF_UNIQUE_ID)
templates = {
CONF_VALUE_TEMPLATE: value_template,
@ -104,6 +107,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
delay_on,
delay_off,
attribute_templates,
unique_id,
)
)
@ -127,6 +131,7 @@ class BinarySensorTemplate(BinarySensorEntity):
delay_on,
delay_off,
attribute_templates,
unique_id,
):
"""Initialize the Template binary sensor."""
self.hass = hass
@ -146,6 +151,7 @@ class BinarySensorTemplate(BinarySensorEntity):
self._available = True
self._attribute_templates = attribute_templates
self._attributes = {}
self._unique_id = unique_id
async def async_added_to_hass(self):
"""Register callbacks."""
@ -175,6 +181,11 @@ class BinarySensorTemplate(BinarySensorEntity):
"""Return the name of the sensor."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this binary sensor."""
return self._unique_id
@property
def icon(self):
"""Return the icon to use in the frontend, if any."""

View file

@ -26,6 +26,7 @@ from homeassistant.const import (
CONF_FRIENDLY_NAME,
CONF_ICON_TEMPLATE,
CONF_OPTIMISTIC,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -90,6 +91,7 @@ COVER_SCHEMA = vol.All(
vol.Optional(TILT_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_FRIENDLY_NAME): cv.string,
vol.Optional(CONF_ENTITY_ID): cv.entity_ids,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
),
cv.has_at_least_one_key(OPEN_ACTION, POSITION_ACTION),
@ -121,6 +123,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
tilt_action = device_config.get(TILT_ACTION)
optimistic = device_config.get(CONF_OPTIMISTIC)
tilt_optimistic = device_config.get(CONF_TILT_OPTIMISTIC)
unique_id = device_config.get(CONF_UNIQUE_ID)
templates = {
CONF_VALUE_TEMPLATE: state_template,
@ -156,6 +159,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
optimistic,
tilt_optimistic,
entity_ids,
unique_id,
)
)
@ -185,6 +189,7 @@ class CoverTemplate(CoverEntity):
optimistic,
tilt_optimistic,
entity_ids,
unique_id,
):
"""Initialize the Template cover."""
self.hass = hass
@ -222,6 +227,7 @@ class CoverTemplate(CoverEntity):
self._tilt_value = None
self._entities = entity_ids
self._available = True
self._unique_id = unique_id
async def async_added_to_hass(self):
"""Register callbacks."""
@ -251,6 +257,11 @@ class CoverTemplate(CoverEntity):
"""Return the name of the cover."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this cover."""
return self._unique_id
@property
def is_closed(self):
"""Return if the cover is closed."""

View file

@ -21,6 +21,7 @@ from homeassistant.components.fan import (
from homeassistant.const import (
CONF_ENTITY_ID,
CONF_FRIENDLY_NAME,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -72,6 +73,7 @@ FAN_SCHEMA = vol.Schema(
CONF_SPEED_LIST, default=[SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
): cv.ensure_list,
vol.Optional(CONF_ENTITY_ID): cv.entity_ids,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -100,6 +102,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
set_direction_action = device_config.get(CONF_SET_DIRECTION_ACTION)
speed_list = device_config[CONF_SPEED_LIST]
unique_id = device_config.get(CONF_UNIQUE_ID)
templates = {
CONF_VALUE_TEMPLATE: state_template,
@ -129,6 +132,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
set_direction_action,
speed_list,
entity_ids,
unique_id,
)
)
@ -155,6 +159,7 @@ class TemplateFan(FanEntity):
set_direction_action,
speed_list,
entity_ids,
unique_id,
):
"""Initialize the fan."""
self.hass = hass
@ -199,6 +204,8 @@ class TemplateFan(FanEntity):
self._supported_features |= SUPPORT_DIRECTION
self._entities = entity_ids
self._unique_id = unique_id
# List of valid speeds
self._speed_list = speed_list
@ -207,6 +214,11 @@ class TemplateFan(FanEntity):
"""Return the display name of this fan."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this fan."""
return self._unique_id
@property
def supported_features(self) -> int:
"""Flag supported features."""

View file

@ -21,6 +21,7 @@ from homeassistant.const import (
CONF_FRIENDLY_NAME,
CONF_ICON_TEMPLATE,
CONF_LIGHTS,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -70,6 +71,7 @@ LIGHT_SCHEMA = vol.Schema(
vol.Optional(CONF_COLOR_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_WHITE_VALUE_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -89,6 +91,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
icon_template = device_config.get(CONF_ICON_TEMPLATE)
entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE)
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
unique_id = device_config.get(CONF_UNIQUE_ID)
on_action = device_config[CONF_ON_ACTION]
off_action = device_config[CONF_OFF_ACTION]
@ -141,6 +144,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
color_template,
white_value_action,
white_value_template,
unique_id,
)
)
@ -170,6 +174,7 @@ class LightTemplate(LightEntity):
color_template,
white_value_action,
white_value_template,
unique_id,
):
"""Initialize the light."""
self.hass = hass
@ -209,6 +214,7 @@ class LightTemplate(LightEntity):
self._white_value = None
self._entities = entity_ids
self._available = True
self._unique_id = unique_id
@property
def brightness(self):
@ -235,6 +241,11 @@ class LightTemplate(LightEntity):
"""Return the display name of this light."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this light."""
return self._unique_id
@property
def supported_features(self):
"""Flag supported features."""

View file

@ -7,6 +7,7 @@ from homeassistant.components.lock import PLATFORM_SCHEMA, LockEntity
from homeassistant.const import (
CONF_NAME,
CONF_OPTIMISTIC,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -38,6 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
vol.Required(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_AVAILABILITY_TEMPLATE): cv.template,
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -67,6 +69,7 @@ async def async_setup_platform(hass, config, async_add_devices, discovery_info=N
config.get(CONF_LOCK),
config.get(CONF_UNLOCK),
config.get(CONF_OPTIMISTIC),
config.get(CONF_UNIQUE_ID),
)
]
)
@ -85,6 +88,7 @@ class TemplateLock(LockEntity):
command_lock,
command_unlock,
optimistic,
unique_id,
):
"""Initialize the lock."""
self._state = None
@ -97,6 +101,7 @@ class TemplateLock(LockEntity):
self._command_unlock = Script(hass, command_unlock)
self._optimistic = optimistic
self._available = True
self._unique_id = unique_id
async def async_added_to_hass(self):
"""Register callbacks."""
@ -135,6 +140,11 @@ class TemplateLock(LockEntity):
"""Return the name of the lock."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this lock."""
return self._unique_id
@property
def is_locked(self):
"""Return true if lock is locked."""

View file

@ -18,6 +18,7 @@ from homeassistant.const import (
CONF_FRIENDLY_NAME_TEMPLATE,
CONF_ICON_TEMPLATE,
CONF_SENSORS,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -49,6 +50,7 @@ SENSOR_SCHEMA = vol.Schema(
vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -71,6 +73,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT)
device_class = device_config.get(CONF_DEVICE_CLASS)
attribute_templates = device_config[CONF_ATTRIBUTE_TEMPLATES]
unique_id = device_config.get(CONF_UNIQUE_ID)
templates = {
CONF_VALUE_TEMPLATE: state_template,
@ -103,6 +106,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
entity_ids,
device_class,
attribute_templates,
unique_id,
)
)
@ -128,6 +132,7 @@ class SensorTemplate(Entity):
entity_ids,
device_class,
attribute_templates,
unique_id,
):
"""Initialize the sensor."""
self.hass = hass
@ -149,6 +154,7 @@ class SensorTemplate(Entity):
self._available = True
self._attribute_templates = attribute_templates
self._attributes = {}
self._unique_id = unique_id
async def async_added_to_hass(self):
"""Register callbacks."""
@ -178,6 +184,11 @@ class SensorTemplate(Entity):
"""Return the name of the sensor."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this sensor."""
return self._unique_id
@property
def state(self):
"""Return the state of the sensor."""

View file

@ -14,6 +14,7 @@ from homeassistant.const import (
CONF_ENTITY_PICTURE_TEMPLATE,
CONF_ICON_TEMPLATE,
CONF_SWITCHES,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -47,6 +48,7 @@ SWITCH_SCHEMA = vol.Schema(
vol.Required(OFF_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(ATTR_FRIENDLY_NAME): cv.string,
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -67,6 +69,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
on_action = device_config[ON_ACTION]
off_action = device_config[OFF_ACTION]
unique_id = device_config.get(CONF_UNIQUE_ID)
templates = {
CONF_VALUE_TEMPLATE: state_template,
@ -92,6 +95,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
on_action,
off_action,
entity_ids,
unique_id,
)
)
@ -113,6 +117,7 @@ class SwitchTemplate(SwitchEntity, RestoreEntity):
on_action,
off_action,
entity_ids,
unique_id,
):
"""Initialize the Template switch."""
self.hass = hass
@ -131,6 +136,7 @@ class SwitchTemplate(SwitchEntity, RestoreEntity):
self._entity_picture = None
self._entities = entity_ids
self._available = True
self._unique_id = unique_id
async def async_added_to_hass(self):
"""Register callbacks."""
@ -172,6 +178,11 @@ class SwitchTemplate(SwitchEntity, RestoreEntity):
"""Return the name of the switch."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this switch."""
return self._unique_id
@property
def is_on(self):
"""Return true if device is on."""

View file

@ -33,6 +33,7 @@ from homeassistant.components.vacuum import (
from homeassistant.const import (
CONF_ENTITY_ID,
CONF_FRIENDLY_NAME,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
EVENT_HOMEASSISTANT_START,
MATCH_ALL,
@ -84,6 +85,7 @@ VACUUM_SCHEMA = vol.Schema(
vol.Optional(SERVICE_SET_FAN_SPEED): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_FAN_SPEED_LIST, default=[]): cv.ensure_list,
vol.Optional(CONF_ENTITY_ID): cv.entity_ids,
vol.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@ -114,6 +116,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
set_fan_speed_action = device_config.get(SERVICE_SET_FAN_SPEED)
fan_speed_list = device_config[CONF_FAN_SPEED_LIST]
unique_id = device_config.get(CONF_UNIQUE_ID)
templates = {
CONF_VALUE_TEMPLATE: state_template,
@ -146,6 +149,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
fan_speed_list,
entity_ids,
attribute_templates,
unique_id,
)
)
@ -174,6 +178,7 @@ class TemplateVacuum(StateVacuumEntity):
fan_speed_list,
entity_ids,
attribute_templates,
unique_id,
):
"""Initialize the vacuum."""
self.hass = hass
@ -233,6 +238,8 @@ class TemplateVacuum(StateVacuumEntity):
self._supported_features |= SUPPORT_BATTERY
self._entities = entity_ids
self._unique_id = unique_id
# List of valid fan speeds
self._fan_speed_list = fan_speed_list
@ -241,6 +248,11 @@ class TemplateVacuum(StateVacuumEntity):
"""Return the display name of this vacuum."""
return self._name
@property
def unique_id(self):
"""Return the unique id of this vacuum."""
return self._unique_id
@property
def supported_features(self) -> int:
"""Flag supported features."""

View file

@ -171,6 +171,7 @@ CONF_TOKEN = "token"
CONF_TRIGGER_TIME = "trigger_time"
CONF_TTL = "ttl"
CONF_TYPE = "type"
CONF_UNIQUE_ID = "unique_id"
CONF_UNIT_OF_MEASUREMENT = "unit_of_measurement"
CONF_UNIT_SYSTEM = "unit_system"
CONF_UNTIL = "until"

View file

@ -586,3 +586,32 @@ async def test_disarm_action(hass):
await hass.async_block_till_done()
assert len(service_calls) == 1
async def test_unique_id(hass):
"""Test unique_id option only creates one alarm control panel per id."""
await setup.async_setup_component(
hass,
"alarm_control_panel",
{
"alarm_control_panel": {
"platform": "template",
"panels": {
"test_template_alarm_control_panel_01": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
},
"test_template_alarm_control_panel_02": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
},
},
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -253,6 +253,7 @@ class TestBinarySensorTemplate(unittest.TestCase):
None,
None,
None,
None,
).result()
assert not vs.should_poll
assert "motion" == vs.device_class
@ -315,6 +316,7 @@ class TestBinarySensorTemplate(unittest.TestCase):
None,
None,
None,
None,
).result()
mock_render.side_effect = TemplateError("foo")
run_callback_threadsafe(self.hass.loop, vs.async_check_state).result()
@ -640,3 +642,32 @@ async def test_no_update_template_match_all(hass, caplog):
assert hass.states.get("binary_sensor.all_icon").state == "off"
assert hass.states.get("binary_sensor.all_entity_picture").state == "off"
assert hass.states.get("binary_sensor.all_attribute").state == "off"
async def test_unique_id(hass):
"""Test unique_id option only creates one binary sensor per id."""
await setup.async_setup_component(
hass,
"binary_sensor",
{
"binary_sensor": {
"platform": "template",
"sensors": {
"test_template_cover_01": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
},
"test_template_cover_02": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
},
},
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -1034,3 +1034,48 @@ async def test_invalid_device_class(hass, calls):
state = hass.states.get("cover.test_template_cover")
assert not state
async def test_unique_id(hass):
"""Test unique_id option only creates one cover per id."""
await setup.async_setup_component(
hass,
"cover",
{
"cover": {
"platform": "template",
"covers": {
"test_template_cover_01": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
"open_cover": {
"service": "cover.open_cover",
"entity_id": "cover.test_state",
},
"close_cover": {
"service": "cover.close_cover",
"entity_id": "cover.test_state",
},
},
"test_template_cover_02": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
"open_cover": {
"service": "cover.open_cover",
"entity_id": "cover.test_state",
},
"close_cover": {
"service": "cover.close_cover",
"entity_id": "cover.test_state",
},
},
},
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -758,3 +758,48 @@ async def _register_components(hass, speed_list=None):
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
async def test_unique_id(hass):
"""Test unique_id option only creates one fan per id."""
await setup.async_setup_component(
hass,
"fan",
{
"fan": {
"platform": "template",
"fans": {
"test_template_fan_01": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
"turn_on": {
"service": "fan.turn_on",
"entity_id": "fan.test_state",
},
"turn_off": {
"service": "fan.turn_off",
"entity_id": "fan.test_state",
},
},
"test_template_fan_02": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
"turn_on": {
"service": "fan.turn_on",
"entity_id": "fan.test_state",
},
"turn_off": {
"service": "fan.turn_off",
"entity_id": "fan.test_state",
},
},
},
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -1164,3 +1164,46 @@ async def test_invalid_availability_template_keeps_component_available(hass, cap
assert hass.states.get("light.test_template_light").state != STATE_UNAVAILABLE
assert ("UndefinedError: 'x' is undefined") in caplog.text
async def test_unique_id(hass):
"""Test unique_id option only creates one light per id."""
await setup.async_setup_component(
hass,
"light",
{
"light": {
"platform": "template",
"lights": {
"test_template_light_01": {
"unique_id": "not-so-unique-anymore",
"turn_on": {
"service": "light.turn_on",
"entity_id": "light.test_state",
},
"turn_off": {
"service": "light.turn_off",
"entity_id": "light.test_state",
},
},
"test_template_light_02": {
"unique_id": "not-so-unique-anymore",
"turn_on": {
"service": "light.turn_on",
"entity_id": "light.test_state",
},
"turn_off": {
"service": "light.turn_off",
"entity_id": "light.test_state",
},
},
},
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -406,3 +406,48 @@ async def test_invalid_availability_template_keeps_component_available(hass, cap
assert hass.states.get("lock.template_lock").state != STATE_UNAVAILABLE
assert ("UndefinedError: 'x' is undefined") in caplog.text
async def test_unique_id(hass):
"""Test unique_id option only creates one lock per id."""
await setup.async_setup_component(
hass,
"lock",
{
"lock": {
"platform": "template",
"name": "test_template_lock_01",
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
"lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"},
"unlock": {
"service": "switch.turn_off",
"entity_id": "switch.test_state",
},
},
},
)
await setup.async_setup_component(
hass,
"lock",
{
"lock": {
"platform": "template",
"name": "test_template_lock_02",
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
"lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"},
"unlock": {
"service": "switch.turn_off",
"entity_id": "switch.test_state",
},
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -639,3 +639,32 @@ async def test_no_template_match_all(hass, caplog):
assert hass.states.get("sensor.invalid_entity_picture").state == "hello"
assert hass.states.get("sensor.invalid_friendly_name").state == "hello"
assert hass.states.get("sensor.invalid_attribute").state == "hello"
async def test_unique_id(hass):
"""Test unique_id option only creates one sensor per id."""
await async_setup_component(
hass,
"sensor",
{
"sensor": {
"platform": "template",
"sensors": {
"test_template_sensor_01": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
},
"test_template_sensor_02": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
},
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -650,3 +650,48 @@ async def test_invalid_availability_template_keeps_component_available(hass, cap
assert hass.states.get("switch.test_template_switch").state != STATE_UNAVAILABLE
assert ("UndefinedError: 'x' is undefined") in caplog.text
async def test_unique_id(hass):
"""Test unique_id option only creates one switch per id."""
await setup.async_setup_component(
hass,
"switch",
{
"switch": {
"platform": "template",
"switches": {
"test_template_switch_01": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
"turn_on": {
"service": "switch.turn_on",
"entity_id": "switch.test_state",
},
"turn_off": {
"service": "switch.turn_off",
"entity_id": "switch.test_state",
},
},
"test_template_switch_02": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
"turn_on": {
"service": "switch.turn_on",
"entity_id": "switch.test_state",
},
"turn_off": {
"service": "switch.turn_off",
"entity_id": "switch.test_state",
},
},
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1

View file

@ -612,3 +612,34 @@ async def _register_components(hass):
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
async def test_unique_id(hass):
"""Test unique_id option only creates one vacuum per id."""
await setup.async_setup_component(
hass,
"vacuum",
{
"vacuum": {
"platform": "template",
"vacuums": {
"test_template_vacuum_01": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
"start": {"service": "script.vacuum_start"},
},
"test_template_vacuum_02": {
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
"start": {"service": "script.vacuum_start"},
},
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1